mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-26 13:40:22 +00:00
Cleanup imports; force registration of audio/video libtypes
This commit is contained in:
parent
276ba26b77
commit
2e2ea93982
7 changed files with 84 additions and 98 deletions
|
@ -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
18
plexapi/compat.py
Normal 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
|
|
@ -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):
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue