mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-22 03:33:08 +00:00
Add support for the new Plex Movie agent (#628)
* guids collection to movie and GuidTag PlexObject * Fix flake8 * Clean up GuidTag and Guid * Add Plex Movie agent to add library doc string * Change test bootstrap server movie library to Plex Movie * Update tests for Plex Movie agent * Mix movie genre test Co-authored-by: andyloree <andy@andyloree.com>
This commit is contained in:
parent
fee5261aaf
commit
ee28bef80f
7 changed files with 60 additions and 40 deletions
|
@ -108,8 +108,8 @@ Usage Examples
|
||||||
|
|
||||||
# Example 6: List all movies directed by the same person as Elephants Dream.
|
# Example 6: List all movies directed by the same person as Elephants Dream.
|
||||||
movies = plex.library.section('Movies')
|
movies = plex.library.section('Movies')
|
||||||
die_hard = movies.get('Elephants Dream')
|
elephants_dream = movies.get('Elephants Dream')
|
||||||
director = die_hard.directors[0]
|
director = elephants_dream.directors[0]
|
||||||
for movie in movies.search(None, director=director):
|
for movie in movies.search(None, director=director):
|
||||||
print(movie.title)
|
print(movie.title)
|
||||||
|
|
||||||
|
|
|
@ -167,11 +167,12 @@ class Library(PlexObject):
|
||||||
|
|
||||||
**Movie Preferences**
|
**Movie Preferences**
|
||||||
|
|
||||||
* **agent** (str): com.plexapp.agents.none, com.plexapp.agents.imdb, com.plexapp.agents.themoviedb
|
* **agent** (str): com.plexapp.agents.none, com.plexapp.agents.imdb, tv.plex.agents.movie,
|
||||||
|
com.plexapp.agents.themoviedb
|
||||||
* **enableBIFGeneration** (bool): Enable video preview thumbnails. Default value true.
|
* **enableBIFGeneration** (bool): Enable video preview thumbnails. Default value true.
|
||||||
* **enableCinemaTrailers** (bool): Enable Cinema Trailers. Default value true.
|
* **enableCinemaTrailers** (bool): Enable Cinema Trailers. Default value true.
|
||||||
* **includeInGlobal** (bool): Include in dashboard. Default value true.
|
* **includeInGlobal** (bool): Include in dashboard. Default value true.
|
||||||
* **scanner** (str): Plex Movie Scanner, Plex Video Files Scanner
|
* **scanner** (str): Plex Movie, Plex Movie Scanner, Plex Video Files Scanner, Plex Video Files
|
||||||
|
|
||||||
**IMDB Movie Options** (com.plexapp.agents.imdb)
|
**IMDB Movie Options** (com.plexapp.agents.imdb)
|
||||||
|
|
||||||
|
|
|
@ -681,6 +681,19 @@ class MediaTag(PlexObject):
|
||||||
return self.fetchItems(self.key)
|
return self.fetchItems(self.key)
|
||||||
|
|
||||||
|
|
||||||
|
class GuidTag(PlexObject):
|
||||||
|
""" Base class for guid tags used only for Guids, as they contain only a string identifier
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id (id): The guid for external metadata sources (e.g. IMDB, TMDB, TVDB).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _loadData(self, data):
|
||||||
|
""" Load attribute values from Plex XML response. """
|
||||||
|
self._data = data
|
||||||
|
self.id = data.attrib.get('id')
|
||||||
|
|
||||||
|
|
||||||
@utils.registerPlexObject
|
@utils.registerPlexObject
|
||||||
class Collection(MediaTag):
|
class Collection(MediaTag):
|
||||||
""" Represents a single Collection media tag.
|
""" Represents a single Collection media tag.
|
||||||
|
@ -760,6 +773,16 @@ class Genre(MediaTag):
|
||||||
FILTER = 'genre'
|
FILTER = 'genre'
|
||||||
|
|
||||||
|
|
||||||
|
@utils.registerPlexObject
|
||||||
|
class Guid(GuidTag):
|
||||||
|
""" Represents a single Guid media tag.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
TAG (str): 'Guid'
|
||||||
|
"""
|
||||||
|
TAG = "Guid"
|
||||||
|
|
||||||
|
|
||||||
@utils.registerPlexObject
|
@utils.registerPlexObject
|
||||||
class Mood(MediaTag):
|
class Mood(MediaTag):
|
||||||
""" Represents a single Mood media tag.
|
""" Represents a single Mood media tag.
|
||||||
|
|
|
@ -275,6 +275,7 @@ class Movie(Playable, Video):
|
||||||
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.
|
||||||
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.
|
||||||
labels (List<:class:`~plexapi.media.Label`>): List of label objects.
|
labels (List<:class:`~plexapi.media.Label`>): List of label objects.
|
||||||
media (List<:class:`~plexapi.media.Media`>): List of media objects.
|
media (List<:class:`~plexapi.media.Media`>): List of media objects.
|
||||||
originallyAvailableAt (datetime): Datetime the movie was released.
|
originallyAvailableAt (datetime): Datetime the movie was released.
|
||||||
|
@ -310,6 +311,7 @@ class Movie(Playable, Video):
|
||||||
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.genres = self.findItems(data, media.Genre)
|
self.genres = self.findItems(data, media.Genre)
|
||||||
|
self.guids = self.findItems(data, media.Guid)
|
||||||
self.labels = self.findItems(data, media.Label)
|
self.labels = self.findItems(data, media.Label)
|
||||||
self.media = self.findItems(data, media.Media)
|
self.media = self.findItems(data, media.Media)
|
||||||
self.originallyAvailableAt = utils.toDatetime(data.attrib.get('originallyAvailableAt'), '%Y-%m-%d')
|
self.originallyAvailableAt = utils.toDatetime(data.attrib.get('originallyAvailableAt'), '%Y-%m-%d')
|
||||||
|
|
|
@ -22,7 +22,7 @@ def test_library_Library_sectionByID_is_equal_section(plex, movies):
|
||||||
|
|
||||||
|
|
||||||
def test_library_sectionByID_with_attrs(plex, movies):
|
def test_library_sectionByID_with_attrs(plex, movies):
|
||||||
assert movies.agent == "com.plexapp.agents.imdb"
|
assert movies.agent == "tv.plex.agents.movie"
|
||||||
# This seems to fail for some reason.
|
# This seems to fail for some reason.
|
||||||
# my account alloew of sync, didnt find any about settings about the library.
|
# my account alloew of sync, didnt find any about settings about the library.
|
||||||
# assert movies.allowSync is ('sync' in plex.ownerFeatures)
|
# assert movies.allowSync is ('sync' in plex.ownerFeatures)
|
||||||
|
@ -34,11 +34,11 @@ def test_library_sectionByID_with_attrs(plex, movies):
|
||||||
assert movies.filters == "1"
|
assert movies.filters == "1"
|
||||||
assert movies._initpath == "/library/sections"
|
assert movies._initpath == "/library/sections"
|
||||||
assert utils.is_int(movies.key)
|
assert utils.is_int(movies.key)
|
||||||
assert movies.language == "en"
|
assert movies.language == "en-US"
|
||||||
assert len(movies.locations) == 1
|
assert len(movies.locations) == 1
|
||||||
assert len(movies.locations[0]) >= 10
|
assert len(movies.locations[0]) >= 10
|
||||||
assert movies.refreshing is False
|
assert movies.refreshing is False
|
||||||
assert movies.scanner == "Plex Movie Scanner"
|
assert movies.scanner == "Plex Movie"
|
||||||
assert movies._server._baseurl == utils.SERVER_BASEURL
|
assert movies._server._baseurl == utils.SERVER_BASEURL
|
||||||
assert movies.thumb == "/:/resources/movie.png"
|
assert movies.thumb == "/:/resources/movie.png"
|
||||||
assert movies.title == "Movies"
|
assert movies.title == "Movies"
|
||||||
|
@ -152,8 +152,8 @@ def test_library_MovieSection_refresh(movies, patched_http_call):
|
||||||
|
|
||||||
|
|
||||||
def test_library_MovieSection_search_genre(movie, movies):
|
def test_library_MovieSection_search_genre(movie, movies):
|
||||||
animation = [i for i in movie.genres if i.tag == "Animation"]
|
genre = movie.genres[0]
|
||||||
assert len(movies.search(genre=animation[0])) > 1
|
assert len(movies.search(genre=genre)) >= 1
|
||||||
|
|
||||||
|
|
||||||
def test_library_MovieSection_cancelUpdate(movies):
|
def test_library_MovieSection_cancelUpdate(movies):
|
||||||
|
@ -255,7 +255,7 @@ def test_library_editAdvanced_default(movies):
|
||||||
movies.reload()
|
movies.reload()
|
||||||
movies.defaultAdvanced()
|
movies.defaultAdvanced()
|
||||||
for setting in movies.settings():
|
for setting in movies.settings():
|
||||||
assert int(setting.value) == int(setting.default)
|
assert str(setting.value) == str(setting.default)
|
||||||
|
|
||||||
|
|
||||||
def test_library_Collection_modeUpdate(collection):
|
def test_library_Collection_modeUpdate(collection):
|
||||||
|
|
|
@ -84,7 +84,7 @@ def test_video_Movie_isFullObject_and_reload(plex):
|
||||||
movie_via_section_search.reload()
|
movie_via_section_search.reload()
|
||||||
assert movie_via_section_search.isFullObject() is True
|
assert movie_via_section_search.isFullObject() is True
|
||||||
# If the verify that the object has been reloaded. xml from search only returns 3 actors.
|
# If the verify that the object has been reloaded. xml from search only returns 3 actors.
|
||||||
assert len(movie_via_section_search.roles) > 3
|
assert len(movie_via_section_search.roles) >= 3
|
||||||
|
|
||||||
|
|
||||||
def test_video_Movie_isPartialObject(movie):
|
def test_video_Movie_isPartialObject(movie):
|
||||||
|
@ -155,27 +155,30 @@ def test_video_Movie_attrs(movies):
|
||||||
assert utils.is_datetime(movie.addedAt)
|
assert utils.is_datetime(movie.addedAt)
|
||||||
assert utils.is_metadata(movie.art)
|
assert utils.is_metadata(movie.art)
|
||||||
assert movie.artUrl
|
assert movie.artUrl
|
||||||
assert movie.audienceRating == 8.5
|
assert float(movie.rating) >= 6.4
|
||||||
# Disabled this since it failed on the last run, wasnt in the original xml result.
|
assert movie.ratingImage == 'rottentomatoes://image.rating.ripe'
|
||||||
# assert movie.audienceRatingImage == 'rottentomatoes://image.rating.upright'
|
assert movie.audienceRating >= 8.5
|
||||||
|
assert movie.audienceRatingImage == 'rottentomatoes://image.rating.upright'
|
||||||
movie.reload() # RELOAD
|
movie.reload() # RELOAD
|
||||||
assert movie.chapterSource is None
|
assert movie.chapterSource is None
|
||||||
assert movie.collections == []
|
assert not movie.collections
|
||||||
assert movie.contentRating in utils.CONTENTRATINGS
|
assert movie.contentRating in utils.CONTENTRATINGS
|
||||||
assert all([i.tag in ["US", "USA"] for i in movie.countries])
|
if movie.countries:
|
||||||
assert [i.tag for i in movie.directors] == ["Nina Paley"]
|
assert "United States of America" in [i.tag for i in movie.countries]
|
||||||
|
if movie.producers:
|
||||||
|
assert "Nina Paley" in [i.tag for i in movie.producers]
|
||||||
|
if movie.directors:
|
||||||
|
assert "Nina Paley" in [i.tag for i in movie.directors]
|
||||||
|
if movie.roles:
|
||||||
|
assert "Reena Shah" in [i.tag for i in movie.roles]
|
||||||
|
if movie.writers:
|
||||||
|
assert "Nina Paley" in [i.tag for i in movie.writers]
|
||||||
assert movie.duration >= 160000
|
assert movie.duration >= 160000
|
||||||
assert movie.fields == []
|
assert not movie.fields
|
||||||
assert movie.posters()
|
assert movie.posters()
|
||||||
assert sorted([i.tag for i in movie.genres]) == [
|
assert "Animation" in [i.tag for i in movie.genres]
|
||||||
"Animation",
|
assert "imdb://tt1172203" in [i.id for i in movie.guids]
|
||||||
"Comedy",
|
assert movie.guid == "plex://movie/5d776846880197001ec967c6"
|
||||||
"Drama",
|
|
||||||
"Fantasy",
|
|
||||||
"Musical",
|
|
||||||
"Romance",
|
|
||||||
]
|
|
||||||
assert movie.guid == "com.plexapp.agents.imdb://tt1172203?lang=en"
|
|
||||||
assert utils.is_metadata(movie._initpath)
|
assert utils.is_metadata(movie._initpath)
|
||||||
assert utils.is_metadata(movie.key)
|
assert utils.is_metadata(movie.key)
|
||||||
assert utils.is_datetime(movie.lastViewedAt)
|
assert utils.is_datetime(movie.lastViewedAt)
|
||||||
|
@ -186,16 +189,7 @@ def test_video_Movie_attrs(movies):
|
||||||
assert movie.playlistItemID is None
|
assert movie.playlistItemID is None
|
||||||
if movie.primaryExtraKey:
|
if movie.primaryExtraKey:
|
||||||
assert utils.is_metadata(movie.primaryExtraKey)
|
assert utils.is_metadata(movie.primaryExtraKey)
|
||||||
assert [i.tag for i in movie.producers] == []
|
|
||||||
assert float(movie.rating) >= 6.4
|
|
||||||
# assert movie.ratingImage == 'rottentomatoes://image.rating.ripe'
|
|
||||||
assert movie.ratingKey >= 1
|
assert movie.ratingKey >= 1
|
||||||
assert set(sorted([i.tag for i in movie.roles])) >= {
|
|
||||||
"Aladdin Ullah",
|
|
||||||
"Annette Hanshaw",
|
|
||||||
"Aseem Chhabra",
|
|
||||||
"Debargo Sanyal",
|
|
||||||
} # noqa
|
|
||||||
assert movie._server._baseurl == utils.SERVER_BASEURL
|
assert movie._server._baseurl == utils.SERVER_BASEURL
|
||||||
assert movie.sessionKey is None
|
assert movie.sessionKey is None
|
||||||
assert movie.studio == "Nina Paley"
|
assert movie.studio == "Nina Paley"
|
||||||
|
@ -211,7 +205,6 @@ def test_video_Movie_attrs(movies):
|
||||||
assert movie.viewCount == 0
|
assert movie.viewCount == 0
|
||||||
assert utils.is_int(movie.viewOffset, gte=0)
|
assert utils.is_int(movie.viewOffset, gte=0)
|
||||||
assert movie.viewedAt is None
|
assert movie.viewedAt is None
|
||||||
assert sorted([i.tag for i in movie.writers][:4]) == ["Nina Paley"] # noqa
|
|
||||||
assert movie.year == 2008
|
assert movie.year == 2008
|
||||||
# Audio
|
# Audio
|
||||||
audio = movie.media[0].parts[0].audioStreams()[0]
|
audio = movie.media[0].parts[0].audioStreams()[0]
|
||||||
|
@ -329,7 +322,7 @@ def test_video_Movie_attrs(movies):
|
||||||
assert part.container in utils.CONTAINERS
|
assert part.container in utils.CONTAINERS
|
||||||
assert part.decision is None
|
assert part.decision is None
|
||||||
assert part.deepAnalysisVersion is None or utils.is_int(part.deepAnalysisVersion)
|
assert part.deepAnalysisVersion is None or utils.is_int(part.deepAnalysisVersion)
|
||||||
assert utils.is_int(part.duration, 160000)
|
assert utils.is_int(part.duration, gte=160000)
|
||||||
assert part.exists
|
assert part.exists
|
||||||
assert len(part.file) >= 10
|
assert len(part.file) >= 10
|
||||||
assert part.has64bitOffsets is False
|
assert part.has64bitOffsets is False
|
||||||
|
|
|
@ -520,8 +520,9 @@ if __name__ == "__main__":
|
||||||
name="Movies",
|
name="Movies",
|
||||||
type="movie",
|
type="movie",
|
||||||
location="/data/Movies" if opts.no_docker is False else movies_path,
|
location="/data/Movies" if opts.no_docker is False else movies_path,
|
||||||
agent="com.plexapp.agents.imdb",
|
agent="tv.plex.agents.movie",
|
||||||
scanner="Plex Movie Scanner",
|
scanner="Plex Movie",
|
||||||
|
language='en-US',
|
||||||
expected_media_count=num_movies,
|
expected_media_count=num_movies,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue