229 lines
6.1 KiB
Python
229 lines
6.1 KiB
Python
|
import plexobjects
|
||
|
import plexstream
|
||
|
import plexrequest
|
||
|
import util
|
||
|
|
||
|
METADATA_RELATED_TRAILER = 1
|
||
|
METADATA_RELATED_DELETED_SCENE = 2
|
||
|
METADATA_RELATED_INTERVIEW = 3
|
||
|
METADATA_RELATED_MUSIC_VIDEO = 4
|
||
|
METADATA_RELATED_BEHIND_THE_SCENES = 5
|
||
|
METADATA_RELATED_SCENE_OR_SAMPLE = 6
|
||
|
METADATA_RELATED_LIVE_MUSIC_VIDEO = 7
|
||
|
METADATA_RELATED_LYRIC_MUSIC_VIDEO = 8
|
||
|
METADATA_RELATED_CONCERT = 9
|
||
|
METADATA_RELATED_FEATURETTE = 10
|
||
|
METADATA_RELATED_SHORT = 11
|
||
|
METADATA_RELATED_OTHER = 12
|
||
|
|
||
|
|
||
|
class MediaItem(plexobjects.PlexObject):
|
||
|
def getIdentifier(self):
|
||
|
identifier = self.get('identifier') or None
|
||
|
|
||
|
if identifier is None:
|
||
|
identifier = self.container.identifier
|
||
|
|
||
|
# HACK
|
||
|
# PMS doesn't return an identifier for playlist items. If we haven't found
|
||
|
# an identifier and the key looks like a library item, then we pretend like
|
||
|
# the identifier was set.
|
||
|
#
|
||
|
if identifier is None: # Modified from Roku code which had no check for None with iPhoto - is that right?
|
||
|
if self.key.startswith('/library/metadata'):
|
||
|
identifier = "com.plexapp.plugins.library"
|
||
|
elif self.isIPhoto():
|
||
|
identifier = "com.plexapp.plugins.iphoto"
|
||
|
|
||
|
return identifier
|
||
|
|
||
|
def getQualityType(self, server=None):
|
||
|
if self.isOnlineItem():
|
||
|
return util.QUALITY_ONLINE
|
||
|
|
||
|
if not server:
|
||
|
server = self.getServer()
|
||
|
|
||
|
return util.QUALITY_LOCAL if server.isLocalConnection() else util.QUALITY_REMOTE
|
||
|
|
||
|
def delete(self):
|
||
|
if not self.ratingKey:
|
||
|
return
|
||
|
|
||
|
req = plexrequest.PlexRequest(self.server, '/library/metadata/{0}'.format(self.ratingKey), method='DELETE')
|
||
|
req.getToStringWithTimeout(10)
|
||
|
self.deleted = req.wasOK()
|
||
|
return self.deleted
|
||
|
|
||
|
def exists(self):
|
||
|
if self.deleted:
|
||
|
return False
|
||
|
|
||
|
data = self.server.query('/library/metadata/{0}'.format(self.ratingKey))
|
||
|
return data.attrib.get('size') != '0'
|
||
|
# req = plexrequest.PlexRequest(self.server, '/library/metadata/{0}'.format(self.ratingKey), method='HEAD')
|
||
|
# req.getToStringWithTimeout(10)
|
||
|
# return not req.wasNotFound()
|
||
|
|
||
|
def fixedDuration(self):
|
||
|
duration = self.duration.asInt()
|
||
|
if duration < 1000:
|
||
|
duration *= 60000
|
||
|
return duration
|
||
|
|
||
|
|
||
|
class Media(plexobjects.PlexObject):
|
||
|
TYPE = 'Media'
|
||
|
|
||
|
def __init__(self, data, initpath=None, server=None, video=None):
|
||
|
plexobjects.PlexObject.__init__(self, data, initpath=initpath, server=server)
|
||
|
self.video = video
|
||
|
self.parts = [MediaPart(elem, initpath=self.initpath, server=self.server, media=self) for elem in data]
|
||
|
|
||
|
def __repr__(self):
|
||
|
title = self.video.title.replace(' ', '.')[0:20]
|
||
|
return '<%s:%s>' % (self.__class__.__name__, title.encode('utf8'))
|
||
|
|
||
|
|
||
|
class MediaPart(plexobjects.PlexObject):
|
||
|
TYPE = 'Part'
|
||
|
|
||
|
def __init__(self, data, initpath=None, server=None, media=None):
|
||
|
plexobjects.PlexObject.__init__(self, data, initpath=initpath, server=server)
|
||
|
self.media = media
|
||
|
self.streams = [MediaPartStream.parse(e, initpath=self.initpath, server=server, part=self) for e in data if e.tag == 'Stream']
|
||
|
|
||
|
def __repr__(self):
|
||
|
return '<%s:%s>' % (self.__class__.__name__, self.id)
|
||
|
|
||
|
def selectedStream(self, stream_type):
|
||
|
streams = filter(lambda x: stream_type == x.type, self.streams)
|
||
|
selected = list(filter(lambda x: x.selected is True, streams))
|
||
|
if len(selected) == 0:
|
||
|
return None
|
||
|
return selected[0]
|
||
|
|
||
|
|
||
|
class MediaPartStream(plexstream.PlexStream):
|
||
|
TYPE = None
|
||
|
STREAMTYPE = None
|
||
|
|
||
|
def __init__(self, data, initpath=None, server=None, part=None):
|
||
|
plexobjects.PlexObject.__init__(self, data, initpath, server)
|
||
|
self.part = part
|
||
|
|
||
|
@staticmethod
|
||
|
def parse(data, initpath=None, server=None, part=None):
|
||
|
STREAMCLS = {
|
||
|
1: VideoStream,
|
||
|
2: AudioStream,
|
||
|
3: SubtitleStream
|
||
|
}
|
||
|
stype = int(data.attrib.get('streamType'))
|
||
|
cls = STREAMCLS.get(stype, MediaPartStream)
|
||
|
return cls(data, initpath=initpath, server=server, part=part)
|
||
|
|
||
|
def __repr__(self):
|
||
|
return '<%s:%s>' % (self.__class__.__name__, self.id)
|
||
|
|
||
|
|
||
|
class VideoStream(MediaPartStream):
|
||
|
TYPE = 'videostream'
|
||
|
STREAMTYPE = plexstream.PlexStream.TYPE_VIDEO
|
||
|
|
||
|
|
||
|
class AudioStream(MediaPartStream):
|
||
|
TYPE = 'audiostream'
|
||
|
STREAMTYPE = plexstream.PlexStream.TYPE_AUDIO
|
||
|
|
||
|
|
||
|
class SubtitleStream(MediaPartStream):
|
||
|
TYPE = 'subtitlestream'
|
||
|
STREAMTYPE = plexstream.PlexStream.TYPE_SUBTITLE
|
||
|
|
||
|
|
||
|
class TranscodeSession(plexobjects.PlexObject):
|
||
|
TYPE = 'TranscodeSession'
|
||
|
|
||
|
|
||
|
class MediaTag(plexobjects.PlexObject):
|
||
|
TYPE = None
|
||
|
ID = 'None'
|
||
|
|
||
|
def __repr__(self):
|
||
|
tag = self.tag.replace(' ', '.')[0:20]
|
||
|
return '<%s:%s:%s>' % (self.__class__.__name__, self.id, tag)
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
if other.__class__ != self.__class__:
|
||
|
return False
|
||
|
|
||
|
return self.id == other.id
|
||
|
|
||
|
def __ne__(self, other):
|
||
|
return not self.__eq__(other)
|
||
|
|
||
|
|
||
|
class Collection(MediaTag):
|
||
|
TYPE = 'Collection'
|
||
|
FILTER = 'collection'
|
||
|
|
||
|
|
||
|
class Country(MediaTag):
|
||
|
TYPE = 'Country'
|
||
|
FILTER = 'country'
|
||
|
|
||
|
|
||
|
class Director(MediaTag):
|
||
|
TYPE = 'Director'
|
||
|
FILTER = 'director'
|
||
|
ID = '4'
|
||
|
|
||
|
|
||
|
class Genre(MediaTag):
|
||
|
TYPE = 'Genre'
|
||
|
FILTER = 'genre'
|
||
|
ID = '1'
|
||
|
|
||
|
|
||
|
class Mood(MediaTag):
|
||
|
TYPE = 'Mood'
|
||
|
FILTER = 'mood'
|
||
|
|
||
|
|
||
|
class Producer(MediaTag):
|
||
|
TYPE = 'Producer'
|
||
|
FILTER = 'producer'
|
||
|
|
||
|
|
||
|
class Role(MediaTag):
|
||
|
TYPE = 'Role'
|
||
|
FILTER = 'actor'
|
||
|
ID = '6'
|
||
|
|
||
|
def sectionRoles(self):
|
||
|
hubs = self.server.hubs(count=10, search_query=self.tag)
|
||
|
for hub in hubs:
|
||
|
if hub.type == 'actor':
|
||
|
break
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
roles = []
|
||
|
|
||
|
for actor in hub.items:
|
||
|
if actor.id == self.id:
|
||
|
roles.append(actor)
|
||
|
|
||
|
return roles or None
|
||
|
|
||
|
|
||
|
class Similar(MediaTag):
|
||
|
TYPE = 'Similar'
|
||
|
FILTER = 'similar'
|
||
|
|
||
|
|
||
|
class Writer(MediaTag):
|
||
|
TYPE = 'Writer'
|
||
|
FILTER = 'writer'
|