Add support for credit markers (#1094)

* Add credit markers

* Disable credit detection in bootstrap test server

* Add `first` property for credits markers

* Add credits detection setting attribute

* Update tests for credits detection setting
This commit is contained in:
JonnyWong16 2023-03-09 13:32:17 -08:00 committed by GitHub
parent 6350e85ff9
commit 1e220eb311
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 36 additions and 2 deletions

View file

@ -1037,9 +1037,11 @@ class Marker(PlexObject):
Attributes: Attributes:
TAG (str): 'Marker' TAG (str): 'Marker'
end (int): The end time of the marker in milliseconds. end (int): The end time of the marker in milliseconds.
final (bool): True if the marker is the final credits marker.
id (int): The ID of the marker. id (int): The ID of the marker.
type (str): The type of marker. type (str): The type of marker.
start (int): The start time of the marker in milliseconds. start (int): The start time of the marker in milliseconds.
version (int): The Plex marker version.
""" """
TAG = 'Marker' TAG = 'Marker'
@ -1053,10 +1055,25 @@ class Marker(PlexObject):
def _loadData(self, data): def _loadData(self, data):
self._data = data self._data = data
self.end = utils.cast(int, data.attrib.get('endTimeOffset')) self.end = utils.cast(int, data.attrib.get('endTimeOffset'))
self.final = utils.cast(bool, data.attrib.get('final'))
self.id = utils.cast(int, data.attrib.get('id')) self.id = utils.cast(int, data.attrib.get('id'))
self.type = data.attrib.get('type') self.type = data.attrib.get('type')
self.start = utils.cast(int, data.attrib.get('startTimeOffset')) self.start = utils.cast(int, data.attrib.get('startTimeOffset'))
attributes = data.find('Attributes')
self.version = attributes.attrib.get('version')
@property
def first(self):
""" Returns True if the marker in the first credits marker. """
if self.type != 'credits':
return None
first = min(
(marker for marker in self._parent().markers if marker.type == 'credits'),
key=lambda m: m.start
)
return first == self
@utils.registerPlexObject @utils.registerPlexObject
class Field(PlexObject): class Field(PlexObject):

View file

@ -311,6 +311,7 @@ class Movie(
directors (List<:class:`~plexapi.media.Director`>): List of director objects. directors (List<:class:`~plexapi.media.Director`>): List of director objects.
duration (int): Duration of the movie in milliseconds. duration (int): Duration of the movie in milliseconds.
editionTitle (str): The edition title of the movie (e.g. Director's Cut, Extended Edition, etc.). editionTitle (str): The edition title of the movie (e.g. Director's Cut, Extended Edition, etc.).
enableCreditsMarkerGeneration (int): Setting that indicates if credits markers detection is enabled.
genres (List<:class:`~plexapi.media.Genre`>): List of genre objects. genres (List<:class:`~plexapi.media.Genre`>): List of genre objects.
guids (List<:class:`~plexapi.media.Guid`>): List of guid objects. guids (List<:class:`~plexapi.media.Guid`>): List of guid objects.
labels (List<:class:`~plexapi.media.Label`>): List of label objects. labels (List<:class:`~plexapi.media.Label`>): List of label objects.
@ -353,6 +354,7 @@ class Movie(
self.directors = self.findItems(data, media.Director) self.directors = self.findItems(data, media.Director)
self.duration = utils.cast(int, data.attrib.get('duration')) self.duration = utils.cast(int, data.attrib.get('duration'))
self.editionTitle = data.attrib.get('editionTitle') self.editionTitle = data.attrib.get('editionTitle')
self.enableCreditsMarkerGeneration = utils.cast(int, data.attrib.get('enableCreditsMarkerGeneration', '-1'))
self.genres = self.findItems(data, media.Genre) self.genres = self.findItems(data, media.Genre)
self.guids = self.findItems(data, media.Guid) self.guids = self.findItems(data, media.Guid)
self.labels = self.findItems(data, media.Label) self.labels = self.findItems(data, media.Label)
@ -390,6 +392,11 @@ class Movie(
""" """
return [part.file for part in self.iterParts() if part] return [part.file for part in self.iterParts() if part]
@property
def hasCreditsMarker(self):
""" Returns True if the movie has a credits marker. """
return any(marker.type == 'credits' for marker in self.markers)
@property @property
def hasPreviewThumbnails(self): def hasPreviewThumbnails(self):
""" Returns True if any of the media parts has generated preview (BIF) thumbnails. """ """ Returns True if any of the media parts has generated preview (BIF) thumbnails. """
@ -444,6 +451,7 @@ class Show(
collections (List<:class:`~plexapi.media.Collection`>): List of collection objects. collections (List<:class:`~plexapi.media.Collection`>): List of collection objects.
contentRating (str) Content rating (PG-13; NR; TV-G). contentRating (str) Content rating (PG-13; NR; TV-G).
duration (int): Typical duration of the show episodes in milliseconds. duration (int): Typical duration of the show episodes in milliseconds.
enableCreditsMarkerGeneration (int): Setting that indicates if credits markers detection is enabled.
episodeSort (int): Setting that indicates how episodes are sorted for the show episodeSort (int): Setting that indicates how episodes are sorted for the show
(-1 = Library default, 0 = Oldest first, 1 = Newest first). (-1 = Library default, 0 = Oldest first, 1 = Newest first).
flattenSeasons (int): Setting that indicates if seasons are set to hidden for the show flattenSeasons (int): Setting that indicates if seasons are set to hidden for the show
@ -493,6 +501,7 @@ class Show(
self.collections = self.findItems(data, media.Collection) self.collections = self.findItems(data, media.Collection)
self.contentRating = data.attrib.get('contentRating') self.contentRating = data.attrib.get('contentRating')
self.duration = utils.cast(int, data.attrib.get('duration')) self.duration = utils.cast(int, data.attrib.get('duration'))
self.enableCreditsMarkerGeneration = utils.cast(int, data.attrib.get('enableCreditsMarkerGeneration', '-1'))
self.episodeSort = utils.cast(int, data.attrib.get('episodeSort', '-1')) self.episodeSort = utils.cast(int, data.attrib.get('episodeSort', '-1'))
self.flattenSeasons = utils.cast(int, data.attrib.get('flattenSeasons', '-1')) self.flattenSeasons = utils.cast(int, data.attrib.get('flattenSeasons', '-1'))
self.genres = self.findItems(data, media.Genre) self.genres = self.findItems(data, media.Genre)
@ -920,14 +929,19 @@ class Episode(
@property @property
def hasCommercialMarker(self): def hasCommercialMarker(self):
""" Returns True if the episode has a commercial marker in the xml. """ """ Returns True if the episode has a commercial marker. """
return any(marker.type == 'commercial' for marker in self.markers) return any(marker.type == 'commercial' for marker in self.markers)
@property @property
def hasIntroMarker(self): def hasIntroMarker(self):
""" Returns True if the episode has an intro marker in the xml. """ """ Returns True if the episode has an intro marker. """
return any(marker.type == 'intro' for marker in self.markers) return any(marker.type == 'intro' for marker in self.markers)
@property
def hasCreditsMarker(self):
""" Returns True if the episode has a credits marker. """
return any(marker.type == 'credits' for marker in self.markers)
@property @property
def hasPreviewThumbnails(self): def hasPreviewThumbnails(self):
""" Returns True if any of the media parts has generated preview (BIF) thumbnails. """ """ Returns True if any of the media parts has generated preview (BIF) thumbnails. """

View file

@ -59,6 +59,7 @@ def test_video_Movie_attrs(movies):
assert not movie.collections assert not movie.collections
assert movie.contentRating in utils.CONTENTRATINGS assert movie.contentRating in utils.CONTENTRATINGS
assert movie.editionTitle is None assert movie.editionTitle is None
assert movie.enableCreditsMarkerGeneration == -1
if movie.countries: if movie.countries:
assert "United States of America" in [i.tag for i in movie.countries] assert "United States of America" in [i.tag for i in movie.countries]
if movie.producers: if movie.producers:
@ -724,6 +725,7 @@ def test_video_Show_attrs(show):
assert show.audienceRatingImage == "themoviedb://image.rating" assert show.audienceRatingImage == "themoviedb://image.rating"
assert show.autoDeletionItemPolicyUnwatchedLibrary == 0 assert show.autoDeletionItemPolicyUnwatchedLibrary == 0
assert show.autoDeletionItemPolicyWatchedLibrary == 0 assert show.autoDeletionItemPolicyWatchedLibrary == 0
assert show.enableCreditsMarkerGeneration == -1
assert show.episodeSort == -1 assert show.episodeSort == -1
assert show.flattenSeasons == -1 assert show.flattenSeasons == -1
assert "Drama" in [i.tag for i in show.genres] assert "Drama" in [i.tag for i in show.genres]

View file

@ -502,6 +502,7 @@ if __name__ == "__main__":
# These tasks won't work on the test server since we are using fake media files # These tasks won't work on the test server since we are using fake media files
if not opts.unclaimed and account and account.subscriptionActive: if not opts.unclaimed and account and account.subscriptionActive:
server.settings.get("GenerateIntroMarkerBehavior").set("never") server.settings.get("GenerateIntroMarkerBehavior").set("never")
server.settings.get("GenerateCreditsMarkerBehavior").set("never")
server.settings.get("GenerateBIFBehavior").set("never") server.settings.get("GenerateBIFBehavior").set("never")
server.settings.get("GenerateChapterThumbBehavior").set("never") server.settings.get("GenerateChapterThumbBehavior").set("never")
server.settings.get("LoudnessAnalysisBehavior").set("never") server.settings.get("LoudnessAnalysisBehavior").set("never")