2019-06-11 05:29:42 +10:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from __future__ import absolute_import, division, unicode_literals
|
|
|
|
|
2020-12-19 03:10:20 +11:00
|
|
|
from builtins import object
|
2019-06-11 05:29:42 +10:00
|
|
|
from .. import utils, variables as v, app
|
|
|
|
|
|
|
|
|
|
|
|
def _transcode_image_path(key, AuthToken, path, width, height):
|
|
|
|
"""
|
|
|
|
Transcode Image support
|
|
|
|
|
|
|
|
parameters:
|
|
|
|
key
|
|
|
|
AuthToken
|
|
|
|
path - source path of current XML: path[srcXML]
|
|
|
|
width
|
|
|
|
height
|
|
|
|
result:
|
|
|
|
final path to image file
|
|
|
|
"""
|
|
|
|
# external address - can we get a transcoding request for external images?
|
|
|
|
if key.startswith('http'):
|
|
|
|
path = key
|
|
|
|
elif key.startswith('/'): # internal full path.
|
|
|
|
path = 'http://127.0.0.1:32400' + key
|
|
|
|
else: # internal path, add-on
|
|
|
|
path = 'http://127.0.0.1:32400' + path + '/' + key
|
|
|
|
# This is bogus (note the extra path component) but ATV is stupid when it
|
|
|
|
# comes to caching images, it doesn't use querystrings. Fortunately PMS is
|
|
|
|
# lenient...
|
|
|
|
transcode_path = ('/photo/:/transcode/%sx%s/%s'
|
|
|
|
% (width, height, utils.quote_plus(path)))
|
|
|
|
args = {
|
|
|
|
'width': width,
|
|
|
|
'height': height,
|
|
|
|
'url': path
|
|
|
|
}
|
|
|
|
if AuthToken:
|
|
|
|
args['X-Plex-Token'] = AuthToken
|
|
|
|
return utils.extend_url(transcode_path, args)
|
|
|
|
|
|
|
|
|
|
|
|
class File(object):
|
2020-02-27 04:10:15 +11:00
|
|
|
def fullpath(self, force_first_media=True, force_addon=False,
|
|
|
|
direct_paths=None, omit_check=False, force_check=False):
|
2019-06-11 05:29:42 +10:00
|
|
|
"""
|
2020-02-27 04:10:15 +11:00
|
|
|
Returns a "fully qualified path" add-on paths or direct paths
|
|
|
|
depending on the current settings as the tupple
|
|
|
|
(fullpath, path, filename)
|
|
|
|
as unicode. Add-on paths are returned as a fallback. Returns None
|
|
|
|
if something went wrong.
|
2019-06-11 05:29:42 +10:00
|
|
|
|
2020-02-27 04:10:15 +11:00
|
|
|
firce_first_media=False prompts the user to choose which version of the
|
|
|
|
media should be returned, if several are present
|
|
|
|
force_addon=True will always return the add-on path
|
|
|
|
direct_path=True if you're calling from another Plex python
|
|
|
|
instance - because otherwise direct paths will
|
|
|
|
evaluate to False!
|
2019-06-11 05:29:42 +10:00
|
|
|
"""
|
2020-02-27 04:10:15 +11:00
|
|
|
direct_paths = app.SYNC.direct_paths if direct_paths is None \
|
|
|
|
else direct_paths
|
2019-06-11 05:29:42 +10:00
|
|
|
if (not direct_paths or force_addon or
|
|
|
|
self.plex_type == v.PLEX_TYPE_CLIP):
|
2020-02-27 04:10:15 +11:00
|
|
|
if self.plex_type == v.PLEX_TYPE_SONG:
|
|
|
|
return self._music_addon_paths(force_first_media)
|
2019-06-11 05:29:42 +10:00
|
|
|
if self.plex_type == v.PLEX_TYPE_EPISODE:
|
|
|
|
# need to include the plex show id in the path
|
|
|
|
path = ('plugin://plugin.video.plexkodiconnect.tvshows/%s/'
|
|
|
|
% self.grandparent_id())
|
|
|
|
else:
|
|
|
|
path = 'plugin://%s/' % v.ADDON_TYPE[self.plex_type]
|
2020-03-03 21:34:54 +11:00
|
|
|
# Filename in Kodi will end with actual filename - hopefully
|
|
|
|
# this is useful for other add-ons
|
|
|
|
filename = self.file_path(force_first_media=force_first_media)
|
|
|
|
if filename:
|
|
|
|
if '/' in filename:
|
|
|
|
filename = filename.rsplit('/', 1)[1]
|
|
|
|
else:
|
|
|
|
filename = filename.rsplit('\\', 1)[1]
|
2020-03-01 23:41:23 +11:00
|
|
|
entirepath = ('%s?mode=play&plex_id=%s&plex_type=%s&filename=%s'
|
|
|
|
% (path, self.plex_id, self.plex_type, filename))
|
|
|
|
else:
|
2020-03-03 21:34:54 +11:00
|
|
|
# E.g. clips or albums
|
2020-03-01 23:41:23 +11:00
|
|
|
entirepath = ('%s?mode=play&plex_id=%s&plex_type=%s'
|
|
|
|
% (path, self.plex_id, self.plex_type))
|
2020-02-27 04:10:15 +11:00
|
|
|
# For Kodi DB, we need to safe the ENTIRE path for filenames
|
|
|
|
filename = entirepath
|
2019-06-11 05:29:42 +10:00
|
|
|
else:
|
2020-02-27 04:10:15 +11:00
|
|
|
entirepath = self.validate_playurl(
|
|
|
|
self.file_path(force_first_media=force_first_media),
|
|
|
|
self.plex_type,
|
|
|
|
force_check=force_check,
|
|
|
|
omit_check=omit_check)
|
|
|
|
try:
|
|
|
|
if '/' in entirepath:
|
|
|
|
filename = entirepath.rsplit('/', 1)[1]
|
|
|
|
else:
|
|
|
|
filename = entirepath.rsplit('\\', 1)[1]
|
|
|
|
except (TypeError, IndexError):
|
|
|
|
# Fallback to add-on paths
|
|
|
|
return self.fullpath(force_first_media=force_first_media,
|
|
|
|
force_addon=True)
|
|
|
|
path = utils.rreplace(entirepath, filename, "", 1)
|
|
|
|
return entirepath, path, filename
|
|
|
|
|
|
|
|
def _music_addon_paths(self, force_first_media):
|
|
|
|
"""
|
|
|
|
For songs only. Normal add-on paths plugin://... don't work with the
|
|
|
|
Kodi music DB, hence use a "direct" url to the music file on the PMS.
|
|
|
|
"""
|
|
|
|
if self.mediastream is None and force_first_media is False:
|
|
|
|
if self.mediastream_number() is None:
|
|
|
|
return
|
|
|
|
streamno = 0 if force_first_media else self.mediastream
|
|
|
|
entirepath = "%s%s" % (app.CONN.server,
|
|
|
|
self.xml[streamno][self.part].get('key'))
|
|
|
|
entirepath = self.attach_plex_token_to_url(entirepath)
|
|
|
|
path, filename = entirepath.rsplit('/', 1)
|
|
|
|
return entirepath, path + '/', filename
|
2019-06-11 05:29:42 +10:00
|
|
|
|
|
|
|
def directory_path(self, section_id=None, plex_type=None, old_key=None,
|
|
|
|
synched=True):
|
|
|
|
key = self.xml.get('fastKey')
|
|
|
|
if not key:
|
|
|
|
key = self.xml.get('key')
|
|
|
|
if old_key:
|
|
|
|
key = '%s/%s' % (old_key, key)
|
|
|
|
elif not key.startswith('/'):
|
|
|
|
key = '/library/sections/%s/%s' % (section_id, key)
|
|
|
|
params = {
|
|
|
|
'mode': 'browseplex',
|
2019-07-07 05:20:23 +10:00
|
|
|
'key': key
|
2019-06-11 05:29:42 +10:00
|
|
|
}
|
2019-07-07 05:20:23 +10:00
|
|
|
if plex_type or self.plex_type:
|
|
|
|
params['plex_type'] = plex_type or self.plex_type
|
2019-06-11 05:29:42 +10:00
|
|
|
if not synched:
|
|
|
|
# No item to be found in the Kodi DB
|
|
|
|
params['synched'] = 'false'
|
|
|
|
if self.xml.get('prompt'):
|
|
|
|
# User input needed, e.g. search for a movie or episode
|
|
|
|
params['prompt'] = self.xml.get('prompt')
|
|
|
|
if section_id:
|
|
|
|
params['id'] = section_id
|
|
|
|
return utils.extend_url('plugin://%s/' % v.ADDON_ID, params)
|
|
|
|
|
|
|
|
def file_name(self, force_first_media=False):
|
|
|
|
"""
|
|
|
|
Returns only the filename, e.g. 'movie.mkv' as unicode or None if not
|
|
|
|
found
|
|
|
|
"""
|
|
|
|
ans = self.file_path(force_first_media=force_first_media)
|
|
|
|
if ans is None:
|
|
|
|
return
|
|
|
|
if "\\" in ans:
|
|
|
|
# Local path
|
|
|
|
filename = ans.rsplit("\\", 1)[1]
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
# Network share
|
|
|
|
filename = ans.rsplit("/", 1)[1]
|
|
|
|
except IndexError:
|
|
|
|
# E.g. certain Plex channels
|
|
|
|
filename = None
|
|
|
|
return filename
|
|
|
|
|
|
|
|
def file_path(self, force_first_media=False):
|
|
|
|
"""
|
|
|
|
Returns the direct path to this item, e.g. '\\NAS\movies\movie.mkv'
|
|
|
|
as unicode or None
|
|
|
|
|
|
|
|
force_first_media=True:
|
|
|
|
will always use 1st media stream, e.g. when several different
|
|
|
|
files are present for the same PMS item
|
|
|
|
"""
|
|
|
|
if self.mediastream is None and force_first_media is False:
|
|
|
|
if self.mediastream_number() is None:
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
if force_first_media is False:
|
2019-06-12 21:02:29 +10:00
|
|
|
ans = self.xml[self.mediastream][self.part].attrib['file']
|
2019-06-11 05:29:42 +10:00
|
|
|
else:
|
2019-06-12 21:02:29 +10:00
|
|
|
ans = self.xml[0][self.part].attrib['file']
|
2019-06-11 05:29:42 +10:00
|
|
|
except (TypeError, AttributeError, IndexError, KeyError):
|
|
|
|
return
|
2019-06-12 21:02:29 +10:00
|
|
|
return ans
|
2019-06-11 05:29:42 +10:00
|
|
|
|
|
|
|
def get_picture_path(self):
|
|
|
|
"""
|
|
|
|
Returns the item's picture path (transcode, if necessary) as string.
|
|
|
|
Will always use addon paths, never direct paths
|
|
|
|
"""
|
|
|
|
path = self.xml[0][0].get('key')
|
|
|
|
extension = path[path.rfind('.'):].lower()
|
|
|
|
if app.SYNC.force_transcode_pix or extension not in v.KODI_SUPPORTED_IMAGES:
|
|
|
|
# Let Plex transcode
|
|
|
|
# max width/height supported by plex image transcoder is 1920x1080
|
|
|
|
path = app.CONN.server + _transcode_image_path(
|
|
|
|
path,
|
|
|
|
app.ACCOUNT.pms_token,
|
|
|
|
"%s%s" % (app.CONN.server, path),
|
|
|
|
1920,
|
|
|
|
1080)
|
|
|
|
else:
|
|
|
|
path = self.attach_plex_token_to_url('%s%s' % (app.CONN.server, path))
|
|
|
|
# Attach Plex id to url to let it be picked up by our playqueue agent
|
|
|
|
# later
|
|
|
|
return '%s&plex_id=%s' % (path, self.plex_id)
|