Cleanup imports; force registration of audio/video libtypes

This commit is contained in:
Michael Shepanski 2016-03-17 01:14:31 -04:00
parent 276ba26b77
commit 2e2ea93982
7 changed files with 84 additions and 98 deletions

View file

@ -2,18 +2,15 @@
PlexAudio
"""
from plexapi import utils
from plexapi.compat import urlencode
from plexapi.media import Media, Genre, Producer
from plexapi.exceptions import Unsupported
from plexapi.utils import NA
from plexapi.utils import cast, toDatetime, register_libtype
from plexapi.video import Video # TODO: remove this when Audio class can stand on its own legs
try:
from urllib import urlencode # Python2
except ImportError:
from urllib.parse import urlencode # Python3
from plexapi.video import Video
NA = utils.NA
class Audio(Video): # TODO: inherit from PlexPartialObject, like the Video class does
# TODO: inherit from PlexPartialObject, like the Video class does
class Audio(Video):
def _loadData(self, data):
self.type = data.attrib.get('type', NA)
@ -24,9 +21,9 @@ class Audio(Video): # TODO: inherit from PlexPartialObject, like the Video clas
self.summary = data.attrib.get('summary', NA)
self.art = data.attrib.get('art', NA)
self.thumb = data.attrib.get('thumb', NA)
self.addedAt = toDatetime(data.attrib.get('addedAt', NA))
self.updatedAt = toDatetime(data.attrib.get('updatedAt', NA))
self.sessionKey = cast(int, data.attrib.get('sessionKey', NA))
self.addedAt = utils.toDatetime(data.attrib.get('addedAt', NA))
self.updatedAt = utils.toDatetime(data.attrib.get('updatedAt', NA))
self.sessionKey = utils.cast(int, data.attrib.get('sessionKey', NA))
self.user = self._find_user(data) # for active sessions
self.player = self._find_player(data) # for active sessions
self.transcodeSession = self._find_transcodeSession(data)
@ -67,7 +64,7 @@ class Audio(Video): # TODO: inherit from PlexPartialObject, like the Video clas
self._loadData(data[0])
@register_libtype
@utils.register_libtype
class Artist(Audio):
TYPE = 'artist'
@ -77,14 +74,14 @@ class Artist(Audio):
self.studio = data.attrib.get('studio', NA)
self.contentRating = data.attrib.get('contentRating', NA)
self.rating = data.attrib.get('rating', NA)
self.year = cast(int, data.attrib.get('year', NA))
self.year = utils.cast(int, data.attrib.get('year', NA))
self.banner = data.attrib.get('banner', NA)
self.theme = data.attrib.get('theme', NA)
self.duration = cast(int, data.attrib.get('duration', NA))
self.originallyAvailableAt = toDatetime(data.attrib.get('originallyAvailableAt', NA), '%Y-%m-%d')
self.leafCount = cast(int, data.attrib.get('leafCount', NA))
self.viewedLeafCount = cast(int, data.attrib.get('viewedLeafCount', NA))
self.childCount = cast(int, data.attrib.get('childCount', NA))
self.duration = utils.cast(int, data.attrib.get('duration', NA))
self.originallyAvailableAt = utils.toDatetime(data.attrib.get('originallyAvailableAt', NA), '%Y-%m-%d')
self.leafCount = utils.cast(int, data.attrib.get('leafCount', NA))
self.viewedLeafCount = utils.cast(int, data.attrib.get('viewedLeafCount', NA))
self.childCount = utils.cast(int, data.attrib.get('childCount', NA))
self.titleSort = data.attrib.get('titleSort', NA)
def albums(self):
@ -116,7 +113,7 @@ class Artist(Audio):
self.server.query('/library/metadata/%s/refresh' % self.ratingKey)
@register_libtype
@utils.register_libtype
class Album(Audio):
TYPE = 'album'
@ -133,9 +130,9 @@ class Album(Audio):
self.parentIndex = data.attrib.get('parentIndex', NA)
self.parentThumb = data.attrib.get('parentThumb', NA)
self.parentTheme = data.attrib.get('parentTheme', NA)
self.leafCount = cast(int, data.attrib.get('leafCount', NA))
self.viewedLeafCount = cast(int, data.attrib.get('viewedLeafCount', NA))
self.year = cast(int, data.attrib.get('year', NA))
self.leafCount = utils.cast(int, data.attrib.get('leafCount', NA))
self.viewedLeafCount = utils.cast(int, data.attrib.get('viewedLeafCount', NA))
self.year = utils.cast(int, data.attrib.get('year', NA))
def tracks(self, watched=None):
childrenKey = '/library/metadata/%s/children' % self.ratingKey
@ -158,7 +155,7 @@ class Album(Audio):
return self.tracks(watched=False)
@register_libtype
@utils.register_libtype
class Track(Audio):
TYPE = 'track'
@ -177,8 +174,8 @@ class Track(Audio):
self.contentRating = data.attrib.get('contentRating', NA)
self.index = data.attrib.get('index', NA)
self.rating = data.attrib.get('rating', NA)
self.duration = cast(int, data.attrib.get('duration', NA))
self.originallyAvailableAt = toDatetime(data.attrib.get('originallyAvailableAt', NA), '%Y-%m-%d')
self.duration = utils.cast(int, data.attrib.get('duration', NA))
self.originallyAvailableAt = utils.toDatetime(data.attrib.get('originallyAvailableAt', NA), '%Y-%m-%d')
@property
def thumbUrl(self):

18
plexapi/compat.py Normal file
View file

@ -0,0 +1,18 @@
"""
Python 2/3 compatability
Always try Py3 first
"""
try:
from urllib.parse import urlencode
except ImportError:
from urllib import urlencode
try:
from urllib.parse import quote
except ImportError:
from urllib import quote
try:
from configparser import ConfigParser
except ImportError:
from ConfigParser import ConfigParser

View file

@ -134,16 +134,16 @@ class LibrarySection(object):
path = '/library/sections/%s/all' % self.key
return utils.find_item(self.server, path, title)
def search(self, title, filter='all', vtype=None, **tags):
def search(self, title, filter='all', libtype=None, **tags):
""" Search section content.
title: Title to search (pass None to search all titles).
filter: One of {'all', 'newest', 'onDeck', 'recentlyAdded', 'recentlyViewed', 'unwatched'}.
videotype: One of {'movie', 'show', 'season', 'episode'}.
filter: One of {all, newest, onDeck, recentlyAdded, recentlyViewed, unwatched}.
libtype: One of {movie, show, season, episode, artist, album, track}.
tags: One of {country, director, genre, producer, actor, writer}.
"""
args = {}
if title: args['title'] = title
if vtype: args['type'] = utils.search_type(vtype)
if libtype: args['type'] = utils.search_type(libtype)
for tag, obj in tags.items():
args[tag] = obj.id
query = '/library/sections/%s/%s%s' % (self.key, filter, utils.joinArgs(args))
@ -181,7 +181,7 @@ class MovieSection(LibrarySection):
return self._secondary_list('resolution', input)
def search(self, title, filter='all', **tags):
return super(MovieSection, self).search(title, filter=filter, vtype='movie', **tags)
return super(MovieSection, self).search(title, filter=filter, libtype='movie', **tags)
class ShowSection(LibrarySection):
@ -191,10 +191,10 @@ class ShowSection(LibrarySection):
return self._primary_list('recentlyViewedShows')
def search(self, title, filter='all', **tags):
return super(ShowSection, self).search(title, filter=filter, vtype='show', **tags)
return super(ShowSection, self).search(title, filter=filter, libtype='show', **tags)
def searchEpisodes(self, title, filter='all', **tags):
return super(ShowSection, self).search(title, filter=filter, vtype='episode', **tags)
return super(ShowSection, self).search(title, filter=filter, libtype='episode', **tags)
class MusicSection(LibrarySection):

View file

@ -5,6 +5,8 @@ import requests
from requests.status_codes import _codes as codes
from plexapi import BASE_HEADERS, TIMEOUT
from plexapi import log, utils
from plexapi import audio, video # flake8:noqa; required
from plexapi.compat import quote
from plexapi.client import Client
from plexapi.exceptions import BadRequest, NotFound
from plexapi.library import Library
@ -12,11 +14,6 @@ from plexapi.myplex import MyPlexAccount
from plexapi.playqueue import PlayQueue
from xml.etree import ElementTree
try:
from urllib import quote # Python2
except ImportError:
from urllib.parse import quote # Python3
TOTAL_QUERIES = 0
DEFAULT_BASEURI = 'http://localhost:32400'

View file

@ -2,17 +2,16 @@
PlexAPI Sync
"""
import requests
from plexapi import utils
from plexapi.exceptions import NotFound
from plexapi.video import list_items
from plexapi.utils import cast
class SyncItem(object):
def __init__(self, device, data, servers=None):
self.device = device
self.servers = servers
self.id = cast(int, data.attrib.get('id'))
self.version = cast(int, data.attrib.get('version'))
self.id = utils.cast(int, data.attrib.get('id'))
self.version = utils.cast(int, data.attrib.get('version'))
self.rootTitle = data.attrib.get('rootTitle')
self.title = data.attrib.get('title')
self.metadataType = data.attrib.get('metadataType')
@ -34,7 +33,7 @@ class SyncItem(object):
def getMedia(self):
server = self.server().connect()
items = list_items(server, '/sync/items/{0}'.format(self.id))
items = utils.list_items(server, '/sync/items/{0}'.format(self.id))
return items
def markAsDone(self, sync_id):

