mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-10 14:14:19 +00:00
Merge branch 'generic-tests'
This commit is contained in:
commit
decdc296b6
20 changed files with 1393 additions and 1315 deletions
|
@ -14,7 +14,7 @@ CONFIG = PlexConfig(CONFIG_PATH)
|
|||
|
||||
# PlexAPI Settings
|
||||
PROJECT = 'PlexAPI'
|
||||
VERSION = '2.9.0'
|
||||
VERSION = '3.0.0'
|
||||
TIMEOUT = CONFIG.get('plexapi.timeout', 30, int)
|
||||
X_PLEX_CONTAINER_SIZE = CONFIG.get('plexapi.container_size', 50, int)
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ class PlexClient(PlexObject):
|
|||
baseurl (str): HTTP URL to connect dirrectly to this client.
|
||||
token (str): X-Plex-Token used for authenication (optional).
|
||||
session (:class:`~requests.Session`): requests.Session object if you want more control (optional).
|
||||
timeout (int): timeout in seconds on initial connect to client (default config.TIMEOUT).
|
||||
|
||||
Attributes:
|
||||
TAG (str): 'Player'
|
||||
|
@ -54,7 +55,7 @@ class PlexClient(PlexObject):
|
|||
TAG = 'Player'
|
||||
key = '/resources'
|
||||
|
||||
def __init__(self, server=None, data=None, initpath=None, baseurl=None, token=None, session=None):
|
||||
def __init__(self, server=None, data=None, initpath=None, baseurl=None, token=None, session=None, timeout=None):
|
||||
super(PlexClient, self).__init__(server, data, initpath)
|
||||
self._baseurl = baseurl.strip('/') if baseurl else None
|
||||
self._token = logfilter.add_secret(token)
|
||||
|
@ -66,9 +67,9 @@ class PlexClient(PlexObject):
|
|||
self._baseurl = CONFIG.get('auth.client_baseurl', 'http://localhost:32433')
|
||||
self._token = logfilter.add_secret(CONFIG.get('auth.client_token'))
|
||||
if self._baseurl and self._token:
|
||||
self.connect()
|
||||
self.connect(timeout=timeout)
|
||||
|
||||
def connect(self):
|
||||
def connect(self, timeout=None):
|
||||
""" Alias of reload as any subsequent requests to this client will be
|
||||
made directly to the device even if the object attributes were initially
|
||||
populated from a PlexServer.
|
||||
|
@ -76,7 +77,7 @@ class PlexClient(PlexObject):
|
|||
if not self.key:
|
||||
raise Unsupported('Cannot reload an object not built from a URL.')
|
||||
self._initpath = self.key
|
||||
data = self.query(self.key)
|
||||
data = self.query(self.key, timeout=timeout)
|
||||
self._loadData(data[0])
|
||||
return self
|
||||
|
||||
|
@ -125,16 +126,17 @@ class PlexClient(PlexObject):
|
|||
raise Unsupported('Cannot use client proxy with unknown server.')
|
||||
self._proxyThroughServer = value
|
||||
|
||||
def query(self, path, method=None, headers=None, **kwargs):
|
||||
def query(self, path, method=None, headers=None, timeout=None, **kwargs):
|
||||
""" Main method used to handle HTTPS requests to the Plex client. This method helps
|
||||
by encoding the response to utf-8 and parsing the returned XML into and
|
||||
ElementTree object. Returns None if no data exists in the response.
|
||||
"""
|
||||
url = self.url(path)
|
||||
method = method or self._session.get
|
||||
timeout = timeout or TIMEOUT
|
||||
log.debug('%s %s', method.__name__.upper(), url)
|
||||
headers = self._headers(**headers or {})
|
||||
response = method(url, headers=headers, timeout=TIMEOUT, **kwargs)
|
||||
response = method(url, headers=headers, timeout=timeout, **kwargs)
|
||||
if response.status_code not in (200, 201):
|
||||
codename = codes.get(response.status_code)[0]
|
||||
log.warn('BadRequest (%s) %s %s' % (response.status_code, codename, response.url))
|
||||
|
|
|
@ -155,128 +155,139 @@ class Library(PlexObject):
|
|||
language (str): Two letter language fx en
|
||||
kwargs (dict): Advanced options should be passed as a dict. where the id is the key.
|
||||
|
||||
== Prefs for photo ==
|
||||
* agent (str): com.plexapp.agents.none
|
||||
* enableAutoPhotoTags (bool): Tag photos. Default value false.
|
||||
* enableBIFGeneration (bool): Enable video preview thumbnails. Default value true.
|
||||
* includeInGlobal (bool): Include in dashboard. Default value true.
|
||||
* scanner (str): Plex Photo Scanner
|
||||
**Photo Preferences**
|
||||
|
||||
== Prefs for other movies ==
|
||||
* agent (str): com.plexapp.agents.none, com.plexapp.agents.imdb, com.plexapp.agents.themoviedb
|
||||
* enableBIFGeneration (bool): Enable video preview thumbnails. Default value true.
|
||||
* enableCinemaTrailers (bool): Enable Cinema Trailers. Default value true.
|
||||
* includeInGlobal (bool): Include in dashboard. Default value true.
|
||||
* scanner (str): Plex Movie Scanner, Plex Video Files Scanner
|
||||
* **agent** (str): com.plexapp.agents.none
|
||||
* **enableAutoPhotoTags** (bool): Tag photos. Default value false.
|
||||
* **enableBIFGeneration** (bool): Enable video preview thumbnails. Default value true.
|
||||
* **includeInGlobal** (bool): Include in dashboard. Default value true.
|
||||
* **scanner** (str): Plex Photo Scanner
|
||||
|
||||
== other movies com.plexapp.agents.imdb settings options ==
|
||||
* title (bool): Localized titles. Default value false.
|
||||
* extras (bool): Find trailers and extras automatically (Plex Pass required). Default value true.
|
||||
* only_trailers (bool): Skip extras which aren't trailers. Default value false.
|
||||
* redband (bool): Use red band (restricted audiences) trailers when available. Default value false.
|
||||
* native_subs (bool): Include extras with subtitles in Library language. Default value false.
|
||||
* cast_list (int): Cast List Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database.
|
||||
* ratings (int): Ratings Source: Default value 0 Possible options:
|
||||
0:Rotten Tomatoes,1:IMDb,2:The Movie Database.
|
||||
* summary (int): Plot Summary Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database.
|
||||
* country (int): Country: Default value 46 Possible options: 0:Argentina,1:Australia,2:Austria,
|
||||
3:Belgium,4:Belize,5:Bolivia,6:Brazil,7:Canada,8:Chile,9:Colombia,10:Costa Rica,11:Czech Republic,
|
||||
12:Denmark,13:Dominican Republic,14:Ecuador,15:El Salvador,16:France,17:Germany,18:Guatemala,
|
||||
19:Honduras,20:Hong Kong SAR,21:Ireland,22:Italy,23:Jamaica,24:Korea,25:Liechtenstein,
|
||||
26:Luxembourg,27:Mexico,28:Netherlands,29:New Zealand,30:Nicaragua,31:Panama,32:Paraguay,
|
||||
33:Peru,34:Portugal,35:Peoples Republic of China,36:Puerto Rico,37:Russia,38:Singapore,
|
||||
39:South Africa,40:Spain,41:Sweden,42:Switzerland,43:Taiwan,44:Trinidad,45:United Kingdom,
|
||||
46:United States,47:Uruguay,48:Venezuela.
|
||||
* collections (bool): Use collection info from The Movie Database. Default value false.
|
||||
* localart (bool): Prefer artwork based on library language. Default value true.
|
||||
* adult (bool): Include adult content. Default value false.
|
||||
* usage (bool): Send anonymous usage data to Plex. Default value true.
|
||||
**Movie Preferences**
|
||||
|
||||
== other movies com.plexapp.agents.themoviedb settings options ==
|
||||
* collections (bool): Use collection info from The Movie Database. Default value false.
|
||||
* localart (bool): Prefer artwork based on library language. Default value true.
|
||||
* adult (bool): Include adult content. Default value false.
|
||||
* country (int): Country (used for release date and content rating). Default value 47 Possible
|
||||
options: 0:,1:Argentina,2:Australia,3:Austria,4:Belgium,5:Belize,6:Bolivia,7:Brazil,8:Canada,
|
||||
9:Chile,10:Colombia,11:Costa Rica,12:Czech Republic,13:Denmark,14:Dominican Republic,
|
||||
15:Ecuador,16:El Salvador,17:France,18:Germany,19:Guatemala,20:Honduras,21:Hong Kong SAR,
|
||||
22:Ireland,23:Italy,24:Jamaica,25:Korea,26:Liechtenstein,27:Luxembourg,28:Mexico,
|
||||
29:Netherlands,30:New Zealand,31:Nicaragua,32:Panama,33:Paraguay,34:Peru,35:Portugal,
|
||||
36:Peoples Republic of China,37:Puerto Rico,38:Russia,39:Singapore,40:South Africa,
|
||||
41:Spain,42:Sweden,43:Switzerland,44:Taiwan,45:Trinidad,46:United Kingdom,
|
||||
47:United States,48:Uruguay,49:Venezuela.
|
||||
* **agent** (str): com.plexapp.agents.none, com.plexapp.agents.imdb, com.plexapp.agents.themoviedb
|
||||
* **enableBIFGeneration** (bool): Enable video preview thumbnails. Default value true.
|
||||
* **enableCinemaTrailers** (bool): Enable Cinema Trailers. Default value true.
|
||||
* **includeInGlobal** (bool): Include in dashboard. Default value true.
|
||||
* **scanner** (str): Plex Movie Scanner, Plex Video Files Scanner
|
||||
|
||||
== Prefs for movie ==
|
||||
agent (str): com.plexapp.agents.none, com.plexapp.agents.imdb, com.plexapp.agents.themoviedb
|
||||
enableBIFGeneration (bool): Enable video preview thumbnails. Default value true.
|
||||
enableCinemaTrailers (bool): Enable Cinema Trailers. Default value true.
|
||||
includeInGlobal (bool): Include in dashboard. Default value true.
|
||||
scanner (str): Plex Movie Scanner, Plex Video Files Scanner
|
||||
**IMDB Movie Options** (com.plexapp.agents.imdb)
|
||||
|
||||
== movie com.plexapp.agents.imdb settings options ==
|
||||
title (bool): Localized titles. Default value false.
|
||||
extras (bool): Find trailers and extras automatically (Plex Pass required). Default value true.
|
||||
only_trailers (bool): Skip extras which aren't trailers. Default value false.
|
||||
redband (bool): Use red band (restricted audiences) trailers when available. Default value false.
|
||||
native_subs (bool): Include extras with subtitles in Library language. Default value false.
|
||||
cast_list (int): Cast List Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database.
|
||||
ratings (int): Ratings Source: Default value 0 Possible options:
|
||||
0:Rotten Tomatoes,1:IMDb,2:The Movie Database.
|
||||
summary (int): Plot Summary Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database.
|
||||
country (int): Country: Default value 46 Possible options: 0:Argentina,1:Australia,2:Austria,
|
||||
3:Belgium,4:Belize,5:Bolivia,6:Brazil,7:Canada,8:Chile,9:Colombia,10:Costa Rica,
|
||||
11:Czech Republic,12:Denmark,13:Dominican Republic,14:Ecuador,15:El Salvador,
|
||||
16:France,17:Germany,18:Guatemala,19:Honduras,20:Hong Kong SAR,21:Ireland,
|
||||
22:Italy,23:Jamaica,24:Korea,25:Liechtenstein,26:Luxembourg,27:Mexico,28:Netherlands,
|
||||
29:New Zealand,30:Nicaragua,31:Panama,32:Paraguay,33:Peru,34:Portugal,
|
||||
35:Peoples Republic of China,36:Puerto Rico,37:Russia,38:Singapore,39:South Africa,
|
||||
40:Spain,41:Sweden,42:Switzerland,43:Taiwan,44:Trinidad,45:United Kingdom,
|
||||
46:United States,47:Uruguay,48:Venezuela.
|
||||
collections (bool): Use collection info from The Movie Database. Default value false.
|
||||
localart (bool): Prefer artwork based on library language. Default value true.
|
||||
adult (bool): Include adult content. Default value false.
|
||||
usage (bool): Send anonymous usage data to Plex. Default value true.
|
||||
* **title** (bool): Localized titles. Default value false.
|
||||
* **extras** (bool): Find trailers and extras automatically (Plex Pass required). Default value true.
|
||||
* **only_trailers** (bool): Skip extras which aren't trailers. Default value false.
|
||||
* **redband** (bool): Use red band (restricted audiences) trailers when available. Default value false.
|
||||
* **native_subs** (bool): Include extras with subtitles in Library language. Default value false.
|
||||
* **cast_list** (int): Cast List Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database.
|
||||
* **ratings** (int): Ratings Source, Default value 0 Possible options:
|
||||
0:Rotten Tomatoes, 1:IMDb, 2:The Movie Database.
|
||||
* **summary** (int): Plot Summary Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database.
|
||||
* **country** (int): Default value 46 Possible options 0:Argentina, 1:Australia, 2:Austria,
|
||||
3:Belgium, 4:Belize, 5:Bolivia, 6:Brazil, 7:Canada, 8:Chile, 9:Colombia, 10:Costa Rica,
|
||||
11:Czech Republic, 12:Denmark, 13:Dominican Republic, 14:Ecuador, 15:El Salvador,
|
||||
16:France, 17:Germany, 18:Guatemala, 19:Honduras, 20:Hong Kong SAR, 21:Ireland,
|
||||
22:Italy, 23:Jamaica, 24:Korea, 25:Liechtenstein, 26:Luxembourg, 27:Mexico, 28:Netherlands,
|
||||
29:New Zealand, 30:Nicaragua, 31:Panama, 32:Paraguay, 33:Peru, 34:Portugal,
|
||||
35:Peoples Republic of China, 36:Puerto Rico, 37:Russia, 38:Singapore, 39:South Africa,
|
||||
40:Spain, 41:Sweden, 42:Switzerland, 43:Taiwan, 44:Trinidad, 45:United Kingdom,
|
||||
46:United States, 47:Uruguay, 48:Venezuela.
|
||||
* **collections** (bool): Use collection info from The Movie Database. Default value false.
|
||||
* **localart** (bool): Prefer artwork based on library language. Default value true.
|
||||
* **adult** (bool): Include adult content. Default value false.
|
||||
* **usage** (bool): Send anonymous usage data to Plex. Default value true.
|
||||
|
||||
== movie com.plexapp.agents.themoviedb settings options ==
|
||||
collections (bool): Use collection info from The Movie Database. Default value false.
|
||||
localart (bool): Prefer artwork based on library language. Default value true.
|
||||
adult (bool): Include adult content. Default value false.
|
||||
country (int): Country (used for release date and content rating). Default value 47 Possible options:
|
||||
0:,1:Argentina,2:Australia,3:Austria,4:Belgium,5:Belize,6:Bolivia,7:Brazil,8:Canada,9:Chile,
|
||||
10:Colombia,11:Costa Rica,12:Czech Republic,13:Denmark,14:Dominican Republic,15:Ecuador,
|
||||
16:El Salvador,17:France,18:Germany,19:Guatemala,20:Honduras,21:Hong Kong SAR,22:Ireland,
|
||||
23:Italy,24:Jamaica,25:Korea,26:Liechtenstein,27:Luxembourg,28:Mexico,29:Netherlands,
|
||||
30:New Zealand,31:Nicaragua,32:Panama,33:Paraguay,34:Peru,35:Portugal,36:Peoples Republic of China,
|
||||
37:Puerto Rico,38:Russia,39:Singapore,40:South Africa,41:Spain,42:Sweden,43:Switzerland,
|
||||
44:Taiwan,45:Trinidad,46:United Kingdom,47:United States,48:Uruguay,49:Venezuela.
|
||||
**TheMovieDB Movie Options** (com.plexapp.agents.themoviedb)
|
||||
|
||||
== Prefs for show ==
|
||||
agent (str): com.plexapp.agents.none, com.plexapp.agents.thetvdb, com.plexapp.agents.themoviedb
|
||||
enableBIFGeneration (bool): Enable video preview thumbnails. Default value true.
|
||||
episodeSort (int): Episode order. Default value -1 Possible options: 0:Oldest first,1:Newest first.
|
||||
flattenSeasons (int): Seasons. Default value 0 Possible options: 0:Show,1:Hide.
|
||||
includeInGlobal (bool): Include in dashboard. Default value true.
|
||||
scanner (str): Plex Series Scanner
|
||||
* **collections** (bool): Use collection info from The Movie Database. Default value false.
|
||||
* **localart** (bool): Prefer artwork based on library language. Default value true.
|
||||
* **adult** (bool): Include adult content. Default value false.
|
||||
* **country** (int): Country (used for release date and content rating). Default value 47 Possible
|
||||
options 0:, 1:Argentina, 2:Australia, 3:Austria, 4:Belgium, 5:Belize, 6:Bolivia, 7:Brazil, 8:Canada, 9:Chile,
|
||||
10:Colombia, 11:Costa Rica, 12:Czech Republic, 13:Denmark, 14:Dominican Republic, 15:Ecuador,
|
||||
16:El Salvador, 17:France, 18:Germany, 19:Guatemala, 20:Honduras, 21:Hong Kong SAR, 22:Ireland,
|
||||
23:Italy, 24:Jamaica, 25:Korea, 26:Liechtenstein, 27:Luxembourg, 28:Mexico, 29:Netherlands,
|
||||
30:New Zealand, 31:Nicaragua, 32:Panama, 33:Paraguay, 34:Peru, 35:Portugal, 36:Peoples Republic of China,
|
||||
37:Puerto Rico, 38:Russia, 39:Singapore, 40:South Africa, 41:Spain, 42:Sweden, 43:Switzerland,
|
||||
44:Taiwan, 45:Trinidad, 46:United Kingdom, 47:United States, 48:Uruguay, 49:Venezuela.
|
||||
|
||||
== show com.plexapp.agents.thetvdb settings options ==
|
||||
extras (bool): Find trailers and extras automatically (Plex Pass required). Default value true.
|
||||
native_subs (bool): Include extras with subtitles in Library language. Default value false.
|
||||
**Show Preferences**
|
||||
|
||||
== show com.plexapp.agents.themoviedb settings ==
|
||||
collections (bool): Use collection info from The Movie Database. Default value false.
|
||||
localart (bool): Prefer artwork based on library language. Default value true.
|
||||
adult (bool): Include adult content. Default value false.
|
||||
country (int): Country (used for release date and content rating). Default value 47 Possible options:
|
||||
0:,1:Argentina,2:Australia,3:Austria,4:Belgium,5:Belize,6:Bolivia,7:Brazil,8:Canada,9:Chile,
|
||||
10:Colombia,11:Costa Rica,12:Czech Republic,13:Denmark,14:Dominican Republic,15:Ecuador,
|
||||
16:El Salvador,17:France,18:Germany,19:Guatemala,20:Honduras,21:Hong Kong SAR,22:Ireland,
|
||||
23:Italy,24:Jamaica,25:Korea,26:Liechtenstein,27:Luxembourg,28:Mexico,29:Netherlands,
|
||||
30:New Zealand,31:Nicaragua,32:Panama,33:Paraguay,34:Peru,35:Portugal,36:Peoples Republic of China,
|
||||
37:Puerto Rico,38:Russia,39:Singapore,40:South Africa,41:Spain,42:Sweden,43:Switzerland,
|
||||
44:Taiwan,45:Trinidad,46:United Kingdom,47:United States,48:Uruguay,49:Venezuela.
|
||||
* **agent** (str): com.plexapp.agents.none, com.plexapp.agents.thetvdb, com.plexapp.agents.themoviedb
|
||||
* **enableBIFGeneration** (bool): Enable video preview thumbnails. Default value true.
|
||||
* **episodeSort** (int): Episode order. Default value -1 Possible options: 0:Oldest first,1:Newest first.
|
||||
* **flattenSeasons** (int): Seasons. Default value 0 Possible options: 0:Show,1:Hide.
|
||||
* **includeInGlobal** (bool): Include in dashboard. Default value true.
|
||||
* **scanner** (str): Plex Series Scanner
|
||||
|
||||
**TheTVDB Show Options** (com.plexapp.agents.thetvdb)
|
||||
|
||||
* **extras** (bool): Find trailers and extras automatically (Plex Pass required). Default value true.
|
||||
* **native_subs** (bool): Include extras with subtitles in Library language. Default value false.
|
||||
|
||||
**TheMovieDB Show Options** (com.plexapp.agents.themoviedb)
|
||||
|
||||
* **collections** (bool): Use collection info from The Movie Database. Default value false.
|
||||
* **localart** (bool): Prefer artwork based on library language. Default value true.
|
||||
* **adult** (bool): Include adult content. Default value false.
|
||||
* **country** (int): Country (used for release date and content rating). Default value 47 Possible options
|
||||
0:, 1:Argentina, 2:Australia, 3:Austria, 4:Belgium, 5:Belize, 6:Bolivia, 7:Brazil, 8:Canada, 9:Chile,
|
||||
10:Colombia, 11:Costa Rica, 12:Czech Republic, 13:Denmark, 14:Dominican Republic, 15:Ecuador,
|
||||
16:El Salvador, 17:France, 18:Germany, 19:Guatemala, 20:Honduras, 21:Hong Kong SAR, 22:Ireland,
|
||||
23:Italy, 24:Jamaica, 25:Korea, 26:Liechtenstein, 27:Luxembourg, 28:Mexico, 29:Netherlands,
|
||||
30:New Zealand, 31:Nicaragua, 32:Panama, 33:Paraguay, 34:Peru, 35:Portugal, 36:Peoples Republic of China,
|
||||
37:Puerto Rico, 38:Russia, 39:Singapore, 40:South Africa, 41:Spain, 42:Sweden, 43:Switzerland,
|
||||
44:Taiwan, 45:Trinidad, 46:United Kingdom, 47:United States, 48:Uruguay, 49:Venezuela.
|
||||
|
||||
**Other Video Preferences**
|
||||
|
||||
* **agent** (str): com.plexapp.agents.none, com.plexapp.agents.imdb, com.plexapp.agents.themoviedb
|
||||
* **enableBIFGeneration** (bool): Enable video preview thumbnails. Default value true.
|
||||
* **enableCinemaTrailers** (bool): Enable Cinema Trailers. Default value true.
|
||||
* **includeInGlobal** (bool): Include in dashboard. Default value true.
|
||||
* **scanner** (str): Plex Movie Scanner, Plex Video Files Scanner
|
||||
|
||||
**IMDB Other Video Options** (com.plexapp.agents.imdb)
|
||||
|
||||
* **title** (bool): Localized titles. Default value false.
|
||||
* **extras** (bool): Find trailers and extras automatically (Plex Pass required). Default value true.
|
||||
* **only_trailers** (bool): Skip extras which aren't trailers. Default value false.
|
||||
* **redband** (bool): Use red band (restricted audiences) trailers when available. Default value false.
|
||||
* **native_subs** (bool): Include extras with subtitles in Library language. Default value false.
|
||||
* **cast_list** (int): Cast List Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database.
|
||||
* **ratings** (int): Ratings Source Default value 0 Possible options:
|
||||
0:Rotten Tomatoes,1:IMDb,2:The Movie Database.
|
||||
* **summary** (int): Plot Summary Source: Default value 1 Possible options: 0:IMDb,1:The Movie Database.
|
||||
* **country** (int): Country: Default value 46 Possible options: 0:Argentina, 1:Australia, 2:Austria,
|
||||
3:Belgium, 4:Belize, 5:Bolivia, 6:Brazil, 7:Canada, 8:Chile, 9:Colombia, 10:Costa Rica, 11:Czech Republic,
|
||||
12:Denmark, 13:Dominican Republic, 14:Ecuador, 15:El Salvador, 16:France, 17:Germany, 18:Guatemala,
|
||||
19:Honduras, 20:Hong Kong SAR, 21:Ireland, 22:Italy, 23:Jamaica, 24:Korea, 25:Liechtenstein,
|
||||
26:Luxembourg, 27:Mexico, 28:Netherlands, 29:New Zealand, 30:Nicaragua, 31:Panama, 32:Paraguay,
|
||||
33:Peru, 34:Portugal, 35:Peoples Republic of China, 36:Puerto Rico, 37:Russia, 38:Singapore,
|
||||
39:South Africa, 40:Spain, 41:Sweden, 42:Switzerland, 43:Taiwan, 44:Trinidad, 45:United Kingdom,
|
||||
46:United States, 47:Uruguay, 48:Venezuela.
|
||||
* **collections** (bool): Use collection info from The Movie Database. Default value false.
|
||||
* **localart** (bool): Prefer artwork based on library language. Default value true.
|
||||
* **adult** (bool): Include adult content. Default value false.
|
||||
* **usage** (bool): Send anonymous usage data to Plex. Default value true.
|
||||
|
||||
**TheMovieDB Other Video Options** (com.plexapp.agents.themoviedb)
|
||||
|
||||
* **collections** (bool): Use collection info from The Movie Database. Default value false.
|
||||
* **localart** (bool): Prefer artwork based on library language. Default value true.
|
||||
* **adult** (bool): Include adult content. Default value false.
|
||||
* **country** (int): Country (used for release date and content rating). Default
|
||||
value 47 Possible options 0:, 1:Argentina, 2:Australia, 3:Austria, 4:Belgium, 5:Belize,
|
||||
6:Bolivia, 7:Brazil, 8:Canada, 9:Chile, 10:Colombia, 11:Costa Rica, 12:Czech Republic,
|
||||
13:Denmark, 14:Dominican Republic, 15:Ecuador, 16:El Salvador, 17:France, 18:Germany,
|
||||
19:Guatemala, 20:Honduras, 21:Hong Kong SAR, 22:Ireland, 23:Italy, 24:Jamaica,
|
||||
25:Korea, 26:Liechtenstein, 27:Luxembourg, 28:Mexico, 29:Netherlands, 30:New Zealand,
|
||||
31:Nicaragua, 32:Panama, 33:Paraguay, 34:Peru, 35:Portugal,
|
||||
36:Peoples Republic of China, 37:Puerto Rico, 38:Russia, 39:Singapore,
|
||||
40:South Africa, 41:Spain, 42:Sweden, 43:Switzerland, 44:Taiwan, 45:Trinidad,
|
||||
46:United Kingdom, 47:United States, 48:Uruguay, 49:Venezuela.
|
||||
"""
|
||||
part = '/library/sections?name=%s&type=%s&agent=%s&scanner=%s&language=%s&location=%s' % (
|
||||
quote_plus(name), type, agent, quote_plus(scanner), language, quote_plus(location)) # noqa E126
|
||||
quote_plus(name), type, agent, quote_plus(scanner), language, quote_plus(location)) # noqa E126
|
||||
if kwargs:
|
||||
part += urlencode(kwargs)
|
||||
return self._server.query(part, method=self._server._session.post)
|
||||
|
@ -342,7 +353,7 @@ class LibrarySection(PlexObject):
|
|||
raise
|
||||
|
||||
def edit(self, **kwargs):
|
||||
""" Edit a library (Note: agent is required). See :class:`~plexapi.library.Library for example usage.
|
||||
""" Edit a library (Note: agent is required). See :class:`~plexapi.library.Library` for example usage.
|
||||
|
||||
Parameters:
|
||||
kwargs (dict): Dict of settings to edit.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import copy
|
||||
import requests
|
||||
import time
|
||||
from requests.status_codes import _codes as codes
|
||||
from plexapi import BASE_HEADERS, CONFIG, TIMEOUT
|
||||
from plexapi import log, logfilter, utils
|
||||
|
@ -17,6 +18,13 @@ class MyPlexAccount(PlexObject):
|
|||
with your username and password. This object represents the data found Account on
|
||||
the myplex.tv servers at the url https://plex.tv/users/account.
|
||||
|
||||
Parameters:
|
||||
username (str): Your MyPlex username.
|
||||
password (str): Your MyPlex password.
|
||||
session (requests.Session, optional): Use your own session object if you want to
|
||||
cache the http responses from PMS
|
||||
timeout (int): timeout in seconds on initial connect to myplex (default config.TIMEOUT).
|
||||
|
||||
Attributes:
|
||||
SIGNIN (str): 'https://my.plexapp.com/users/sign_in.xml'
|
||||
key (str): 'https://plex.tv/users/account'
|
||||
|
@ -51,12 +59,12 @@ class MyPlexAccount(PlexObject):
|
|||
WEBHOOKS = 'https://plex.tv/api/v2/user/webhooks'
|
||||
key = 'https://plex.tv/users/account'
|
||||
|
||||
def __init__(self, username=None, password=None, session=None):
|
||||
def __init__(self, username=None, password=None, session=None, timeout=None):
|
||||
self._session = session or requests.Session()
|
||||
self._token = None
|
||||
username = username or CONFIG.get('auth.myplex_username')
|
||||
password = password or CONFIG.get('auth.myplex_password')
|
||||
data = self.query(self.SIGNIN, method=self._session.post, auth=(username, password))
|
||||
data = self.query(self.SIGNIN, method=self._session.post, auth=(username, password), timeout=timeout)
|
||||
super(MyPlexAccount, self).__init__(self, data, self.SIGNIN)
|
||||
|
||||
def _loadData(self, data):
|
||||
|
@ -108,14 +116,15 @@ class MyPlexAccount(PlexObject):
|
|||
data = self.query(MyPlexDevice.key)
|
||||
return [MyPlexDevice(self, elem) for elem in data]
|
||||
|
||||
def query(self, url, method=None, headers=None, **kwargs):
|
||||
def query(self, url, method=None, headers=None, timeout=None, **kwargs):
|
||||
method = method or self._session.get
|
||||
delim = '&' if '?' in url else '?'
|
||||
url = '%s%sX-Plex-Token=%s' % (url, delim, self._token)
|
||||
timeout = timeout or TIMEOUT
|
||||
log.debug('%s %s', method.__name__.upper(), url)
|
||||
allheaders = BASE_HEADERS.copy()
|
||||
allheaders.update(headers or {})
|
||||
response = method(url, headers=allheaders, timeout=TIMEOUT, **kwargs)
|
||||
response = method(url, headers=allheaders, timeout=timeout, **kwargs)
|
||||
if response.status_code not in (200, 201):
|
||||
codename = codes.get(response.status_code)[0]
|
||||
log.warn('BadRequest (%s) %s %s' % (response.status_code, codename, response.url))
|
||||
|
@ -283,7 +292,7 @@ class MyPlexResource(PlexObject):
|
|||
self.presence = utils.cast(bool, data.attrib.get('presence'))
|
||||
self.connections = self.findItems(data, ResourceConnection)
|
||||
|
||||
def connect(self, ssl=None):
|
||||
def connect(self, ssl=None, timeout=None):
|
||||
""" Returns a new :class:`~server.PlexServer` object. Often times there is more than
|
||||
one address specified for a server or client. This function will prioritize local
|
||||
connections before remote and HTTPS before HTTP. After trying to connect to all
|
||||
|
@ -309,26 +318,10 @@ class MyPlexResource(PlexObject):
|
|||
else: connections = https + http
|
||||
# Try connecting to all known resource connections in parellel, but
|
||||
# only return the first server (in order) that provides a response.
|
||||
listargs = [[c] for c in connections]
|
||||
results = utils.threaded(self._connect, listargs)
|
||||
# At this point we have a list of result tuples containing (url, token, PlexServer)
|
||||
# or (url, token, None) in the case a connection could not be
|
||||
# established.
|
||||
for url, token, result in results:
|
||||
okerr = 'OK' if result else 'ERR'
|
||||
log.info('Testing resource connection: %s?X-Plex-Token=%s %s', url, token, okerr)
|
||||
results = [r[2] for r in results if r and r[2] is not None]
|
||||
if not results:
|
||||
raise NotFound('Unable to connect to resource: %s' % self.name)
|
||||
log.info('Connecting to server: %s?X-Plex-Token=%s', results[0]._baseurl, results[0]._token)
|
||||
return results[0]
|
||||
|
||||
def _connect(self, url, results, i):
|
||||
try:
|
||||
results[i] = (url, self.accessToken, PlexServer(url, self.accessToken))
|
||||
except Exception as err:
|
||||
log.error('%s: %s', url, err)
|
||||
results[i] = (url, self.accessToken, None)
|
||||
listargs = [[PlexServer, url, self.accessToken, timeout] for url in connections]
|
||||
log.info('Testing %s resource connections..', len(listargs))
|
||||
results = utils.threaded(_connect, listargs)
|
||||
return _chooseConnection('Resource', self.name, results)
|
||||
|
||||
|
||||
class ResourceConnection(PlexObject):
|
||||
|
@ -409,38 +402,50 @@ class MyPlexDevice(PlexObject):
|
|||
self.lastSeenAt = utils.toDatetime(data.attrib.get('lastSeenAt'))
|
||||
self.connections = [connection.attrib.get('uri') for connection in data.iter('Connection')]
|
||||
|
||||
def connect(self):
|
||||
def connect(self, timeout=None):
|
||||
""" Returns a new :class:`~plexapi.client.PlexClient` object. Sometimes there is more than
|
||||
one address specified for a server or client. After trying to connect to all
|
||||
available addresses for this client and assuming at least one connection was
|
||||
successful, the PlexClient object is built and returned.
|
||||
one address specified for a server or client. After trying to connect to all available
|
||||
addresses for this client and assuming at least one connection was successful, the
|
||||
PlexClient object is built and returned.
|
||||
|
||||
Raises:
|
||||
:class:`~plexapi.exceptions.NotFound`: When unable to connect to any addresses for this device.
|
||||
"""
|
||||
# Try connecting to all known clients in parellel, but
|
||||
# only return the first server (in order) that provides a response.
|
||||
listargs = [[c] for c in self.connections]
|
||||
results = utils.threaded(self._connect, listargs)
|
||||
# At this point we have a list of result tuples containing (url, token, PlexServer)
|
||||
# or (url, token, None) in the case a connection could not be established.
|
||||
for url, token, result in results:
|
||||
okerr = 'OK' if result else 'ERR'
|
||||
log.info('Testing device connection: %s?X-Plex-Token=%s %s', url, token, okerr)
|
||||
results = [r[2] for r in results if r and r[2] is not None]
|
||||
if not results:
|
||||
raise NotFound('Unable to connect to client: %s' % self.name)
|
||||
log.info('Connecting to client: %s?X-Plex-Token=%s', results[0]._baseurl, results[0]._token)
|
||||
return results[0]
|
||||
|
||||
def _connect(self, url, results, i):
|
||||
try:
|
||||
results[i] = (url, self.token, PlexClient(baseurl=url, token=self.token))
|
||||
except Exception as err:
|
||||
log.error('%s: %s', url, err)
|
||||
results[i] = (url, self.token, None)
|
||||
listargs = [[PlexClient, url, self.token, timeout] for url in self.connections]
|
||||
log.info('Testing %s device connections..', len(listargs))
|
||||
results = utils.threaded(_connect, listargs)
|
||||
_chooseConnection('Device', self.name, results)
|
||||
|
||||
def delete(self):
|
||||
""" Remove this device from your account. """
|
||||
key = 'https://plex.tv/devices/%s.xml' % self.id
|
||||
self._server.query(key, self._server._session.delete)
|
||||
|
||||
|
||||
def _connect(cls, url, token, timeout, results, i):
|
||||
""" Connects to the specified cls with url and token. Stores the connection
|
||||
information to results[i] in a threadsafe way.
|
||||
"""
|
||||
starttime = time.time()
|
||||
try:
|
||||
device = cls(baseurl=url, token=token, timeout=timeout)
|
||||
runtime = int(time.time() - starttime)
|
||||
results[i] = (url, token, device, runtime)
|
||||
except Exception as err:
|
||||
runtime = int(time.time() - starttime)
|
||||
log.error('%s: %s', url, err)
|
||||
results[i] = (url, token, None, runtime)
|
||||
|
||||
|
||||
def _chooseConnection(ctype, name, results):
|
||||
""" Chooses the first (best) connection from the given _connect results. """
|
||||
# At this point we have a list of result tuples containing (url, token, PlexServer, runtime)
|
||||
# or (url, token, None, runtime) in the case a connection could not be established.
|
||||
for url, token, result, runtime in results:
|
||||
okerr = 'OK' if result else 'ERR'
|
||||
log.info('%s connection %s (%ss): %s?X-Plex-Token=%s', ctype, okerr, runtime, url, token)
|
||||
results = [r[2] for r in results if r and r[2] is not None]
|
||||
if results:
|
||||
log.info('Connecting to %s: %s?X-Plex-Token=%s', ctype, results[0]._baseurl, results[0]._token)
|
||||
return results[0]
|
||||
raise NotFound('Unable to connect to %s: %s' % (ctype.lower(), name))
|
||||
|
|
|
@ -31,6 +31,7 @@ class PlexServer(PlexObject):
|
|||
token (str): Required Plex authentication token to access the server.
|
||||
session (requests.Session, optional): Use your own session object if you want to
|
||||
cache the http responses from PMS
|
||||
timeout (int): timeout in seconds on initial connect to server (default config.TIMEOUT).
|
||||
|
||||
Attributes:
|
||||
allowCameraUpload (bool): True if server allows camera upload.
|
||||
|
@ -90,13 +91,14 @@ class PlexServer(PlexObject):
|
|||
"""
|
||||
key = '/'
|
||||
|
||||
def __init__(self, baseurl=None, token=None, session=None):
|
||||
def __init__(self, baseurl=None, token=None, session=None, timeout=None):
|
||||
self._baseurl = baseurl or CONFIG.get('auth.server_baseurl', 'http://localhost:32400')
|
||||
self._token = logfilter.add_secret(token or CONFIG.get('auth.server_token'))
|
||||
self._session = session or requests.Session()
|
||||
self._library = None # cached library
|
||||
self._settings = None # cached settings
|
||||
super(PlexServer, self).__init__(self, self.query(self.key), self.key)
|
||||
data = self.query(self.key, timeout=timeout)
|
||||
super(PlexServer, self).__init__(self, data, self.key)
|
||||
|
||||
def _loadData(self, data):
|
||||
""" Load attribute values from Plex XML response. """
|
||||
|
@ -260,20 +262,17 @@ class PlexServer(PlexObject):
|
|||
"""
|
||||
return self.fetchItem('/playlists', title=title)
|
||||
|
||||
def query(self, key, method=None, headers=None, **kwargs):
|
||||
def query(self, key, method=None, headers=None, timeout=None, **kwargs):
|
||||
""" Main method used to handle HTTPS requests to the Plex server. This method helps
|
||||
by encoding the response to utf-8 and parsing the returned XML into and
|
||||
ElementTree object. Returns None if no data exists in the response.
|
||||
"""
|
||||
if not key.startswith('http'):
|
||||
url = self.url(key)
|
||||
else:
|
||||
url = key
|
||||
|
||||
url = self.url(key)
|
||||
method = method or self._session.get
|
||||
timeout = timeout or TIMEOUT
|
||||
log.debug('%s %s', method.__name__.upper(), url)
|
||||
headers = self._headers(**headers or {})
|
||||
response = method(url, headers=headers, timeout=TIMEOUT, **kwargs)
|
||||
response = method(url, headers=headers, timeout=timeout, **kwargs)
|
||||
if response.status_code not in (200, 201):
|
||||
codename = codes.get(response.status_code)[0]
|
||||
log.warn('BadRequest (%s) %s %s' % (response.status_code, codename, response.url))
|
||||
|
|
|
@ -160,6 +160,7 @@ def threaded(callback, listargs):
|
|||
args += [results, len(results)]
|
||||
results.append(None)
|
||||
threads.append(Thread(target=callback, args=args))
|
||||
threads[-1].setDaemon(True)
|
||||
threads[-1].start()
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
@ -226,7 +227,7 @@ def downloadSessionImages(server, filename=None, height=150, width=150, opacity=
|
|||
return info
|
||||
|
||||
|
||||
def download(url, filename=None, savepath=None, session=None, chunksize=4024, mocked=False, unpack=False):
|
||||
def download(url, filename=None, savepath=None, session=None, chunksize=4024, unpack=False, mocked=False):
|
||||
""" Helper to download a thumb, videofile or other media item. Returns the local
|
||||
path to the downloaded file.
|
||||
|
||||
|
|
|
@ -9,5 +9,6 @@ flake8
|
|||
pillow
|
||||
pytest
|
||||
pytest-cov
|
||||
pytest-cache
|
||||
requests
|
||||
websocket-client
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#---------------------------------------------------------
|
||||
# PlexAPI requirements to build documentation.
|
||||
# pip install -r requirments_doc.txt
|
||||
#
|
||||
# If testing locally:
|
||||
# sudo apt-get install python-sphinx
|
||||
#---------------------------------------------------------
|
||||
recommonmark
|
||||
sphinx-rtd-theme
|
||||
|
|
|
@ -1,144 +1,130 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import betamax, os, plexapi
|
||||
import pytest, requests
|
||||
from betamax_serializers import pretty_json
|
||||
import plexapi, pytest, requests
|
||||
from plexapi import compat
|
||||
from plexapi.client import PlexClient
|
||||
from datetime import datetime
|
||||
from plexapi.myplex import MyPlexAccount
|
||||
from plexapi.server import PlexServer
|
||||
from functools import partial
|
||||
|
||||
test_baseurl = plexapi.CONFIG.get('auth.server_baseurl')
|
||||
test_token = plexapi.CONFIG.get('auth.server_token')
|
||||
test_username = plexapi.CONFIG.get('auth.myplex_username')
|
||||
test_password = plexapi.CONFIG.get('auth.myplex_password')
|
||||
SERVER_BASEURL = plexapi.CONFIG.get('auth.server_baseurl')
|
||||
SERVER_TOKEN = plexapi.CONFIG.get('auth.server_token')
|
||||
MYPLEX_USERNAME = plexapi.CONFIG.get('auth.myplex_username')
|
||||
MYPLEX_PASSWORD = plexapi.CONFIG.get('auth.myplex_password')
|
||||
|
||||
MIN_DATETIME = datetime(2017, 1, 1)
|
||||
REGEX_EMAIL = r'(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)'
|
||||
REGEX_IPADDR = r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def pms(request):
|
||||
from plexapi.server import PlexServer
|
||||
sess = requests.Session()
|
||||
# CASSETTE_LIBRARY_DIR = 'response/'
|
||||
# betamax.Betamax.register_serializer(pretty_json.PrettyJSONSerializer)
|
||||
# config = betamax.Betamax.configure()
|
||||
# config.define_cassette_placeholder('MASKED', token)
|
||||
# config.define_cassette_placeholder('MASKED', test_token)
|
||||
# recorder = betamax.Betamax(sess, cassette_library_dir=CASSETTE_LIBRARY_DIR)
|
||||
# recorder.use_cassette('http_responses', serialize_with='prettyjson') # record='new_episodes'
|
||||
# recorder.start()
|
||||
assert test_baseurl
|
||||
assert test_token
|
||||
pms = PlexServer(test_baseurl, test_token, session=sess)
|
||||
#request.addfinalizer(recorder.stop)
|
||||
return pms
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def freshpms():
|
||||
from plexapi.server import PlexServer
|
||||
sess = requests.Session()
|
||||
assert test_baseurl
|
||||
assert test_token
|
||||
pms = PlexServer(test_baseurl, test_token, session=sess)
|
||||
return pms
|
||||
AUDIOCHANNELS = [2, 6]
|
||||
AUDIOLAYOUTS = ['5.1', 'stereo']
|
||||
CODECS = ['aac', 'h264', 'mp3', 'mpeg4']
|
||||
CONTAINERS = ['avi', 'mp4']
|
||||
CONTENTRATINGS = ['TV-14']
|
||||
FRAMERATES = ['24p', 'PAL']
|
||||
PROFILES = ['advanced simple', 'main']
|
||||
RESOLUTIONS = ['720', 'sd']
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--req_client", action="store_true",
|
||||
help="Run tests that interact with a client")
|
||||
parser.addoption('--client', help='Run client tests against specified baseurl.')
|
||||
parser.addoption('--token', help='Token required to connect to client.')
|
||||
|
||||
|
||||
def pytest_runtest_setup(item):
|
||||
if 'req_client' in item.keywords and not item.config.getvalue("req_client"):
|
||||
pytest.skip("need --req_client option to run")
|
||||
else:
|
||||
item.config.getvalue("req_client")
|
||||
if 'client' in item.keywords and not item.config.getvalue('client'):
|
||||
return pytest.skip('Need --client option to run.')
|
||||
if 'client' in item.keywords and not item.config.getvalue('token'):
|
||||
return pytest.skip('Need --token option to run.')
|
||||
|
||||
|
||||
#---------------------------------
|
||||
# Fixtures
|
||||
#---------------------------------
|
||||
|
||||
@pytest.fixture()
|
||||
def account():
|
||||
assert MYPLEX_USERNAME, 'Required MYPLEX_USERNAME not specified.'
|
||||
assert MYPLEX_PASSWORD, 'Required MYPLEX_PASSWORD not specified.'
|
||||
return MyPlexAccount(MYPLEX_USERNAME, MYPLEX_PASSWORD)
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def plex():
|
||||
assert SERVER_BASEURL, 'Required SERVER_BASEURL not specified.'
|
||||
assert SERVER_TOKEN, 'Requred SERVER_TOKEN not specified.'
|
||||
session = requests.Session()
|
||||
return PlexServer(SERVER_BASEURL, SERVER_TOKEN, session=session)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def plex_account():
|
||||
from plexapi.myplex import MyPlexAccount
|
||||
username = test_username
|
||||
password = test_password
|
||||
assert username and password
|
||||
account = MyPlexAccount(username, password)
|
||||
assert account
|
||||
return account
|
||||
def plex2():
|
||||
return plex()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def a_movie(pms):
|
||||
m = pms.library.search('16 blocks')
|
||||
assert m
|
||||
return m[0]
|
||||
def client(request):
|
||||
client = request.config.getoption('--client')
|
||||
token = request.config.getoption('--token')
|
||||
return PlexClient(baseurl=client, token=token)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def a_tv_section(pms):
|
||||
sec = pms.library.section('TV Shows')
|
||||
assert sec
|
||||
return sec
|
||||
def tvshows(plex):
|
||||
return plex.library.section('TV Shows')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def a_movie_section(pms):
|
||||
sec = pms.library.section('Movies')
|
||||
assert sec
|
||||
return sec
|
||||
def movies(plex):
|
||||
return plex.library.section('Movies')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def a_music_section(pms):
|
||||
sec = pms.library.section('Music')
|
||||
assert sec
|
||||
return sec
|
||||
def music(plex):
|
||||
return plex.library.section('Music')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def a_photo_section(pms):
|
||||
sec = pms.library.section('Photos')
|
||||
assert sec
|
||||
return sec
|
||||
def photos(plex):
|
||||
return plex.library.section('Photos')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def a_artist(a_music_section):
|
||||
sec = a_music_section.get('Infinite State')
|
||||
assert sec
|
||||
return sec
|
||||
def movie(movies):
|
||||
return movies.get('16 blocks')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def a_music_album(a_music_section):
|
||||
sec = a_music_section.get('Infinite State').album('Unmastered Impulses')
|
||||
assert sec
|
||||
return sec
|
||||
def artist(music):
|
||||
return music.get('Infinite State')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def a_track(a_music_album):
|
||||
track = a_music_album.track('Holy Moment')
|
||||
assert track
|
||||
return track
|
||||
def album(artist):
|
||||
return artist.album('Unmastered Impulses')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def a_show(a_tv_section):
|
||||
sec = a_tv_section.get('The 100')
|
||||
assert sec
|
||||
return sec
|
||||
def track(album):
|
||||
return album.track('Holy Moment')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def a_episode(a_show):
|
||||
ep = a_show.get('Pilot')
|
||||
assert ep
|
||||
return ep
|
||||
def show(tvshows):
|
||||
return tvshows.get('The 100')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def a_photo_album(pms):
|
||||
sec = pms.library.section('Photos')
|
||||
assert sec
|
||||
album = sec.get('photo_album1')
|
||||
assert album
|
||||
return album
|
||||
def episode(show):
|
||||
return show.get('Pilot')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def photoalbum(photos):
|
||||
try:
|
||||
return photos.get('Cats')
|
||||
except:
|
||||
return photos.get('photo_album1')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
@ -146,3 +132,45 @@ def monkeydownload(request, monkeypatch):
|
|||
monkeypatch.setattr('plexapi.utils.download', partial(plexapi.utils.download, mocked=True))
|
||||
yield
|
||||
monkeypatch.undo()
|
||||
|
||||
|
||||
#---------------------------------
|
||||
# Utility Functions
|
||||
#---------------------------------
|
||||
|
||||
def is_datetime(value):
|
||||
return value > MIN_DATETIME
|
||||
|
||||
|
||||
def is_int(value, gte=1):
|
||||
return int(value) >= gte
|
||||
|
||||
|
||||
def is_float(value, gte=1.0):
|
||||
return float(value) >= gte
|
||||
|
||||
|
||||
def is_metadata(key, prefix='/library/metadata/', contains='', suffix=''):
|
||||
try:
|
||||
assert key.startswith(prefix)
|
||||
assert contains in key
|
||||
assert key.endswith(suffix)
|
||||
return True
|
||||
except AssertionError:
|
||||
return False
|
||||
|
||||
|
||||
def is_part(key):
|
||||
return is_metadata(key, prefix='/library/parts/')
|
||||
|
||||
|
||||
def is_section(key):
|
||||
return is_metadata(key, prefix='/library/sections/')
|
||||
|
||||
|
||||
def is_string(value, gte=1):
|
||||
return isinstance(value, compat.string_type) and len(value) >= gte
|
||||
|
||||
|
||||
def is_thumb(key):
|
||||
return is_metadata(key, contains='/thumb/')
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
def test_mark_movie_watched(a_movie):
|
||||
a_movie.markUnwatched()
|
||||
print('Marking movie watched: %s' % a_movie)
|
||||
print('View count: %s' % a_movie.viewCount)
|
||||
a_movie.markWatched()
|
||||
print('View count: %s' % a_movie.viewCount)
|
||||
assert a_movie.viewCount == 1, 'View count 0 after watched.'
|
||||
a_movie.markUnwatched()
|
||||
print('View count: %s' % a_movie.viewCount)
|
||||
assert a_movie.viewCount == 0, 'View count 1 after unwatched.'
|
||||
|
||||
def test_mark_movie_watched(movie):
|
||||
movie.markUnwatched()
|
||||
print('Marking movie watched: %s' % movie)
|
||||
print('View count: %s' % movie.viewCount)
|
||||
movie.markWatched()
|
||||
print('View count: %s' % movie.viewCount)
|
||||
assert movie.viewCount == 1, 'View count 0 after watched.'
|
||||
movie.markUnwatched()
|
||||
print('View count: %s' % movie.viewCount)
|
||||
assert movie.viewCount == 0, 'View count 1 after unwatched.'
|
||||
|
||||
|
||||
def test_refresh_section(pms):
|
||||
shows = pms.library.section('TV Shows')
|
||||
#shows.refresh()
|
||||
def test_refresh_section(tvshows):
|
||||
tvshows.refresh()
|
||||
|
||||
|
||||
def test_refresh_video(pms):
|
||||
result = pms.search('16 blocks')
|
||||
#result[0].refresh()
|
||||
def test_refresh_video(movie):
|
||||
movie.refresh()
|
||||
|
|
|
@ -1,310 +1,314 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
def test_audio_Artist_attr(a_artist):
|
||||
m = a_artist
|
||||
m.reload()
|
||||
assert str(m.addedAt.date()) == '2017-01-17'
|
||||
assert m.countries == []
|
||||
assert [i.tag for i in m.genres] == ['Electronic']
|
||||
assert m.guid == 'com.plexapp.agents.lastfm://Infinite%20State?lang=en'
|
||||
assert m.index == '1'
|
||||
assert m._initpath == '/library/metadata/20'
|
||||
assert m.key == '/library/metadata/20'
|
||||
assert m.librarySectionID == '3'
|
||||
assert m.listType == 'audio'
|
||||
assert m.locations == ['/media/music/unmastered_impulses']
|
||||
assert m.ratingKey == 20
|
||||
assert m._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert m.similar == []
|
||||
assert m.summary == ""
|
||||
assert m.title == 'Infinite State'
|
||||
assert m.titleSort == 'Infinite State'
|
||||
assert m.type == 'artist'
|
||||
# keeps breaking because of timezone differences between us
|
||||
# assert str(m.updatedAt.date()) == '2017-02-01'
|
||||
assert m.viewCount == 0
|
||||
from datetime import datetime
|
||||
from . import conftest as utils
|
||||
|
||||
|
||||
def test_audio_Artist_get(a_artist, a_music_section):
|
||||
a_artist == a_music_section.searchArtists(**{'title': 'Infinite State'})[0]
|
||||
a_artist.title == 'Infinite State'
|
||||
def test_audio_Artist_attr(artist):
|
||||
artist.reload()
|
||||
assert utils.is_datetime(artist.addedAt)
|
||||
assert artist.countries == []
|
||||
assert [i.tag for i in artist.genres] == ['Electronic']
|
||||
assert 'lastfm' in artist.guid
|
||||
assert artist.index == '1'
|
||||
assert utils.is_metadata(artist._initpath)
|
||||
assert utils.is_metadata(artist.key)
|
||||
assert utils.is_int(artist.librarySectionID)
|
||||
assert artist.listType == 'audio'
|
||||
assert len(artist.locations) == 1
|
||||
assert len(artist.locations[0]) >= 10
|
||||
assert artist.ratingKey >= 1
|
||||
assert artist._server._baseurl == utils.SERVER_BASEURL
|
||||
assert artist.similar == []
|
||||
assert artist.summary == ''
|
||||
assert artist.title == 'Infinite State'
|
||||
assert artist.titleSort == 'Infinite State'
|
||||
assert artist.type == 'artist'
|
||||
assert utils.is_datetime(artist.updatedAt)
|
||||
assert artist.viewCount == 0
|
||||
|
||||
|
||||
def test_audio_Artist_track(a_artist):
|
||||
track = a_artist.track('Holy Moment')
|
||||
def test_audio_Artist_get(artist, music):
|
||||
artist == music.searchArtists(**{'title': 'Infinite State'})[0]
|
||||
artist.title == 'Infinite State'
|
||||
|
||||
|
||||
def test_audio_Artist_track(artist):
|
||||
track = artist.track('Holy Moment')
|
||||
assert track.title == 'Holy Moment'
|
||||
|
||||
|
||||
def test_audio_Artist_tracks(a_artist):
|
||||
tracks = a_artist.tracks()
|
||||
def test_audio_Artist_tracks(artist):
|
||||
tracks = artist.tracks()
|
||||
assert len(tracks) == 14
|
||||
|
||||
|
||||
def test_audio_Artist_album(a_artist):
|
||||
album = a_artist.album('Unmastered Impulses')
|
||||
def test_audio_Artist_album(artist):
|
||||
album = artist.album('Unmastered Impulses')
|
||||
assert album.title == 'Unmastered Impulses'
|
||||
|
||||
|
||||
def test_audio_Artist_albums(a_artist):
|
||||
albums = a_artist.albums()
|
||||
def test_audio_Artist_albums(artist):
|
||||
albums = artist.albums()
|
||||
assert len(albums) == 1 and albums[0].title == 'Unmastered Impulses'
|
||||
|
||||
|
||||
def test_audio_Album_attrs(a_music_album):
|
||||
m = a_music_album
|
||||
assert str(m.addedAt.date()) == '2017-01-17'
|
||||
assert [i.tag for i in m.genres] == ['Electronic']
|
||||
assert m.index == '1'
|
||||
assert m._initpath == '/library/metadata/20/children'
|
||||
assert m.key == '/library/metadata/21'
|
||||
assert m.librarySectionID == '3'
|
||||
assert m.listType == 'audio'
|
||||
assert str(m.originallyAvailableAt.date()) == '2016-01-01'
|
||||
assert m.parentKey == '/library/metadata/20'
|
||||
assert m.parentRatingKey == '20'
|
||||
assert m.parentThumb is None
|
||||
assert m.parentTitle == 'Infinite State'
|
||||
assert m.ratingKey == 21
|
||||
assert m._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert m.studio is None
|
||||
assert m.summary == ''
|
||||
assert m.thumb == '/library/metadata/21/thumb/1484693407'
|
||||
assert m.title == 'Unmastered Impulses'
|
||||
assert m.titleSort == 'Unmastered Impulses'
|
||||
assert m.type == 'album'
|
||||
assert str(m.updatedAt.date()) == '2017-01-17'
|
||||
assert m.viewCount == 0
|
||||
assert m.year == 2016
|
||||
def test_audio_Album_attrs(album):
|
||||
assert utils.is_datetime(album.addedAt)
|
||||
assert [i.tag for i in album.genres] == ['Electronic']
|
||||
assert album.index == '1'
|
||||
assert utils.is_metadata(album._initpath)
|
||||
assert utils.is_metadata(album.key)
|
||||
assert utils.is_int(album.librarySectionID)
|
||||
assert album.listType == 'audio'
|
||||
assert album.originallyAvailableAt == datetime(2016, 1, 1)
|
||||
assert utils.is_metadata(album.parentKey)
|
||||
assert utils.is_int(album.parentRatingKey)
|
||||
if album.parentThumb:
|
||||
assert utils.is_metadata(album.parentThumb, contains='/thumb/')
|
||||
assert album.parentTitle == 'Infinite State'
|
||||
assert album.ratingKey >= 1
|
||||
assert album._server._baseurl == utils.SERVER_BASEURL
|
||||
assert album.studio is None
|
||||
assert album.summary == ''
|
||||
assert utils.is_metadata(album.thumb, contains='/thumb/')
|
||||
assert album.title == 'Unmastered Impulses'
|
||||
assert album.titleSort == 'Unmastered Impulses'
|
||||
assert album.type == 'album'
|
||||
assert utils.is_datetime(album.updatedAt)
|
||||
assert album.viewCount == 0
|
||||
assert album.year == 2016
|
||||
|
||||
|
||||
def test_audio_Album_tracks(a_music_album):
|
||||
tracks = a_music_album.tracks()
|
||||
def test_audio_Album_tracks(album):
|
||||
tracks = album.tracks()
|
||||
track = tracks[0]
|
||||
assert len(tracks) == 14
|
||||
assert tracks[0].grandparentKey == '/library/metadata/20'
|
||||
assert tracks[0].grandparentRatingKey == '20'
|
||||
assert tracks[0].grandparentTitle == 'Infinite State'
|
||||
assert tracks[0].index == '1'
|
||||
assert tracks[0]._initpath == '/library/metadata/21/children'
|
||||
assert tracks[0].key == '/library/metadata/22'
|
||||
assert tracks[0].listType == 'audio'
|
||||
assert tracks[0].originalTitle == 'Kenneth Reitz'
|
||||
assert tracks[0].parentIndex == '1'
|
||||
assert tracks[0].parentKey == '/library/metadata/21'
|
||||
assert tracks[0].parentRatingKey == '21'
|
||||
assert tracks[0].parentThumb == '/library/metadata/21/thumb/1484693407'
|
||||
assert tracks[0].parentTitle == 'Unmastered Impulses'
|
||||
assert tracks[0].player is None
|
||||
assert tracks[0].ratingCount == 9
|
||||
assert tracks[0].ratingKey == 22
|
||||
assert tracks[0]._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert tracks[0].summary == ""
|
||||
assert tracks[0].thumb == '/library/metadata/21/thumb/1484693407'
|
||||
assert tracks[0].title == 'Holy Moment'
|
||||
assert tracks[0].titleSort == 'Holy Moment'
|
||||
assert tracks[0].transcodeSession is None
|
||||
assert tracks[0].type == 'track'
|
||||
assert str(tracks[0].updatedAt.date()) == '2017-01-17'
|
||||
assert tracks[0].username is None
|
||||
assert tracks[0].viewCount == 0
|
||||
assert tracks[0].viewOffset == 0
|
||||
|
||||
|
||||
def test_audio_Album_track(a_music_album):
|
||||
# this is not reloaded. its not that much info missing.
|
||||
track = a_music_album.track('Holy Moment')
|
||||
assert str(track.addedAt.date()) == '2017-01-17'
|
||||
assert track.duration == 298606
|
||||
assert track.grandparentKey == '/library/metadata/20'
|
||||
assert track.grandparentRatingKey == '20'
|
||||
assert utils.is_metadata(track.grandparentKey)
|
||||
assert utils.is_int(track.grandparentRatingKey)
|
||||
assert track.grandparentTitle == 'Infinite State'
|
||||
assert track.index == '1'
|
||||
assert track._initpath == '/library/metadata/21/children'
|
||||
assert track.key == '/library/metadata/22'
|
||||
assert utils.is_metadata(track._initpath)
|
||||
assert utils.is_metadata(track.key)
|
||||
assert track.listType == 'audio'
|
||||
# Assign 0 track.media
|
||||
med0 = track.media[0]
|
||||
assert track.originalTitle == 'Kenneth Reitz'
|
||||
assert track.parentIndex == '1'
|
||||
assert track.parentKey == '/library/metadata/21'
|
||||
assert track.parentRatingKey == '21'
|
||||
assert track.parentThumb == '/library/metadata/21/thumb/1484693407'
|
||||
assert utils.is_int(track.parentIndex)
|
||||
assert utils.is_metadata(track.parentKey)
|
||||
assert utils.is_int(track.parentRatingKey)
|
||||
assert utils.is_metadata(track.parentThumb, contains='/thumb/')
|
||||
assert track.parentTitle == 'Unmastered Impulses'
|
||||
assert track.player is None
|
||||
assert track.ratingCount == 9
|
||||
assert track.ratingKey == 22
|
||||
assert track._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert track.summary == ''
|
||||
assert track.thumb == '/library/metadata/21/thumb/1484693407'
|
||||
assert utils.is_int(track.ratingKey)
|
||||
assert track._server._baseurl == utils.SERVER_BASEURL
|
||||
assert track.summary == ""
|
||||
assert utils.is_metadata(track.thumb, contains='/thumb/')
|
||||
assert track.title == 'Holy Moment'
|
||||
assert track.titleSort == 'Holy Moment'
|
||||
assert track.transcodeSession is None
|
||||
assert track.type == 'track'
|
||||
assert str(track.updatedAt.date()) == '2017-01-17'
|
||||
assert utils.is_datetime(track.updatedAt)
|
||||
assert track.username is None
|
||||
assert track.viewCount == 0
|
||||
assert track.viewOffset == 0
|
||||
assert med0.aspectRatio is None
|
||||
assert med0.audioChannels == 2
|
||||
assert med0.audioCodec == 'mp3'
|
||||
assert med0.bitrate == 385
|
||||
assert med0.container == 'mp3'
|
||||
assert med0.duration == 298606
|
||||
assert med0.height is None
|
||||
assert med0.id == 22
|
||||
assert med0._initpath == '/library/metadata/21/children'
|
||||
assert med0.optimizedForStreaming is None
|
||||
# Assign 0 med0.parts
|
||||
par0 = med0.parts[0]
|
||||
assert med0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert med0.videoCodec is None
|
||||
assert med0.videoFrameRate is None
|
||||
assert med0.videoResolution is None
|
||||
assert med0.width is None
|
||||
assert par0.container == 'mp3'
|
||||
assert par0.duration == 298606
|
||||
assert par0.file == '/media/music/unmastered_impulses/01-Holy_Moment.mp3'
|
||||
assert par0.id == 22
|
||||
assert par0._initpath == '/library/metadata/21/children'
|
||||
assert par0.key == '/library/parts/22/1484693136/file.mp3'
|
||||
assert par0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert par0.size == 14360402
|
||||
|
||||
|
||||
def test_audio_Album_get():
|
||||
""" Just a alias for track(); skip it. """
|
||||
pass
|
||||
def test_audio_Album_track(album, track=None):
|
||||
# this is not reloaded. its not that much info missing.
|
||||
track = track or album.track('Holy Moment')
|
||||
assert utils.is_datetime(track.addedAt)
|
||||
assert track.duration == 298606
|
||||
assert utils.is_metadata(track.grandparentKey)
|
||||
assert utils.is_int(track.grandparentRatingKey)
|
||||
assert track.grandparentTitle == 'Infinite State'
|
||||
assert int(track.index) == 1
|
||||
assert utils.is_metadata(track._initpath)
|
||||
assert utils.is_metadata(track.key)
|
||||
assert track.listType == 'audio'
|
||||
# Assign 0 track.media
|
||||
media = track.media[0]
|
||||
assert track.originalTitle == 'Kenneth Reitz'
|
||||
assert utils.is_int(track.parentIndex)
|
||||
assert utils.is_metadata(track.parentKey)
|
||||
assert utils.is_int(track.parentRatingKey)
|
||||
assert utils.is_metadata(track.parentThumb, contains='/thumb/')
|
||||
assert track.parentTitle == 'Unmastered Impulses'
|
||||
assert track.player is None
|
||||
assert track.ratingCount == 9
|
||||
assert utils.is_int(track.ratingKey)
|
||||
assert track._server._baseurl == utils.SERVER_BASEURL
|
||||
assert track.summary == ''
|
||||
assert utils.is_metadata(track.thumb, contains='/thumb/')
|
||||
assert track.title == 'Holy Moment'
|
||||
assert track.titleSort == 'Holy Moment'
|
||||
assert track.transcodeSession is None
|
||||
assert track.type == 'track'
|
||||
assert utils.is_datetime(track.updatedAt)
|
||||
assert track.username is None
|
||||
assert track.viewCount == 0
|
||||
assert track.viewOffset == 0
|
||||
assert media.aspectRatio is None
|
||||
assert media.audioChannels == 2
|
||||
assert media.audioCodec == 'mp3'
|
||||
assert media.bitrate == 385
|
||||
assert media.container == 'mp3'
|
||||
assert media.duration == 298606
|
||||
assert media.height is None
|
||||
assert media.id == 22
|
||||
assert utils.is_metadata(media._initpath)
|
||||
assert media.optimizedForStreaming is None
|
||||
# Assign 0 media.parts
|
||||
part = media.parts[0]
|
||||
assert media._server._baseurl == utils.SERVER_BASEURL
|
||||
assert media.videoCodec is None
|
||||
assert media.videoFrameRate is None
|
||||
assert media.videoResolution is None
|
||||
assert media.width is None
|
||||
assert part.container == 'mp3'
|
||||
assert part.duration == 298606
|
||||
assert part.file.endswith('.mp3')
|
||||
assert utils.is_int(part.id)
|
||||
assert utils.is_metadata(part._initpath)
|
||||
assert utils.is_part(part.key)
|
||||
assert part._server._baseurl == utils.SERVER_BASEURL
|
||||
assert part.size == 14360402
|
||||
|
||||
|
||||
def test_audio_Album_artist(a_music_album):
|
||||
artist = a_music_album.artist()
|
||||
def test_audio_Album_get(album):
|
||||
# alias for album.track()
|
||||
track = album.get('Holy Moment')
|
||||
test_audio_Album_track(album, track=track)
|
||||
|
||||
|
||||
def test_audio_Album_artist(album):
|
||||
artist = album.artist()
|
||||
artist.title == 'Infinite State'
|
||||
|
||||
|
||||
def test_audio_Track_attrs(a_music_album):
|
||||
track = a_music_album.get('Holy Moment')
|
||||
track.reload()
|
||||
assert str(track.addedAt.date()) == '2017-01-17'
|
||||
def test_audio_Track_attrs(album):
|
||||
track = album.get('Holy Moment').reload()
|
||||
assert utils.is_datetime(track.addedAt)
|
||||
assert track.art is None
|
||||
assert track.chapterSource is None
|
||||
assert track.duration == 298606
|
||||
assert track.grandparentArt is None
|
||||
assert track.grandparentKey == '/library/metadata/20'
|
||||
assert track.grandparentRatingKey == '20'
|
||||
assert track.grandparentThumb is None
|
||||
assert utils.is_metadata(track.grandparentKey)
|
||||
assert utils.is_int(track.grandparentRatingKey)
|
||||
if track.grandparentThumb:
|
||||
assert utils.is_metadata(track.grandparentThumb, contains='/thumb/')
|
||||
assert track.grandparentTitle == 'Infinite State'
|
||||
assert track.guid == 'local://22'
|
||||
assert track.index == '1'
|
||||
assert track._initpath == '/library/metadata/22'
|
||||
assert track.key == '/library/metadata/22'
|
||||
assert track.guid.startswith('local://')
|
||||
assert int(track.index) == 1
|
||||
assert utils.is_metadata(track._initpath)
|
||||
assert utils.is_metadata(track.key)
|
||||
assert track.lastViewedAt is None
|
||||
assert track.librarySectionID == '3'
|
||||
assert utils.is_int(track.librarySectionID)
|
||||
assert track.listType == 'audio'
|
||||
# Assign 0 track.media
|
||||
med0 = track.media[0]
|
||||
media = track.media[0]
|
||||
assert track.moods == []
|
||||
assert track.originalTitle == 'Kenneth Reitz'
|
||||
assert track.parentIndex == '1'
|
||||
assert track.parentKey == '/library/metadata/21'
|
||||
assert track.parentRatingKey == '21'
|
||||
assert track.parentThumb == '/library/metadata/21/thumb/1484693407'
|
||||
assert int(track.parentIndex) == 1
|
||||
assert utils.is_metadata(track.parentKey)
|
||||
assert utils.is_int(track.parentRatingKey)
|
||||
assert utils.is_metadata(track.parentThumb, contains='/thumb/')
|
||||
assert track.parentTitle == 'Unmastered Impulses'
|
||||
assert track.player is None
|
||||
assert track.playlistItemID is None
|
||||
assert track.primaryExtraKey is None
|
||||
assert track.ratingCount == 9
|
||||
assert track.ratingKey == 22
|
||||
assert track._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert utils.is_int(track.ratingKey)
|
||||
assert track._server._baseurl == utils.SERVER_BASEURL
|
||||
assert track.sessionKey is None
|
||||
assert track.summary == ''
|
||||
assert track.thumb == '/library/metadata/21/thumb/1484693407'
|
||||
assert utils.is_metadata(track.thumb, contains='/thumb/')
|
||||
assert track.title == 'Holy Moment'
|
||||
assert track.titleSort == 'Holy Moment'
|
||||
assert track.transcodeSession is None
|
||||
assert track.type == 'track'
|
||||
assert str(track.updatedAt.date()) == '2017-01-17'
|
||||
assert utils.is_datetime(track.updatedAt)
|
||||
assert track.username is None
|
||||
assert track.viewCount == 0
|
||||
assert track.viewOffset == 0
|
||||
assert track.viewedAt is None
|
||||
assert track.year is None
|
||||
assert med0.aspectRatio is None
|
||||
assert med0.audioChannels == 2
|
||||
assert med0.audioCodec == 'mp3'
|
||||
assert med0.bitrate == 385
|
||||
assert med0.container == 'mp3'
|
||||
assert med0.duration == 298606
|
||||
assert med0.height is None
|
||||
assert med0.id == 22
|
||||
assert med0._initpath == '/library/metadata/22'
|
||||
assert med0.optimizedForStreaming is None
|
||||
# Assign 0 med0.parts
|
||||
par0 = med0.parts[0]
|
||||
assert med0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert med0.videoCodec is None
|
||||
assert med0.videoFrameRate is None
|
||||
assert med0.videoResolution is None
|
||||
assert med0.width is None
|
||||
assert par0.container == 'mp3'
|
||||
assert par0.duration == 298606
|
||||
assert par0.file == '/media/music/unmastered_impulses/01-Holy_Moment.mp3'
|
||||
assert par0.id == 22
|
||||
assert par0._initpath == '/library/metadata/22'
|
||||
assert par0.key == '/library/parts/22/1484693136/file.mp3'
|
||||
#assert par0.media == <Media:Holy.Moment>
|
||||
assert par0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert par0.size == 14360402
|
||||
# Assign 0 par0.streams
|
||||
str0 = par0.streams[0]
|
||||
assert str0.audioChannelLayout == 'stereo'
|
||||
assert str0.bitDepth is None
|
||||
assert str0.bitrate == 320
|
||||
assert str0.bitrateMode is None
|
||||
assert str0.channels == 2
|
||||
assert str0.codec == 'mp3'
|
||||
assert str0.codecID is None
|
||||
assert str0.dialogNorm is None
|
||||
assert str0.duration is None
|
||||
assert str0.id == 44
|
||||
assert str0.index == 0
|
||||
assert str0._initpath == '/library/metadata/22'
|
||||
assert str0.language is None
|
||||
assert str0.languageCode is None
|
||||
#assert str0.part == <MediaPart:22>
|
||||
assert str0.samplingRate == 44100
|
||||
assert str0.selected is True
|
||||
assert str0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert str0.streamType == 2
|
||||
assert str0.title is None
|
||||
assert str0.type == 2
|
||||
assert media.aspectRatio is None
|
||||
assert media.audioChannels == 2
|
||||
assert media.audioCodec == 'mp3'
|
||||
assert media.bitrate == 385
|
||||
assert media.container == 'mp3'
|
||||
assert media.duration == 298606
|
||||
assert media.height is None
|
||||
assert media.id == 22
|
||||
assert utils.is_metadata(media._initpath)
|
||||
assert media.optimizedForStreaming is None
|
||||
# Assign 0 media.parts
|
||||
part = media.parts[0]
|
||||
assert media._server._baseurl == utils.SERVER_BASEURL
|
||||
assert media.videoCodec is None
|
||||
assert media.videoFrameRate is None
|
||||
assert media.videoResolution is None
|
||||
assert media.width is None
|
||||
assert part.container == 'mp3'
|
||||
assert part.duration == 298606
|
||||
assert part.file.endswith('.mp3')
|
||||
assert utils.is_int(part.id)
|
||||
assert utils.is_metadata(part._initpath)
|
||||
assert utils.is_part(part.key)
|
||||
#assert part.media == <Media:Holy.Moment>
|
||||
assert part._server._baseurl == utils.SERVER_BASEURL
|
||||
assert part.size == 14360402
|
||||
# Assign 0 part.streams
|
||||
stream = part.streams[0]
|
||||
assert stream.audioChannelLayout == 'stereo'
|
||||
assert stream.bitDepth is None
|
||||
assert stream.bitrate == 320
|
||||
assert stream.bitrateMode is None
|
||||
assert stream.channels == 2
|
||||
assert stream.codec == 'mp3'
|
||||
assert stream.codecID is None
|
||||
assert stream.dialogNorm is None
|
||||
assert stream.duration is None
|
||||
assert utils.is_int(stream.id)
|
||||
assert stream.index == 0
|
||||
assert utils.is_metadata(stream._initpath)
|
||||
assert stream.language is None
|
||||
assert stream.languageCode is None
|
||||
#assert stream.part == <MediaPart:22>
|
||||
assert stream.samplingRate == 44100
|
||||
assert stream.selected is True
|
||||
assert stream._server._baseurl == utils.SERVER_BASEURL
|
||||
assert stream.streamType == 2
|
||||
assert stream.title is None
|
||||
assert stream.type == 2
|
||||
|
||||
|
||||
def test_audio_Track_album(a_music_album):
|
||||
tracks = a_music_album.tracks()
|
||||
assert tracks[0].album() == a_music_album
|
||||
def test_audio_Track_album(album):
|
||||
tracks = album.tracks()
|
||||
assert tracks[0].album() == album
|
||||
|
||||
|
||||
def test_audio_Track_artist(a_music_album, a_artist):
|
||||
tracks = a_music_album.tracks()
|
||||
assert tracks[0].artist() == a_artist
|
||||
def test_audio_Track_artist(album, artist):
|
||||
tracks = album.tracks()
|
||||
assert tracks[0].artist() == artist
|
||||
|
||||
|
||||
def test_audio_Audio_section(a_artist, a_music_album, a_track):
|
||||
assert a_artist.section()
|
||||
assert a_music_album.section()
|
||||
assert a_track.section()
|
||||
assert a_track.section().key == a_music_album.section().key == a_artist.section().key
|
||||
def test_audio_Audio_section(artist, album, track):
|
||||
assert artist.section()
|
||||
assert album.section()
|
||||
assert track.section()
|
||||
assert track.section().key == album.section().key == artist.section().key
|
||||
|
||||
|
||||
def test_audio_Track_download(monkeydownload, tmpdir, a_track):
|
||||
f = a_track.download(savepath=str(tmpdir))
|
||||
def test_audio_Track_download(monkeydownload, tmpdir, track):
|
||||
f = track.download(savepath=str(tmpdir))
|
||||
assert f
|
||||
|
||||
|
||||
def test_audio_album_download(monkeydownload, a_music_album, tmpdir):
|
||||
f = a_music_album.download(savepath=str(tmpdir))
|
||||
def test_audio_album_download(monkeydownload, album, tmpdir):
|
||||
f = album.download(savepath=str(tmpdir))
|
||||
assert len(f) == 14
|
||||
|
||||
|
||||
def test_audio_Artist_download(monkeydownload, a_artist, tmpdir):
|
||||
f = a_artist.download(savepath=str(tmpdir))
|
||||
def test_audio_Artist_download(monkeydownload, artist, tmpdir):
|
||||
f = artist.download(savepath=str(tmpdir))
|
||||
assert len(f) == 14
|
||||
|
|
|
@ -2,7 +2,42 @@
|
|||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.req_client
|
||||
def test_list_clients(account, plex, client):
|
||||
# List resources from MyPlex
|
||||
print('\nResources listed on MyPlex')
|
||||
print('---------------------------')
|
||||
resources = account.resources()
|
||||
for resource in resources:
|
||||
print('%s (%s)' % (resource.name, resource.product))
|
||||
for connection in resource.connections:
|
||||
print('* baseurl=%s, token=%s' % (connection.uri, resource.accessToken))
|
||||
print('* baseurl=%s, token=%s' % (connection.httpuri, resource.accessToken))
|
||||
assert resources, 'MyPlex is not listing any devlices.'
|
||||
# List devices from MyPlex
|
||||
print('\nDevices listed on MyPlex')
|
||||
print('--------------------------')
|
||||
devices = account.devices()
|
||||
for device in devices:
|
||||
print('%s (%s)' % (device.name, device.product))
|
||||
for connection in device.connections:
|
||||
print('* baseurl=%s, token=%s' % (connection, device.token))
|
||||
assert devices, 'MyPlex is not listing any devlices.'
|
||||
# List clients from PlexServer
|
||||
clients = plex.clients()
|
||||
print('\nClients listed on PlexServer')
|
||||
print('------------------------------')
|
||||
for client in clients:
|
||||
print('%s (%s)' % (client.title, client.product))
|
||||
print('* baseurl=%s, token=%s' % (client._baseurl, plex._token))
|
||||
assert clients, 'PlexServer is not listing any clients.'
|
||||
|
||||
|
||||
@pytest.mark.client
|
||||
def test_test(client):
|
||||
print(client)
|
||||
|
||||
|
||||
@pytest.mark.client
|
||||
def _test_client_PlexClient__loadData(pms):
|
||||
pass
|
||||
|
||||
|
|
|
@ -1,212 +1,203 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
from plexapi.exceptions import NotFound
|
||||
from . import conftest as utils
|
||||
|
||||
|
||||
def test_library_Library_section(pms):
|
||||
sections = pms.library.sections()
|
||||
def test_library_Library_section(plex):
|
||||
sections = plex.library.sections()
|
||||
assert len(sections) == 4
|
||||
lfs = 'TV Shows'
|
||||
section_name = pms.library.section(lfs)
|
||||
assert section_name.title == lfs
|
||||
section_name = plex.library.section('TV Shows')
|
||||
assert section_name.title == 'TV Shows'
|
||||
with pytest.raises(NotFound):
|
||||
assert pms.library.section('gfdsas')
|
||||
assert plex.library.section('cant-find-me')
|
||||
|
||||
|
||||
def test_library_Library_sectionByID_is_equal_section(pms, freshpms):
|
||||
def test_library_Library_sectionByID_is_equal_section(plex, plex2):
|
||||
# test that sctionmyID refreshes the section if the key is missing
|
||||
# this is needed if there isnt any cached sections
|
||||
assert freshpms.library.sectionByID('1')
|
||||
assert pms.library.sectionByID('1').uuid == pms.library.section('Movies').uuid
|
||||
assert plex2.library.sectionByID('1')
|
||||
assert plex.library.sectionByID('1').uuid == plex.library.section('Movies').uuid
|
||||
|
||||
|
||||
def test_library_sectionByID_with_attrs(pms):
|
||||
m = pms.library.sectionByID('1')
|
||||
assert m.agent == 'com.plexapp.agents.imdb'
|
||||
assert m.allowSync is False
|
||||
assert m.art == '/:/resources/movie-fanart.jpg'
|
||||
assert '/library/sections/1/composite/' in m.composite
|
||||
assert m.createdAt > datetime(2017, 1, 16)
|
||||
assert m.filters == '1'
|
||||
assert m._initpath == '/library/sections'
|
||||
assert m.key == '1'
|
||||
assert m.language == 'en'
|
||||
assert m.locations == ['/media/movies']
|
||||
assert m.refreshing is False
|
||||
assert m.scanner == 'Plex Movie Scanner'
|
||||
assert m._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert m.thumb == '/:/resources/movie.png'
|
||||
assert m.title == 'Movies'
|
||||
assert m.type == 'movie'
|
||||
assert m.updatedAt > datetime(2017, 1, 16)
|
||||
assert m.uuid == '2b72d593-3881-43f4-a8b8-db541bd3535a'
|
||||
def test_library_sectionByID_with_attrs(plex):
|
||||
section = plex.library.sectionByID('1')
|
||||
assert section.agent == 'com.plexapp.agents.imdb'
|
||||
assert section.allowSync is False
|
||||
assert section.art == '/:/resources/movie-fanart.jpg'
|
||||
assert '/library/sections/1/composite/' in section.composite
|
||||
assert utils.is_datetime(section.createdAt)
|
||||
assert section.filters == '1'
|
||||
assert section._initpath == '/library/sections'
|
||||
assert section.key == '1'
|
||||
assert section.language == 'en'
|
||||
assert len(section.locations) == 1
|
||||
assert len(section.locations[0]) >= 10
|
||||
assert section.refreshing is False
|
||||
assert section.scanner == 'Plex Movie Scanner'
|
||||
assert section._server._baseurl == utils.SERVER_BASEURL
|
||||
assert section.thumb == '/:/resources/movie.png'
|
||||
assert section.title == 'Movies'
|
||||
assert section.type == 'movie'
|
||||
assert utils.is_datetime(section.updatedAt)
|
||||
assert len(section.uuid) == 36
|
||||
|
||||
|
||||
def test_library_section_get_movie(pms): # fix me
|
||||
m = pms.library.section('Movies').get('16 blocks')
|
||||
assert m
|
||||
def test_library_section_get_movie(plex):
|
||||
assert plex.library.section('Movies').get('16 blocks')
|
||||
|
||||
|
||||
def test_library_section_delete(monkeypatch, pms):
|
||||
m = pms.library.section('Movies')
|
||||
def test_library_section_delete(monkeypatch, movies):
|
||||
monkeypatch.delattr("requests.sessions.Session.request")
|
||||
try:
|
||||
m.delete()
|
||||
movies.delete()
|
||||
except AttributeError:
|
||||
pass # this will always raise because there is no request anymore.
|
||||
# will always raise because there is no request
|
||||
pass
|
||||
|
||||
|
||||
def test_library_fetchItem(pms):
|
||||
m = pms.library.fetchItem('/library/metadata/1')
|
||||
f = pms.library.fetchItem(1)
|
||||
assert m.title == '16 Blocks'
|
||||
assert f == m
|
||||
def test_library_fetchItem(plex, movie):
|
||||
item1 = plex.library.fetchItem('/library/metadata/%s' % movie.ratingKey)
|
||||
item2 = plex.library.fetchItem(movie.ratingKey)
|
||||
assert item1.title == '16 Blocks'
|
||||
assert item1 == item2 == movie
|
||||
|
||||
|
||||
def test_library_onDeck(pms):
|
||||
assert len(list(pms.library.onDeck()))
|
||||
def test_library_onDeck(plex):
|
||||
assert len(list(plex.library.onDeck()))
|
||||
|
||||
|
||||
def test_library_recentlyAdded(pms):
|
||||
assert len(list(pms.library.recentlyAdded()))
|
||||
def test_library_recentlyAdded(plex):
|
||||
assert len(list(plex.library.recentlyAdded()))
|
||||
|
||||
|
||||
def test_library_search(pms):
|
||||
m = pms.library.search('16 blocks')[0]
|
||||
assert m.title == '16 Blocks'
|
||||
def test_library_search(plex):
|
||||
item = plex.library.search('16 blocks')[0]
|
||||
assert item.title == '16 Blocks'
|
||||
|
||||
def test_library_add_edit_delete(pms):
|
||||
d = dict(name='zomg strange11', type='movie', agent='com.plexapp.agents.imdb',
|
||||
scanner='Plex Movie Scanner', language='en')
|
||||
|
||||
rn = dict(name='a renamed lib', type='movie', agent='com.plexapp.agents.imdb')
|
||||
|
||||
# We dont want to add a location because we dont want to start scanning.
|
||||
pms.library.add(**d)
|
||||
|
||||
assert pms.library.section('zomg strange11')
|
||||
|
||||
edited_library = pms.library.section('zomg strange11').edit(**rn)
|
||||
def test_library_add_edit_delete(plex):
|
||||
# Dont add a location to prevent scanning scanning
|
||||
plex.library.add(name='zomg strange11', type='movie', agent='com.plexapp.agents.imdb',
|
||||
scanner='Plex Movie Scanner', language='en')
|
||||
assert plex.library.section('zomg strange11')
|
||||
edited_library = plex.library.section('zomg strange11').edit(name='a renamed lib',
|
||||
type='movie', agent='com.plexapp.agents.imdb')
|
||||
assert edited_library.title == 'a renamed lib'
|
||||
|
||||
pms.library.section('a renamed lib').delete()
|
||||
plex.library.section('a renamed lib').delete()
|
||||
|
||||
|
||||
def test_library_Library_cleanBundle(pms):
|
||||
pms.library.cleanBundles()
|
||||
def test_library_Library_cleanBundle(plex):
|
||||
plex.library.cleanBundles()
|
||||
|
||||
|
||||
def test_library_Library_optimize(pms):
|
||||
pms.library.optimize()
|
||||
def test_library_Library_optimize(plex):
|
||||
plex.library.optimize()
|
||||
|
||||
|
||||
def test_library_Library_emptyTrash(pms):
|
||||
pms.library.emptyTrash()
|
||||
def test_library_Library_emptyTrash(plex):
|
||||
plex.library.emptyTrash()
|
||||
|
||||
|
||||
def _test_library_Library_refresh(pms):
|
||||
pms.library.refresh() # fix mangle and proof the sections attrs
|
||||
|
||||
def test_library_Library_update(pms):
|
||||
pms.library.update()
|
||||
|
||||
def test_library_Library_cancelUpdate(pms):
|
||||
pms.library.cancelUpdate()
|
||||
def _test_library_Library_refresh(plex):
|
||||
# TODO: fix mangle and proof the sections attrs
|
||||
plex.library.refresh()
|
||||
|
||||
|
||||
def test_library_Library_deleteMediaPreviews(pms):
|
||||
pms.library.deleteMediaPreviews()
|
||||
def test_library_Library_update(plex):
|
||||
plex.library.update()
|
||||
|
||||
|
||||
def _test_library_MovieSection_refresh(a_movie_section):
|
||||
a_movie_section.refresh()
|
||||
def test_library_Library_cancelUpdate(plex):
|
||||
plex.library.cancelUpdate()
|
||||
|
||||
|
||||
def test_library_MovieSection_update(a_movie_section):
|
||||
a_movie_section.update()
|
||||
def test_library_Library_deleteMediaPreviews(plex):
|
||||
plex.library.deleteMediaPreviews()
|
||||
|
||||
|
||||
def test_library_MovieSection_cancelUpdate(a_movie_section):
|
||||
a_movie_section.cancelUpdate()
|
||||
|
||||
def test_librarty_deleteMediaPreviews(a_movie_section):
|
||||
a_movie_section.deleteMediaPreviews()
|
||||
def _test_library_MovieSection_refresh(movies):
|
||||
movies.refresh()
|
||||
|
||||
|
||||
def _test_library_MovieSection_refresh(a_movie_section):
|
||||
a_movie_section.refresh() # check how much this breaks test before enabling it.
|
||||
def test_library_MovieSection_update(movies):
|
||||
movies.update()
|
||||
|
||||
|
||||
def test_library_MovieSection_onDeck(a_movie_section):
|
||||
assert len(a_movie_section.onDeck())
|
||||
def test_library_MovieSection_cancelUpdate(movies):
|
||||
movies.cancelUpdate()
|
||||
|
||||
|
||||
def test_library_MovieSection_recentlyAdded(a_movie_section):
|
||||
assert len(a_movie_section.recentlyAdded())
|
||||
def test_librarty_deleteMediaPreviews(movies):
|
||||
movies.deleteMediaPreviews()
|
||||
|
||||
|
||||
def test_library_MovieSection_analyze(a_movie_section):
|
||||
a_movie_section.analyze()
|
||||
def test_library_MovieSection_onDeck(movies):
|
||||
assert len(movies.onDeck())
|
||||
|
||||
|
||||
def test_library_ShowSection_searchShows(a_tv_section):
|
||||
s = a_tv_section.searchShows(**{'title': 'The 100'})
|
||||
assert s
|
||||
def test_library_MovieSection_recentlyAdded(movies):
|
||||
assert len(movies.recentlyAdded())
|
||||
|
||||
|
||||
def test_library_ShowSection_searchEpisodes(a_tv_section):
|
||||
s = a_tv_section.searchEpisodes(**{'title': 'Pilot'})
|
||||
assert s
|
||||
def test_library_MovieSection_analyze(movies):
|
||||
movies.analyze()
|
||||
|
||||
|
||||
def test_library_ShowSection_recentlyAdded(a_tv_section):
|
||||
assert len(a_tv_section.recentlyAdded())
|
||||
def test_library_ShowSection_searchShows(tvshows):
|
||||
assert tvshows.searchShows(title='The 100')
|
||||
|
||||
|
||||
def test_library_MusicSection_albums(a_music_section):
|
||||
assert len(a_music_section.albums())
|
||||
def test_library_ShowSection_searchEpisodes(tvshows):
|
||||
assert tvshows.searchEpisodes(title='Pilot')
|
||||
|
||||
|
||||
def test_library_MusicSection_searchTracks(a_music_section):
|
||||
assert len(a_music_section.searchTracks(**{'title': 'Holy Moment'}))
|
||||
def test_library_ShowSection_recentlyAdded(tvshows):
|
||||
assert len(tvshows.recentlyAdded())
|
||||
|
||||
|
||||
def test_library_MusicSection_searchAlbums(a_music_section):
|
||||
assert len(a_music_section.searchAlbums(**{'title': 'Unmastered Impulses'}))
|
||||
def test_library_MusicSection_albums(music):
|
||||
assert len(music.albums())
|
||||
|
||||
|
||||
def test_library_PhotoSection_searchAlbums(a_photo_section):
|
||||
albums = a_photo_section.searchAlbums('photo_album1')
|
||||
def test_library_MusicSection_searchTracks(music):
|
||||
assert len(music.searchTracks(title='Holy Moment'))
|
||||
|
||||
|
||||
def test_library_MusicSection_searchAlbums(music):
|
||||
assert len(music.searchAlbums(title='Unmastered Impulses'))
|
||||
|
||||
|
||||
def test_library_PhotoSection_searchAlbums(photos, photoalbum):
|
||||
title = photoalbum.title
|
||||
albums = photos.searchAlbums(title)
|
||||
assert len(albums)
|
||||
print([i.TYPE for i in albums])
|
||||
|
||||
|
||||
def test_library_PhotoSection_searchPhotos(a_photo_section):
|
||||
assert len(a_photo_section.searchPhotos('lolcat2'))
|
||||
def test_library_PhotoSection_searchPhotos(photos, photoalbum):
|
||||
title = photoalbum.photos()[0].title
|
||||
assert len(photos.searchPhotos(title))
|
||||
|
||||
|
||||
# Start on library search
|
||||
def test_library_and_section_search_for_movie(pms):
|
||||
def test_library_and_section_search_for_movie(plex):
|
||||
find = '16 blocks'
|
||||
l_search = pms.library.search(find)
|
||||
s_search = pms.library.section('Movies').search(find)
|
||||
l_search = plex.library.search(find)
|
||||
s_search = plex.library.section('Movies').search(find)
|
||||
assert l_search == s_search
|
||||
|
||||
|
||||
def test_search_with_apostrophe(pms):
|
||||
show_title = "Marvel's Daredevil" # Test ' in show title
|
||||
result_root = pms.search(show_title)
|
||||
result_shows = pms.library.section('TV Shows').search(show_title)
|
||||
def test_search_with_apostrophe(plex):
|
||||
show_title = "Marvel's Daredevil"
|
||||
result_root = plex.search(show_title)
|
||||
result_shows = plex.library.section('TV Shows').search(show_title)
|
||||
assert result_root
|
||||
assert result_shows
|
||||
assert result_root == result_shows
|
||||
|
||||
|
||||
def test_crazy_search(pms, a_movie):
|
||||
movie = a_movie
|
||||
movies = pms.library.section('Movies')
|
||||
assert movie in pms.library.search(genre=29, libtype='movie')
|
||||
def test_crazy_search(plex, movie):
|
||||
movies = plex.library.section('Movies')
|
||||
assert movie in movies.search(actor=movie.actors[0], sort='titleSort'), 'Unable to search movie by actor.'
|
||||
assert movie in movies.search(director=movie.directors[0]), 'Unable to search movie by director.'
|
||||
assert movie in movies.search(year=['2006', '2007']), 'Unable to search movie by year.'
|
||||
|
|
|
@ -11,21 +11,24 @@ SKIP_EXAMPLES = ['Example 4']
|
|||
@pytest.mark.skipif(os.name == 'nt', reason='No make.bat specified for Windows')
|
||||
def test_build_documentation():
|
||||
docroot = join(dirname(dirname(abspath(__file__))), 'docs')
|
||||
cmd = shlex.split('sphinx-build . _build')
|
||||
cmd = shlex.split('sphinx-build -aE . _build')
|
||||
proc = subprocess.Popen(cmd, cwd=docroot, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
status = proc.wait()
|
||||
assert status == 0
|
||||
issues = []
|
||||
for output in proc.communicate():
|
||||
for line in str(output).split('\\n'):
|
||||
line = line.lower().strip()
|
||||
assert 'warning' not in line
|
||||
assert 'error' not in line
|
||||
assert 'traceback' not in line
|
||||
if 'warning' in line or 'error' in line or 'traceback' in line:
|
||||
issues.append(line)
|
||||
for line in issues:
|
||||
print(line)
|
||||
assert not issues
|
||||
|
||||
|
||||
def test_readme_examples(pms):
|
||||
def test_readme_examples(plex):
|
||||
failed = 0
|
||||
examples = _fetch_examples(pms)
|
||||
examples = _fetch_examples()
|
||||
assert len(examples), 'No examples found in README'
|
||||
for title, example in examples:
|
||||
if _check_run_example(title):
|
||||
|
@ -38,7 +41,7 @@ def test_readme_examples(pms):
|
|||
assert not failed, '%s examples raised an exception.' % failed
|
||||
|
||||
|
||||
def _fetch_examples(pms):
|
||||
def _fetch_examples():
|
||||
parsing = False
|
||||
examples = []
|
||||
filepath = join(dirname(dirname(abspath(__file__))), 'README.rst')
|
||||
|
@ -48,7 +51,7 @@ def _fetch_examples(pms):
|
|||
if line.startswith('# Example '):
|
||||
parsing = True
|
||||
title = line.lstrip('# ')
|
||||
examples.append([title, ['plex = pms']])
|
||||
examples.append([title, []])
|
||||
elif parsing and line == '':
|
||||
parsing = False
|
||||
elif parsing:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
def test_myplex_accounts(plex_account, pms):
|
||||
account = plex_account
|
||||
|
||||
def test_myplex_accounts(account, plex):
|
||||
assert account, 'Must specify username, password & resource to run this test.'
|
||||
print('MyPlexAccount:')
|
||||
print('username: %s' % account.username)
|
||||
|
@ -13,7 +13,7 @@ def test_myplex_accounts(plex_account, pms):
|
|||
assert account.email, 'Account has no email'
|
||||
assert account.home is not None, 'Account has no home'
|
||||
assert account.queueEmail, 'Account has no queueEmail'
|
||||
account = pms.account()
|
||||
account = plex.account()
|
||||
print('Local PlexServer.account():')
|
||||
print('username: %s' % account.username)
|
||||
print('authToken: %s' % account.authToken)
|
||||
|
@ -23,8 +23,7 @@ def test_myplex_accounts(plex_account, pms):
|
|||
assert account.signInState, 'Account has no signInState'
|
||||
|
||||
|
||||
def test_myplex_resources(plex_account):
|
||||
account = plex_account
|
||||
def test_myplex_resources(account):
|
||||
assert account, 'Must specify username, password & resource to run this test.'
|
||||
resources = account.resources()
|
||||
for resource in resources:
|
||||
|
@ -35,17 +34,15 @@ def test_myplex_resources(plex_account):
|
|||
assert resources, 'No resources found for account: %s' % account.name
|
||||
|
||||
|
||||
def test_myplex_connect_to_resource(plex_account):
|
||||
for resource in plex_account.resources():
|
||||
if resource.name == 'PMS_API_TEST_SERVER':
|
||||
def test_myplex_connect_to_resource(plex, account):
|
||||
servername = plex.friendlyName
|
||||
for resource in account.resources():
|
||||
if resource.name == servername:
|
||||
break
|
||||
server = resource.connect()
|
||||
assert 'Ohno' in server.url('Ohno')
|
||||
assert server
|
||||
assert resource.connect(timeout=10)
|
||||
|
||||
|
||||
def test_myplex_devices(plex_account):
|
||||
account = plex_account
|
||||
def test_myplex_devices(account):
|
||||
devices = account.devices()
|
||||
for device in devices:
|
||||
name = device.name or 'Unknown'
|
||||
|
@ -54,9 +51,7 @@ def test_myplex_devices(plex_account):
|
|||
assert devices, 'No devices found for account: %s' % account.name
|
||||
|
||||
|
||||
#@pytest.mark.req_client # this need to be recorded?
|
||||
def _test_myplex_connect_to_device(plex_account):
|
||||
account = plex_account
|
||||
def _test_myplex_connect_to_device(account):
|
||||
devices = account.devices()
|
||||
for device in devices:
|
||||
if device.name == 'some client name' and len(device.connections):
|
||||
|
@ -65,11 +60,10 @@ def _test_myplex_connect_to_device(plex_account):
|
|||
assert client, 'Unable to connect to device'
|
||||
|
||||
|
||||
def test_myplex_users(plex_account):
|
||||
account = plex_account
|
||||
def test_myplex_users(account):
|
||||
users = account.users()
|
||||
assert users, 'Found no users on account: %s' % account.name
|
||||
print('Found %s users.' % len(users))
|
||||
user = account.user('Hellowlol')
|
||||
user = account.user(users[0].title)
|
||||
print('Found user: %s' % user)
|
||||
assert user, 'Could not find user Hellowlol'
|
||||
assert user, 'Could not find user %s' % users[0].title
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
def test_navigate_around_show(plex_account, pms):
|
||||
show = pms.library.section('TV Shows').get('The 100')
|
||||
|
||||
def test_navigate_around_show(account, plex):
|
||||
show = plex.library.section('TV Shows').get('The 100')
|
||||
seasons = show.seasons()
|
||||
season = show.season('Season 1')
|
||||
episodes = show.episodes()
|
||||
|
@ -16,8 +17,8 @@ def test_navigate_around_show(plex_account, pms):
|
|||
assert episode.season() == season, 'episode.season() doesnt match expected season.'
|
||||
|
||||
|
||||
def test_navigate_around_artist(plex_account, pms):
|
||||
artist = pms.library.section('Music').get('Infinite State')
|
||||
def test_navigate_around_artist(account, plex):
|
||||
artist = plex.library.section('Music').get('Infinite State')
|
||||
albums = artist.albums()
|
||||
album = artist.album('Unmastered Impulses')
|
||||
tracks = artist.tracks()
|
||||
|
|
|
@ -2,52 +2,44 @@
|
|||
import pytest, time
|
||||
|
||||
|
||||
def test_create_playlist(pms, a_show):
|
||||
def test_create_playlist(plex, show):
|
||||
# create the playlist
|
||||
title = 'test_create_playlist_a_show'
|
||||
title = 'test_create_playlist_show'
|
||||
#print('Creating playlist %s..' % title)
|
||||
episodes = a_show.episodes()
|
||||
playlist = pms.createPlaylist(title, episodes[:3])
|
||||
episodes = show.episodes()
|
||||
playlist = plex.createPlaylist(title, episodes[:3])
|
||||
try:
|
||||
items = playlist.items()
|
||||
#log(4, 'Title: %s' % playlist.title)
|
||||
#log(4, 'Items: %s' % items)
|
||||
#log(4, 'Duration: %s min' % int(playlist.duration / 60000.0))
|
||||
# Test create playlist
|
||||
assert playlist.title == title, 'Playlist not created successfully.'
|
||||
assert len(items) == 3, 'Playlist does not contain 3 items.'
|
||||
assert items[0].ratingKey == episodes[0].ratingKey, 'Items not in proper order [0a].'
|
||||
assert items[1].ratingKey == episodes[1].ratingKey, 'Items not in proper order [1a].'
|
||||
assert items[2].ratingKey == episodes[2].ratingKey, 'Items not in proper order [2a].'
|
||||
# move items around (b)
|
||||
#print('Testing move items..')
|
||||
# Test move items around (b)
|
||||
playlist.moveItem(items[1])
|
||||
items = playlist.items()
|
||||
assert items[0].ratingKey == episodes[1].ratingKey, 'Items not in proper order [0b].'
|
||||
assert items[1].ratingKey == episodes[0].ratingKey, 'Items not in proper order [1b].'
|
||||
assert items[2].ratingKey == episodes[2].ratingKey, 'Items not in proper order [2b].'
|
||||
# move items around (c)
|
||||
# Test move items around (c)
|
||||
playlist.moveItem(items[0], items[1])
|
||||
items = playlist.items()
|
||||
assert items[0].ratingKey == episodes[0].ratingKey, 'Items not in proper order [0c].'
|
||||
assert items[1].ratingKey == episodes[1].ratingKey, 'Items not in proper order [1c].'
|
||||
assert items[2].ratingKey == episodes[2].ratingKey, 'Items not in proper order [2c].'
|
||||
# add an item
|
||||
#print('Testing add item: %s' % episodes[3])
|
||||
# Test add item
|
||||
playlist.addItems(episodes[3])
|
||||
items = playlist.items()
|
||||
#log(4, '4th Item: %s' % items[3])
|
||||
assert items[3].ratingKey == episodes[3].ratingKey, 'Missing added item: %s' % episodes[3]
|
||||
# add two items
|
||||
#print('Testing add item: %s' % episodes[4:6])
|
||||
# Test add two items
|
||||
playlist.addItems(episodes[4:6])
|
||||
items = playlist.items()
|
||||
#log(4, '5th+ Items: %s' % items[4:])
|
||||
assert items[4].ratingKey == episodes[4].ratingKey, 'Missing added item: %s' % episodes[4]
|
||||
assert items[5].ratingKey == episodes[5].ratingKey, 'Missing added item: %s' % episodes[5]
|
||||
assert len(items) == 6, 'Playlist should have 6 items, %s found' % len(items)
|
||||
# remove item
|
||||
# Test remove item
|
||||
toremove = items[3]
|
||||
#print('Testing remove item: %s' % toremove)
|
||||
playlist.removeItem(toremove)
|
||||
items = playlist.items()
|
||||
assert toremove not in items, 'Removed item still in playlist: %s' % items[3]
|
||||
|
@ -57,41 +49,44 @@ def test_create_playlist(pms, a_show):
|
|||
|
||||
|
||||
@pytest.mark.req_client
|
||||
def test_playlist_play(pms):
|
||||
def test_playlist_play(plex):
|
||||
client = getclient(CONFIG.client, CONFIG.client_baseurl, plex)
|
||||
artist = plex.library.section(CONFIG.audio_section).get(CONFIG.audio_artist)
|
||||
album = artist.album(CONFIG.audio_album)
|
||||
pl_name = 'test_play_playlist'
|
||||
playlist = plex.createPlaylist(pl_name, album)
|
||||
try:
|
||||
playlist_name = 'test_play_playlist'
|
||||
playlist = plex.createPlaylist(playlist_name, album)
|
||||
client.playMedia(playlist); time.sleep(5)
|
||||
client.stop('music'); time.sleep(1)
|
||||
finally:
|
||||
playlist.delete()
|
||||
assert pl_name not in [i.title for i in pms.playlists()]
|
||||
assert playlist_name not in [i.title for i in plex.playlists()]
|
||||
|
||||
|
||||
def test_playlist_photos(pms, a_photo_album):
|
||||
album = a_photo_album
|
||||
def test_playlist_photos(plex, photoalbum):
|
||||
album = photoalbum
|
||||
photos = album.photos()
|
||||
pl_name = 'test_playlist_photos'
|
||||
playlist = pms.createPlaylist(pl_name, photos)
|
||||
try:
|
||||
assert len(playlist.items()) == 4
|
||||
playlist_name = 'test_playlist_photos'
|
||||
playlist = plex.createPlaylist(playlist_name, photos)
|
||||
assert len(playlist.items()) >= 1
|
||||
finally:
|
||||
playlist.delete()
|
||||
assert pl_name not in [i.title for i in pms.playlists()]
|
||||
assert playlist_name not in [i.title for i in plex.playlists()]
|
||||
|
||||
|
||||
def test_playlist_playQueue(pms):
|
||||
pl = pms.playlists()[0]
|
||||
pq = pl.playQueue(**dict(shuffle=1))
|
||||
assert 'shuffle=1' in pq._initpath
|
||||
assert pq.playQueueShuffled is True
|
||||
def test_playlist_playQueue(plex, album):
|
||||
try:
|
||||
playlist = plex.createPlaylist('test_playlist', album)
|
||||
playqueue = playlist.playQueue(**dict(shuffle=1))
|
||||
assert 'shuffle=1' in playqueue._initpath
|
||||
assert playqueue.playQueueShuffled is True
|
||||
finally:
|
||||
playlist.delete()
|
||||
|
||||
|
||||
@pytest.mark.req_client
|
||||
def _test_play_photos(account, plex):
|
||||
def test_play_photos(account, plex):
|
||||
client = getclient('iphone-mike', CONFIG.client_baseurl, plex)
|
||||
photosection = plex.library.section(CONFIG.photo_section)
|
||||
album = photosection.get(CONFIG.photo_album)
|
||||
|
@ -101,9 +96,9 @@ def _test_play_photos(account, plex):
|
|||
time.sleep(2)
|
||||
|
||||
|
||||
def test_play_queues(pms):
|
||||
episode = pms.library.section('TV Shows').get('the 100').get('Pilot')
|
||||
playqueue = pms.createPlayQueue(episode)
|
||||
def test_playqueues(plex):
|
||||
episode = plex.library.section('TV Shows').get('the 100').get('Pilot')
|
||||
playqueue = plex.createPlayQueue(episode)
|
||||
assert len(playqueue.items) == 1, 'No items in play queue.'
|
||||
assert playqueue.items[0].title == episode.title, 'Wrong show queued.'
|
||||
assert playqueue.playQueueID, 'Play queue ID not set.'
|
||||
|
|
|
@ -1,218 +1,223 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os, pytest
|
||||
from plexapi import CONFIG
|
||||
import pytest, re, time
|
||||
from plexapi.exceptions import BadRequest, NotFound
|
||||
from plexapi.server import PlexServer
|
||||
from plexapi.utils import download
|
||||
from PIL import Image, ImageStat
|
||||
from requests import Session
|
||||
from . import conftest as utils
|
||||
|
||||
|
||||
def test_server_attr(pms):
|
||||
assert pms._baseurl == 'http://138.68.157.5:32400'
|
||||
assert pms.friendlyName == 'PMS_API_TEST_SERVER'
|
||||
assert pms.machineIdentifier == 'e42470b5c527c7e5ebbdc017b5a32c8c683f6f8b'
|
||||
assert pms.myPlex is True
|
||||
assert pms.myPlexMappingState == 'mapped'
|
||||
assert pms.myPlexSigninState == 'ok'
|
||||
assert pms.myPlexSubscription == '0'
|
||||
assert pms.myPlexUsername == 'testplexapi@gmail.com'
|
||||
assert pms.platform == 'Linux'
|
||||
assert pms.platformVersion == '4.4.0-59-generic (#80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017)'
|
||||
#assert pms.session == <requests.sessions.Session object at 0x029A5E10>
|
||||
assert pms._token == CONFIG.get('auth.server_token')
|
||||
assert pms.transcoderActiveVideoSessions == 0
|
||||
#assert str(pms.updatedAt.date()) == '2017-01-20'
|
||||
assert pms.version == '1.3.3.3148-b38628e'
|
||||
def test_server_attr(plex):
|
||||
assert plex._baseurl == utils.SERVER_BASEURL
|
||||
assert len(plex.friendlyName) >= 1
|
||||
assert len(plex.machineIdentifier) == 40
|
||||
assert plex.myPlex is True
|
||||
assert plex.myPlexMappingState == 'mapped'
|
||||
assert plex.myPlexSigninState == 'ok'
|
||||
assert plex.myPlexSubscription == '0'
|
||||
assert re.match(utils.REGEX_EMAIL, plex.myPlexUsername)
|
||||
assert plex.platform in ('Linux', 'Windows')
|
||||
assert len(plex.platformVersion) >= 5
|
||||
assert plex._token == utils.SERVER_TOKEN
|
||||
assert plex.transcoderActiveVideoSessions == 0
|
||||
assert utils.is_datetime(plex.updatedAt)
|
||||
assert len(plex.version) >= 5
|
||||
|
||||
|
||||
def test_server_alert_listener(pms, a_movie_section):
|
||||
import time
|
||||
messages = []
|
||||
listener = pms.startAlertListener(messages.append)
|
||||
a_movie_section.refresh()
|
||||
time.sleep(5)
|
||||
listener.stop()
|
||||
assert len(messages) >= 3
|
||||
def test_server_alert_listener(plex, movies):
|
||||
try:
|
||||
messages = []
|
||||
listener = plex.startAlertListener(messages.append)
|
||||
movies.refresh()
|
||||
time.sleep(5)
|
||||
assert len(messages) >= 3
|
||||
finally:
|
||||
listener.stop()
|
||||
|
||||
|
||||
@pytest.mark.req_client
|
||||
def test_server_session():
|
||||
# TODO: Implement test_server_session
|
||||
pass
|
||||
|
||||
|
||||
def test_server_library(pms):
|
||||
assert pms.library
|
||||
def test_server_library(plex):
|
||||
# TODO: Implement test_server_library
|
||||
assert plex.library
|
||||
|
||||
|
||||
def test_server_url(pms):
|
||||
assert 'ohno' in pms.url('ohno')
|
||||
def test_server_url(plex):
|
||||
assert 'ohno' in plex.url('ohno')
|
||||
|
||||
|
||||
def test_server_transcodeImage(tmpdir, pms, a_show):
|
||||
# Ideally we should also test the black white but this has to do for now.
|
||||
from PIL import Image
|
||||
def test_server_transcodeImage(tmpdir, plex, show):
|
||||
width, height = 500, 500
|
||||
img_url_resize = pms.transcodeImage(a_show.banner, height, width)
|
||||
gray = img_url_resize = pms.transcodeImage(a_show.banner, height, width, saturation=0)
|
||||
resized_image = download(img_url_resize, savepath=str(tmpdir), filename='resize_image')
|
||||
org_image = download(a_show._server.url(a_show.banner), savepath=str(tmpdir), filename='org_image')
|
||||
gray_image = download(gray, savepath=str(tmpdir), filename='gray_image')
|
||||
with Image.open(resized_image) as im:
|
||||
assert width, height == im.size
|
||||
with Image.open(org_image) as im:
|
||||
assert width, height != im.size
|
||||
assert _detect_color_image(gray_image, thumb_size=150) == 'grayscale'
|
||||
imgurl = plex.transcodeImage(show.banner, height, width)
|
||||
gray = imgurl = plex.transcodeImage(show.banner, height, width, saturation=0)
|
||||
resized_img = download(imgurl, savepath=str(tmpdir), filename='resize_image')
|
||||
original_img = download(show._server.url(show.banner), savepath=str(tmpdir), filename='original_img')
|
||||
grayscale_img = download(gray, savepath=str(tmpdir), filename='grayscale_img')
|
||||
with Image.open(resized_img) as image:
|
||||
assert width, height == image.size
|
||||
with Image.open(original_img) as image:
|
||||
assert width, height != image.size
|
||||
assert _detect_color_image(grayscale_img, thumb_size=150) == 'grayscale'
|
||||
|
||||
|
||||
def _detect_color_image(file, thumb_size=150, MSE_cutoff=22, adjust_color_bias=True):
|
||||
# from http://stackoverflow.com/questions/20068945/detect-if-image-is-color-grayscale-or-black-and-white-with-python-pil
|
||||
from PIL import Image, ImageStat
|
||||
pil_img = Image.open(file)
|
||||
bands = pil_img.getbands()
|
||||
# http://stackoverflow.com/questions/20068945/detect-if-image-is-color-grayscale-or-black-and-white-with-python-pil
|
||||
pilimg = Image.open(file)
|
||||
bands = pilimg.getbands()
|
||||
if bands == ('R', 'G', 'B') or bands == ('R', 'G', 'B', 'A'):
|
||||
thumb = pil_img.resize((thumb_size, thumb_size))
|
||||
SSE, bias = 0, [0, 0, 0]
|
||||
thumb = pilimg.resize((thumb_size, thumb_size))
|
||||
sse, bias = 0, [0, 0, 0]
|
||||
if adjust_color_bias:
|
||||
bias = ImageStat.Stat(thumb).mean[:3]
|
||||
bias = [b - sum(bias) / 3 for b in bias]
|
||||
for pixel in thumb.getdata():
|
||||
mu = sum(pixel) / 3
|
||||
SSE += sum((pixel[i] - mu - bias[i]) * (pixel[i] - mu - bias[i]) for i in [0, 1, 2])
|
||||
MSE = float(SSE) / (thumb_size * thumb_size)
|
||||
if MSE <= MSE_cutoff:
|
||||
return 'grayscale'
|
||||
else:
|
||||
return 'color'
|
||||
sse += sum((pixel[i] - mu - bias[i]) * (pixel[i] - mu - bias[i]) for i in [0, 1, 2])
|
||||
mse = float(sse) / (thumb_size * thumb_size)
|
||||
return 'grayscale' if mse <= MSE_cutoff else 'color'
|
||||
elif len(bands) == 1:
|
||||
return 'blackandwhite'
|
||||
|
||||
|
||||
def test_server_search(pms):
|
||||
# basic search. see test_search.py
|
||||
assert pms.search('16 Blocks')
|
||||
assert pms.search('16 blocks', mediatype='movie')
|
||||
def test_server_search(plex):
|
||||
assert plex.search('16 Blocks')
|
||||
assert plex.search('16 blocks', mediatype='movie')
|
||||
|
||||
|
||||
def test_server_playlist(pms):
|
||||
pl = pms.playlist('some_playlist')
|
||||
assert pl.title == 'some_playlist'
|
||||
with pytest.raises(NotFound):
|
||||
pms.playlist('124xxx11y')
|
||||
def test_server_playlist(plex, show):
|
||||
episodes = show.episodes()
|
||||
playlist = plex.createPlaylist('test_playlist', episodes[:3])
|
||||
try:
|
||||
assert playlist.title == 'test_playlist'
|
||||
with pytest.raises(NotFound):
|
||||
plex.playlist('<playlist-not-found>')
|
||||
finally:
|
||||
playlist.delete()
|
||||
|
||||
|
||||
def test_server_playlists(pms):
|
||||
playlists = pms.playlists()
|
||||
assert len(playlists)
|
||||
def test_server_playlists(plex, show):
|
||||
playlists = plex.playlists()
|
||||
count = len(playlists)
|
||||
episodes = show.episodes()
|
||||
playlist = plex.createPlaylist('test_playlist', episodes[:3])
|
||||
try:
|
||||
playlists = plex.playlists()
|
||||
assert len(playlists) == count + 1
|
||||
finally:
|
||||
playlist.delete()
|
||||
|
||||
|
||||
def test_server_history(pms):
|
||||
history = pms.history()
|
||||
|
||||
def test_server_history(plex):
|
||||
history = plex.history()
|
||||
assert len(history)
|
||||
|
||||
|
||||
def test_server_Server_query(pms):
|
||||
assert pms.query('/')
|
||||
from plexapi.server import PlexServer
|
||||
def test_server_Server_query(plex):
|
||||
assert plex.query('/')
|
||||
with pytest.raises(BadRequest):
|
||||
assert pms.query('/asdasdsada/12123127/aaaa', headers={'random_headers': '1337'})
|
||||
assert plex.query('/asdf/1234/asdf', headers={'random_headers': '1234'})
|
||||
with pytest.raises(BadRequest):
|
||||
# This is really requests.exceptions.HTTPError:
|
||||
# 401 Client Error: Unauthorized for url:
|
||||
PlexServer('http://138.68.157.5:32400', '1234')
|
||||
# This is really requests.exceptions.HTTPError
|
||||
# 401 Client Error: Unauthorized for url
|
||||
PlexServer(utils.SERVER_BASEURL, '1234')
|
||||
|
||||
|
||||
def test_server_Server_session():
|
||||
from requests import Session
|
||||
from plexapi.server import PlexServer
|
||||
|
||||
# Mock Sesstion
|
||||
class MySession(Session):
|
||||
def __init__(self):
|
||||
super(self.__class__, self).__init__()
|
||||
self.plexapi_session_test = True
|
||||
|
||||
plex = PlexServer('http://138.68.157.5:32400',
|
||||
CONFIG.get('auth.server_token'), session=MySession())
|
||||
# Test Code
|
||||
plex = PlexServer(utils.SERVER_BASEURL, utils.SERVER_TOKEN, session=MySession())
|
||||
assert hasattr(plex._session, 'plexapi_session_test')
|
||||
pl = plex.playlists()
|
||||
assert hasattr(pl[0]._server._session, 'plexapi_session_test')
|
||||
# TODO: Check client in test_server_Server_session.
|
||||
# TODO: Check myplex in test_server_Server_session.
|
||||
|
||||
|
||||
def test_server_token_in_headers(pms):
|
||||
h = pms._headers()
|
||||
assert 'X-Plex-Token' in h and len(h['X-Plex-Token'])
|
||||
def test_server_token_in_headers(plex):
|
||||
headers = plex._headers()
|
||||
assert 'X-Plex-Token' in headers
|
||||
assert len(headers['X-Plex-Token']) >= 1
|
||||
|
||||
|
||||
def test_server_createPlayQueue(pms, a_movie):
|
||||
pq = pms.createPlayQueue(a_movie, **dict(shuffle=1, repeat=1))
|
||||
assert 'shuffle=1' and 'repeat=1' in pq._initpath
|
||||
assert pq.playQueueShuffled is True
|
||||
def test_server_createPlayQueue(plex, movie):
|
||||
playqueue = plex.createPlayQueue(movie, shuffle=1, repeat=1)
|
||||
assert 'shuffle=1' in playqueue._initpath
|
||||
assert 'repeat=1' in playqueue._initpath
|
||||
assert playqueue.playQueueShuffled is True
|
||||
|
||||
|
||||
def _test_server_createPlaylist():
|
||||
# TODO: Implement _test_server_createPlaylist()
|
||||
# see test_playlists.py
|
||||
pass
|
||||
|
||||
|
||||
def test_server_client_not_found(pms):
|
||||
def test_server_client_not_found(plex):
|
||||
with pytest.raises(NotFound):
|
||||
pms.client('<This-client-should-not-be-found>')
|
||||
plex.client('<This-client-should-not-be-found>')
|
||||
|
||||
|
||||
@pytest.mark.req_client
|
||||
def test_server_client(pms):
|
||||
assert pms.client('Plex Web (Chrome)')
|
||||
def test_server_client(plex):
|
||||
assert plex.client('Plex Web (Chrome)')
|
||||
|
||||
|
||||
def test_server_Server_sessions(pms):
|
||||
assert len(pms.sessions()) == 0
|
||||
def test_server_Server_sessions(plex):
|
||||
assert len(plex.sessions()) == 0
|
||||
|
||||
|
||||
@pytest.mark.req_client
|
||||
def test_server_clients(pms):
|
||||
assert len(pms.clients())
|
||||
m = pms.clients()[0]
|
||||
assert m._baseurl == 'http://127.0.0.1:32400'
|
||||
assert m.device is None
|
||||
assert m.deviceClass == 'pc'
|
||||
assert m.machineIdentifier == '89hgkrbqxaxmf45o1q2949ru'
|
||||
assert m.model is None
|
||||
assert m.platform is None
|
||||
assert m.platformVersion is None
|
||||
assert m.product == 'Plex Web'
|
||||
assert m.protocol == 'plex'
|
||||
assert m.protocolCapabilities == ['timeline', 'playback', 'navigation', 'mirror', 'playqueues']
|
||||
assert m.protocolVersion == '1'
|
||||
assert m._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert m.state is None
|
||||
assert m.title == 'Plex Web (Chrome)'
|
||||
assert m.token is None
|
||||
assert m.vendor is None
|
||||
assert m.version == '2.12.5'
|
||||
def test_server_clients(plex):
|
||||
assert len(plex.clients())
|
||||
client = plex.clients()[0]
|
||||
assert client._baseurl == 'http://127.0.0.1:32400'
|
||||
assert client.device is None
|
||||
assert client.deviceClass == 'pc'
|
||||
assert client.machineIdentifier == '89hgkrbqxaxmf45o1q2949ru'
|
||||
assert client.model is None
|
||||
assert client.platform is None
|
||||
assert client.platformVersion is None
|
||||
assert client.product == 'Plex Web'
|
||||
assert client.protocol == 'plex'
|
||||
assert client.protocolCapabilities == ['timeline', 'playback', 'navigation', 'mirror', 'playqueues']
|
||||
assert client.protocolVersion == '1'
|
||||
assert client._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert client.state is None
|
||||
assert client.title == 'Plex Web (Chrome)'
|
||||
assert client.token is None
|
||||
assert client.vendor is None
|
||||
assert client.version == '2.12.5'
|
||||
|
||||
|
||||
def test_server_account(pms):
|
||||
acc = pms.account()
|
||||
assert acc.authToken
|
||||
def test_server_account(plex):
|
||||
account = plex.account()
|
||||
assert account.authToken
|
||||
# TODO: Figure out why this is missing from time to time.
|
||||
#assert acc.mappingError == 'publisherror'
|
||||
assert acc.mappingErrorMessage is None
|
||||
assert acc.mappingState == 'mapped'
|
||||
assert acc.privateAddress == '138.68.157.5'
|
||||
assert acc.privatePort == '32400'
|
||||
assert acc.publicAddress == '138.68.157.5'
|
||||
assert acc.publicPort == '32400'
|
||||
assert acc.signInState == 'ok'
|
||||
assert acc.subscriptionActive == False
|
||||
assert acc.subscriptionFeatures == []
|
||||
assert acc.subscriptionState == 'Unknown'
|
||||
assert acc.username == 'testplexapi@gmail.com'
|
||||
# assert account.mappingError == 'publisherror'
|
||||
assert account.mappingErrorMessage is None
|
||||
assert account.mappingState == 'mapped'
|
||||
assert re.match(utils.REGEX_IPADDR, account.privateAddress)
|
||||
assert int(account.privatePort) >= 1000
|
||||
assert re.match(utils.REGEX_IPADDR, account.publicAddress)
|
||||
assert int(account.publicPort) >= 1000
|
||||
assert account.signInState == 'ok'
|
||||
assert account.subscriptionActive is False
|
||||
assert account.subscriptionFeatures == []
|
||||
assert account.subscriptionState == 'Unknown'
|
||||
assert re.match(utils.REGEX_EMAIL, account.username)
|
||||
|
||||
|
||||
def test_server_downloadLogs(tmpdir, pms):
|
||||
pms.downloadLogs(savepath=str(tmpdir), unpack=True)
|
||||
def test_server_downloadLogs(tmpdir, plex):
|
||||
plex.downloadLogs(savepath=str(tmpdir), unpack=True)
|
||||
assert len(tmpdir.listdir()) > 1
|
||||
|
||||
|
||||
def test_server_downloadDatabases(tmpdir, pms):
|
||||
pms.downloadDatabases(savepath=str(tmpdir), unpack=True)
|
||||
def test_server_downloadDatabases(tmpdir, plex):
|
||||
plex.downloadDatabases(savepath=str(tmpdir), unpack=True)
|
||||
assert len(tmpdir.listdir()) > 1
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pytest
|
||||
import pytest, time
|
||||
import plexapi.utils as utils
|
||||
from plexapi.exceptions import NotFound
|
||||
|
||||
|
@ -7,18 +7,24 @@ from plexapi.exceptions import NotFound
|
|||
def test_utils_toDatetime():
|
||||
assert str(utils.toDatetime('2006-03-03', format='%Y-%m-%d')) == '2006-03-03 00:00:00'
|
||||
assert str(utils.toDatetime('0'))[:-9] in ['1970-01-01', '1969-12-31']
|
||||
# should this handle args as '0' # no need element attrs are strings.
|
||||
|
||||
|
||||
def _test_utils_threaded():
|
||||
# TODO: Implement test_utils_threaded
|
||||
def test_utils_threaded():
|
||||
def _squared(num, results, i):
|
||||
time.sleep(0.5)
|
||||
results[i] = num * num
|
||||
starttime = time.time()
|
||||
results = utils.threaded(_squared, [[1], [2], [3], [4], [5]])
|
||||
assert results == [1, 4, 9, 16, 25]
|
||||
assert (time.time() - starttime) < 1
|
||||
|
||||
|
||||
@pytest.mark.req_client
|
||||
def test_utils_downloadSessionImages():
|
||||
# TODO: Implement test_utils_downloadSessionImages()
|
||||
pass
|
||||
|
||||
|
||||
def _downloadSessionImages():
|
||||
pass # TODO Add this when we got clients fixed.
|
||||
|
||||
|
||||
def test_utils_searchType():
|
||||
st = utils.searchType('movie')
|
||||
assert st == 1
|
||||
|
@ -34,29 +40,30 @@ def test_utils_joinArgs():
|
|||
|
||||
|
||||
def test_utils_cast():
|
||||
t_int_int = utils.cast(int, 1)
|
||||
t_int_str_int = utils.cast(int, '1')
|
||||
t_bool_str_int = utils.cast(bool, '1')
|
||||
t_bool_int = utils.cast(bool, 1)
|
||||
t_float_int = utils.cast(float, 1)
|
||||
t_float_float = utils.cast(float, 1)
|
||||
t_float_str = utils.cast(float, 'kek')
|
||||
assert t_int_int == 1 and isinstance(t_int_int, int)
|
||||
assert t_int_str_int == 1 and isinstance(t_int_str_int, int)
|
||||
assert t_bool_str_int is True
|
||||
assert t_bool_int is True
|
||||
assert t_float_float == 1.0 and isinstance(t_float_float, float)
|
||||
assert t_float_str != t_float_str # nan is never equal
|
||||
int_int = utils.cast(int, 1)
|
||||
int_str = utils.cast(int, '1')
|
||||
bool_str = utils.cast(bool, '1')
|
||||
bool_int = utils.cast(bool, 1)
|
||||
float_int = utils.cast(float, 1)
|
||||
float_float = utils.cast(float, 1.0)
|
||||
float_str = utils.cast(float, '1.2')
|
||||
float_nan = utils.cast(float, 'wut?')
|
||||
assert int_int == 1 and isinstance(int_int, int)
|
||||
assert int_str == 1 and isinstance(int_str, int)
|
||||
assert bool_str is True
|
||||
assert bool_int is True
|
||||
assert float_int == 1.0 and isinstance(float_int, float)
|
||||
assert float_float == 1.0 and isinstance(float_float, float)
|
||||
assert float_str == 1.2 and isinstance(float_str, float)
|
||||
assert float_nan != float_nan # nan is never equal
|
||||
with pytest.raises(ValueError):
|
||||
t_bool_str = utils.cast(bool, 'kek') # should we catch this in cast?
|
||||
bool_str = utils.cast(bool, 'kek')
|
||||
|
||||
|
||||
def test_utils_download(a_episode):
|
||||
without_session = utils.download(a_episode.getStreamURL(),
|
||||
filename=a_episode.locations[0], mocked=True)
|
||||
assert without_session
|
||||
with_session = utils.download(a_episode.getStreamURL(), filename=a_episode.locations[0],
|
||||
session=a_episode._server._session, mocked=True)
|
||||
assert with_session
|
||||
img = utils.download(a_episode.thumbUrl, filename=a_episode.title, mocked=True)
|
||||
assert img
|
||||
def test_utils_download(episode):
|
||||
url = episode.getStreamURL()
|
||||
locations = episode.locations[0]
|
||||
session = episode._server._session
|
||||
assert utils.download(url, filename=locations, mocked=True)
|
||||
assert utils.download(url, filename=locations, session=session, mocked=True)
|
||||
assert utils.download(episode.thumbUrl, filename=episode.title, mocked=True)
|
||||
|
|
|
@ -1,40 +1,38 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import plexapi, pytest
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
from plexapi.exceptions import BadRequest, NotFound
|
||||
from . import conftest as utils
|
||||
|
||||
|
||||
def test_video_Movie(a_movie_section):
|
||||
m = a_movie_section.get('Cars')
|
||||
assert m.title == 'Cars'
|
||||
|
||||
def test_video_Movie_delete(monkeypatch, pms):
|
||||
m = pms.library.section('Movies').get('16 blocks')
|
||||
monkeypatch.delattr("requests.sessions.Session.request")
|
||||
try:
|
||||
m.delete()
|
||||
except AttributeError:
|
||||
# Silence this because it will always raise beause of monkeypatch
|
||||
pass
|
||||
def test_video_Movie(movies):
|
||||
movie = movies.get('Cars')
|
||||
assert movie.title == 'Cars'
|
||||
|
||||
|
||||
|
||||
def test_video_Movie_getStreamURL(a_movie):
|
||||
server_token = plexapi.CONFIG.get('auth.server_token')
|
||||
assert a_movie.getStreamURL() == "http://138.68.157.5:32400/video/:/transcode/universal/start.m3u8?X-Plex-Platform=Chrome©ts=1&mediaIndex=0&offset=0&path=%2Flibrary%2Fmetadata%2F1&X-Plex-Token={0}".format(server_token)
|
||||
assert a_movie.getStreamURL(videoResolution='800x600') == "http://138.68.157.5:32400/video/:/transcode/universal/start.m3u8?X-Plex-Platform=Chrome©ts=1&mediaIndex=0&offset=0&path=%2Flibrary%2Fmetadata%2F1&videoResolution=800x600&X-Plex-Token={0}".format(server_token)
|
||||
def test_video_Movie_delete(monkeypatch, plex):
|
||||
movie = plex.library.section('Movies').get('16 blocks')
|
||||
monkeypatch.delattr('requests.sessions.Session.request')
|
||||
with pytest.raises(AttributeError):
|
||||
movie.delete()
|
||||
|
||||
|
||||
def test_video_Movie_isFullObject_and_reload(pms):
|
||||
movie = pms.library.section('Movies').get('16 Blocks')
|
||||
def test_video_Movie_getStreamURL(movie):
|
||||
key = movie.ratingKey
|
||||
assert movie.getStreamURL() == '{0}/video/:/transcode/universal/start.m3u8?X-Plex-Platform=Chrome©ts=1&mediaIndex=0&offset=0&path=%2Flibrary%2Fmetadata%2F{1}&X-Plex-Token={2}'.format(utils.SERVER_BASEURL, key, utils.SERVER_TOKEN) # noqa
|
||||
assert movie.getStreamURL(videoResolution='800x600') == '{0}/video/:/transcode/universal/start.m3u8?X-Plex-Platform=Chrome©ts=1&mediaIndex=0&offset=0&path=%2Flibrary%2Fmetadata%2F{1}&videoResolution=800x600&X-Plex-Token={2}'.format(utils.SERVER_BASEURL, key, utils.SERVER_TOKEN) # noqa
|
||||
|
||||
|
||||
def test_video_Movie_isFullObject_and_reload(plex):
|
||||
movie = plex.library.section('Movies').get('Cars')
|
||||
assert movie.isFullObject() is False
|
||||
movie.reload()
|
||||
assert movie.isFullObject() is True
|
||||
movie_via_search = pms.library.search('16 Blocks')[0]
|
||||
movie_via_search = plex.library.search('Cars')[0]
|
||||
assert movie_via_search.isFullObject() is False
|
||||
movie_via_search.reload()
|
||||
assert movie_via_search.isFullObject() is True
|
||||
movie_via_section_search = pms.library.section('Movies').search('16 Blocks')[0]
|
||||
movie_via_section_search = plex.library.section('Movies').search('Cars')[0]
|
||||
assert movie_via_section_search.isFullObject() is False
|
||||
movie_via_section_search.reload()
|
||||
assert movie_via_section_search.isFullObject() is True
|
||||
|
@ -42,501 +40,497 @@ def test_video_Movie_isFullObject_and_reload(pms):
|
|||
assert len(movie_via_section_search.roles) > 3
|
||||
|
||||
|
||||
def test_video_Movie_isPartialObject(a_movie):
|
||||
assert a_movie.isPartialObject()
|
||||
def test_video_Movie_isPartialObject(movie):
|
||||
assert movie.isPartialObject()
|
||||
|
||||
|
||||
def test_video_Movie_iterParts(a_movie):
|
||||
assert len(list(a_movie.iterParts())) == 1
|
||||
def test_video_Movie_iterParts(movie):
|
||||
assert len(list(movie.iterParts())) >= 1
|
||||
|
||||
|
||||
def test_video_Movie_download(monkeydownload, tmpdir, a_movie):
|
||||
downloaded_movie = a_movie.download(savepath=str(tmpdir))
|
||||
assert len(downloaded_movie) == 1
|
||||
downloaded_movie2 = a_movie.download(savepath=str(tmpdir), **{'videoResolution': '500x300'})
|
||||
assert len(downloaded_movie2) == 1
|
||||
def test_video_Movie_download(monkeydownload, tmpdir, movie):
|
||||
filepaths1 = movie.download(savepath=str(tmpdir))
|
||||
assert len(filepaths1) >= 1
|
||||
filepaths2 = movie.download(savepath=str(tmpdir), videoResolution='500x300')
|
||||
assert len(filepaths2) >= 1
|
||||
|
||||
|
||||
def test_video_Movie_attrs_as_much_as_possible(a_movie_section):
|
||||
m = a_movie_section.get('Cars')
|
||||
assert m.locations == ['/media/movies/cars/cars.mp4']
|
||||
assert m.addedAt > datetime(2017, 1, 1)
|
||||
assert '/library/metadata/2/art/' in m.art
|
||||
assert m.audienceRating == 7.9
|
||||
assert m.audienceRatingImage == 'rottentomatoes://image.rating.upright'
|
||||
# Assign 0 m.audioStreams
|
||||
m.reload()
|
||||
aud0 = m.media[0].parts[0].audioStreams[0]
|
||||
assert m.chapterSource == 'agent'
|
||||
assert m.collections == []
|
||||
assert m.contentRating == 'G'
|
||||
#assert m.countries == [<Country:35:USA>]
|
||||
assert [i.tag for i in m.directors] == ['John Lasseter', 'Joe Ranft']
|
||||
assert m.duration == 170859
|
||||
assert m.fields == []
|
||||
assert [i.tag for i in m.genres] == ['Animation', 'Family', 'Comedy', 'Sport', 'Adventure']
|
||||
assert m.guid == 'com.plexapp.agents.imdb://tt0317219?lang=en'
|
||||
assert m._initpath == '/library/metadata/2'
|
||||
assert m.key == '/library/metadata/2'
|
||||
assert m.lastViewedAt > datetime(2017, 1, 1)
|
||||
assert m.librarySectionID == '1'
|
||||
assert m.listType == 'video'
|
||||
# Assign 0 m.media
|
||||
med0 = m.media[0]
|
||||
assert m.originalTitle is None
|
||||
assert str(m.originallyAvailableAt.date()) == '2006-06-09'
|
||||
assert m.player is None
|
||||
assert m.playlistItemID is None
|
||||
assert m.primaryExtraKey is None
|
||||
#assert m.producers == [<Producer:130:Darla.K..Anderson>]
|
||||
assert m.rating == '7.4'
|
||||
assert m.ratingImage == 'rottentomatoes://image.rating.certified'
|
||||
assert m.ratingKey == 2
|
||||
assert [i.tag for i in m.roles] == ['Owen Wilson', 'Paul Newman', 'Bonnie Hunt', 'Larry the Cable Guy', 'Cheech Marin', 'Tony Shalhoub', 'Guido Quaroni', 'Jenifer Lewis', 'Paul Dooley', 'Michael Wallis', 'George Carlin', 'Katherine Helmond', 'John Ratzenberger', 'Michael Keaton', 'Joe Ranft', 'Richard Petty', 'Jeremy Piven', 'Bob Costas', 'Darrell Waltrip', 'Richard Kind', 'Edie McClurg', 'Humpy Wheeler', 'Tom Magliozzi', 'Ray Magliozzi', 'Lynda Petty', 'Andrew Stanton', 'Dale Earnhardt Jr.', 'Michael Schumacher', 'Jay Leno', 'Sarah Clark', 'Mike Nelson', 'Joe Ranft', 'Jonas Rivera', 'Lou Romano', 'Adrian Ochoa', 'E.J. Holowicki', 'Elissa Knight', 'Lindsey Collins', 'Larry Benton', 'Douglas Keever', 'Tom Hanks', 'Tim Allen', 'John Ratzenberger', 'Billy Crystal', 'John Goodman', 'John Ratzenberger', 'Dave Foley', 'John Ratzenberger', 'Vanness Wu']
|
||||
assert m._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert m.sessionKey is None
|
||||
assert m.studio == 'Walt Disney Pictures'
|
||||
assert m.summary == u"Lightning McQueen, a hotshot rookie race car driven to succeed, discovers that life is about the journey, not the finish line, when he finds himself unexpectedly detoured in the sleepy Route 66 town of Radiator Springs. On route across the country to the big Piston Cup Championship in California to compete against two seasoned pros, McQueen gets to know the town's offbeat characters."
|
||||
assert m.tagline == "Ahhh... it's got that new movie smell."
|
||||
assert '/library/metadata/2/thumb/' in m.thumb
|
||||
assert m.title == 'Cars'
|
||||
assert m.titleSort == 'Cars'
|
||||
assert m.transcodeSession is None
|
||||
assert m.type == 'movie'
|
||||
assert m.updatedAt > datetime(2017, 1, 1)
|
||||
assert m.userRating is None
|
||||
assert m.username is None
|
||||
# Assign 0 m.videoStreams
|
||||
vid0 = m.media[0].parts[0].videoStreams[0]
|
||||
assert m.viewCount == 0
|
||||
assert m.viewOffset == 88870
|
||||
assert m.viewedAt is None
|
||||
assert [i.tag for i in m.writers] == ['Dan Fogelman', 'Joe Ranft', 'John Lasseter', 'Kiel Murray', 'Phil Lorin', 'Jorgen Klubien']
|
||||
assert m.year == 2006
|
||||
assert aud0.audioChannelLayout == '5.1'
|
||||
assert aud0.bitDepth is None
|
||||
assert aud0.bitrate == 388
|
||||
assert aud0.bitrateMode is None
|
||||
assert aud0.channels == 6
|
||||
assert aud0.codec == 'aac'
|
||||
assert aud0.codecID is None
|
||||
assert aud0.dialogNorm is None
|
||||
assert aud0.duration is None
|
||||
assert aud0.id == 10
|
||||
assert aud0.index == 1
|
||||
assert aud0._initpath == '/library/metadata/2'
|
||||
assert aud0.language is None
|
||||
assert aud0.languageCode is None
|
||||
#assert aud0.part == <MediaPart:2>
|
||||
assert aud0.samplingRate == 48000
|
||||
assert aud0.selected is True
|
||||
assert aud0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert aud0.streamType == 2
|
||||
assert aud0.title is None
|
||||
assert aud0.type == 2
|
||||
assert med0.aspectRatio == 1.78
|
||||
assert med0.audioChannels == 6
|
||||
assert med0.audioCodec == 'aac'
|
||||
assert med0.bitrate == 1474
|
||||
assert med0.container == 'mp4'
|
||||
assert med0.duration == 170859
|
||||
assert med0.height == 720
|
||||
assert med0.id == 2
|
||||
assert med0._initpath == '/library/metadata/2'
|
||||
assert med0.optimizedForStreaming is False
|
||||
# Assign 0 med0.parts
|
||||
par0 = med0.parts[0]
|
||||
assert med0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert med0.videoCodec == 'h264'
|
||||
assert med0.videoFrameRate == 'PAL'
|
||||
assert med0.videoResolution == '720'
|
||||
assert med0.width == 1280
|
||||
assert vid0.bitDepth == 8
|
||||
assert vid0.bitrate == 1086
|
||||
assert vid0.cabac is None
|
||||
assert vid0.chromaSubsampling == '4:2:0'
|
||||
assert vid0.codec == 'h264'
|
||||
assert vid0.codecID is None
|
||||
assert vid0.colorSpace is None
|
||||
assert vid0.duration is None
|
||||
assert vid0.frameRate == 25.0
|
||||
assert vid0.frameRateMode is None
|
||||
assert vid0.hasScallingMatrix is None
|
||||
assert vid0.height == 720
|
||||
assert vid0.id == 9
|
||||
assert vid0.index == 0
|
||||
assert vid0._initpath == '/library/metadata/2'
|
||||
assert vid0.language is None
|
||||
assert vid0.languageCode is None
|
||||
assert vid0.level == 31
|
||||
#assert vid0.part == <MediaPart:2>
|
||||
assert vid0.profile == 'main'
|
||||
assert vid0.refFrames == 1
|
||||
assert vid0.scanType is None
|
||||
assert vid0.selected is False
|
||||
assert vid0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert vid0.streamType == 1
|
||||
assert vid0.title is None
|
||||
assert vid0.type == 1
|
||||
assert vid0.width == 1280
|
||||
assert par0.container == 'mp4'
|
||||
assert par0.duration == 170859
|
||||
assert par0.file == '/media/movies/cars/cars.mp4'
|
||||
assert par0.id == 2
|
||||
assert par0._initpath == '/library/metadata/2'
|
||||
assert par0.key == '/library/parts/2/1484679008/file.mp4'
|
||||
#assert par0.media == <Media:Cars>
|
||||
assert par0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert par0.size == 31491130
|
||||
# Assign 0 par0.streams
|
||||
str0 = par0.streams[0]
|
||||
# Assign 1 par0.streams
|
||||
str1 = par0.streams[1]
|
||||
assert str0.bitDepth == 8
|
||||
assert str0.bitrate == 1086
|
||||
assert str0.cabac is None
|
||||
assert str0.chromaSubsampling == '4:2:0'
|
||||
assert str0.codec == 'h264'
|
||||
assert str0.codecID is None
|
||||
assert str0.colorSpace is None
|
||||
assert str0.duration is None
|
||||
assert str0.frameRate == 25.0
|
||||
assert str0.frameRateMode is None
|
||||
assert str0.hasScallingMatrix is None
|
||||
assert str0.height == 720
|
||||
assert str0.id == 9
|
||||
assert str0.index == 0
|
||||
assert str0._initpath == '/library/metadata/2'
|
||||
assert str0.language is None
|
||||
assert str0.languageCode is None
|
||||
assert str0.level == 31
|
||||
#assert str0.part == <MediaPart:2>
|
||||
assert str0.profile == 'main'
|
||||
assert str0.refFrames == 1
|
||||
assert str0.scanType is None
|
||||
assert str0.selected is False
|
||||
assert str0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert str0.streamType == 1
|
||||
assert str0.title is None
|
||||
assert str0.type == 1
|
||||
assert str0.width == 1280
|
||||
assert str1.audioChannelLayout == '5.1'
|
||||
assert str1.bitDepth is None
|
||||
assert str1.bitrate == 388
|
||||
assert str1.bitrateMode is None
|
||||
assert str1.channels == 6
|
||||
assert str1.codec == 'aac'
|
||||
assert str1.codecID is None
|
||||
assert str1.dialogNorm is None
|
||||
assert str1.duration is None
|
||||
assert str1.id == 10
|
||||
assert str1.index == 1
|
||||
assert str1._initpath == '/library/metadata/2'
|
||||
assert str1.language is None
|
||||
assert str1.languageCode is None
|
||||
#assert str1.part == <MediaPart:2>
|
||||
assert str1.samplingRate == 48000
|
||||
assert str1.selected is True
|
||||
assert str1._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert str1.streamType == 2
|
||||
assert str1.title is None
|
||||
assert str1.type == 2
|
||||
def test_video_Movie_attrs(movies):
|
||||
movie = movies.get('Cars')
|
||||
assert len(movie.locations[0]) >= 10
|
||||
assert utils.is_datetime(movie.addedAt)
|
||||
assert utils.is_metadata(movie.art)
|
||||
assert movie.audienceRating == 7.9
|
||||
assert movie.audienceRatingImage == 'rottentomatoes://image.rating.upright'
|
||||
movie.reload() # RELOAD
|
||||
assert movie.chapterSource == 'agent'
|
||||
assert movie.collections == []
|
||||
assert movie.contentRating == 'G'
|
||||
assert all([i.tag in ['US', 'USA'] for i in movie.countries])
|
||||
assert [i.tag for i in movie.directors] == ['John Lasseter', 'Joe Ranft']
|
||||
assert movie.duration >= 160000
|
||||
assert movie.fields == []
|
||||
assert sorted([i.tag for i in movie.genres]) == ['Adventure', 'Animation', 'Comedy', 'Family', 'Sport']
|
||||
assert movie.guid == 'com.plexapp.agents.imdb://tt0317219?lang=en'
|
||||
assert utils.is_metadata(movie._initpath)
|
||||
assert utils.is_metadata(movie.key)
|
||||
if movie.lastViewedAt:
|
||||
assert utils.is_datetime(movie.lastViewedAt)
|
||||
assert int(movie.librarySectionID) >= 1
|
||||
assert movie.listType == 'video'
|
||||
assert movie.originalTitle is None
|
||||
assert movie.originallyAvailableAt.strftime('%Y-%m-%d') == '2006-06-09'
|
||||
assert movie.player is None
|
||||
assert movie.playlistItemID is None
|
||||
assert movie.primaryExtraKey is None
|
||||
assert [i.tag for i in movie.producers] == ['Darla K. Anderson']
|
||||
assert movie.rating == '7.4'
|
||||
assert movie.ratingImage == 'rottentomatoes://image.rating.certified'
|
||||
assert movie.ratingKey >= 1
|
||||
assert sorted([i.tag for i in movie.roles]) == ['Adrian Ochoa', 'Andrew Stanton', 'Billy Crystal', 'Bob Costas', 'Bonnie Hunt', 'Cheech Marin', 'Dale Earnhardt Jr.', 'Darrell Waltrip', 'Dave Foley', 'Douglas Keever', 'E.J. Holowicki', 'Edie McClurg', 'Elissa Knight', 'George Carlin', 'Guido Quaroni', 'Humpy Wheeler', 'Jay Leno', 'Jenifer Lewis', 'Jeremy Piven', 'Joe Ranft', 'Joe Ranft', 'John Goodman', 'John Ratzenberger', 'John Ratzenberger', 'John Ratzenberger', 'John Ratzenberger', 'Jonas Rivera', 'Katherine Helmond', 'Larry Benton', 'Larry the Cable Guy', 'Lindsey Collins', 'Lou Romano', 'Lynda Petty', 'Michael Keaton', 'Michael Schumacher', 'Michael Wallis', 'Mike Nelson', 'Owen Wilson', 'Paul Dooley', 'Paul Newman', 'Ray Magliozzi', 'Richard Kind', 'Richard Petty', 'Sarah Clark', 'Tim Allen', 'Tom Hanks', 'Tom Magliozzi', 'Tony Shalhoub', 'Vanness Wu'] # noqa
|
||||
assert movie._server._baseurl == utils.SERVER_BASEURL
|
||||
assert movie.sessionKey is None
|
||||
assert movie.studio == 'Walt Disney Pictures'
|
||||
assert utils.is_string(movie.summary, gte=100)
|
||||
assert movie.tagline == "Ahhh... it's got that new movie smell."
|
||||
assert utils.is_thumb(movie.thumb)
|
||||
assert movie.title == 'Cars'
|
||||
assert movie.titleSort == 'Cars'
|
||||
assert movie.transcodeSession is None
|
||||
assert movie.type == 'movie'
|
||||
assert movie.updatedAt > datetime(2017, 1, 1)
|
||||
assert movie.userRating is None
|
||||
assert movie.username is None
|
||||
assert movie.viewCount == 0
|
||||
assert utils.is_int(movie.viewOffset, gte=0)
|
||||
assert movie.viewedAt is None
|
||||
assert sorted([i.tag for i in movie.writers]) == ['Dan Fogelman', 'Joe Ranft', 'John Lasseter', 'Jorgen Klubien', 'Kiel Murray', 'Phil Lorin'] # noqa
|
||||
assert movie.year == 2006
|
||||
# Audio
|
||||
audio = movie.media[0].parts[0].audioStreams[0]
|
||||
assert audio.audioChannelLayout in utils.AUDIOLAYOUTS
|
||||
assert audio.bitDepth is None
|
||||
assert utils.is_int(audio.bitrate)
|
||||
assert audio.bitrateMode is None
|
||||
assert audio.channels in utils.AUDIOCHANNELS
|
||||
assert audio.codec in utils.CODECS
|
||||
assert audio.codecID is None
|
||||
assert audio.dialogNorm is None
|
||||
assert audio.duration is None
|
||||
assert audio.id >= 1
|
||||
assert audio.index == 1
|
||||
assert utils.is_metadata(audio._initpath)
|
||||
assert audio.language is None
|
||||
assert audio.languageCode is None
|
||||
assert audio.samplingRate == 48000
|
||||
assert audio.selected is True
|
||||
assert audio._server._baseurl == utils.SERVER_BASEURL
|
||||
assert audio.streamType == 2
|
||||
assert audio.title is None
|
||||
assert audio.type == 2
|
||||
# Media
|
||||
media = movie.media[0]
|
||||
assert media.aspectRatio == 1.78
|
||||
assert media.audioChannels in utils.AUDIOCHANNELS
|
||||
assert media.audioCodec in utils.CODECS
|
||||
assert utils.is_int(media.bitrate)
|
||||
assert media.container in utils.CONTAINERS
|
||||
assert utils.is_int(media.duration, gte=160000)
|
||||
assert utils.is_int(media.height)
|
||||
assert utils.is_int(media.id)
|
||||
assert utils.is_metadata(media._initpath)
|
||||
assert media.optimizedForStreaming in [None, False]
|
||||
assert media._server._baseurl == utils.SERVER_BASEURL
|
||||
assert media.videoCodec in utils.CODECS
|
||||
assert media.videoFrameRate in utils.FRAMERATES
|
||||
assert media.videoResolution in utils.RESOLUTIONS
|
||||
assert utils.is_int(media.width, gte=200)
|
||||
# Video
|
||||
video = movie.media[0].parts[0].videoStreams[0]
|
||||
assert video.bitDepth == 8
|
||||
assert utils.is_int(video.bitrate)
|
||||
assert video.cabac is None
|
||||
assert video.chromaSubsampling == '4:2:0'
|
||||
assert video.codec in utils.CODECS
|
||||
assert video.codecID is None
|
||||
assert video.colorSpace is None
|
||||
assert video.duration is None
|
||||
assert utils.is_float(video.frameRate, gte=20.0)
|
||||
assert video.frameRateMode is None
|
||||
assert video.hasScallingMatrix is None
|
||||
assert utils.is_int(video.height, gte=300)
|
||||
assert utils.is_int(video.id)
|
||||
assert utils.is_int(video.index, gte=0)
|
||||
assert utils.is_metadata(video._initpath)
|
||||
assert video.language is None
|
||||
assert video.languageCode is None
|
||||
assert utils.is_int(video.level)
|
||||
assert video.profile in utils.PROFILES
|
||||
assert utils.is_int(video.refFrames)
|
||||
assert video.scanType is None
|
||||
assert video.selected is False
|
||||
assert video._server._baseurl == utils.SERVER_BASEURL
|
||||
assert utils.is_int(video.streamType)
|
||||
assert video.title is None
|
||||
assert video.type == 1
|
||||
assert utils.is_int(video.width, gte=400)
|
||||
# Part
|
||||
part = media.parts[0]
|
||||
assert part.container in utils.CONTAINERS
|
||||
assert utils.is_int(part.duration, 160000)
|
||||
assert len(part.file) >= 10
|
||||
assert utils.is_int(part.id)
|
||||
assert utils.is_metadata(part._initpath)
|
||||
assert len(part.key) >= 10
|
||||
assert part._server._baseurl == utils.SERVER_BASEURL
|
||||
assert utils.is_int(part.size, gte=1000000)
|
||||
# Stream 1
|
||||
stream1 = part.streams[0]
|
||||
assert stream1.bitDepth == 8
|
||||
assert utils.is_int(stream1.bitrate)
|
||||
assert stream1.cabac is None
|
||||
assert stream1.chromaSubsampling == '4:2:0'
|
||||
assert stream1.codec in utils.CODECS
|
||||
assert stream1.codecID is None
|
||||
assert stream1.colorSpace is None
|
||||
assert stream1.duration is None
|
||||
assert utils.is_float(stream1.frameRate, gte=20.0)
|
||||
assert stream1.frameRateMode is None
|
||||
assert stream1.hasScallingMatrix is None
|
||||
assert utils.is_int(stream1.height, gte=300)
|
||||
assert utils.is_int(stream1.id)
|
||||
assert utils.is_int(stream1.index, gte=0)
|
||||
assert utils.is_metadata(stream1._initpath)
|
||||
assert stream1.language is None
|
||||
assert stream1.languageCode is None
|
||||
assert utils.is_int(stream1.level)
|
||||
assert stream1.profile in utils.PROFILES
|
||||
assert stream1.refFrames == 1
|
||||
assert stream1.scanType is None
|
||||
assert stream1.selected is False
|
||||
assert stream1._server._baseurl == utils.SERVER_BASEURL
|
||||
assert utils.is_int(stream1.streamType)
|
||||
assert stream1.title is None
|
||||
assert stream1.type == 1
|
||||
assert utils.is_int(stream1.width, gte=400)
|
||||
# Stream 2
|
||||
stream2 = part.streams[1]
|
||||
assert stream2.audioChannelLayout in utils.AUDIOLAYOUTS
|
||||
assert stream2.bitDepth is None
|
||||
assert utils.is_int(stream2.bitrate)
|
||||
assert stream2.bitrateMode is None
|
||||
assert stream2.channels in utils.AUDIOCHANNELS
|
||||
assert stream2.codec in utils.CODECS
|
||||
assert stream2.codecID is None
|
||||
assert stream2.dialogNorm is None
|
||||
assert stream2.duration is None
|
||||
assert utils.is_int(stream2.id)
|
||||
assert utils.is_int(stream2.index)
|
||||
assert utils.is_metadata(stream2._initpath)
|
||||
assert stream2.language is None
|
||||
assert stream2.languageCode is None
|
||||
assert utils.is_int(stream2.samplingRate)
|
||||
assert stream2.selected is True
|
||||
assert stream2._server._baseurl == utils.SERVER_BASEURL
|
||||
assert stream2.streamType == 2
|
||||
assert stream2.title is None
|
||||
assert stream2.type == 2
|
||||
|
||||
|
||||
def test_video_Show(a_show):
|
||||
assert a_show.title == 'The 100'
|
||||
def test_video_Show(show):
|
||||
assert show.title == 'The 100'
|
||||
|
||||
|
||||
def test_video_Show_attrs(a_show):
|
||||
m = a_show
|
||||
assert m.addedAt > datetime(2017, 1, 1)
|
||||
assert '/library/metadata/12/art/' in m.art
|
||||
assert '/library/metadata/12/banner/' in m.banner
|
||||
assert m.childCount == 2
|
||||
assert m.contentRating == 'TV-14'
|
||||
assert m.duration == 2700000
|
||||
assert m._initpath == '/library/sections/2/all'
|
||||
def test_video_Show_attrs(show):
|
||||
assert show.addedAt > datetime(2017, 1, 1)
|
||||
assert utils.is_metadata(show.art, contains='/art/')
|
||||
assert utils.is_metadata(show.banner, contains='/banner/')
|
||||
assert utils.is_int(show.childCount)
|
||||
assert show.contentRating in utils.CONTENTRATINGS
|
||||
assert utils.is_int(show.duration, gte=1600000)
|
||||
assert utils.is_section(show._initpath)
|
||||
# Check reloading the show loads the full list of genres
|
||||
assert [i.tag for i in m.genres] == ['Drama', 'Science-Fiction', 'Suspense']
|
||||
m.reload()
|
||||
assert [i.tag for i in m.genres] == ['Drama', 'Science-Fiction', 'Suspense', 'Thriller']
|
||||
assert sorted([i.tag for i in show.genres]) == ['Drama', 'Science-Fiction', 'Suspense']
|
||||
show.reload()
|
||||
assert sorted([i.tag for i in show.genres]) == ['Drama', 'Science-Fiction', 'Suspense', 'Thriller']
|
||||
# So the initkey should have changed because of the reload
|
||||
assert m._initpath == '/library/metadata/12'
|
||||
assert m.index == '1'
|
||||
assert m.key == '/library/metadata/12'
|
||||
assert m.lastViewedAt > datetime(2017, 1, 1)
|
||||
assert m.leafCount == 9
|
||||
assert m.listType == 'video'
|
||||
assert m.locations == ['/media/tvshows/the 100']
|
||||
assert str(m.originallyAvailableAt.date()) == '2014-03-19'
|
||||
assert m.rating == 8.1
|
||||
assert m.ratingKey == 12
|
||||
assert [i.tag for i in m.roles][:3] == ['Richard Harmon', 'Alycia Debnam-Carey', 'Lindsey Morgan']
|
||||
assert [i.tag for i in m.actors][:3] == ['Richard Harmon', 'Alycia Debnam-Carey', 'Lindsey Morgan']
|
||||
assert m._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert m.studio == 'The CW'
|
||||
assert m.summary == u"When nuclear Armageddon destroys civilization on Earth, the only survivors are those on the 12 international space stations in orbit at the time. Three generations later, the 4,000 survivors living on a space ark of linked stations see their resources dwindle and face draconian measures established to ensure humanity's future. Desperately looking for a solution, the ark's leaders send 100 juvenile prisoners back to the planet to test its habitability. Having always lived in space, the exiles find the planet fascinating and terrifying, but with the fate of the human race in their hands, they must forge a path into the unknown."
|
||||
assert '/library/metadata/12/theme/' in m.theme
|
||||
assert '/library/metadata/12/thumb/' in m.thumb
|
||||
assert m.title == 'The 100'
|
||||
assert m.titleSort == '100'
|
||||
assert m.type == 'show'
|
||||
assert m.updatedAt > datetime(2017, 1, 1)
|
||||
assert m.viewCount == 1
|
||||
assert m.viewedLeafCount == 1
|
||||
assert m.year == 2014
|
||||
assert utils.is_metadata(show._initpath)
|
||||
assert utils.is_int(show.index)
|
||||
assert utils.is_metadata(show.key)
|
||||
assert utils.is_datetime(show.lastViewedAt)
|
||||
assert utils.is_int(show.leafCount)
|
||||
assert show.listType == 'video'
|
||||
assert len(show.locations[0]) >= 10
|
||||
assert show.originallyAvailableAt.strftime('%Y-%m-%d') == '2014-03-19'
|
||||
assert show.rating >= 8.0
|
||||
assert utils.is_int(show.ratingKey)
|
||||
assert sorted([i.tag for i in show.roles][:3]) == ['Alycia Debnam-Carey', 'Lindsey Morgan', 'Richard Harmon']
|
||||
assert sorted([i.tag for i in show.actors][:3]) == ['Alycia Debnam-Carey', 'Lindsey Morgan', 'Richard Harmon']
|
||||
assert show._server._baseurl == utils.SERVER_BASEURL
|
||||
assert show.studio == 'The CW'
|
||||
assert utils.is_string(show.summary, gte=100)
|
||||
assert utils.is_metadata(show.theme, contains='/theme/')
|
||||
assert utils.is_metadata(show.thumb, contains='/thumb/')
|
||||
assert show.title == 'The 100'
|
||||
assert show.titleSort == '100'
|
||||
assert show.type == 'show'
|
||||
assert utils.is_datetime(show.updatedAt)
|
||||
assert utils.is_int(show.viewCount, gte=0)
|
||||
assert utils.is_int(show.viewedLeafCount, gte=0)
|
||||
assert show.year == 2014
|
||||
|
||||
|
||||
def test_video_Show_watched(a_show):
|
||||
watched = a_show.watched()
|
||||
def test_video_Show_watched(show):
|
||||
show.episodes()[0].markWatched()
|
||||
watched = show.watched()
|
||||
assert len(watched) == 1 and watched[0].title == 'Pilot'
|
||||
|
||||
|
||||
def test_video_Show_unwatched(a_show):
|
||||
assert len(a_show.unwatched()) == 8
|
||||
def test_video_Show_unwatched(show):
|
||||
episodes = show.episodes()
|
||||
episodes[0].markWatched()
|
||||
unwatched = show.unwatched()
|
||||
assert len(unwatched) == len(episodes) - 1
|
||||
|
||||
|
||||
def test_video_Show_location(pms):
|
||||
# This should be a part of test test_video_Show_attrs
|
||||
# But is excluded because of https://github.com/mjs7231/python-plexapi/issues/97
|
||||
s = pms.library.section('TV Shows').get('The 100')
|
||||
# This will require a reload since the xml from http://138.68.157.5:32400/library/sections/2/all
|
||||
# Does not contain a location
|
||||
assert s.locations == ['/media/tvshows/the 100']
|
||||
|
||||
def test_video_Show_reload(pms):
|
||||
s = pms.library.section('TV Shows').get('Game of Thrones')
|
||||
assert s._initpath == '/library/sections/2/all'
|
||||
s.reload()
|
||||
assert s._initpath == '/library/metadata/6'
|
||||
assert len(s.roles) > 3
|
||||
def test_video_Show_location(plex):
|
||||
# This should be a part of test test_video_Show_attrs but is excluded
|
||||
# because of https://github.com/mjs7231/python-plexapi/issues/97
|
||||
show = plex.library.section('TV Shows').get('The 100')
|
||||
assert len(show.locations) >= 1
|
||||
|
||||
|
||||
def test_video_Show_episodes(a_show):
|
||||
inc_watched = a_show.episodes()
|
||||
ex_watched = a_show.episodes(viewCount=0)
|
||||
assert len(inc_watched) == 9
|
||||
assert len(ex_watched) == 8
|
||||
def test_video_Show_reload(plex):
|
||||
show = plex.library.section('TV Shows').get('Game of Thrones')
|
||||
assert utils.is_metadata(show._initpath, prefix='/library/sections/')
|
||||
assert len(show.roles) == 3
|
||||
show.reload()
|
||||
assert utils.is_metadata(show._initpath, prefix='/library/metadata/')
|
||||
assert len(show.roles) > 3
|
||||
|
||||
|
||||
def test_video_Show_download(monkeydownload, tmpdir, a_show):
|
||||
f = a_show.download(savepath=str(tmpdir))
|
||||
assert len(f) == 9
|
||||
def test_video_Show_episodes(show):
|
||||
episodes = show.episodes()
|
||||
episodes[0].markWatched()
|
||||
unwatched = show.episodes(viewCount=0)
|
||||
assert len(unwatched) == len(episodes) - 1
|
||||
|
||||
|
||||
def test_video_Season_download(monkeydownload, tmpdir, a_show):
|
||||
sn = a_show.season('Season 1')
|
||||
f = sn.download(savepath=str(tmpdir))
|
||||
assert len(f) == 8
|
||||
def test_video_Show_download(monkeydownload, tmpdir, show):
|
||||
episodes = show.episodes()
|
||||
filepaths = show.download(savepath=str(tmpdir))
|
||||
assert len(filepaths) == len(episodes)
|
||||
|
||||
|
||||
def test_video_Episode_download(monkeydownload, tmpdir, a_episode):
|
||||
f = a_episode.download(savepath=str(tmpdir))
|
||||
def test_video_Season_download(monkeydownload, tmpdir, show):
|
||||
season = show.season('Season 1')
|
||||
filepaths = season.download(savepath=str(tmpdir))
|
||||
assert len(filepaths) == 8
|
||||
|
||||
|
||||
def test_video_Episode_download(monkeydownload, tmpdir, episode):
|
||||
f = episode.download(savepath=str(tmpdir))
|
||||
assert len(f) == 1
|
||||
with_sceen_size = a_episode.download(savepath=str(tmpdir), **{'videoResolution': '500x300'})
|
||||
with_sceen_size = episode.download(savepath=str(tmpdir), **{'videoResolution': '500x300'})
|
||||
assert len(with_sceen_size) == 1
|
||||
|
||||
|
||||
def test_video_Show_thumbUrl(a_show):
|
||||
assert 'http://138.68.157.5:32400/library/metadata/12/thumb/' in a_show.thumbUrl
|
||||
def test_video_Show_thumbUrl(show):
|
||||
assert utils.SERVER_BASEURL in show.thumbUrl
|
||||
assert '/library/metadata/' in show.thumbUrl
|
||||
assert '/thumb/' in show.thumbUrl
|
||||
|
||||
|
||||
@pytest.mark.xfail
|
||||
def test_video_Show_analyze(a_show):
|
||||
show = a_show.analyze() # this isnt possble.. should it even be available?
|
||||
def test_video_Show_analyze(show):
|
||||
show = show.analyze()
|
||||
|
||||
|
||||
def test_video_Show_markWatched(a_tv_section):
|
||||
show = a_tv_section.get("Marvel's Daredevil")
|
||||
def test_video_Show_markWatched(tvshows):
|
||||
show = tvshows.get("Marvel's Daredevil")
|
||||
show.markWatched()
|
||||
assert a_tv_section.get("Marvel's Daredevil").isWatched
|
||||
assert tvshows.get("Marvel's Daredevil").isWatched
|
||||
|
||||
|
||||
def test_video_Show_markUnwatched(a_tv_section):
|
||||
show = a_tv_section.get("Marvel's Daredevil")
|
||||
def test_video_Show_markUnwatched(tvshows):
|
||||
show = tvshows.get("Marvel's Daredevil")
|
||||
show.markUnwatched()
|
||||
assert not a_tv_section.get("Marvel's Daredevil").isWatched
|
||||
assert not tvshows.get("Marvel's Daredevil").isWatched
|
||||
|
||||
|
||||
def test_video_Show_refresh(a_tv_section):
|
||||
show = a_tv_section.get("Marvel's Daredevil")
|
||||
def test_video_Show_refresh(tvshows):
|
||||
show = tvshows.get("Marvel's Daredevil")
|
||||
show.refresh()
|
||||
|
||||
|
||||
def test_video_Show_get(a_show):
|
||||
assert a_show.get('Pilot').title == 'Pilot'
|
||||
def test_video_Show_get(show):
|
||||
assert show.get('Pilot').title == 'Pilot'
|
||||
|
||||
|
||||
def test_video_Show_isWatched(a_show):
|
||||
assert not a_show.isWatched
|
||||
def test_video_Show_isWatched(show):
|
||||
assert not show.isWatched
|
||||
|
||||
|
||||
def test_video_Show_section(a_show):
|
||||
section = a_show.section()
|
||||
def test_video_Show_section(show):
|
||||
section = show.section()
|
||||
assert section.title == 'TV Shows'
|
||||
|
||||
|
||||
def test_video_Episode(a_show):
|
||||
pilot = a_show.episode('Pilot')
|
||||
assert pilot == a_show.episode(season=1, episode=1)
|
||||
def test_video_Episode(show):
|
||||
episode = show.episode('Pilot')
|
||||
assert episode == show.episode(season=1, episode=1)
|
||||
with pytest.raises(BadRequest):
|
||||
a_show.episode()
|
||||
show.episode()
|
||||
with pytest.raises(NotFound):
|
||||
a_show.episode(season=1337, episode=1337)
|
||||
show.episode(season=1337, episode=1337)
|
||||
|
||||
|
||||
def test_video_Episode_analyze(a_tv_section):
|
||||
ep = a_tv_section.get("Marvel's Daredevil").episode(season=1, episode=1)
|
||||
ep.analyze()
|
||||
def test_video_Episode_analyze(tvshows):
|
||||
episode = tvshows.get("Marvel's Daredevil").episode(season=1, episode=1)
|
||||
episode.analyze()
|
||||
|
||||
|
||||
def test_video_Episode_attrs(a_episode):
|
||||
ep = a_episode
|
||||
assert ep.addedAt > datetime(2017, 1, 1)
|
||||
assert ep.contentRating == 'TV-14'
|
||||
assert [i.tag for i in ep.directors] == ['Bharat Nalluri']
|
||||
assert ep.duration == 170859
|
||||
assert ep.grandparentTitle == 'The 100'
|
||||
assert ep.index == 1
|
||||
assert ep._initpath == '/library/metadata/12/allLeaves'
|
||||
assert ep.key == '/library/metadata/14'
|
||||
assert ep.listType == 'video'
|
||||
# Assign 0 ep.media
|
||||
med0 = ep.media[0]
|
||||
assert str(ep.originallyAvailableAt.date()) == '2014-03-19'
|
||||
assert ep.parentIndex == '1'
|
||||
assert ep.parentKey == '/library/metadata/13'
|
||||
assert ep.parentRatingKey == 13
|
||||
assert '/library/metadata/13/thumb/' in ep.parentThumb
|
||||
#assert ep.parentThumb == '/library/metadata/13/thumb/1485096623'
|
||||
assert ep.player is None
|
||||
assert ep.rating == 7.4
|
||||
assert ep.ratingKey == 14
|
||||
assert ep._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert ep.summary == u'Ninety-seven years ago, nuclear Armageddon decimated planet Earth, destroying civilization. The only survivors were the 400 inhabitants of 12 international space stations that were in orbit at the time. Three generations have been born in space, the survivors now number 4,000, and resources are running out on their dying "Ark." Among the 100 young exiles are Clarke, the bright teenage daughter of the Ark’s chief medical officer; the daredevil Finn; the brother/sister duo of Bellamy and Octavia, whose illegal sibling status has always led them to flaunt the rules, the lighthearted Jasper and the resourceful Monty. Technologically blind to what’s happening on the planet below them, the Ark’s leaders — Clarke’s widowed mother, Abby; Chancellor Jaha; and his shadowy second in command, Kane — are faced with difficult decisions about life, death and the continued existence of the human race.'
|
||||
assert ep.thumb == '/library/metadata/14/thumb/1485115318'
|
||||
assert ep.title == 'Pilot'
|
||||
assert ep.titleSort == 'Pilot'
|
||||
assert ep.transcodeSession is None
|
||||
assert ep.type == 'episode'
|
||||
assert ep.updatedAt > datetime(2017, 1, 1)
|
||||
assert ep.username is None
|
||||
assert ep.viewCount == 1
|
||||
assert ep.viewOffset == 0
|
||||
assert [i.tag for i in ep.writers] == ['Jason Rothenberg']
|
||||
assert ep.year == 2014
|
||||
assert med0.aspectRatio == 1.78
|
||||
assert med0.audioChannels == 6
|
||||
assert med0.audioCodec == 'aac'
|
||||
assert med0.bitrate == 1474
|
||||
assert med0.container == 'mp4'
|
||||
assert med0.duration == 170859
|
||||
assert med0.height == 720
|
||||
assert med0.id == 12
|
||||
assert med0._initpath == '/library/metadata/12/allLeaves'
|
||||
assert med0.optimizedForStreaming is False
|
||||
# Assign 0 med0.parts
|
||||
par0 = med0.parts[0]
|
||||
assert med0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
#assert med0.video == <Episode:14:The 100:S1:E1:Pilot>
|
||||
assert med0.videoCodec == 'h264'
|
||||
assert med0.videoFrameRate == 'PAL'
|
||||
assert med0.videoResolution == '720'
|
||||
assert med0.width == 1280
|
||||
assert par0.container == 'mp4'
|
||||
assert par0.duration == 170859
|
||||
assert par0.file == '/media/tvshows/the 100/season 1/the.100.s01e01.mp4'
|
||||
assert par0.id == 12
|
||||
assert par0._initpath == '/library/metadata/12/allLeaves'
|
||||
assert par0.key == '/library/parts/12/1484679008/file.mp4'
|
||||
#assert par0.media == <Media:Pilot>
|
||||
assert par0._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert par0.size == 31491130
|
||||
assert ep.isWatched is True
|
||||
def test_video_Episode_attrs(episode):
|
||||
assert utils.is_datetime(episode.addedAt)
|
||||
assert episode.contentRating in utils.CONTENTRATINGS
|
||||
assert [i.tag for i in episode.directors] == ['Bharat Nalluri']
|
||||
assert utils.is_int(episode.duration, gte=120000)
|
||||
assert episode.grandparentTitle == 'The 100'
|
||||
assert episode.index == 1
|
||||
assert utils.is_metadata(episode._initpath)
|
||||
assert utils.is_metadata(episode.key)
|
||||
assert episode.listType == 'video'
|
||||
assert episode.originallyAvailableAt.strftime('%Y-%m-%d') == '2014-03-19'
|
||||
assert utils.is_int(episode.parentIndex)
|
||||
assert utils.is_metadata(episode.parentKey)
|
||||
assert utils.is_int(episode.parentRatingKey)
|
||||
assert utils.is_metadata(episode.parentThumb, contains='/thumb/')
|
||||
assert episode.player is None
|
||||
assert episode.rating == 7.4
|
||||
assert utils.is_int(episode.ratingKey)
|
||||
assert episode._server._baseurl == utils.SERVER_BASEURL
|
||||
assert utils.is_string(episode.summary, gte=100)
|
||||
assert utils.is_metadata(episode.thumb, contains='/thumb/')
|
||||
assert episode.title == 'Pilot'
|
||||
assert episode.titleSort == 'Pilot'
|
||||
assert episode.transcodeSession is None
|
||||
assert episode.type == 'episode'
|
||||
assert utils.is_datetime(episode.updatedAt)
|
||||
assert episode.username is None
|
||||
assert utils.is_int(episode.viewCount)
|
||||
assert episode.viewOffset == 0
|
||||
assert [i.tag for i in episode.writers] == ['Jason Rothenberg']
|
||||
assert episode.year == 2014
|
||||
assert episode.isWatched is True
|
||||
# Media
|
||||
media = episode.media[0]
|
||||
assert media.aspectRatio == 1.78
|
||||
assert media.audioChannels in utils.AUDIOCHANNELS
|
||||
assert media.audioCodec in utils.CODECS
|
||||
assert utils.is_int(media.bitrate)
|
||||
assert media.container in utils.CONTAINERS
|
||||
assert utils.is_int(media.duration, gte=150000)
|
||||
assert utils.is_int(media.height, gte=200)
|
||||
assert utils.is_int(media.id)
|
||||
assert utils.is_metadata(media._initpath)
|
||||
assert isinstance(media.optimizedForStreaming, bool)
|
||||
assert media._server._baseurl == utils.SERVER_BASEURL
|
||||
assert media.videoCodec in utils.CODECS
|
||||
assert media.videoFrameRate in utils.FRAMERATES
|
||||
assert media.videoResolution in utils.RESOLUTIONS
|
||||
assert utils.is_int(media.width, gte=400)
|
||||
# Part
|
||||
part = media.parts[0]
|
||||
assert part.container in utils.CONTAINERS
|
||||
assert utils.is_int(part.duration, gte=150000)
|
||||
assert len(part.file) >= 10
|
||||
assert utils.is_int(part.id)
|
||||
assert utils.is_metadata(part._initpath)
|
||||
assert len(part.key) >= 10
|
||||
assert part._server._baseurl == utils.SERVER_BASEURL
|
||||
assert utils.is_int(part.size, gte=30000000)
|
||||
|
||||
|
||||
def test_video_Season(a_show):
|
||||
seasons = a_show.seasons()
|
||||
def test_video_Season(show):
|
||||
seasons = show.seasons()
|
||||
assert len(seasons) == 2
|
||||
assert ['Season 1', 'Season 2'] == [s.title for s in seasons]
|
||||
assert a_show.season('Season 1') == seasons[0]
|
||||
assert show.season('Season 1') == seasons[0]
|
||||
|
||||
|
||||
def test_video_Season_attrs(a_show):
|
||||
m = a_show.season('Season 1')
|
||||
assert m.addedAt > datetime(2017, 1, 1)
|
||||
assert m.index == 1
|
||||
assert m._initpath == '/library/metadata/12/children'
|
||||
assert m.key == '/library/metadata/13'
|
||||
assert m.lastViewedAt > datetime(2017, 1, 1)
|
||||
assert m.leafCount == 8
|
||||
assert m.listType == 'video'
|
||||
assert m.parentKey == '/library/metadata/12'
|
||||
assert m.parentRatingKey == 12
|
||||
assert m.parentTitle == 'The 100'
|
||||
assert m.ratingKey == 13
|
||||
assert m._server._baseurl == 'http://138.68.157.5:32400'
|
||||
assert m.summary == ''
|
||||
assert '/library/metadata/13/thumb/' in m.thumb
|
||||
#assert m.thumb == '/library/metadata/13/thumb/1485096623'
|
||||
assert m.title == 'Season 1'
|
||||
assert m.titleSort == 'Season 1'
|
||||
assert m.type == 'season'
|
||||
assert m.updatedAt > datetime(2017, 1, 1)
|
||||
assert m.viewCount == 1
|
||||
assert m.viewedLeafCount == 1
|
||||
assert m.seasonNumber == 1
|
||||
def test_video_Season_attrs(show):
|
||||
season = show.season('Season 1')
|
||||
assert utils.is_datetime(season.addedAt)
|
||||
assert season.index == 1
|
||||
assert utils.is_metadata(season._initpath)
|
||||
assert utils.is_metadata(season.key)
|
||||
assert utils.is_datetime(season.lastViewedAt)
|
||||
assert utils.is_int(season.leafCount, gte=3)
|
||||
assert season.listType == 'video'
|
||||
assert utils.is_metadata(season.parentKey)
|
||||
assert utils.is_int(season.parentRatingKey)
|
||||
assert season.parentTitle == 'The 100'
|
||||
assert utils.is_int(season.ratingKey)
|
||||
assert season._server._baseurl == utils.SERVER_BASEURL
|
||||
assert season.summary == ''
|
||||
assert utils.is_metadata(season.thumb, contains='/thumb/')
|
||||
assert season.title == 'Season 1'
|
||||
assert season.titleSort == 'Season 1'
|
||||
assert season.type == 'season'
|
||||
assert utils.is_datetime(season.updatedAt)
|
||||
assert utils.is_int(season.viewCount)
|
||||
assert utils.is_int(season.viewedLeafCount)
|
||||
assert utils.is_int(season.seasonNumber)
|
||||
|
||||
|
||||
def test_video_Season_show(a_show):
|
||||
sn = a_show.seasons()[0]
|
||||
season_by_name = a_show.season('Season 1')
|
||||
assert a_show.ratingKey == sn.parentRatingKey and season_by_name.parentRatingKey
|
||||
assert sn.ratingKey == season_by_name.ratingKey
|
||||
def test_video_Season_show(show):
|
||||
season = show.seasons()[0]
|
||||
season_by_name = show.season('Season 1')
|
||||
assert show.ratingKey == season.parentRatingKey and season_by_name.parentRatingKey
|
||||
assert season.ratingKey == season_by_name.ratingKey
|
||||
|
||||
|
||||
def test_video_Season_watched(a_tv_section):
|
||||
show = a_tv_section.get("Marvel's Daredevil")
|
||||
sn = show.season(1)
|
||||
def test_video_Season_watched(tvshows):
|
||||
show = tvshows.get("Marvel's Daredevil")
|
||||
season = show.season(1)
|
||||
sne = show.season('Season 1')
|
||||
assert sn == sne
|
||||
sn.markWatched()
|
||||
assert sn.isWatched
|
||||
assert season == sne
|
||||
season.markWatched()
|
||||
assert season.isWatched
|
||||
|
||||
|
||||
def test_video_Season_unwatched(a_tv_section):
|
||||
sn = a_tv_section.get("Marvel's Daredevil").season(1)
|
||||
sn.markUnwatched()
|
||||
assert not sn.isWatched
|
||||
def test_video_Season_unwatched(tvshows):
|
||||
season = tvshows.get("Marvel's Daredevil").season(1)
|
||||
season.markUnwatched()
|
||||
assert not season.isWatched
|
||||
|
||||
|
||||
def test_video_Season_get(a_show):
|
||||
ep = a_show.season(1).get('Pilot')
|
||||
assert ep.title == 'Pilot'
|
||||
def test_video_Season_get(show):
|
||||
episode = show.season(1).get('Pilot')
|
||||
assert episode.title == 'Pilot'
|
||||
|
||||
|
||||
def test_video_Season_episode(a_show):
|
||||
ep = a_show.season(1).get('Pilot')
|
||||
assert ep.title == 'Pilot'
|
||||
def test_video_Season_episode(show):
|
||||
episode = show.season(1).get('Pilot')
|
||||
assert episode.title == 'Pilot'
|
||||
|
||||
|
||||
def test_video_Season_episodes(a_show):
|
||||
sn_eps = a_show.season(2).episodes()
|
||||
assert len(sn_eps) == 1
|
||||
def test_video_Season_episodes(show):
|
||||
episodes = show.season(2).episodes()
|
||||
assert len(episodes) >= 1
|
||||
|
||||
|
||||
def test_that_reload_return_the_same_object(pms):
|
||||
def test_that_reload_return_the_same_object(plex):
|
||||
# we want to check this that all the urls are correct
|
||||
movie_library_search = pms.library.section('Movies').search('16 Blocks')[0]
|
||||
movie_search = pms.search('16 Blocks')[0]
|
||||
movie_section_get = pms.library.section('Movies').get('16 Blocks')
|
||||
movie_library_search = plex.library.section('Movies').search('16 Blocks')[0]
|
||||
movie_search = plex.search('16 Blocks')[0]
|
||||
movie_section_get = plex.library.section('Movies').get('16 Blocks')
|
||||
movie_library_search_key = movie_library_search.key
|
||||
movie_search_key = movie_search.key
|
||||
movie_section_get_key = movie_section_get.key
|
||||
assert movie_library_search_key == movie_library_search.reload().key == movie_search_key == movie_search.reload().key == movie_section_get_key == movie_section_get.reload().key
|
||||
tvshow_library_search = pms.library.section('TV Shows').search('The 100')[0]
|
||||
tvshow_search = pms.search('The 100')[0]
|
||||
tvshow_section_get = pms.library.section('TV Shows').get('The 100')
|
||||
tvshow_library_search = plex.library.section('TV Shows').search('The 100')[0]
|
||||
tvshow_search = plex.search('The 100')[0]
|
||||
tvshow_section_get = plex.library.section('TV Shows').get('The 100')
|
||||
tvshow_library_search_key = tvshow_library_search.key
|
||||
tvshow_search_key = tvshow_search.key
|
||||
tvshow_section_get_key = tvshow_section_get.key
|
||||
|
|
Loading…
Reference in a new issue