2016-03-21 04:26:02 +00:00
|
|
|
|
# -*- coding: utf-8 -*-
|
2014-12-29 03:21:58 +00:00
|
|
|
|
"""
|
|
|
|
|
PlexVideo
|
|
|
|
|
"""
|
2016-03-17 04:51:20 +00:00
|
|
|
|
from plexapi import media, utils
|
2016-04-04 03:55:29 +00:00
|
|
|
|
from plexapi.utils import Playable, PlexPartialObject
|
2016-03-17 04:51:20 +00:00
|
|
|
|
NA = utils.NA
|
|
|
|
|
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
2016-04-04 03:55:29 +00:00
|
|
|
|
class Video(PlexPartialObject):
|
2014-12-29 03:21:58 +00:00
|
|
|
|
TYPE = None
|
|
|
|
|
|
2016-04-04 03:55:29 +00:00
|
|
|
|
def __init__(self, server, data, initpath):
|
|
|
|
|
super(Video, self).__init__(data, initpath, server)
|
|
|
|
|
|
2014-12-29 03:21:58 +00:00
|
|
|
|
def _loadData(self, data):
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.addedAt = utils.toDatetime(data.attrib.get('addedAt', NA))
|
2014-12-29 03:21:58 +00:00
|
|
|
|
self.key = data.attrib.get('key', NA)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.lastViewedAt = utils.toDatetime(data.attrib.get('lastViewedAt', NA))
|
2015-02-17 20:35:17 +00:00
|
|
|
|
self.librarySectionID = data.attrib.get('librarySectionID', NA)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
self.ratingKey = data.attrib.get('ratingKey', NA)
|
|
|
|
|
self.summary = data.attrib.get('summary', NA)
|
|
|
|
|
self.thumb = data.attrib.get('thumb', NA)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.title = data.attrib.get('title', NA)
|
|
|
|
|
self.titleSort = data.attrib.get('titleSort', self.title)
|
|
|
|
|
self.type = data.attrib.get('type', NA)
|
2016-03-17 04:51:20 +00:00
|
|
|
|
self.updatedAt = utils.toDatetime(data.attrib.get('updatedAt', NA))
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.viewCount = utils.cast(int, data.attrib.get('viewCount', 0))
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
2016-04-04 03:55:29 +00:00
|
|
|
|
@property
|
|
|
|
|
def thumbUrl(self):
|
|
|
|
|
return self.server.url(self.thumb)
|
|
|
|
|
|
2014-12-29 03:21:58 +00:00
|
|
|
|
def analyze(self):
|
2016-03-21 04:26:02 +00:00
|
|
|
|
""" The primary purpose of media analysis is to gather information about that media
|
|
|
|
|
item. All of the media you add to a Library has properties that are useful to
|
|
|
|
|
know–whether it's a video file, a music track, or one of your photos.
|
|
|
|
|
"""
|
2014-12-29 03:21:58 +00:00
|
|
|
|
self.server.query('/%s/analyze' % self.key)
|
|
|
|
|
|
|
|
|
|
def markWatched(self):
|
|
|
|
|
path = '/:/scrobble?key=%s&identifier=com.plexapp.plugins.library' % self.ratingKey
|
|
|
|
|
self.server.query(path)
|
|
|
|
|
self.reload()
|
|
|
|
|
|
|
|
|
|
def markUnwatched(self):
|
|
|
|
|
path = '/:/unscrobble?key=%s&identifier=com.plexapp.plugins.library' % self.ratingKey
|
|
|
|
|
self.server.query(path)
|
|
|
|
|
self.reload()
|
|
|
|
|
|
|
|
|
|
def refresh(self):
|
2016-03-22 03:12:12 +00:00
|
|
|
|
self.server.query('%s/refresh' % self.key, method=self.server.session.put)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
|
|
2016-03-17 04:51:20 +00:00
|
|
|
|
@utils.register_libtype
|
2016-04-04 03:55:29 +00:00
|
|
|
|
class Movie(Video, Playable):
|
2014-12-29 03:21:58 +00:00
|
|
|
|
TYPE = 'movie'
|
|
|
|
|
|
|
|
|
|
def _loadData(self, data):
|
|
|
|
|
super(Movie, self)._loadData(data)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.art = data.attrib.get('art', NA)
|
|
|
|
|
self.audienceRating = utils.cast(float, data.attrib.get('audienceRating', NA))
|
|
|
|
|
self.audienceRatingImage = data.attrib.get('audienceRatingImage', NA)
|
|
|
|
|
self.chapterSource = data.attrib.get('chapterSource', NA)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
self.contentRating = data.attrib.get('contentRating', NA)
|
2016-03-17 04:51:20 +00:00
|
|
|
|
self.duration = utils.cast(int, data.attrib.get('duration', NA))
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.guid = data.attrib.get('guid', NA)
|
|
|
|
|
self.originalTitle = data.attrib.get('originalTitle', NA)
|
2016-03-17 04:51:20 +00:00
|
|
|
|
self.originallyAvailableAt = utils.toDatetime(data.attrib.get('originallyAvailableAt', NA), '%Y-%m-%d')
|
2014-12-29 03:21:58 +00:00
|
|
|
|
self.primaryExtraKey = data.attrib.get('primaryExtraKey', NA)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.rating = data.attrib.get('rating', NA)
|
|
|
|
|
self.ratingImage = data.attrib.get('ratingImage', NA)
|
|
|
|
|
self.studio = data.attrib.get('studio', NA)
|
|
|
|
|
self.tagline = data.attrib.get('tagline', NA)
|
|
|
|
|
self.userRating = utils.cast(float, data.attrib.get('userRating', NA))
|
|
|
|
|
self.viewOffset = utils.cast(int, data.attrib.get('viewOffset', 0))
|
|
|
|
|
self.year = utils.cast(int, data.attrib.get('year', NA))
|
|
|
|
|
if self.isFullObject():
|
2016-03-22 03:12:12 +00:00
|
|
|
|
self.collections = [media.Collection(self.server, e) for e in data if e.tag == media.Collection.TYPE]
|
|
|
|
|
self.countries = [media.Country(self.server, e) for e in data if e.tag == media.Country.TYPE]
|
|
|
|
|
self.directors = [media.Director(self.server, e) for e in data if e.tag == media.Director.TYPE]
|
|
|
|
|
self.genres = [media.Genre(self.server, e) for e in data if e.tag == media.Genre.TYPE]
|
|
|
|
|
self.media = [media.Media(self.server, e, self.initpath, self) for e in data if e.tag == media.Media.TYPE]
|
|
|
|
|
self.producers = [media.Producer(self.server, e) for e in data if e.tag == media.Producer.TYPE]
|
|
|
|
|
self.roles = [media.Role(self.server, e) for e in data if e.tag == media.Role.TYPE]
|
|
|
|
|
self.writers = [media.Writer(self.server, e) for e in data if e.tag == media.Writer.TYPE]
|
2016-04-04 03:17:29 +00:00
|
|
|
|
self.videoStreams = utils.findStreams(self.media, 'videostream')
|
|
|
|
|
self.audioStreams = utils.findStreams(self.media, 'audiostream')
|
|
|
|
|
self.subtitleStreams = utils.findStreams(self.media, 'subtitlestream')
|
2016-03-21 04:26:02 +00:00
|
|
|
|
# data for active sessions
|
|
|
|
|
self.sessionKey = utils.cast(int, data.attrib.get('sessionKey', NA))
|
2016-04-04 03:17:29 +00:00
|
|
|
|
self.user = utils.findUser(data, self.initpath)
|
|
|
|
|
self.player = utils.findPlayer(self.server, data)
|
|
|
|
|
self.transcodeSession = utils.findTranscodeSession(self.server, data)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def actors(self):
|
|
|
|
|
return self.roles
|
|
|
|
|
|
|
|
|
|
@property
|
2016-03-22 03:12:12 +00:00
|
|
|
|
def isWatched(self):
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return bool(self.viewCount > 0)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
|
|
2016-03-17 04:51:20 +00:00
|
|
|
|
@utils.register_libtype
|
2014-12-29 03:21:58 +00:00
|
|
|
|
class Show(Video):
|
|
|
|
|
TYPE = 'show'
|
2015-02-24 03:42:29 +00:00
|
|
|
|
|
2014-12-29 03:21:58 +00:00
|
|
|
|
def _loadData(self, data):
|
|
|
|
|
super(Show, self)._loadData(data)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.art = data.attrib.get('art', NA)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
self.banner = data.attrib.get('banner', NA)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.childCount = utils.cast(int, data.attrib.get('childCount', NA))
|
|
|
|
|
self.contentRating = data.attrib.get('contentRating', NA)
|
2016-03-17 04:51:20 +00:00
|
|
|
|
self.duration = utils.cast(int, data.attrib.get('duration', NA))
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.guid = data.attrib.get('guid', NA)
|
2016-03-17 04:51:20 +00:00
|
|
|
|
self.leafCount = utils.cast(int, data.attrib.get('leafCount', NA))
|
2016-04-04 03:17:29 +00:00
|
|
|
|
self.location = utils.findLocation(data)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.originallyAvailableAt = utils.toDatetime(data.attrib.get('originallyAvailableAt', NA), '%Y-%m-%d')
|
|
|
|
|
self.rating = utils.cast(float, data.attrib.get('rating', NA))
|
|
|
|
|
self.studio = data.attrib.get('studio', NA)
|
|
|
|
|
self.theme = data.attrib.get('theme', NA)
|
2016-03-17 04:51:20 +00:00
|
|
|
|
self.viewedLeafCount = utils.cast(int, data.attrib.get('viewedLeafCount', NA))
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.year = utils.cast(int, data.attrib.get('year', NA))
|
|
|
|
|
if self.isFullObject():
|
2016-03-22 03:12:12 +00:00
|
|
|
|
self.genres = [media.Genre(self.server, e) for e in data if e.tag == media.Genre.TYPE]
|
|
|
|
|
self.roles = [media.Role(self.server, e) for e in data if e.tag == media.Role.TYPE]
|
2016-03-21 04:26:02 +00:00
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def actors(self):
|
|
|
|
|
return self.roles
|
|
|
|
|
|
|
|
|
|
@property
|
2016-03-22 03:12:12 +00:00
|
|
|
|
def isWatched(self):
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return bool(self.viewedLeafCount == self.leafCount)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
|
def seasons(self):
|
|
|
|
|
path = '/library/metadata/%s/children' % self.ratingKey
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return utils.listItems(self.server, path, Season.TYPE)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
|
def season(self, title):
|
|
|
|
|
path = '/library/metadata/%s/children' % self.ratingKey
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return utils.findItem(self.server, path, title)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
2015-06-02 02:55:20 +00:00
|
|
|
|
def episodes(self, watched=None):
|
2014-12-29 03:21:58 +00:00
|
|
|
|
leavesKey = '/library/metadata/%s/allLeaves' % self.ratingKey
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return utils.listItems(self.server, leavesKey, watched=watched)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
|
def episode(self, title):
|
|
|
|
|
path = '/library/metadata/%s/allLeaves' % self.ratingKey
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return utils.findItem(self.server, path, title)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
2015-06-02 02:55:20 +00:00
|
|
|
|
def watched(self):
|
|
|
|
|
return self.episodes(watched=True)
|
|
|
|
|
|
|
|
|
|
def unwatched(self):
|
|
|
|
|
return self.episodes(watched=False)
|
|
|
|
|
|
2014-12-29 03:21:58 +00:00
|
|
|
|
def get(self, title):
|
|
|
|
|
return self.episode(title)
|
|
|
|
|
|
2015-06-02 02:27:43 +00:00
|
|
|
|
def refresh(self):
|
|
|
|
|
self.server.query('/library/metadata/%s/refresh' % self.ratingKey)
|
|
|
|
|
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
2016-03-17 04:51:20 +00:00
|
|
|
|
@utils.register_libtype
|
2014-12-29 03:21:58 +00:00
|
|
|
|
class Season(Video):
|
|
|
|
|
TYPE = 'season'
|
|
|
|
|
|
|
|
|
|
def _loadData(self, data):
|
|
|
|
|
super(Season, self)._loadData(data)
|
2016-03-17 04:51:20 +00:00
|
|
|
|
self.leafCount = utils.cast(int, data.attrib.get('leafCount', NA))
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.parentKey = data.attrib.get('parentKey', NA)
|
|
|
|
|
self.parentRatingKey = data.attrib.get('parentRatingKey', NA)
|
2016-03-17 04:51:20 +00:00
|
|
|
|
self.viewedLeafCount = utils.cast(int, data.attrib.get('viewedLeafCount', NA))
|
2016-03-21 04:26:02 +00:00
|
|
|
|
|
|
|
|
|
@property
|
2016-03-22 03:12:12 +00:00
|
|
|
|
def isWatched(self):
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return bool(self.viewedLeafCount == self.leafCount)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
2015-06-02 02:55:20 +00:00
|
|
|
|
def episodes(self, watched=None):
|
2014-12-29 03:21:58 +00:00
|
|
|
|
childrenKey = '/library/metadata/%s/children' % self.ratingKey
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return utils.listItems(self.server, childrenKey, watched=watched)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
|
def episode(self, title):
|
|
|
|
|
path = '/library/metadata/%s/children' % self.ratingKey
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return utils.findItem(self.server, path, title)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
|
def get(self, title):
|
|
|
|
|
return self.episode(title)
|
|
|
|
|
|
|
|
|
|
def show(self):
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return utils.listItems(self.server, self.parentKey)[0]
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
2015-06-02 02:55:20 +00:00
|
|
|
|
def watched(self):
|
|
|
|
|
return self.episodes(watched=True)
|
|
|
|
|
|
|
|
|
|
def unwatched(self):
|
|
|
|
|
return self.episodes(watched=False)
|
|
|
|
|
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
2016-03-17 04:51:20 +00:00
|
|
|
|
@utils.register_libtype
|
2016-04-04 03:55:29 +00:00
|
|
|
|
class Episode(Video, Playable):
|
2014-12-29 03:21:58 +00:00
|
|
|
|
TYPE = 'episode'
|
|
|
|
|
|
|
|
|
|
def _loadData(self, data):
|
|
|
|
|
super(Episode, self)._loadData(data)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.art = data.attrib.get('art', NA)
|
|
|
|
|
self.chapterSource = data.attrib.get('chapterSource', NA)
|
|
|
|
|
self.contentRating = data.attrib.get('contentRating', NA)
|
|
|
|
|
self.duration = utils.cast(int, data.attrib.get('duration', NA))
|
|
|
|
|
self.grandparentArt = data.attrib.get('grandparentArt', NA)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
self.grandparentKey = data.attrib.get('grandparentKey', NA)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.grandparentRatingKey = data.attrib.get('grandparentRatingKey', NA)
|
|
|
|
|
self.grandparentTheme = data.attrib.get('grandparentTheme', NA)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
self.grandparentThumb = data.attrib.get('grandparentThumb', NA)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.grandparentTitle = data.attrib.get('grandparentTitle', NA)
|
|
|
|
|
self.guid = data.attrib.get('guid', NA)
|
|
|
|
|
self.originallyAvailableAt = utils.toDatetime(data.attrib.get('originallyAvailableAt', NA), '%Y-%m-%d')
|
2014-12-29 03:21:58 +00:00
|
|
|
|
self.parentIndex = data.attrib.get('parentIndex', NA)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.parentKey = data.attrib.get('parentKey', NA)
|
|
|
|
|
self.parentRatingKey = data.attrib.get('parentRatingKey', NA)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
self.parentThumb = data.attrib.get('parentThumb', NA)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
self.rating = utils.cast(float, data.attrib.get('rating', NA))
|
2016-03-17 04:51:20 +00:00
|
|
|
|
self.viewOffset = utils.cast(int, data.attrib.get('viewOffset', 0))
|
|
|
|
|
self.year = utils.cast(int, data.attrib.get('year', NA))
|
2016-03-21 04:26:02 +00:00
|
|
|
|
if self.isFullObject():
|
2016-03-22 03:12:12 +00:00
|
|
|
|
self.directors = [media.Director(self.server, e) for e in data if e.tag == media.Director.TYPE]
|
|
|
|
|
self.media = [media.Media(self.server, e, self.initpath, self) for e in data if e.tag == media.Media.TYPE]
|
|
|
|
|
self.writers = [media.Writer(self.server, e) for e in data if e.tag == media.Writer.TYPE]
|
2016-04-04 03:17:29 +00:00
|
|
|
|
self.videoStreams = utils.findStreams(self.media, 'videostream')
|
|
|
|
|
self.audioStreams = utils.findStreams(self.media, 'audiostream')
|
|
|
|
|
self.subtitleStreams = utils.findStreams(self.media, 'subtitlestream')
|
2016-03-21 04:26:02 +00:00
|
|
|
|
# data for active sessions
|
|
|
|
|
self.sessionKey = utils.cast(int, data.attrib.get('sessionKey', NA))
|
2016-04-04 03:17:29 +00:00
|
|
|
|
self.user = utils.findUser(data, self.initpath)
|
|
|
|
|
self.player = utils.findPlayer(self.server, data)
|
|
|
|
|
self.transcodeSession = utils.findTranscodeSession(self.server, data)
|
2016-03-21 04:26:02 +00:00
|
|
|
|
|
|
|
|
|
@property
|
2016-03-22 03:12:12 +00:00
|
|
|
|
def isWatched(self):
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return bool(self.viewCount > 0)
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
2015-02-24 03:42:29 +00:00
|
|
|
|
@property
|
|
|
|
|
def thumbUrl(self):
|
|
|
|
|
return self.server.url(self.grandparentThumb)
|
|
|
|
|
|
2014-12-29 03:21:58 +00:00
|
|
|
|
def season(self):
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return utils.listItems(self.server, self.parentKey)[0]
|
2014-12-29 03:21:58 +00:00
|
|
|
|
|
|
|
|
|
def show(self):
|
2016-03-21 04:26:02 +00:00
|
|
|
|
return utils.listItems(self.server, self.grandparentKey)[0]
|