Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions plexapi/library.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# -*- coding: utf-8 -*-

import logging
from plexapi import X_PLEX_CONTAINER_SIZE, log, utils
from plexapi.compat import unquote
from plexapi.media import MediaTag, Genre, Role, Director
from plexapi.exceptions import BadRequest, NotFound
from plexapi.media import MediaTag

Expand Down Expand Up @@ -152,6 +155,9 @@ def refresh(self):
"""
self.server.query('/library/sections/all/refresh')

def __len__(self):
return len(self.sections())


class LibrarySection(object):
""" Base class for a single library section.
Expand Down Expand Up @@ -471,6 +477,42 @@ def searchPhotos(self, title, **kwargs):
return [i for i in photos if i.title.lower() == title.lower()]


@utils.register_libtype
class Hub(object):
TYPE = 'Hub'

def __init__(self, server, data, initpath):
self.server = server
self.initpath = initpath
self.type = data.attrib.get('type')
self.hubIdentifier = data.attrib.get('hubIdentifier')
self.title = data.attrib.get('title')
self._items = []
self.size = utils.cast(int, data.attrib.get('title'), 0)

if self.type == 'genre':
self._items = [Genre(self.server, elem) for elem in data]
elif self.type == 'director':
self._items = [Director(self.server, elem) for elem in data]
elif self.type == 'actor':
self._items = [Role(self.server, elem) for elem in data]
else:
for elem in data:
try:
self._items.append(utils.buildItem(self.server, elem, '/hubs'))
except Exception as e:
logging.exception('Failed %s to build %s' % (self.type, self.title))

def __repr__(self):
return '<Hub:%s>' % self.title.encode('utf8')

def __len__(self):
return self.size

def all(self):
return self._items


@utils.register_libtype
class FilterChoice(object):
""" Represents a single filter choice. These objects are gathered when using filters
Expand Down
2 changes: 1 addition & 1 deletion plexapi/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def __init__(self, server, data):
self.tag = data.attrib.get('tag')

def __repr__(self):
tag = self.tag.replace(' ','.')[0:20]
tag = self.tag.replace(' ', '.')[0:20].encode('utf-8')
return '<%s:%s:%s>' % (self.__class__.__name__, self.id, tag)


Expand Down
47 changes: 40 additions & 7 deletions plexapi/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from plexapi import log, logfilter, utils
from plexapi import audio, video, photo, playlist # noqa; required # why is this needed?
from plexapi.client import PlexClient
from plexapi.compat import quote
from plexapi.compat import quote, urlencode
from plexapi.exceptions import BadRequest, NotFound
from plexapi.library import Library
from plexapi.playlist import Playlist
Expand Down Expand Up @@ -211,20 +211,55 @@ def query(self, path, method=None, headers=None, **kwargs):
data = response.text.encode('utf8')
return ElementTree.fromstring(data) if data else None

def search(self, query, mediatype=None):
def search(self, query, mediatype=None, limit=None, **kwargs):
"""Searching within a library section is much more powerful.

Args:
query (str): Search str
mediatype (str, optional): Limit your search to a media type.
kwargs (dict): #TODO

Returns:
List
"""
items = utils.listItems(self, '/search?query=%s' % quote(query))
if query and not kwargs:
items = []
for item in self.hubs(query, mediatype=mediatype, limit=limit):
items.extend(item.all())
return items

else:
items = utils.listItems(self, '/search?query=%s' % quote(query))
if mediatype:
return [item for item in items if item.type == mediatype]
else:
return items

def hubs(self, query, mediatype=None, limit=None):
"""Searching within a library section is much more powerful.

Args:
query (str): Search str
mediatype (str, optional): Limit your search to a media type.
limit (int) Default to None, limit your results to x results

Returns:
List: of Hub
"""
p = {}

if query:
p['query'] = query
if mediatype:
return [item for item in items if item.type == mediatype]
return items
p['section'] = utils.SEARCHTYPES[mediatype]
if limit:
p['limit'] = limit

u = '/hubs/search?%s' % urlencode(p)

hubs = utils.listItems(self, u, bytag=True)

return [i for i in hubs if i]

def sessions(self):
"""List all active sessions."""
Expand Down Expand Up @@ -258,8 +293,6 @@ def transcodeImage(self, media, height, width, opacity=100, saturation=100):
return self.url(transcode_url)




class Account(object):
"""This is the locally cached MyPlex account information.
The properties provided don't matchthe myplex.MyPlexAccount object very well.
Expand Down
23 changes: 13 additions & 10 deletions plexapi/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,16 +255,19 @@ def cast(func, value):
func (func): Calback function to used cast to type (int, bool, float, etc).
value (any): value to be cast and returned.
"""
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
if not value:
return

if func in (int, float):
try:
return func(value)
except ValueError:
return float('nan')

elif func == bool:
return bool(int(value))
else:
raise TypeError('Cast only allows int, float and bool')


def findKey(server, key):
Expand Down