View file

@ -2,13 +2,8 @@
PlexAPI Utils
"""
from datetime import datetime
from threading import Thread
from Queue import Queue
from plexapi.compat import quote
from plexapi.exceptions import UnknownType
try:
from urllib import quote # Python2
except ImportError:
from urllib.parse import quote # Python3
# Registry of library types we may come across when parsing XML. This allows us to
@ -81,6 +76,19 @@ def build_item(server, elem, initpath):
raise UnknownType('Unknown library type: %s' % libtype)
def cast(func, value):
if value not in [None, NA]:
if func == bool:
return bool(int(value))
elif func in [int, float]:
try:
return func(value)
except ValueError:
return float('nan')
return func(value)
return value
def find_key(server, key):
path = '/library/metadata/{0}'.format(key)
try:
@ -98,6 +106,15 @@ def find_item(server, path, title):
raise NotFound('Unable to find item: %s' % title)
def joinArgs(args):
if not args: return ''
arglist = []
for key in sorted(args, key=lambda x:x.lower()):
value = str(args[key])
arglist.append('%s=%s' % (key, quote(value)))
return '?%s' % '&'.join(arglist)
def list_items(server, path, libtype=None, watched=None):
items = []
for elem in server.query(path):
@ -112,54 +129,16 @@ def list_items(server, path, libtype=None, watched=None):
def search_type(libtype):
if libtype == LIBRARY_TYPES['movie'].TYPE: return 1
elif libtype == LIBRARY_TYPES['show'].TYPE: return 2
elif libtype == LIBRARY_TYPES['season'].TYPE: return 3
elif libtype == LIBRARY_TYPES['episode'].TYPE: return 4
elif libtype == LIBRARY_TYPES['artist'].TYPE: return 8
elif libtype == LIBRARY_TYPES['album'].TYPE: return 9
elif libtype == LIBRARY_TYPES['track'].TYPE: return 10
if libtype == 'movie': return 1
elif libtype == 'show': return 2
elif libtype == 'season': return 3
elif libtype == 'episode': return 4
elif libtype == 'artist': return 8
elif libtype == 'album': return 9
elif libtype == 'track': return 10
raise NotFound('Unknown libtype: %s' % libtype)
def cast(func, value):
if value not in [None, NA]:
if func == bool:
return bool(int(value))
elif func in [int, float]:
try:
return func(value)
except ValueError:
return float('nan')
return func(value)
return value
def joinArgs(args):
if not args: return ''
arglist = []
for key in sorted(args, key=lambda x:x.lower()):
value = str(args[key])
arglist.append('%s=%s' % (key, quote(value)))
return '?%s' % '&'.join(arglist)
def threaded(funcs, *args, **kwargs):
def _run(func, _args, _kwargs, results):
results.put(func(*_args, **_kwargs))
threads = []
results = Queue(len(funcs) + 1)
for func in funcs:
targs = [func, args, kwargs, results]
threads.append(Thread(target=_run, args=targs))
for thread in threads:
thread.start()
for thread in threads:
thread.join()
results.put(None)
return results
def toDatetime(value, format=None):
if value and value != NA:
if format: value = datetime.strptime(value, format)

View file

@ -5,13 +5,9 @@ import re
from requests import put
from plexapi import media, utils
from plexapi.client import Client
from plexapi.compat import urlencode
from plexapi.myplex import MyPlexUser
from plexapi.exceptions import Unsupported
try:
from urllib import urlencode # Python2
except ImportError:
from urllib.parse import urlencode # Python3
NA = utils.NA