This commit is contained in:
Hellowlol 2017-01-31 01:02:22 +01:00
parent bbcefe2d53
commit e06f6250a3
9 changed files with 194 additions and 54 deletions

View file

@ -67,7 +67,7 @@ class Library(object):
def sectionByID(self, sectionID):
""" Returns the :class:`~plexapi.library.LibrarySection` that matches the specified sectionID.
Parameters:
sectionID (int): ID of the section to return.
"""
@ -89,7 +89,7 @@ class Library(object):
return utils.listItems(self.server, '/library/recentlyAdded')
def get(self, title): # this should use hub search when its merged
""" Return the first item from all items with the specified title.
""" Return the first item from all items with the specified title.
Parameters:
title (str): Title of the item to return.
@ -99,7 +99,7 @@ class Library(object):
reutrn i
def getByKey(self, key):
""" Return the first item from all items with the specified key.
""" Return the first item from all items with the specified key.
Parameters:
key (str): Key of the item to return.
@ -132,6 +132,9 @@ class Library(object):
server will automatically clean up old bundles once a week as part of Scheduled Tasks.
"""
self.server.query('/library/clean/bundles')
# Should this return true or false?
# check element if if has the correct mediaprefix?
def emptyTrash(self):
""" If a library has items in the Library Trash, use this option to empty the Trash. """
@ -227,7 +230,7 @@ class LibrarySection(object):
def recentlyAdded(self, maxresults=50):
""" Returns a list of media items recently added from this library section.
Parameters:
maxresults (int): Max number of items to return (default 50).
"""
@ -235,7 +238,7 @@ class LibrarySection(object):
def analyze(self):
""" Run an analysis on all of the items in this library section. """
self.server.query('/library/sections/%s/analyze' % self.key)
self.server.query('/library/sections/%s/analyze' % self.key, method=self.server.session.put)
def emptyTrash(self):
""" If a section has items in the Trash, use this option to empty the Trash. """
@ -367,7 +370,7 @@ class LibrarySection(object):
class MovieSection(LibrarySection):
""" Represents a :class:`~plexapi.library.LibrarySection` section containing movies.
Attributes:
ALLOWED_FILTERS (list<str>): List of allowed search filters. ('unwatched',
'duplicate', 'year', 'decade', 'genre', 'contentRating', 'collection',
@ -386,7 +389,7 @@ class MovieSection(LibrarySection):
class ShowSection(LibrarySection):
""" Represents a :class:`~plexapi.library.LibrarySection` section containing tv shows.
Attributes:
ALLOWED_FILTERS (list<str>): List of allowed search filters. ('unwatched',
'year', 'genre', 'contentRating', 'network', 'collection')
@ -409,7 +412,7 @@ class ShowSection(LibrarySection):
def recentlyAdded(self, libtype='episode', maxresults=50):
""" Returns a list of recently added episodes from this library section.
Parameters:
maxresults (int): Max number of items to return (default 50).
"""
@ -418,7 +421,7 @@ class ShowSection(LibrarySection):
class MusicSection(LibrarySection):
""" Represents a :class:`~plexapi.library.LibrarySection` section containing music artists.
Attributes:
ALLOWED_FILTERS (list<str>): List of allowed search filters. ('genre',
'country', 'collection')
@ -449,23 +452,26 @@ class MusicSection(LibrarySection):
class PhotoSection(LibrarySection):
""" Represents a :class:`~plexapi.library.LibrarySection` section containing photos.
Attributes:
ALLOWED_FILTERS (list<str>): List of allowed search filters. <NONE>
ALLOWED_SORT (list<str>): List of allowed sorting keys. <NONE>
TYPE (str): 'photo'
"""
ALLOWED_FILTERS = ()
ALLOWED_FILTERS = ('all', 'iso', 'make', 'lens', 'aperture', 'exposure')
ALLOWED_SORT = ()
TYPE = 'photo'
def searchAlbums(self, **kwargs):
def searchAlbums(self, title, **kwargs): # lets use this for now.
""" Search for an album. See :func:`~plexapi.library.LibrarySection.search()` for usage. """
return self.search(libtype='photo', **kwargs)
albums = utils.listItems(self.server, '/library/sections/%s/all?type=14' % self.key)
return [i for i in albums if i.title.lower() == title.lower()]
def searchPhotos(self, **kwargs):
def searchPhotos(self, title, **kwargs):
""" Search for a photo. See :func:`~plexapi.library.LibrarySection.search()` for usage. """
return self.search(libtype='photo', **kwargs)
photos = utils.listItems(self.server, '/library/sections/%s/all?type=13' % self.key)
return [i for i in photos if i.title.lower() == title.lower()]
@utils.register_libtype
@ -480,7 +486,7 @@ class FilterChoice(object):
fastKey (str): API path to quickly list all items in this filter
(/library/sections/<section>/all?genre=<key>)
key (str): Short key (id) of this filter option (used ad <key> in fastKey above).
thumb (str): Thumbnail used to represent this filter option.
thumb (str): Thumbnail used to represent this filter option.
title (str): Human readable name for this filter option.
type (str): Filter type (genre, contentRating, etc).
"""

View file

@ -207,7 +207,7 @@ class PlexServer(object):
h.update(headers)
response = method(url, headers=h, timeout=TIMEOUT, **kwargs)
#print(response.url)
if response.status_code not in [200, 201]:
if response.status_code not in [200, 201]: # pragma: no cover
codename = codes.get(response.status_code)[0]
raise BadRequest('(%s) %s %s' % (response.status_code, codename, response.url))
data = response.text.encode('utf8')

View file

@ -11,8 +11,11 @@ from threading import Thread
# Search Types - Plex uses these to filter specific media types when searching.
SEARCHTYPES = {'movie': 1, 'show': 2, 'season': 3, 'episode': 4,
'artist': 8, 'album': 9, 'track': 10}
SEARCHTYPES = {'movie': 1, 'show': 2, 'season': 3,
'episode': 4, 'artist': 8, 'album': 9, 'track': 10,
'photo': 14}
LIBRARY_TYPES = {}
@ -407,13 +410,8 @@ def joinArgs(args):
return '?%s' % '&'.join(arglist)
<<<<<<< HEAD
def listChoices(server, path):
""" Returns a dict of {title:key} for all simple choices in a search filter.
=======
def listChoices(server, path): # pragma: no cover # Dont think its is used
"""ListChoices is by _cleanSort etc.
>>>>>>> more cov
Parameters:
server (:class:`~plexapi.server.PlexServer`): PlexServer object this is from.

View file

@ -40,6 +40,20 @@ def pms(request):
return pms
@pytest.fixture()
def freshpms():
from plexapi.server import PlexServer
sess = requests.Session()
url = 'http://138.68.157.5:32400'
assert test_token
assert url
pms = PlexServer(url, test_token, session=sess)
return pms
def pytest_addoption(parser):
parser.addoption("--req_client", action="store_true",
help="Run tests that interact with a client")
@ -90,6 +104,12 @@ def a_music_section(pms):
assert sec
return sec
@pytest.fixture()
def a_photo_section(pms):
sec = pms.library.section('Photos')
assert sec
return sec
@pytest.fixture()
def a_artist(a_music_section):

View file

@ -1,8 +1,13 @@
# -*- coding: utf-8 -*-
import pytest
from plexapi.exceptions import NotFound
def test_section(pms):
# func naming should follow:
# tests_file_class_method_some_description
def test_library_Library_section(pms):
sections = pms.library.sections()
assert len(sections) == 4
@ -10,11 +15,19 @@ def test_section(pms):
section_name = pms.library.section(lfs)
assert section_name.title == lfs
with pytest.raises(NotFound):
assert pms.library.section('gfdsas')
def test_library_Library_sectionByID_is_equal_section(pms, freshpms):
# 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')
def test_library_sectionByID_is_equal_section(pms):
assert pms.library.sectionByID('1').uuid == pms.library.section('Movies').uuid
def test_library_sectionByID_with_attrs(pms):
m = pms.library.sectionByID('1')
assert m.agent == 'com.plexapp.agents.imdb'
@ -59,6 +72,77 @@ def test_library_get(pms):
m = pms.library.get('16 blocks')
assert m.title == '16 Blocks'
def test_library_Library_cleanBundle(pms):
pms.library.cleanBundles()
def test_library_Library_optimize(pms):
pms.library.optimize()
def test_library_Library_emptyTrash(pms):
pms.library.emptyTrash()
def _test_library_Library_refresh(pms):
pms.library.refresh() # fix mangle and proof the sections attrs
def _test_library_MovieSection_refresh(a_movie_section):
a_movie_section.refresh()
def test_library_MovieSection_onDeck(a_movie_section):
assert len(a_movie_section.onDeck())
def test_library_MovieSection_recentlyAdded(a_movie_section):
assert len(a_movie_section.recentlyAdded())
def test_library_MovieSection_analyze(a_movie_section):
a_movie_section.analyze()
def test_library_ShowSection_searchShows(a_tv_section):
s = a_tv_section.searchShows(**{'title': 'The 100'})
assert s
def test_library_ShowSection_searchEpisodes(a_tv_section):
s = a_tv_section.searchEpisodes(**{'title': 'Pilot'})
assert s
def test_library_ShowSection_recentlyAdded(a_tv_section):
assert len(a_tv_section.recentlyAdded())
def test_library_MusicSection_albums(a_music_section):
assert len(a_music_section.albums())
def test_library_MusicSection_searchTracks(a_music_section):
assert len(a_music_section.searchTracks(**{'title': 'Holy Moment'}))
def test_library_MusicSection_searchAlbums(a_music_section):
assert len(a_music_section.searchAlbums(**{'title': 'Unmastered Impulses'}))
def test_library_PhotoSection_searchAlbums(a_photo_section):
albums = a_photo_section.searchAlbums('photo_album1')
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'))
#### Start on library search
@ -82,7 +166,9 @@ def test_search_with_apostrophe(pms):
def test_crazy_search(pms, a_movie):
movie = a_movie
movies = pms.library.section('Movies')
assert movie in movies.search(actor=movie.actors[0]), 'Unable to search movie by actor.'
assert movie in pms.library.search(genre=29, libtype='movie')
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.'
assert movie not in movies.search(year=2007), 'Unable to filter movie by year.'

View file

@ -43,6 +43,7 @@ def test_myplex_connect_to_resource(plex_account):
if resource.name == 'PMS_API_TEST_SERVER':
break
server = resource.connect()
assert 'Ohno' in server.url('Ohno')
assert server

View file

@ -4,10 +4,6 @@ import pytest
def test_list_playlists(pms):
playlists = pms.playlists()
print(playlists)
assert len(playlists)
def test_create_playlist(pms, a_show):

View file

@ -1,6 +1,7 @@
import pytest
import os
from plexapi.exceptions import BadRequest, NotFound
from plexapi.utils import download
@ -82,23 +83,20 @@ def test_server_transcodeImage(tmpdir, pms, a_show):
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_playlist(pms):
pl = pms.playlist('some_playlist')
assert pl.title == 'some_playlist'
with pytest.raises(NotFound):
pms.playlist('124xxx11y')
def test_server_playlists(pms):
playlists = pms.playlists()
@ -110,21 +108,48 @@ def test_server_history(pms):
assert len(history)
def test_server_Server_query(pms):
assert pms.query('/')
from plexapi.server import PlexServer
with pytest.raises(BadRequest):
assert pms.query('/asdasdsada/12123127/aaaa', headers={'random_headers': '1337'})
with pytest.raises(NotFound):
# This is really requests.exceptions.HTTPError:
# 401 Client Error: Unauthorized for url:
PlexServer('http://138.68.157.5:32400', '1234')
def test_server_Server_session():
from requests import Session
from plexapi.server import PlexServer
class MySession(Session):
def __init__(self):
super(self.__class__, self).__init__()
self.plexapi_session_test = True
plex = PlexServer('http://138.68.157.5:32400',
os.environ.get('PLEX_TEST_TOKEN'),
session=MySession())
assert hasattr(plex.session, 'plexapi_session_test')
pl = plex.playlists()
assert hasattr(pl[0].server.session, 'plexapi_session_test')
# check client
# check myplex.
def test_server_token_in_headers(pms):
h = pms.headers()
assert 'X-Plex-Token' in h and len(h['X-Plex-Token'])
'''
{
'X-Plex-Platform': plexapi.X_PLEX_PLATFORM,
'X-Plex-Platform-Version': plexapi.X_PLEX_PLATFORM_VERSION,
'X-Plex-Provides': plexapi.X_PLEX_PROVIDES,
'X-Plex-Product': plexapi.X_PLEX_PRODUCT,
'X-Plex-Version': plexapi.X_PLEX_VERSION,
'X-Plex-Device': plexapi.X_PLEX_DEVICE,
'X-Plex-Device-Name': plexapi.X_PLEX_DEVICE_NAME,
'X-Plex-Client-Identifier': plexapi.X_PLEX_IDENTIFIER,
}
'''
def _test_server_createPlayQueue():
# see test_playlists.py
@ -134,10 +159,19 @@ def _test_server_createPlaylist():
# see test_playlists.py
pass
def test_server_client_not_found(pms):
with pytest.raises(NotFound):
pms.client('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
@pytest.mark.req_client
def test_server_client(pms):
assert pms.client('Plex Web (Chrome)')
def test_server_Server_sessions(pms):
assert len(pms.sessions()) == 0
@pytest.mark.req_client
def test_server_clients(pms):
@ -180,4 +214,3 @@ def test_server_account(pms):
assert acc.username == 'testplexapi@gmail.com'

View file

@ -73,7 +73,7 @@ def test_video_Movie_attrs_as_much_as_possible(a_movie_section):
assert m.guid == 'com.plexapp.agents.imdb://tt0317219?lang=en'
assert m.initpath == '/library/metadata/2'
assert m.key == '/library/metadata/2'
assert str(m.lastViewedAt) == '__NA__'
assert str(m.lastViewedAt) == '2017-01-30 23:19:38'
assert m.librarySectionID == '1'
assert m.listType == 'video'
# Assign 0 m.media
@ -104,7 +104,7 @@ def test_video_Movie_attrs_as_much_as_possible(a_movie_section):
# Assign 0 m.videoStreams
vid0 = m.videoStreams[0]
assert m.viewCount == 0
assert m.viewOffset == 0
assert m.viewOffset == 88870
assert str(m.viewedAt) == '__NA__'
assert [i.tag for i in m.writers] == ['Dan Fogelman', 'Joe Ranft', 'John Lasseter', 'Kiel Murray', 'Phil Lorin', 'Jorgen Klubien']
assert m.year == 2006