Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions plexapi/mixins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
TrackArtistMixin, TrackDiscNumberMixin, TrackNumberMixin,
UserRatingMixin, WriterMixin
)
from .editions import EditionsMixin
from .objects import ExtrasMixin, HubsMixin
from .played_unplayed import PlayedUnplayedMixin
from .rating import RatingMixin
Expand Down Expand Up @@ -43,7 +44,7 @@ class MovieEditMixins(

class ShowEditMixins(
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
AddedAtMixin, AudienceRatingMixin, ContentRatingMixin, CriticRatingMixin,
AddedAtMixin, AudienceRatingMixin, ContentRatingMixin, CriticRatingMixin, EditionTitleMixin,
OriginallyAvailableMixin, OriginalTitleMixin, SortTitleMixin, StudioMixin,
SummaryMixin, TaglineMixin, TitleMixin, UserRatingMixin,
CollectionMixin, GenreMixin, LabelMixin,
Expand Down Expand Up @@ -128,7 +129,7 @@ class PlaylistEditMixins(


class MovieMixins(
AdvancedSettingsMixin, SplitMergeMixin, UnmatchMatchMixin, ExtrasMixin, HubsMixin, RatingMixin,
AdvancedSettingsMixin, SplitMergeMixin, UnmatchMatchMixin, ExtrasMixin, HubsMixin, RatingMixin, EditionsMixin,
ArtMixin, LogoMixin, PosterMixin, SquareArtMixin, ThemeMixin,
MovieEditMixins,
WatchlistMixin
Expand All @@ -137,7 +138,7 @@ class MovieMixins(


class ShowMixins(
AdvancedSettingsMixin, SplitMergeMixin, UnmatchMatchMixin, ExtrasMixin, HubsMixin, RatingMixin,
AdvancedSettingsMixin, SplitMergeMixin, UnmatchMatchMixin, ExtrasMixin, HubsMixin, RatingMixin, EditionsMixin,
ArtMixin, LogoMixin, PosterMixin, SquareArtMixin, ThemeMixin,
ShowEditMixins,
WatchlistMixin
Expand All @@ -146,15 +147,15 @@ class ShowMixins(


class SeasonMixins(
AdvancedSettingsMixin, ExtrasMixin, RatingMixin,
AdvancedSettingsMixin, ExtrasMixin, RatingMixin, EditionsMixin,
ArtMixin, LogoMixin, PosterMixin, SquareArtMixin, ThemeUrlMixin,
SeasonEditMixins
):
pass


class EpisodeMixins(
ExtrasMixin, RatingMixin,
ExtrasMixin, RatingMixin, EditionsMixin,
ArtMixin, LogoMixin, PosterMixin, SquareArtMixin, ThemeUrlMixin,
EpisodeEditMixins
):
Expand Down
12 changes: 12 additions & 0 deletions plexapi/mixins/editions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class EditionsMixin:
""" Mixin for Plex objects that can have edition. """

def editions(self):
""" Returns a list of :class:`~plexapi.base.PlexPartialObject` objects
for other editions of the same media.
"""
filters = {
'guid': self.guid,
'id!': self.ratingKey
}
return self.section().search(libtype=self.TYPE, filters=filters)
16 changes: 6 additions & 10 deletions plexapi/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,16 +512,6 @@ def reviews(self):
key = f'{self.key}?includeReviews=1'
return self.fetchItems(key, cls=media.Review, rtag='Video')

def editions(self):
""" Returns a list of :class:`~plexapi.video.Movie` objects
for other editions of the same movie.
"""
filters = {
'guid': self.guid,
'id!': self.ratingKey
}
return self.section().search(filters=filters)

def removeFromContinueWatching(self):
""" Remove the movie from continue watching. """
key = '/actions/removeFromContinueWatching'
Expand Down Expand Up @@ -560,6 +550,7 @@ class Show(
commonSenseMedia (:class:`~plexapi.media.CommonSenseMedia`): Common Sense Media object.
contentRating (str) Content rating (PG-13; NR; TV-G).
duration (int): Typical duration of the show episodes in milliseconds.
editionTitle (str): The edition title of the show (e.g. Director's Cut, Extended Edition, etc.).
enableCreditsMarkerGeneration (int): Setting that indicates if credits markers detection is enabled.
(-1 = Library default, 0 = Disabled).
episodeSort (int): Setting that indicates how episodes are sorted for the show
Expand Down Expand Up @@ -616,6 +607,7 @@ def _loadData(self, data):
self.childCount = utils.cast(int, data.attrib.get('childCount'))
self.contentRating = data.attrib.get('contentRating')
self.duration = utils.cast(int, data.attrib.get('duration'))
self.editionTitle = data.attrib.get('editionTitle')
self.enableCreditsMarkerGeneration = utils.cast(int, data.attrib.get('enableCreditsMarkerGeneration', '-1'))
self.episodeSort = utils.cast(int, data.attrib.get('episodeSort', '-1'))
self.flattenSeasons = utils.cast(int, data.attrib.get('flattenSeasons', '-1'))
Expand Down Expand Up @@ -796,6 +788,7 @@ class Season(
audienceRating (float): Audience rating.
audioLanguage (str): Setting that indicates the preferred audio language.
collections (List<:class:`~plexapi.media.Collection`>): List of collection objects.
editionTitle (str): The edition title of the show (e.g. Director's Cut, Extended Edition, etc.).
guids (List<:class:`~plexapi.media.Guid`>): List of guid objects.
index (int): Season number.
key (str): API URL (/library/metadata/<ratingkey>).
Expand Down Expand Up @@ -828,6 +821,7 @@ def _loadData(self, data):
Video._loadData(self, data)
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating'))
self.audioLanguage = data.attrib.get('audioLanguage', '')
self.editionTitle = data.attrib.get('editionTitle')
self.index = utils.cast(int, data.attrib.get('index'))
self.key = self.key.replace('/children', '') # FIX_BUG_50
self.leafCount = utils.cast(int, data.attrib.get('leafCount'))
Expand Down Expand Up @@ -981,6 +975,7 @@ class Episode(
contentRating (str) Content rating (PG-13; NR; TV-G).
directors (List<:class:`~plexapi.media.Director`>): List of director objects.
duration (int): Duration of the episode in milliseconds.
editionTitle (str): The edition title of the show (e.g. Director's Cut, Extended Edition, etc.).
grandparentArt (str): URL to show artwork (/library/metadata/<grandparentRatingKey>/art/<artid>).
grandparentGuid (str): Plex GUID for the show (plex://show/5d9c086fe9d5a1001f4d9fe6).
grandparentKey (str): API URL of the show (/library/metadata/<grandparentRatingKey>).
Expand Down Expand Up @@ -1027,6 +1022,7 @@ def _loadData(self, data):
self.chapterSource = data.attrib.get('chapterSource')
self.contentRating = data.attrib.get('contentRating')
self.duration = utils.cast(int, data.attrib.get('duration'))
self.editionTitle = data.attrib.get('editionTitle')
self.grandparentArt = data.attrib.get('grandparentArt')
self.grandparentGuid = data.attrib.get('grandparentGuid')
self.grandparentKey = data.attrib.get('grandparentKey')
Expand Down
28 changes: 28 additions & 0 deletions tests/test_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,7 @@ def test_video_Show_attrs(show):
assert show.audioLanguage == ''
assert show.autoDeletionItemPolicyUnwatchedLibrary == 0
assert show.autoDeletionItemPolicyWatchedLibrary == 0
assert show.editionTitle is None
assert show.enableCreditsMarkerGeneration == -1
assert show.episodeSort == -1
assert show.flattenSeasons == -1
Expand Down Expand Up @@ -887,6 +888,10 @@ def test_video_Show_attrs(show):
assert show.url(None) is None


def test_video_Show_editions(show):
assert len(show.editions()) == 0


def test_video_Show_episode(show):
episode = show.episode("Winter Is Coming")
assert episode == show.episode(season=1, episode=1)
Expand Down Expand Up @@ -1303,6 +1308,29 @@ def test_video_Season_mixins_fields(show):
test_mixins.edit_user_rating(season)


@pytest.mark.anonymously
def test_video_Show_mixins_fields_edition(show):
with pytest.raises(BadRequest):
test_mixins.edit_edition_title(show)


@pytest.mark.authenticated
def test_video_Show_mixins_fields_edition_authenticated(account_plexpass, show):
test_mixins.edit_edition_title(show)

# Manually edit edition title to check season and episode
editionTitle = "Test Edition"
show.editEditionTitle(editionTitle).reload()
assert show.editionTitle == editionTitle
season = show.season(1)
assert season.editionTitle == editionTitle
episode = season.episode(1)
assert episode.editionTitle == editionTitle

show.editEditionTitle("", locked=False).reload()
assert show.editionTitle is None


def test_video_Season_mixins_tags(show):
season = show.season(season=1)
test_mixins.edit_collection(season)
Expand Down