diff --git a/resources/lib/api.py b/resources/lib/api.py new file mode 100644 index 0000000..4ef1723 --- /dev/null +++ b/resources/lib/api.py @@ -0,0 +1,152 @@ +from __future__ import division, absolute_import, print_function, unicode_literals + +import xbmcaddon +from kodi_six.utils import py2_decode + +import requests + +from .utils import get_device_id, get_version +from .loghandler import LazyLogger + +log = LazyLogger(__name__) + + +class API: + def __init__(self, server=None, user_id=None, token=None): + self.server = server + self.user_id = user_id + self.token = token + + self.settings = xbmcaddon.Addon() + + self.headers = {} + self.create_headers() + + + def get(self, path): + if not self.headers: + self.create_headers() + + url = '{}{}'.format(self.server, path) + + r = requests.get(url, headers=self.headers) + return r.json() + + def post(self, url, payload): + if not self.headers: + self.create_headers() + + url = '{}{}'.format(self.server, url) + + r = requests.post(url, json=payload, headers=self.headers) + try: + response_data = r.json() + except: + response_data = {} + return response_data + + def delete(self, url): + url = '{}{}'.format(self.server, url) + + r = requests.delete(url, headers=self.headers) + + def get_user_items(self, path): + url = '/Users/{}/{}'.format(self.user_id, path) + r = self.get('url') + + def authenticate(self, auth_data): + response = self.post('/Users/AuthenticateByName', auth_data) + token = response.get('AccessToken') + if token: + self.token = token + self.user_id = response.get('User').get('Id') + # Create headers again to include auth token + self.create_headers() + return response + else: + log.error('Unable to authenticate to Jellyfin server') + return {} + + def create_headers(self): + + # If the headers already exist with an auth token, return + if self.headers and 'x-mediabrowser-token' in self.headers: + return + + headers = {} + device_name = self.settings.getSetting('deviceName') + if len(device_name) == 0: + device_name = "JellyCon" + # Ensure ascii and remove invalid characters + device_name = py2_decode(device_name).replace('"', '_').replace(',', '_') + device_id = get_device_id() + version = get_version() + + authorization = ( + 'MediaBrowser Client="Kodi JellyCon", Device="{device}", ' + 'DeviceId="{device_id}", Version="{version}"' + ).format( + device=device_name, + device_id=device_id, + version=version + ) + + headers['x-emby-authorization'] = authorization + + # If we have a valid token, ensure it's included in the headers + if self.token: + headers['x-mediabrowser-token'] = self.token + + # Make headers available to api calls + self.headers = headers + + + def post_capabilities(self): + url = '{}/Sessions/Capabilities/Full'.format(self.server) + + data = { + 'SupportsMediaControl': True, + 'PlayableMediaTypes': ["Video", "Audio"], + 'SupportedCommands': ["MoveUp", + "MoveDown", + "MoveLeft", + "MoveRight", + "Select", + "Back", + "ToggleContextMenu", + "ToggleFullscreen", + "ToggleOsdMenu", + "GoHome", + "PageUp", + "NextLetter", + "GoToSearch", + "GoToSettings", + "PageDown", + "PreviousLetter", + "TakeScreenshot", + "VolumeUp", + "VolumeDown", + "ToggleMute", + "SendString", + "DisplayMessage", + "SetAudioStreamIndex", + "SetSubtitleStreamIndex", + "SetRepeatMode", + "Mute", + "Unmute", + "SetVolume", + "PlayNext", + "Play", + "Playstate", + "PlayMediaSource"] + } + + self.post(url, data) + + def speedtest(self, test_data_size): + self.create_headers() + + url = '{}/playback/bitratetest?size={}'.format(self.server, test_data_size) + response = requests.get(url, stream=True, headers=self.headers) + + return response diff --git a/resources/lib/cache_images.py b/resources/lib/cache_images.py index d85699a..fce1f93 100644 --- a/resources/lib/cache_images.py +++ b/resources/lib/cache_images.py @@ -14,15 +14,13 @@ import xbmcplugin import xbmc import xbmcaddon -from .downloadutils import DownloadUtils from .loghandler import LazyLogger from .jsonrpc import JsonRpc, get_value from .datamanager import DataManager -from .utils import translate_string +from .utils import translate_string, load_user_details from .kodi_utils import HomeWindow from .item_functions import get_art -downloadUtils = DownloadUtils() log = LazyLogger(__name__) @@ -214,9 +212,11 @@ class CacheArtwork(threading.Thread): def get_jellyfin_artwork(self, progress): log.debug("get_jellyfin_artwork") + user_details = load_user_details() + user_id = user_details.get('user_id') url = "" - url += "{server}/Users/{userid}/Items" + url += "/Users/{}/Items".format(user_id) url += "?Recursive=true" url += "&EnableUserData=False" url += "&Fields=BasicSyncInfo" @@ -232,7 +232,7 @@ class CacheArtwork(threading.Thread): if isinstance(results, dict): results = results.get("Items") - server = downloadUtils.get_server() + server = settings.getSetting('server_address') log.debug("Jellyfin Item Count Count: {0}".format(len(results))) if self.stop_all_activity: diff --git a/resources/lib/datamanager.py b/resources/lib/datamanager.py index 08465c7..a6f2fc5 100644 --- a/resources/lib/datamanager.py +++ b/resources/lib/datamanager.py @@ -7,13 +7,13 @@ import os import time from six.moves import cPickle -from .downloadutils import DownloadUtils +from .api import API from .loghandler import LazyLogger from .item_functions import extract_item_info from .kodi_utils import HomeWindow from .tracking import timer from .filelock import FileLock -from .utils import translate_string +from .utils import translate_string, load_user_details import xbmc import xbmcaddon @@ -42,11 +42,18 @@ class DataManager: addon_dir = xbmc.translatePath(xbmcaddon.Addon().getAddonInfo('profile')) def __init__(self, *args): - pass + self.user_details = load_user_details() + + settings = xbmcaddon.Addon() + self.api = API( + settings.getSetting('server_address'), + self.user_details.get('user_id'), + self.user_details.get('token') + ) @timer def get_content(self, url): - return DownloadUtils().download_url(url) + return self.api.get(url) @timer def get_items(self, url, gui_options, use_cache=False): @@ -55,9 +62,8 @@ class DataManager: log.debug("last_content_url : use_cache={0} url={1}".format(use_cache, url)) home_window.set_property("last_content_url", url) - download_utils = DownloadUtils() - user_id = download_utils.get_user_id() - server = download_utils.get_server() + user_id = self.user_details.get('user_id') + server = self.api.server m = hashlib.md5() m.update('{}|{}|{}'.format(user_id, server, url).encode()) diff --git a/resources/lib/dir_functions.py b/resources/lib/dir_functions.py index c0cfb91..f36e48e 100644 --- a/resources/lib/dir_functions.py +++ b/resources/lib/dir_functions.py @@ -10,10 +10,9 @@ import sys import re from .datamanager import DataManager -from .downloadutils import DownloadUtils from .loghandler import LazyLogger from .item_functions import add_gui_item, ItemDetails -from .utils import send_event_notification, translate_string +from .utils import send_event_notification, translate_string, load_user_details, get_default_filters from .tracking import timer log = LazyLogger(__name__) @@ -218,8 +217,9 @@ def process_directory(url, progress, params, use_cache_data=False): data_manager = DataManager() settings = xbmcaddon.Addon() - download_utils = DownloadUtils() - server = download_utils.get_server() + server = settings.getSetting('server_address') + user_details = load_user_details() + user_id = user_details.get('user_id') name_format = params.get("name_format", None) name_format_type = None @@ -248,13 +248,13 @@ def process_directory(url, progress, params, use_cache_data=False): if flatten_single_season and len(item_list) == 1 and item_list[0].item_type == "Season": season_id = item_list[0].id series_id = item_list[0].series_id - season_url = ('{server}/Shows/' + series_id + + season_url = ('/Shows/' + series_id + '/Episodes' - '?userId={userid}' + + '?userId={}'.format(user_id) + '&seasonId=' + season_id + '&IsVirtualUnAired=false' + '&IsMissing=false' + - '&Fields=SpecialEpisodeNumbers,{field_filters}' + + '&Fields=SpecialEpisodeNumbers,{}'.format(get_default_filters()) + '&format=json') if progress is not None: progress.close() @@ -313,28 +313,28 @@ def process_directory(url, progress, params, use_cache_data=False): if item_details.is_folder is True: if item_details.item_type == "Series": - u = ('{server}/Shows/' + item_details.id + + u = ('/Shows/' + item_details.id + '/Seasons' - '?userId={userid}' + - '&Fields={field_filters}' + + '?userId={}'.format(user_id) + + '&Fields={}'.format(get_default_filters()) + '&format=json') elif item_details.item_type == "Season": - u = ('{server}/Shows/' + item_details.series_id + + u = ('/Shows/' + item_details.series_id + '/Episodes' - '?userId={userid}' + + '?userId={}'.format(user_id) + '&seasonId=' + item_details.id + '&IsVirtualUnAired=false' + '&IsMissing=false' + - '&Fields=SpecialEpisodeNumbers,{field_filters}' + + '&Fields=SpecialEpisodeNumbers,{}'.format(get_default_filters()) + '&format=json') else: - u = ('{server}/Users/{userid}/items' + + u = ('/Users/{}/items'.format(user_id) + '?ParentId=' + item_details.id + '&IsVirtualUnAired=false' + '&IsMissing=false' + - '&Fields={field_filters}' + + '&Fields={}'.format(get_default_filters()) + '&format=json') default_sort = item_details.item_type == "Playlist" @@ -347,7 +347,7 @@ def process_directory(url, progress, params, use_cache_data=False): log.debug("Dropping empty folder item : {0}".format(item_details.__dict__)) elif item_details.item_type == "MusicArtist": - u = ('{server}/Users/{userid}/items' + + u = ('/Users/{}/items'.format(user_id) + '?ArtistIds=' + item_details.id + '&IncludeItemTypes=MusicAlbum' + '&CollapseBoxSetItems=false' + @@ -369,12 +369,12 @@ def process_directory(url, progress, params, use_cache_data=False): and first_season_item is not None and len(dir_items) > 1 and first_season_item.series_id is not None): - series_url = ('{server}/Shows/' + first_season_item.series_id + + series_url = ('/Shows/' + first_season_item.series_id + '/Episodes' - '?userId={userid}' + + '?userId={}'.format(user_id) + '&IsVirtualUnAired=false' + '&IsMissing=false' + - '&Fields=SpecialEpisodeNumbers,{field_filters}' + + '&Fields=SpecialEpisodeNumbers,{}'.format(get_default_filters()) + '&format=json') played = 0 overlay = "7" diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py deleted file mode 100644 index 01bdfb1..0000000 --- a/resources/lib/downloadutils.py +++ /dev/null @@ -1,615 +0,0 @@ -# Gnu General Public License - see LICENSE.TXT -from __future__ import division, absolute_import, print_function, unicode_literals - -import xbmcgui -import xbmcaddon - -import requests -import json -from six.moves.urllib.parse import urlparse -from base64 import b64encode -from collections import defaultdict -from traceback import format_exc -from kodi_six.utils import py2_decode - -from .kodi_utils import HomeWindow -from .loghandler import LazyLogger -from .tracking import timer -from .utils import get_device_id, get_version, translate_string, load_user_details, save_user_details - -log = LazyLogger(__name__) - - -def get_details_string(): - - addon_settings = xbmcaddon.Addon() - include_media = addon_settings.getSetting("include_media") == "true" - include_people = addon_settings.getSetting("include_people") == "true" - include_overview = addon_settings.getSetting("include_overview") == "true" - - filer_list = [ - "DateCreated", - "EpisodeCount", - "SeasonCount", - "Path", - "Genres", - "Studios", - "Etag", - "Taglines", - "SortName", - "RecursiveItemCount", - "ChildCount", - "ProductionLocations", - "CriticRating", - "OfficialRating", - "CommunityRating", - "PremiereDate", - "ProductionYear", - "AirTime", - "Status", - "Tags" - ] - - if include_media: - filer_list.append("MediaStreams") - - if include_people: - filer_list.append("People") - - if include_overview: - filer_list.append("Overview") - - return ",".join(filer_list) - - -class DownloadUtils: - use_https = False - verify_cert = False - - def __init__(self, *args): - settings = xbmcaddon.Addon() - - self.use_https = False - if settings.getSetting('protocol') == "1": - self.use_https = True - log.debug("use_https: {0}".format(self.use_https)) - - self.verify_cert = settings.getSetting('verify_cert') == 'true' - log.debug("verify_cert: {0}".format(self.verify_cert)) - - def post_capabilities(self): - - url = "{server}/Sessions/Capabilities/Full?format=json" - data = { - 'SupportsMediaControl': True, - 'PlayableMediaTypes': ["Video", "Audio"], - 'SupportedCommands': ["MoveUp", - "MoveDown", - "MoveLeft", - "MoveRight", - "Select", - "Back", - "ToggleContextMenu", - "ToggleFullscreen", - "ToggleOsdMenu", - "GoHome", - "PageUp", - "NextLetter", - "GoToSearch", - "GoToSettings", - "PageDown", - "PreviousLetter", - "TakeScreenshot", - "VolumeUp", - "VolumeDown", - "ToggleMute", - "SendString", - "DisplayMessage", - "SetAudioStreamIndex", - "SetSubtitleStreamIndex", - "SetRepeatMode", - "Mute", - "Unmute", - "SetVolume", - "PlayNext", - "Play", - "Playstate", - "PlayMediaSource"] - } - - self.download_url(url, post_body=data, method="POST") - log.debug("Posted Capabilities: {0}".format(data)) - - def get_item_playback_info(self, item_id, force_transcode): - - addon_settings = xbmcaddon.Addon() - - filtered_codecs = [] - if addon_settings.getSetting("force_transcode_h265") == "true": - filtered_codecs.append("hevc") - filtered_codecs.append("h265") - if addon_settings.getSetting("force_transcode_mpeg2") == "true": - filtered_codecs.append("mpeg2video") - if addon_settings.getSetting("force_transcode_msmpeg4v3") == "true": - filtered_codecs.append("msmpeg4v3") - if addon_settings.getSetting("force_transcode_mpeg4") == "true": - filtered_codecs.append("mpeg4") - - playback_bitrate = addon_settings.getSetting("max_stream_bitrate") - force_playback_bitrate = addon_settings.getSetting("force_max_stream_bitrate") - if force_transcode: - playback_bitrate = force_playback_bitrate - - audio_codec = addon_settings.getSetting("audio_codec") - audio_playback_bitrate = addon_settings.getSetting("audio_playback_bitrate") - audio_max_channels = addon_settings.getSetting("audio_max_channels") - - audio_bitrate = int(audio_playback_bitrate) * 1000 - bitrate = int(playback_bitrate) * 1000 - - profile = { - "Name": "Kodi", - "MaxStaticBitrate": bitrate, - "MaxStreamingBitrate": bitrate, - "MusicStreamingTranscodingBitrate": audio_bitrate, - "TimelineOffsetSeconds": 5, - "TranscodingProfiles": [ - { - "Type": "Audio" - }, - { - "Container": "ts", - "Protocol": "hls", - "Type": "Video", - "AudioCodec": audio_codec, - "VideoCodec": "h264", - "MaxAudioChannels": audio_max_channels - }, - { - "Container": "jpeg", - "Type": "Photo" - } - ], - "DirectPlayProfiles": [ - { - "Type": "Video" - }, - { - "Type": "Audio" - }, - { - "Type": "Photo" - } - ], - "ResponseProfiles": [], - "ContainerProfiles": [], - "CodecProfiles": [], - "SubtitleProfiles": [ - { - "Format": "srt", - "Method": "External" - }, - { - "Format": "srt", - "Method": "Embed" - }, - { - "Format": "ass", - "Method": "External" - }, - { - "Format": "ass", - "Method": "Embed" - }, - { - "Format": "sub", - "Method": "Embed" - }, - { - "Format": "sub", - "Method": "External" - }, - { - "Format": "ssa", - "Method": "Embed" - }, - { - "Format": "ssa", - "Method": "External" - }, - { - "Format": "smi", - "Method": "Embed" - }, - { - "Format": "smi", - "Method": "External" - }, - { - "Format": "pgssub", - "Method": "Embed" - }, - { - "Format": "pgssub", - "Method": "External" - }, - { - "Format": "dvdsub", - "Method": "Embed" - }, - { - "Format": "dvdsub", - "Method": "External" - }, - { - "Format": "pgs", - "Method": "Embed" - }, - { - "Format": "pgs", - "Method": "External" - } - ] - } - - if len(filtered_codecs) > 0: - profile['DirectPlayProfiles'][0]['VideoCodec'] = "-%s" % ",".join(filtered_codecs) - - if force_transcode: - profile['DirectPlayProfiles'] = [] - - if addon_settings.getSetting("playback_video_force_8") == "true": - profile['CodecProfiles'].append( - { - "Type": "Video", - "Codec": "h264", - "Conditions": [ - { - "Condition": "LessThanEqual", - "Property": "VideoBitDepth", - "Value": "8", - "IsRequired": False - } - ] - } - ) - profile['CodecProfiles'].append( - { - "Type": "Video", - "Codec": "h265,hevc", - "Conditions": [ - { - "Condition": "EqualsAny", - "Property": "VideoProfile", - "Value": "main" - } - ] - } - ) - - playback_info = { - 'UserId': self.get_user_id(), - 'DeviceProfile': profile, - 'AutoOpenLiveStream': True - } - - if force_transcode: - url = "{server}/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s&EnableDirectPlay=false&EnableDirectStream=false" % (item_id, bitrate) - else: - url = "{server}/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s" % (item_id, bitrate) - - log.debug("PlaybackInfo : {0}".format(url)) - log.debug("PlaybackInfo : {0}".format(profile)) - play_info_result = self.download_url(url, post_body=playback_info, method="POST") - log.debug("PlaybackInfo : {0}".format(play_info_result)) - - return play_info_result - - def get_server(self): - settings = xbmcaddon.Addon() - - # For migration from storing URL parts to just one URL - if settings.getSetting('ipaddress') != "" and settings.getSetting('ipaddress') != "<none>": - log.info("Migrating to new server url storage") - url = ("http://" if settings.getSetting('protocol') == "0" else "https://") + settings.getSetting('ipaddress') + ":" + settings.getSetting('port') - settings.setSetting('server_address', url) - settings.setSetting('ipaddress', "") - - return settings.getSetting('server_address') - - @staticmethod - def get_all_artwork(item, server): - all_art = defaultdict(lambda: "") - - item_id = item["Id"] - item_type = item["Type"] - image_tags = item["ImageTags"] - - # All the image tags - for tag_name in image_tags: - tag = image_tags[tag_name] - art_url = "%s/Items/%s/Images/%s/0?Format=original&Tag=%s" % (server, item_id, tag_name, tag) - all_art[tag_name] = art_url - - # Series images - if item_type in ["Episode", "Season"]: - image_tag = item["SeriesPrimaryImageTag"] - series_id = item["SeriesId"] - if image_tag and series_id: - art_url = "%s/Items/%s/Images/Primary/0?Format=original&Tag=%s" % (server, series_id, image_tag) - all_art["Primary.Series"] = art_url - - return all_art - - def get_artwork(self, data, art_type, parent=False, index=0, server=None): - - item_id = data["Id"] - item_type = data["Type"] - - if item_type in ["Episode", "Season"]: - if art_type != "Primary" or parent is True: - item_id = data["SeriesId"] - - image_tag = "" - - # for episodes always use the parent BG - if item_type == "Episode" and art_type == "Backdrop": - item_id = data.get("ParentBackdropItemId") - bg_item_tags = data.get("ParentBackdropImageTags", []) - if bg_item_tags: - image_tag = bg_item_tags[0] - elif art_type == "Backdrop" and parent is True: - item_id = data.get("ParentBackdropItemId") - bg_item_tags = data.get("ParentBackdropImageTags", []) - if bg_item_tags: - image_tag = bg_item_tags[0] - elif art_type == "Backdrop": - bg_tags = data.get("BackdropImageTags", []) - if bg_tags: - image_tag = bg_tags[index] - elif parent is False: - image_tags = data.get("ImageTags", []) - if image_tags: - image_tag_type = image_tags.get(art_type) - if image_tag_type: - image_tag = image_tag_type - elif parent is True: - if (item_type == "Episode" or item_type == "Season") and art_type == 'Primary': - tag_name = 'SeriesPrimaryImageTag' - id_name = 'SeriesId' - else: - tag_name = 'Parent%sImageTag' % art_type - id_name = 'Parent%sItemId' % art_type - parent_image_id = data.get(id_name) - parent_image_tag = data.get(tag_name) - if parent_image_id is not None and parent_image_tag is not None: - item_id = parent_image_id - image_tag = parent_image_tag - - # ParentTag not passed for Banner and Art - if not image_tag and not ((art_type == 'Banner' or art_type == 'Art') and parent is True): - return "" - - artwork = "%s/Items/%s/Images/%s/%s?Format=original&Tag=%s" % (server, item_id, art_type, index, image_tag) - - if self.use_https and not self.verify_cert: - artwork += "|verifypeer=false" - - return artwork - - def image_url(self, item_id, art_type, index, width, height, image_tag, server): - - # test imageTag e3ab56fe27d389446754d0fb04910a34 - artwork = "%s/Items/%s/Images/%s/%s?Format=original&Tag=%s" % (server, item_id, art_type, index, image_tag) - if int(width) > 0: - artwork += '&MaxWidth=%s' % width - if int(height) > 0: - artwork += '&MaxHeight=%s' % height - - if self.use_https and not self.verify_cert: - artwork += "|verifypeer=false" - - return artwork - - def get_user_artwork(self, user, item_type): - - if "PrimaryImageTag" not in user: - return "" - user_id = user.get("Id") - tag = user.get("PrimaryImageTag") - server = self.get_server() - - artwork = "%s/Users/%s/Images/%s?Format=original&tag=%s" % (server, user_id, item_type, tag) - - if self.use_https and not self.verify_cert: - artwork += "|verifypeer=false" - - return artwork - - - def get_user_id(self): - user_details = load_user_details() - user_id = user_details.get('user_id', '') - return user_id - - - def authenticate(self): - window = HomeWindow() - user_name = window.get_property('user_name') - user_details = load_user_details() - - if user_details.get('token', ''): - log.debug("JellyCon DownloadUtils -> Returning saved AccessToken") - # Resave credentials to update settings file - save_user_details(user_details.get('user_name'), - user_details.get('user_id'), - user_details.get('token')) - return user_details.get('token') - - settings = xbmcaddon.Addon() - - url = "{server}/Users/AuthenticateByName" - - ''' - TODO: Make the authenticate function operate with arguments during - network rework. Password could be leaked like this, but it's better - than it was previously - ''' - password = window.get_property('password') - window.clear_property('password') - message_data = {'username': user_name, 'pw': password} - - result = self.download_url(url, post_body=message_data, method="POST", suppress=True, authenticate=False) - - access_token = result.get("AccessToken") - userid = result["User"].get("Id") - - if access_token is not None: - log.debug('User authenticated successfully') - save_user_details(user_name, userid, access_token) - self.post_capabilities() - else: - log.debug("User NOT Authenticated") - - def get_auth_header(self, authenticate=True): - device_id = get_device_id() - version = get_version() - client = 'Kodi JellyCon' - - settings = xbmcaddon.Addon() - device_name = settings.getSetting('deviceName') - # remove none ascii chars - device_name = py2_decode(device_name) - # remove some chars not valid for names - device_name = device_name.replace("\"", "_") - if len(device_name) == 0: - device_name = "JellyCon" - - headers = {} - headers["Accept-encoding"] = "gzip" - headers["Accept-Charset"] = "UTF-8,*" - - if authenticate is False: - auth_string = 'MediaBrowser Client="{}",Device="{}",DeviceId="{}",Version="{}'.format(client, device_name, device_id, version) - headers['X-Emby-Authorization'] = auth_string - return headers - else: - userid = self.get_user_id() - auth_string = 'MediaBrowser UserId="{}",Client="{}",Device="{}",DeviceId="{}",Version="{}"'.format(userid, client, device_name, device_id, version) - headers['X-Emby-Authorization'] = auth_string - - auth_token = self.authenticate() - if auth_token != "": - headers["X-MediaBrowser-Token"] = auth_token - - log.debug("JellyCon Authentication Header: {0}".format(headers)) - return headers - - @timer - def download_url(self, url, suppress=False, post_body=None, method="GET", authenticate=True, headers=None): - log.debug("downloadUrl") - - home_window = HomeWindow() - settings = xbmcaddon.Addon() - user_details = load_user_details() - username = user_details.get('user_name') - user_id = user_details.get('user_id', '') - server = None - - http_timeout = int(settings.getSetting("http_timeout")) - - if authenticate and username == "": - return {} - - if settings.getSetting("suppressErrors") == "true": - suppress = True - - log.debug("Before: {0}".format(url)) - - if url.find("{server}") != -1: - server = self.get_server() - if server is None: - return {} - url = url.replace("{server}", server) - - if url.find("{userid}") != -1: - url = url.replace("{userid}", user_id) - - if url.find("{ItemLimit}") != -1: - show_x_filtered_items = settings.getSetting("show_x_filtered_items") - url = url.replace("{ItemLimit}", show_x_filtered_items) - - if url.find("{field_filters}") != -1: - filter_string = get_details_string() - url = url.replace("{field_filters}", filter_string) - - if url.find("{random_movies}") != -1: - random_movies = home_window.get_property("random-movies") - if not random_movies: - return {} - url = url.replace("{random_movies}", random_movies) - - log.debug("After: {0}".format(url)) - - try: - url_bits = urlparse(url.strip()) - user_name = url_bits.username - user_password = url_bits.password - - head = self.get_auth_header(authenticate) - - if user_name and user_password: - log.info("Replacing username & Password info") - # add basic auth headers - user_and_pass = b64encode(b"%s:%s" % (user_name, user_password)).decode("ascii") - head["Authorization"] = 'Basic %s' % user_and_pass - - head["User-Agent"] = "JellyCon-" + get_version() - - http_request = getattr(requests, method.lower()) - - if post_body: - - if isinstance(post_body, dict): - head["Content-Type"] = "application/json" - post_body = json.dumps(post_body) - else: - head["Content-Type"] = "application/x-www-form-urlencoded" - - log.debug("Content-Type: {0}".format(head["Content-Type"])) - log.debug("POST DATA: {0}".format(post_body)) - - data = http_request(url, data=post_body, headers=head) - else: - data = http_request(url, headers=head) - - if data.status_code == 200: - - if headers is not None and isinstance(headers, dict): - headers.update(data.headers) - log.debug("{0}".format(data.json())) - - elif data.status_code >= 400: - - if data.status_code == 401: - # remove any saved password - log.error("HTTP response error 401 auth error, removing any saved passwords for user: {0}".format(username)) - save_user_details(username, user_id, '') - - log.error("HTTP response error for {0}: {1} {2}".format(url, data.status_code, data.content)) - if suppress is False: - xbmcgui.Dialog().notification(translate_string(30316), - '{}: {}'.format(translate_string(30200), data.content), - icon="special://home/addons/plugin.video.jellycon/icon.png") - try: - result = data.json() - except: - result = {} - return result - except Exception as msg: - log.error("{0}".format(format_exc())) - log.error("Unable to connect to {0} : {1}".format(server, msg)) - if not suppress: - xbmcgui.Dialog().notification(translate_string(30316), - str(msg), - icon="special://home/addons/plugin.video.jellycon/icon.png") diff --git a/resources/lib/functions.py b/resources/lib/functions.py index 24fb11e..249782a 100644 --- a/resources/lib/functions.py +++ b/resources/lib/functions.py @@ -14,8 +14,8 @@ import xbmcgui import xbmcaddon import xbmc -from .downloadutils import DownloadUtils -from .utils import convert_size, translate_string, get_version, load_user_details +from .api import API +from .utils import convert_size, translate_string, get_version, load_user_details, get_art_url, get_default_filters from .item_functions import get_art from .kodi_utils import HomeWindow from .datamanager import DataManager, clear_cached_server_data @@ -42,7 +42,14 @@ log = LazyLogger(__name__) kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2]) -downloadUtils = DownloadUtils() +settings = xbmcaddon.Addon() +user_details = load_user_details() +api = API( + settings.getSetting('server_address'), + user_details.get('user_id'), + user_details.get('token') +) + dataManager = DataManager() @@ -66,13 +73,13 @@ def main_entry_point(): log.debug("Script argument data: {0}".format(sys.argv)) params = get_params() - log.info("Script params: {0}".format(params)) + log.debug("Script params: {0}".format(params)) request_path = params.get("request_path", None) param_url = params.get('url', None) - if param_url: - param_url = unquote(param_url) + #if param_url: + # param_url = unquote(param_url) mode = params.get("mode", None) @@ -187,9 +194,9 @@ def __get_parent_id_from(params): show_provider_ids = params.get("show_ids") if show_provider_ids is not None: log.debug("TV show providers IDs: {}".format(show_provider_ids)) - get_show_url = "{server}/Users/{userid}/Items?fields=MediaStreams&Recursive=true" \ + get_show_url = "/Users/{}/Items?fields=MediaStreams&Recursive=true" \ "&IncludeItemTypes=series&IncludeMedia=true&ImageTypeLimit=1&Limit=16" \ - "&AnyProviderIdEquals=" + show_provider_ids + "&AnyProviderIdEquals={}".format(api.user_id, item_id, show_provider_ids) content = dataManager.get_content(get_show_url) show = content.get("Items") if len(show) == 1: @@ -206,7 +213,7 @@ def toggle_watched(params): item_id = params.get("item_id", None) if item_id is None: return - url = "{server}/Users/{userid}/Items/" + item_id + "?format=json" + url = "/Users/{}/Items/{}?format=json".format(api.user_id, item_id) data_manager = DataManager() result = data_manager.get_content(url) log.debug("toggle_watched item info: {0}".format(result)) @@ -223,8 +230,8 @@ def toggle_watched(params): def mark_item_watched(item_id): log.debug("Mark Item Watched: {0}".format(item_id)) - url = "{server}/Users/{userid}/PlayedItems/" + item_id - downloadUtils.download_url(url, post_body="", method="POST") + url = "/Users/{}/PlayedItems/{}".format(user_details.get('user_id'), item_id) + api.post(url) check_for_new_content() home_window = HomeWindow() last_url = home_window.get_property("last_content_url") @@ -237,8 +244,8 @@ def mark_item_watched(item_id): def mark_item_unwatched(item_id): log.debug("Mark Item UnWatched: {0}".format(item_id)) - url = "{server}/Users/{userid}/PlayedItems/" + item_id - downloadUtils.download_url(url, method="DELETE") + url = "/Users/{}/PlayedItems/".format(user_details.get('user_id'), item_id) + api.delete(url) check_for_new_content() home_window = HomeWindow() last_url = home_window.get_property("last_content_url") @@ -251,8 +258,8 @@ def mark_item_unwatched(item_id): def mark_item_favorite(item_id): log.debug("Add item to favourites: {0}".format(item_id)) - url = "{server}/Users/{userid}/FavoriteItems/" + item_id - downloadUtils.download_url(url, post_body="", method="POST") + url = "/Users/{}/FavoriteItems/{}".format(user_details.get('user_id'), item_id) + api.post(url) check_for_new_content() home_window = HomeWindow() last_url = home_window.get_property("last_content_url") @@ -264,8 +271,8 @@ def mark_item_favorite(item_id): def unmark_item_favorite(item_id): log.debug("Remove item from favourites: {0}".format(item_id)) - url = "{server}/Users/{userid}/FavoriteItems/" + item_id - downloadUtils.download_url(url, method="DELETE") + url = "/Users/{}/FavoriteItems/{}".format(user_details.get('user_id'), item_id) + api.delete(url) check_for_new_content() home_window = HomeWindow() last_url = home_window.get_property("last_content_url") @@ -277,7 +284,7 @@ def unmark_item_favorite(item_id): def delete(item_id): - item = downloadUtils.download_url("{server}/Users/{userid}/Items/" + item_id + "?format=json") + item = api.delete("/Users/{}/Items/{}".format(user_details.get('user_id'), item_id)) item_id = item.get("Id") item_name = item.get("Name", "") @@ -301,10 +308,10 @@ def delete(item_id): return_value = xbmcgui.Dialog().yesno(translate_string(30091), '{}\n{}'.format(final_name, translate_string(30092))) if return_value: log.debug('Deleting Item: {0}'.format(item_id)) - url = '{server}/Items/' + item_id + url = '/Items/{}'.format(item_id) progress = xbmcgui.DialogProgress() progress.create(translate_string(30052), translate_string(30053)) - downloadUtils.download_url(url, method="DELETE") + api.delete(url) progress.close() check_for_new_content() home_window = HomeWindow() @@ -343,7 +350,7 @@ def show_menu(params): settings = xbmcaddon.Addon() item_id = params["item_id"] - url = "{server}/Users/{userid}/Items/" + item_id + "?format=json" + url = "/Users/{}/Items/{}?format=json".format(api.user_id, item_id) data_manager = DataManager() result = data_manager.get_content(url) log.debug("Menu item info: {0}".format(result)) @@ -485,22 +492,22 @@ def show_menu(params): settings.setSetting(view_key, "") elif selected_action == "refresh_server": - url = ("{server}/Items/" + item_id + "/Refresh" + + url = ("/Items/" + item_id + "/Refresh" + "?Recursive=true" + "&ImageRefreshMode=FullRefresh" + "&MetadataRefreshMode=FullRefresh" + "&ReplaceAllImages=true" + "&ReplaceAllMetadata=true") - res = downloadUtils.download_url(url, post_body="", method="POST") + res = api.post(url) log.debug("Refresh Server Responce: {0}".format(res)) elif selected_action == "hide": user_details = load_user_details() user_name = user_details["user_name"] hide_tag_string = "hide-" + user_name - url = "{server}/Items/" + item_id + "/Tags/Add" + url = "/Items/{}/Tags/Add".format(item_id) post_tag_data = {"Tags": [{"Name": hide_tag_string}]} - res = downloadUtils.download_url(url, post_body=post_tag_data, method="POST") + res = api.post(url, post_tag_data) log.debug("Add Tag Responce: {0}".format(res)) check_for_new_content() @@ -554,62 +561,8 @@ def show_menu(params): elif selected_action == "delete": delete(item_id) - elif selected_action == "safe_delete": - url = "{server}/jellyfin_safe_delete/delete_item/" + item_id - result = downloadUtils.download_url(url) - dialog = xbmcgui.Dialog() - if result: - log.debug("Safe_Delete_Action: {0}".format(result)) - action_token = result["action_token"] - - message = "You are about to delete the following item[CR][CR]" - - message += "Type: " + result["item_info"]["Item_type"] + "[CR]" - - if result["item_info"]["Item_type"] == "Series": - message += "Name: " + result["item_info"]["item_name"] + "[CR]" - elif result["item_info"]["Item_type"] == "Season": - message += "Season: " + str(result["item_info"]["season_number"]) + "[CR]" - message += "Name: " + result["item_info"]["season_name"] + "[CR]" - elif result["item_info"]["Item_type"] == "Episode": - message += "Series: " + result["item_info"]["series_name"] + "[CR]" - message += "Season: " + result["item_info"]["season_name"] + "[CR]" - message += "Episode: " + str(result["item_info"]["episode_number"]) + "[CR]" - message += "Name: " + result["item_info"]["item_name"] + "[CR]" - else: - message += "Name: " + result["item_info"]["item_name"] + "[CR]" - - message += "[CR]File List[CR][CR]" - - for file_info in result["file_list"]: - message += " - " + file_info["Key"] + " (" + convert_size(file_info["Value"]) + ")[CR]" - message += "[CR][CR]Are you sure?[CR][CR]" - - confirm_dialog = SafeDeleteDialog("SafeDeleteDialog.xml", PLUGINPATH, "default", "720p") - confirm_dialog.message = message - confirm_dialog.heading = "Confirm delete files?" - confirm_dialog.doModal() - log.debug("safe_delete_confirm_dialog: {0}".format(confirm_dialog.confirm)) - - if confirm_dialog.confirm: - url = "{server}/jellyfin_safe_delete/delete_item_action" - playback_info = { - 'item_id': item_id, - 'action_token': action_token - } - delete_action = downloadUtils.download_url(url, method="POST", post_body=playback_info) - log.debug("Delete result action: {0}".format(delete_action)) - if not delete_action: - dialog.ok("Error", "Error deleting files", "Error in responce from server") - elif not delete_action.get("result"): - dialog.ok("Error", "Error deleting files", delete_action["message"]) - else: - dialog.ok("Deleted", "Files deleted") - else: - dialog.ok("Error", "Error getting safe delete confirmation") - elif selected_action == "show_extras": - u = "{server}/Users/{userid}/Items/" + item_id + "/SpecialFeatures" + u = "/Users/{}/Items/{}/SpecialFeatures".format(api.user_id, item_id) action_url = ("plugin://plugin.video.jellycon/?url=" + quote(u) + "&mode=GET_CONTENT&media_type=Videos") built_in_command = 'ActivateWindow(Videos, ' + action_url + ', return)' xbmc.executebuiltin(built_in_command) @@ -618,13 +571,13 @@ def show_menu(params): xbmc.executebuiltin("Dialog.Close(all,true)") parent_id = result["ParentId"] series_id = result["SeriesId"] - u = ('{server}/Shows/' + series_id + + u = ('/Shows/' + series_id + '/Episodes' - '?userId={userid}' + + '?userId={}'.format(api.user_id) + '&seasonId=' + parent_id + '&IsVirtualUnAired=false' + '&IsMissing=false' + - '&Fields=SpecialEpisodeNumbers,{field_filters}' + + '&Fields=SpecialEpisodeNumbers,{}'.format(get_default_filters()) + '&format=json') action_url = ("plugin://plugin.video.jellycon/?url=" + quote(u) + "&mode=GET_CONTENT&media_type=Season") built_in_command = 'ActivateWindow(Videos, ' + action_url + ', return)' @@ -637,10 +590,10 @@ def show_menu(params): if not series_id: series_id = item_id - u = ('{server}/Shows/' + series_id + + u = ('/Shows/' + series_id + '/Seasons' - '?userId={userid}' + - '&Fields={field_filters}' + + '?userId={}'.format(api.user_id) + + '&Fields={}'.format(get_default_filters()) + '&format=json') action_url = ("plugin://plugin.video.jellycon/?url=" + quote(u) + "&mode=GET_CONTENT&media_type=Series") @@ -663,15 +616,15 @@ def show_menu(params): def populate_listitem(item_id): log.debug("populate_listitem: {0}".format(item_id)) - url = "{server}/Users/{userid}/Items/" + item_id + "?format=json" - result = downloadUtils.download_url(url) + url = "/Users/{}/Items/{}".format(user_details.get('user_id'), item_id) + result = api.get(url) log.debug("populate_listitem item info: {0}".format(result)) item_title = result.get("Name", translate_string(30280)) list_item = xbmcgui.ListItem(label=item_title) - server = downloadUtils.get_server() + server = settings.getSetting('server_address') art = get_art(result, server=server) list_item.setIconImage(art['thumb']) # back compat @@ -704,11 +657,11 @@ def show_content(params): if item_type.lower().find("movie") == -1: group_movies = False - content_url = ("{server}/Users/{userid}/Items" + + content_url = ("/Users/{}/Items".format(api.user_id) + "?format=json" + "&ImageTypeLimit=1" + "&IsMissing=False" + - "&Fields={field_filters}" + + "&Fields={}".format(get_default_filters()) + '&CollapseBoxSetItems=' + str(group_movies) + '&GroupItemsIntoCollections=' + str(group_movies) + "&Recursive=true" + @@ -726,10 +679,10 @@ def search_results_person(params): handle = int(sys.argv[1]) person_id = params.get("person_id") - details_url = ('{server}/Users/{userid}/items' + + details_url = ('/Users/{}/Items'.format(api.user_id) + '?PersonIds=' + person_id + '&Recursive=true' + - '&Fields={field_filters}' + + '&Fields={}'.format(get_default_filters()) + '&format=json') params["name_format"] = "Episode|episode_name_format" @@ -831,7 +784,7 @@ def search_results(params): # what type of search if item_type == "person": - search_url = ("{server}/Persons" + + search_url = ("/Persons" + "?searchTerm=" + query + "&IncludePeople=true" + "&IncludeMedia=false" + @@ -843,7 +796,7 @@ def search_results(params): "&Recursive=true" + "&EnableTotalRecordCount=false" + "&ImageTypeLimit=1" + - "&userId={userid}") + "&userId={}".format(api.user_id)) person_search_results = dataManager.get_content(search_url) log.debug("Person Search Result : {0}".format(person_search_results)) @@ -852,12 +805,12 @@ def search_results(params): person_items = person_search_results.get("Items", []) - server = downloadUtils.get_server() + server = settings.getSetting('server_address') list_items = [] for item in person_items: person_id = item.get('Id') person_name = item.get('Name') - person_thumbnail = downloadUtils.get_artwork(item, "Primary", server=server) + person_thumbnail = get_art_url(item, "Primary", server=server) action_url = sys.argv[0] + "?mode=NEW_SEARCH_PERSON&person_id=" + person_id @@ -879,7 +832,7 @@ def search_results(params): xbmcplugin.endOfDirectory(handle, cacheToDisc=False) else: - search_url = ("{server}/Users/{userid}/Items" + + search_url = ("/Users/{}/Items".format(api.user_id) + "?searchTerm=" + query + "&IncludePeople=false" + "&IncludeMedia=true" + @@ -888,7 +841,7 @@ def search_results(params): "&IncludeArtists=false" + "&IncludeItemTypes=" + item_type + "&Limit=16" + - "&Fields={field_filters}" + + "&Fields={}".format(get_default_filters()) + "&Recursive=true" + "&EnableTotalRecordCount=false" + "&ImageTypeLimit=1") @@ -955,9 +908,9 @@ def play_action(params): def play_item_trailer(item_id): log.debug("== ENTER: playTrailer ==") - url = ("{server}/Users/{userid}/Items/%s/LocalTrailers?format=json" % item_id) + url = "/Users/{}/Items//LocalTrailers?format=json".format(user_details.get('user_id'), item_id) - result = downloadUtils.download_url(url) + result = api.get(url) if result is None: return @@ -980,8 +933,8 @@ def play_item_trailer(item_id): trailer_names.append(name) trailer_list.append(info) - url = ("{server}/Users/{userid}/Items/%s?format=json&Fields=RemoteTrailers" % item_id) - result = downloadUtils.download_url(url) + url = "/Users/{}/Items/{}?format=json&Fields=RemoteTrailers".format(user_details.get('user_id'), item_id) + result = api.get(url) log.debug("RemoteTrailers: {0}".format(result)) count = 1 diff --git a/resources/lib/image_server.py b/resources/lib/image_server.py index e21ae41..3491256 100644 --- a/resources/lib/image_server.py +++ b/resources/lib/image_server.py @@ -2,6 +2,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera import xbmcvfs import xbmc +import xbmcaddon import base64 import re from random import shuffle @@ -15,7 +16,6 @@ import io from .loghandler import LazyLogger from .datamanager import DataManager -from .downloadutils import DownloadUtils from .item_functions import get_art pil_loaded = False @@ -31,8 +31,8 @@ log = LazyLogger(__name__) def get_image_links(url): - download_utils = DownloadUtils() - server = download_utils.get_server() + settings = xbmcaddon.Addon() + server = settings.getSetting('server_address') if server is None: return [] diff --git a/resources/lib/item_functions.py b/resources/lib/item_functions.py index 5f87829..0d815c7 100644 --- a/resources/lib/item_functions.py +++ b/resources/lib/item_functions.py @@ -10,9 +10,8 @@ import xbmc import xbmcaddon import xbmcgui -from .utils import datetime_from_string +from .utils import datetime_from_string, get_art_url, image_url from .loghandler import LazyLogger -from .downloadutils import DownloadUtils from six import ensure_text log = LazyLogger(__name__) @@ -244,7 +243,6 @@ def extract_item_info(item, gui_options): people = item.get("People", []) if people is not None: cast = [] - download_utils = DownloadUtils() for person in people: person_type = person.get("Type") if person_type == "Director": @@ -257,10 +255,9 @@ def extract_item_info(item, gui_options): person_id = person.get("Id") person_tag = person.get("PrimaryImageTag") if person_tag: - person_thumbnail = download_utils.image_url(person_id, - "Primary", 0, 400, 400, - person_tag, - server=gui_options["server"]) + person_thumbnail = image_url(person_id, "Primary", 0, 400, + 400, person_tag, + server=gui_options["server"]) else: person_thumbnail = "" person = {"name": person_name, "role": person_role, "thumbnail": person_thumbnail} @@ -613,7 +610,6 @@ def add_gui_item(url, item_details, display_options, folder=True, default_sort=F def get_art(item, server): - download_utils = DownloadUtils() art = { 'thumb': '', @@ -634,63 +630,63 @@ def get_art(item, server): image_tags = item.get("ImageTags", {}) if image_tags and image_tags.get("Primary"): - art['thumb'] = download_utils.get_artwork(item, "Primary", server=server) + art['thumb'] = get_art_url(item, "Primary", server=server) item_type = item["Type"] if item_type == "Genre": - art['poster'] = download_utils.get_artwork(item, "Primary", server=server) + art['poster'] = get_art_url(item, "Primary", server=server) elif item_type == "Episode": - art['tvshow.poster'] = download_utils.get_artwork(item, "Primary", parent=True, server=server) - art['tvshow.clearart'] = download_utils.get_artwork(item, "Art", parent=True, server=server) - art['clearart'] = download_utils.get_artwork(item, "Art", parent=True, server=server) - art['tvshow.clearlogo'] = download_utils.get_artwork(item, "Logo", parent=True, server=server) - art['clearlogo'] = download_utils.get_artwork(item, "Logo", parent=True, server=server) - art['tvshow.banner'] = download_utils.get_artwork(item, "Banner", parent=True, server=server) - art['banner'] = download_utils.get_artwork(item, "Banner", parent=True, server=server) - art['tvshow.landscape'] = download_utils.get_artwork(item, "Thumb", parent=True, server=server) - art['landscape'] = download_utils.get_artwork(item, "Thumb", parent=True, server=server) - art['tvshow.fanart'] = download_utils.get_artwork(item, "Backdrop", parent=True, server=server) - art['fanart'] = download_utils.get_artwork(item, "Backdrop", parent=True, server=server) + art['tvshow.poster'] = get_art_url(item, "Primary", parent=True, server=server) + art['tvshow.clearart'] = get_art_url(item, "Art", parent=True, server=server) + art['clearart'] = get_art_url(item, "Art", parent=True, server=server) + art['tvshow.clearlogo'] = get_art_url(item, "Logo", parent=True, server=server) + art['clearlogo'] = get_art_url(item, "Logo", parent=True, server=server) + art['tvshow.banner'] = get_art_url(item, "Banner", parent=True, server=server) + art['banner'] = get_art_url(item, "Banner", parent=True, server=server) + art['tvshow.landscape'] = get_art_url(item, "Thumb", parent=True, server=server) + art['landscape'] = get_art_url(item, "Thumb", parent=True, server=server) + art['tvshow.fanart'] = get_art_url(item, "Backdrop", parent=True, server=server) + art['fanart'] = get_art_url(item, "Backdrop", parent=True, server=server) elif item_type == "Season": - art['tvshow.poster'] = download_utils.get_artwork(item, "Primary", parent=True, server=server) - art['season.poster'] = download_utils.get_artwork(item, "Primary", parent=False, server=server) - art['poster'] = download_utils.get_artwork(item, "Primary", parent=False, server=server) - art['tvshow.clearart'] = download_utils.get_artwork(item, "Art", parent=True, server=server) - art['clearart'] = download_utils.get_artwork(item, "Art", parent=True, server=server) - art['tvshow.clearlogo'] = download_utils.get_artwork(item, "Logo", parent=True, server=server) - art['clearlogo'] = download_utils.get_artwork(item, "Logo", parent=True, server=server) - art['tvshow.banner'] = download_utils.get_artwork(item, "Banner", parent=True, server=server) - art['season.banner'] = download_utils.get_artwork(item, "Banner", parent=False, server=server) - art['banner'] = download_utils.get_artwork(item, "Banner", parent=False, server=server) - art['tvshow.landscape'] = download_utils.get_artwork(item, "Thumb", parent=True, server=server) - art['season.landscape'] = download_utils.get_artwork(item, "Thumb", parent=False, server=server) - art['landscape'] = download_utils.get_artwork(item, "Thumb", parent=False, server=server) - art['tvshow.fanart'] = download_utils.get_artwork(item, "Backdrop", parent=True, server=server) - art['fanart'] = download_utils.get_artwork(item, "Backdrop", parent=True, server=server) + art['tvshow.poster'] = get_art_url(item, "Primary", parent=True, server=server) + art['season.poster'] = get_art_url(item, "Primary", parent=False, server=server) + art['poster'] = get_art_url(item, "Primary", parent=False, server=server) + art['tvshow.clearart'] = get_art_url(item, "Art", parent=True, server=server) + art['clearart'] = get_art_url(item, "Art", parent=True, server=server) + art['tvshow.clearlogo'] = get_art_url(item, "Logo", parent=True, server=server) + art['clearlogo'] = get_art_url(item, "Logo", parent=True, server=server) + art['tvshow.banner'] = get_art_url(item, "Banner", parent=True, server=server) + art['season.banner'] = get_art_url(item, "Banner", parent=False, server=server) + art['banner'] = get_art_url(item, "Banner", parent=False, server=server) + art['tvshow.landscape'] = get_art_url(item, "Thumb", parent=True, server=server) + art['season.landscape'] = get_art_url(item, "Thumb", parent=False, server=server) + art['landscape'] = get_art_url(item, "Thumb", parent=False, server=server) + art['tvshow.fanart'] = get_art_url(item, "Backdrop", parent=True, server=server) + art['fanart'] = get_art_url(item, "Backdrop", parent=True, server=server) elif item_type == "Series": - art['tvshow.poster'] = download_utils.get_artwork(item, "Primary", parent=False, server=server) - art['poster'] = download_utils.get_artwork(item, "Primary", parent=False, server=server) - art['tvshow.clearart'] = download_utils.get_artwork(item, "Art", parent=False, server=server) - art['clearart'] = download_utils.get_artwork(item, "Art", parent=False, server=server) - art['tvshow.clearlogo'] = download_utils.get_artwork(item, "Logo", parent=False, server=server) - art['clearlogo'] = download_utils.get_artwork(item, "Logo", parent=False, server=server) - art['tvshow.banner'] = download_utils.get_artwork(item, "Banner", parent=False, server=server) - art['banner'] = download_utils.get_artwork(item, "Banner", parent=False, server=server) - art['tvshow.landscape'] = download_utils.get_artwork(item, "Thumb", parent=False, server=server) - art['landscape'] = download_utils.get_artwork(item, "Thumb", parent=False, server=server) - art['tvshow.fanart'] = download_utils.get_artwork(item, "Backdrop", parent=False, server=server) - art['fanart'] = download_utils.get_artwork(item, "Backdrop", parent=False, server=server) + art['tvshow.poster'] = get_art_url(item, "Primary", parent=False, server=server) + art['poster'] = get_art_url(item, "Primary", parent=False, server=server) + art['tvshow.clearart'] = get_art_url(item, "Art", parent=False, server=server) + art['clearart'] = get_art_url(item, "Art", parent=False, server=server) + art['tvshow.clearlogo'] = get_art_url(item, "Logo", parent=False, server=server) + art['clearlogo'] = get_art_url(item, "Logo", parent=False, server=server) + art['tvshow.banner'] = get_art_url(item, "Banner", parent=False, server=server) + art['banner'] = get_art_url(item, "Banner", parent=False, server=server) + art['tvshow.landscape'] = get_art_url(item, "Thumb", parent=False, server=server) + art['landscape'] = get_art_url(item, "Thumb", parent=False, server=server) + art['tvshow.fanart'] = get_art_url(item, "Backdrop", parent=False, server=server) + art['fanart'] = get_art_url(item, "Backdrop", parent=False, server=server) elif item_type == "Movie" or item_type == "BoxSet": - art['poster'] = download_utils.get_artwork(item, "Primary", server=server) - art['landscape'] = download_utils.get_artwork(item, "Thumb", server=server) - art['banner'] = download_utils.get_artwork(item, "Banner", server=server) - art['clearlogo'] = download_utils.get_artwork(item, "Logo", server=server) - art['clearart'] = download_utils.get_artwork(item, "Art", server=server) - art['discart'] = download_utils.get_artwork(item, "Disc", server=server) + art['poster'] = get_art_url(item, "Primary", server=server) + art['landscape'] = get_art_url(item, "Thumb", server=server) + art['banner'] = get_art_url(item, "Banner", server=server) + art['clearlogo'] = get_art_url(item, "Logo", server=server) + art['clearart'] = get_art_url(item, "Art", server=server) + art['discart'] = get_art_url(item, "Disc", server=server) - art['fanart'] = download_utils.get_artwork(item, "Backdrop", server=server) + art['fanart'] = get_art_url(item, "Backdrop", server=server) if not art['fanart']: - art['fanart'] = download_utils.get_artwork(item, "Backdrop", parent=True, server=server) + art['fanart'] = get_art_url(item, "Backdrop", parent=True, server=server) return art diff --git a/resources/lib/menu_functions.py b/resources/lib/menu_functions.py index 117243d..6b3f6a1 100644 --- a/resources/lib/menu_functions.py +++ b/resources/lib/menu_functions.py @@ -11,17 +11,18 @@ import string import xbmcplugin import xbmcaddon -from .downloadutils import DownloadUtils from .kodi_utils import add_menu_directory_item, HomeWindow from .loghandler import LazyLogger from .datamanager import DataManager -from .utils import get_jellyfin_url, translate_string +from .utils import get_jellyfin_url, translate_string, get_art_url, load_user_details, get_default_filters from .item_functions import get_art log = LazyLogger(__name__) -downloadUtils = DownloadUtils() __addon__ = xbmcaddon.Addon() +user_details = load_user_details() +user_id = user_details.get('user_id') +settings = xbmcaddon.Addon() def show_movie_tags(menu_params): @@ -29,7 +30,7 @@ def show_movie_tags(menu_params): parent_id = menu_params.get("parent_id") url_params = {} - url_params["UserId"] = "{userid}" + url_params["UserId"] = user_id url_params["SortBy"] = "SortName" url_params["SortOrder"] = "Ascending" url_params["CollapseBoxSetItems"] = False @@ -43,7 +44,7 @@ def show_movie_tags(menu_params): if parent_id: url_params["ParentId"] = parent_id - url = get_jellyfin_url("{server}/Tags", url_params) + url = get_jellyfin_url("/Tags", url_params) data_manager = DataManager() result = data_manager.get_content(url) @@ -67,13 +68,13 @@ def show_movie_tags(menu_params): url_params["ImageTypeLimit"] = 1 url_params["SortBy"] = "Name" url_params["SortOrder"] = "Ascending" - url_params["Fields"] = "{field_filters}" + url_params["Fields"] = get_default_filters() url_params["TagIds"] = tag_id if parent_id: menu_params["ParentId"] = parent_id - item_url = get_jellyfin_url("{server}/Users/{userid}/Items", url_params) + item_url = get_jellyfin_url("/Users/{}/Items".format(user_id), url_params) art = {"thumb": "http://localhost:24276/{}".format(ensure_text(base64.b64encode(ensure_binary(item_url))))} @@ -94,7 +95,7 @@ def show_movie_years(menu_params): group_into_decades = menu_params.get("group") == "true" url_params = {} - url_params["UserId"] = "{userid}" + url_params["UserId"] = user_id url_params["SortBy"] = "SortName" url_params["SortOrder"] = "Ascending" url_params["CollapseBoxSetItems"] = False @@ -108,7 +109,7 @@ def show_movie_years(menu_params): if parent_id: url_params["ParentId"] = parent_id - url = get_jellyfin_url("{server}/Years", url_params) + url = get_jellyfin_url("/Years", url_params) data_manager = DataManager() result = data_manager.get_content(url) @@ -153,13 +154,13 @@ def show_movie_years(menu_params): params["ImageTypeLimit"] = 1 params["SortBy"] = "Name" params["SortOrder"] = "Ascending" - params["Fields"] = "{field_filters}" + params["Fields"] = get_default_filters() params["Years"] = value if parent_id: params["ParentId"] = parent_id - item_url = get_jellyfin_url("{server}/Users/{userid}/Items", params) + item_url = get_jellyfin_url("/Users/{}/Items".format(user_id), params) art = {"thumb": "http://localhost:24276/{}".format(ensure_text(base64.b64encode(ensure_binary(item_url))))} @@ -178,7 +179,6 @@ def show_movie_pages(menu_params): log.debug("showMoviePages: {0}".format(menu_params)) parent_id = menu_params.get("parent_id") - settings = xbmcaddon.Addon() group_movies = settings.getSetting('group_movies') == "true" params = {} @@ -192,7 +192,7 @@ def show_movie_pages(menu_params): if parent_id: params["ParentId"] = parent_id - url = get_jellyfin_url("{server}/Users/{userid}/Items", params) + url = get_jellyfin_url("/Users/{}/Items".format(user_id), params) data_manager = DataManager() result = data_manager.get_content(url) @@ -224,14 +224,14 @@ def show_movie_pages(menu_params): params["ImageTypeLimit"] = 1 params["SortBy"] = "Name" params["SortOrder"] = "Ascending" - params["Fields"] = "{field_filters}" + params["Fields"] = get_default_filters() params["StartIndex"] = start_index params["Limit"] = page_limit if parent_id: params["ParentId"] = parent_id - item_url = get_jellyfin_url("{server}/Users/{userid}/Items", params) + item_url = get_jellyfin_url("/Users/{}/Items".format(user_id), params) page_upper = start_index + page_limit if page_upper > total_results: @@ -261,7 +261,7 @@ def show_movie_pages(menu_params): def show_genre_list(menu_params): log.debug("showGenreList: {0}".format(menu_params)) - server = downloadUtils.get_server() + server = settings.getSetting('server_address') if server is None: return @@ -276,7 +276,7 @@ def show_genre_list(menu_params): params = {} params["IncludeItemTypes"] = jellyfin_type - params["UserId"] = "{userid}" + params["UserId"] = user_id params["Recursive"] = True params["SortBy"] = "Name" params["SortOrder"] = "Ascending" @@ -285,7 +285,7 @@ def show_genre_list(menu_params): if parent_id is not None: params["ParentId"] = parent_id - url = get_jellyfin_url("{server}/Genres", params) + url = get_jellyfin_url("/Genres", params) data_manager = DataManager() result = data_manager.get_content(url) @@ -295,7 +295,6 @@ def show_genre_list(menu_params): else: result = [] - settings = xbmcaddon.Addon() group_movies = settings.getSetting('group_movies') == "true" collections = [] @@ -316,12 +315,12 @@ def show_genre_list(menu_params): params["GenreIds"] = genre.get("Id") params["IncludeItemTypes"] = jellyfin_type params["ImageTypeLimit"] = 1 - params["Fields"] = "{field_filters}" + params["Fields"] = get_default_filters() if parent_id is not None: params["ParentId"] = parent_id - url = get_jellyfin_url("{server}/Users/{userid}/Items", params) + url = get_jellyfin_url("/Users/{}/Items".format(user_id), params) art = {"thumb": "http://localhost:24276/{}".format(ensure_text(base64.b64encode(ensure_binary(url))))} item_data['art'] = art @@ -344,8 +343,7 @@ def show_movie_alpha_list(menu_params): xbmcplugin.setContent(int(sys.argv[1]), 'movies') - settings = xbmcaddon.Addon() - server = downloadUtils.get_server() + server = settings.getSetting('server_address') if server is None: return @@ -356,7 +354,7 @@ def show_movie_alpha_list(menu_params): url_params["IncludeItemTypes"] = "Movie" url_params["Recursive"] = True url_params["GroupItemsIntoCollections"] = group_movies - url_params["UserId"] = "{userid}" + url_params["UserId"] = user_id url_params["SortBy"] = "Name" url_params["SortOrder"] = "Ascending" if parent_id is not None: @@ -371,7 +369,7 @@ def show_movie_alpha_list(menu_params): item_data['media_type'] = "Movies" params = {} - params["Fields"] = "{field_filters}" + params["Fields"] = get_default_filters() params["CollapseBoxSetItems"] = group_movies params["GroupItemsIntoCollections"] = group_movies params["Recursive"] = True @@ -388,7 +386,7 @@ def show_movie_alpha_list(menu_params): else: params["NameStartsWith"] = alpha_name - url = get_jellyfin_url("{server}/Users/{userid}/Items", params) + url = get_jellyfin_url("/Users/{}/Items".format(user_id), params) item_data['path'] = url art = {"thumb": "http://localhost:24276/{}".format(ensure_text(base64.b64encode(ensure_binary(url))))} @@ -408,7 +406,7 @@ def show_movie_alpha_list(menu_params): def show_tvshow_alpha_list(menu_params): log.debug("== ENTER: showTvShowAlphaList() ==") - server = downloadUtils.get_server() + server = settings.getSetting('server_address') if server is None: return @@ -417,7 +415,7 @@ def show_tvshow_alpha_list(menu_params): url_params = {} url_params["IncludeItemTypes"] = "Series" url_params["Recursive"] = True - url_params["UserId"] = "{userid}" + url_params["UserId"] = user_id url_params["SortBy"] = "Name" url_params["SortOrder"] = "Ascending" if parent_id is not None: @@ -432,7 +430,7 @@ def show_tvshow_alpha_list(menu_params): item_data['media_type'] = "tvshows" params = {} - params["Fields"] = "{field_filters}" + params["Fields"] = get_default_filters() params["ImageTypeLimit"] = 1 params["IncludeItemTypes"] = "Series" params["SortBy"] = "Name" @@ -448,7 +446,7 @@ def show_tvshow_alpha_list(menu_params): else: params["NameStartsWith"] = alpha_name - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) item_data['path'] = path @@ -518,8 +516,7 @@ def show_global_types(params): def display_homevideos_type(menu_params, view): handle = int(sys.argv[1]) view_name = view.get("Name") - settings = xbmcaddon.Addon() - show_x_filtered_items = settings.getSetting("show_x_filtered_items") + item_limit = settings.getSetting("show_x_filtered_items") hide_watched = settings.getSetting("hide_watched") == "true" # All Home Movies @@ -527,9 +524,9 @@ def display_homevideos_type(menu_params, view): base_params["ParentId"] = view.get("Id") base_params["Recursive"] = False base_params["IsMissing"] = False - base_params["Fields"] = "{field_filters}" + base_params["Fields"] = get_default_filters() base_params["ImageTypeLimit"] = 1 - path = get_jellyfin_url("{server}/Users/{userid}/Items", base_params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), base_params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=homevideos" add_menu_directory_item(view_name + translate_string(30405), url) @@ -538,10 +535,10 @@ def display_homevideos_type(menu_params, view): params.update(base_params) params["Filters"] = "IsResumable" params["Recursive"] = True - params["Limit"] = "{ItemLimit}" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + params["Limit"] = item_limit + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=homevideos" - add_menu_directory_item(view_name + translate_string(30267) + " (" + show_x_filtered_items + ")", url) + add_menu_directory_item(view_name + translate_string(30267) + " (" + item_limit + ")", url) # Recently added params = {} @@ -552,10 +549,10 @@ def display_homevideos_type(menu_params, view): params["Filters"] = "IsNotFolder" if hide_watched: params["IsPlayed"] = False - params["Limit"] = "{ItemLimit}" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + params["Limit"] = item_limit + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=homevideos" - add_menu_directory_item(view_name + translate_string(30268) + " (" + show_x_filtered_items + ")", url) + add_menu_directory_item(view_name + translate_string(30268) + " (" + item_limit + ")", url) xbmcplugin.endOfDirectory(handle) @@ -583,19 +580,18 @@ def display_tvshow_type(menu_params, view): if view is not None: view_name = view.get("Name") - settings = xbmcaddon.Addon() - show_x_filtered_items = settings.getSetting("show_x_filtered_items") + item_limit = settings.getSetting("show_x_filtered_items") # All TV Shows base_params = {} if view is not None: base_params["ParentId"] = view.get("Id") - base_params["Fields"] = "{field_filters}" + base_params["Fields"] = get_default_filters() base_params["ImageTypeLimit"] = 1 base_params["IsMissing"] = False base_params["IncludeItemTypes"] = "Series" base_params["Recursive"] = True - path = get_jellyfin_url("{server}/Users/{userid}/Items", base_params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), base_params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=tvshows" add_menu_directory_item(view_name + translate_string(30405), url) @@ -603,7 +599,7 @@ def display_tvshow_type(menu_params, view): params = {} params.update(base_params) params["Filters"] = "IsFavorite" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=tvshows" add_menu_directory_item(view_name + translate_string(30414), url) @@ -611,60 +607,60 @@ def display_tvshow_type(menu_params, view): params = {} params.update(base_params) params["IsPlayed"] = False - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=tvshows" add_menu_directory_item(view_name + translate_string(30285), url) # In progress episodes params = {} params.update(base_params) - params["Limit"] = "{ItemLimit}" + params["Limit"] = item_limit params["SortBy"] = "DatePlayed" params["SortOrder"] = "Descending" params["Filters"] = "IsResumable" params["IncludeItemTypes"] = "Episode" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=Episodes&sort=none" url += "&name_format=" + quote('Episode|episode_name_format') - add_menu_directory_item(view_name + translate_string(30267) + " (" + show_x_filtered_items + ")", url) + add_menu_directory_item(view_name + translate_string(30267) + " (" + item_limit + ")", url) # Latest Episodes params = {} params.update(base_params) - params["Limit"] = "{ItemLimit}" + params["Limit"] = item_limit params["SortBy"] = "DateCreated" params["SortOrder"] = "Descending" params["IncludeItemTypes"] = "Episode" - path = get_jellyfin_url("{server}/Users/{userid}/Items/Latest", params) + path = get_jellyfin_url("/Users/{}/Items/Latest".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=tvshows&sort=none" - add_menu_directory_item(view_name + translate_string(30288) + " (" + show_x_filtered_items + ")", url) + add_menu_directory_item(view_name + translate_string(30288) + " (" + item_limit + ")", url) # Recently Added params = {} params.update(base_params) - params["Limit"] = "{ItemLimit}" + params["Limit"] = item_limit params["SortBy"] = "DateCreated" params["SortOrder"] = "Descending" params["Filters"] = "IsNotFolder" params["IncludeItemTypes"] = "Episode" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=Episodes&sort=none" url += "&name_format=" + quote('Episode|episode_name_format') - add_menu_directory_item(view_name + translate_string(30268) + " (" + show_x_filtered_items + ")", url) + add_menu_directory_item(view_name + translate_string(30268) + " (" + item_limit + ")", url) # Next Up Episodes params = {} params.update(base_params) - params["Limit"] = "{ItemLimit}" - params["Userid"] = "{userid}" + params["Limit"] = item_limit + params["Userid"] = user_id params["SortBy"] = "DateCreated" params["SortOrder"] = "Descending" params["Filters"] = "IsNotFolder" params["IncludeItemTypes"] = "Episode" - path = get_jellyfin_url("{server}/Shows/NextUp", params) + path = get_jellyfin_url("/Shows/NextUp", params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=Episodes&sort=none" url += "&name_format=" + quote('Episode|episode_name_format') - add_menu_directory_item(view_name + translate_string(30278) + " (" + show_x_filtered_items + ")", url) + add_menu_directory_item(view_name + translate_string(30278) + " (" + item_limit + ")", url) # TV Show Genres path = "plugin://plugin.video.jellycon/?mode=GENRES&item_type=tvshow" @@ -685,8 +681,7 @@ def display_music_type(menu_params, view): handle = int(sys.argv[1]) view_name = view.get("Name") - settings = xbmcaddon.Addon() - show_x_filtered_items = settings.getSetting("show_x_filtered_items") + item_limit = settings.getSetting("show_x_filtered_items") # all albums params = {} @@ -694,7 +689,7 @@ def display_music_type(menu_params, view): params["Recursive"] = True params["ImageTypeLimit"] = 1 params["IncludeItemTypes"] = "MusicAlbum" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=MusicAlbums" add_menu_directory_item(view_name + translate_string(30320), url) @@ -703,10 +698,10 @@ def display_music_type(menu_params, view): params["ParentId"] = view.get("Id") params["ImageTypeLimit"] = 1 params["IncludeItemTypes"] = "Audio" - params["Limit"] = "{ItemLimit}" - path = get_jellyfin_url("{server}/Users/{userid}/Items/Latest", params) + params["Limit"] = item_limit + path = get_jellyfin_url("/Users/{}/Items/Latest".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=MusicAlbums" - add_menu_directory_item(view_name + translate_string(30268) + " (" + show_x_filtered_items + ")", url) + add_menu_directory_item(view_name + translate_string(30268) + " (" + item_limit + ")", url) # recently played params = {} @@ -714,13 +709,13 @@ def display_music_type(menu_params, view): params["Recursive"] = True params["ImageTypeLimit"] = 1 params["IncludeItemTypes"] = "Audio" - params["Limit"] = "{ItemLimit}" + params["Limit"] = item_limit params["IsPlayed"] = True params["SortBy"] = "DatePlayed" params["SortOrder"] = "Descending" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=MusicAlbum" - add_menu_directory_item(view_name + translate_string(30349) + " (" + show_x_filtered_items + ")", url) + add_menu_directory_item(view_name + translate_string(30349) + " (" + item_limit + ")", url) # most played params = {} @@ -728,20 +723,20 @@ def display_music_type(menu_params, view): params["Recursive"] = True params["ImageTypeLimit"] = 1 params["IncludeItemTypes"] = "Audio" - params["Limit"] = "{ItemLimit}" + params["Limit"] = item_limit params["IsPlayed"] = True params["SortBy"] = "PlayCount" params["SortOrder"] = "Descending" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=MusicAlbum" - add_menu_directory_item(view_name + translate_string(30353) + " (" + show_x_filtered_items + ")", url) + add_menu_directory_item(view_name + translate_string(30353) + " (" + item_limit + ")", url) # artists params = {} params["ParentId"] = view.get("Id") params["Recursive"] = True params["ImageTypeLimit"] = 1 - path = get_jellyfin_url("{server}/Artists/AlbumArtists", params) + path = get_jellyfin_url("/Artists/AlbumArtists", params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=MusicArtists" add_menu_directory_item(view_name + translate_string(30321), url) @@ -760,8 +755,8 @@ def display_musicvideos_type(params, view): params["Recursive"] = False params["ImageTypeLimit"] = 1 params["IsMissing"] = False - params["Fields"] = "{field_filters}" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + params["Fields"] = get_default_filters() + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=musicvideos" add_menu_directory_item(view_name + translate_string(30405), url) @@ -776,33 +771,33 @@ def display_livetv_type(menu_params, view): # channels params = {} - params["UserId"] = "{userid}" + params["UserId"] = user_id params["Recursive"] = False params["ImageTypeLimit"] = 1 - params["Fields"] = "{field_filters}" - path = get_jellyfin_url("{server}/LiveTv/Channels", params) + params["Fields"] = get_default_filters() + path = get_jellyfin_url("/LiveTv/Channels", params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=livetv" add_menu_directory_item(view_name + translate_string(30360), url) # programs params = {} - params["UserId"] = "{userid}" + params["UserId"] = user_id params["IsAiring"] = True params["ImageTypeLimit"] = 1 - params["Fields"] = "ChannelInfo,{field_filters}" + params["Fields"] = get_default_filters() + "ChannelInfo" params["EnableTotalRecordCount"] = False - path = get_jellyfin_url("{server}/LiveTv/Programs/Recommended", params) + path = get_jellyfin_url("/LiveTv/Programs/Recommended", params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=livetv" add_menu_directory_item(view_name + translate_string(30361), url) # recordings params = {} - params["UserId"] = "{userid}" + params["UserId"] = user_id params["Recursive"] = False params["ImageTypeLimit"] = 1 - params["Fields"] = "{field_filters}" + params["Fields"] = get_default_filters() params["EnableTotalRecordCount"] = False - path = get_jellyfin_url("{server}/LiveTv/Recordings", params) + path = get_jellyfin_url("/LiveTv/Recordings", params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=livetv" add_menu_directory_item(view_name + translate_string(30362), url) @@ -817,8 +812,7 @@ def display_movies_type(menu_params, view): if view is not None: view_name = view.get("Name") - settings = xbmcaddon.Addon() - show_x_filtered_items = settings.getSetting("show_x_filtered_items") + item_limit = settings.getSetting("show_x_filtered_items") group_movies = settings.getSetting('group_movies') == "true" hide_watched = settings.getSetting("hide_watched") == "true" @@ -830,11 +824,11 @@ def display_movies_type(menu_params, view): base_params["GroupItemsIntoCollections"] = str(group_movies) base_params["Recursive"] = True base_params["IsMissing"] = False - base_params["Fields"] = "{field_filters}" + base_params["Fields"] = get_default_filters() base_params["ImageTypeLimit"] = 1 # All Movies - path = get_jellyfin_url("{server}/Users/{userid}/Items", base_params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), base_params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies" add_menu_directory_item('{}{}'.format(view_name, translate_string(30405)), url) @@ -844,7 +838,7 @@ def display_movies_type(menu_params, view): params["CollapseBoxSetItems"] = False params["GroupItemsIntoCollections"] = False params["Filters"] = "IsFavorite" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies" add_menu_directory_item('{}{}'.format(view_name, translate_string(30414)), url) @@ -854,7 +848,7 @@ def display_movies_type(menu_params, view): params["CollapseBoxSetItems"] = False params["GroupItemsIntoCollections"] = False params["IsPlayed"] = False - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies" add_menu_directory_item('{}{}'.format(view_name, translate_string(30285)), url) @@ -866,10 +860,10 @@ def display_movies_type(menu_params, view): params["SortOrder"] = "Descending" params["CollapseBoxSetItems"] = False params["GroupItemsIntoCollections"] = False - params["Limit"] = "{ItemLimit}" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + params["Limit"] = item_limit + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies&sort=none" - add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30349), show_x_filtered_items), url) + add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30349), item_limit), url) # Resumable Movies params = {} @@ -877,10 +871,10 @@ def display_movies_type(menu_params, view): params["Filters"] = "IsResumable" params["SortBy"] = "DatePlayed" params["SortOrder"] = "Descending" - params["Limit"] = "{ItemLimit}" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + params["Limit"] = item_limit + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies&sort=none" - add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30267), show_x_filtered_items), url) + add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30267), item_limit), url) # Recently Added Movies params = {} @@ -890,25 +884,25 @@ def display_movies_type(menu_params, view): params["SortBy"] = "DateCreated" params["SortOrder"] = "Descending" params["Filters"] = "IsNotFolder" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=movies&sort=none" - add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30268), show_x_filtered_items), url) + add_menu_directory_item('{}{} ({})'.format(view_name, translate_string(30268), item_limit), url) # Collections params = {} if view is not None: params["ParentId"] = view.get("Id") - params["Fields"] = "{field_filters}" + params["Fields"] = get_default_filters() params["ImageTypeLimit"] = 1 params["IncludeItemTypes"] = "Boxset" params["Recursive"] = True - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=boxsets" add_menu_directory_item('{}{}'.format(view_name, translate_string(30410)), url) # Favorite Collections params["Filters"] = "IsFavorite" - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=boxsets" add_menu_directory_item('{}{}'.format(view_name, translate_string(30415)), url) @@ -955,12 +949,12 @@ def display_library_views(params): handle = int(sys.argv[1]) xbmcplugin.setContent(handle, 'files') - server = downloadUtils.get_server() + server = settings.getSetting('server_address') if server is None: return data_manager = DataManager() - views_url = "{server}/Users/{userid}/Views?format=json" + views_url = "/Users/{}/Views?format=json".format(user_id) views = data_manager.get_content(views_url) if not views: return [] @@ -974,7 +968,7 @@ def display_library_views(params): if collection_type in view_types or item_type == "Channel": view_name = view.get("Name") art = get_art(item=view, server=server) - art['landscape'] = downloadUtils.get_artwork(view, "Primary", server=server) + art['landscape'] = get_art_url(view, "Primary", server=server) plugin_path = "plugin://plugin.video.jellycon/?mode=SHOW_ADDON_MENU&type=library_item&view_id=" + view.get("Id") @@ -993,10 +987,10 @@ def display_library_views(params): def get_playlist_path(view_info): params = {} params["ParentId"] = view_info.get("Id") - params["Fields"] = "{field_filters}" + params["Fields"] = get_default_filters() params["ImageTypeLimit"] = 1 - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=playlists" return url @@ -1004,7 +998,7 @@ def get_playlist_path(view_info): def get_collection_path(view_info): params = {} params["ParentId"] = view_info.get("Id") - params["Fields"] = "{field_filters}" + params["Fields"] = get_default_filters() params["ImageTypeLimit"] = 1 params["IncludeItemTypes"] = "Boxset" params["CollapseBoxSetItems"] = True @@ -1012,7 +1006,7 @@ def get_collection_path(view_info): params["Recursive"] = True params["IsMissing"] = False - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=boxsets" return url @@ -1022,9 +1016,9 @@ def get_channel_path(view): params["ParentId"] = view.get("Id") params["IsMissing"] = False params["ImageTypeLimit"] = 1 - params["Fields"] = "{field_filters}" + params["Fields"] = get_default_filters() - path = get_jellyfin_url("{server}/Users/{userid}/Items", params) + path = get_jellyfin_url("/Users/{}/Items".format(user_id), params) url = sys.argv[0] + "?url=" + quote(path) + "&mode=GET_CONTENT&media_type=files" return url @@ -1032,7 +1026,7 @@ def get_channel_path(view): def display_library_view(params): node_id = params.get("view_id") - view_info_url = "{server}/Users/{userid}/Items/" + node_id + view_info_url = "/Users/{}/Items/".format(user_id) + node_id data_manager = DataManager() view_info = data_manager.get_content(view_info_url) @@ -1055,28 +1049,27 @@ def display_library_view(params): def show_widgets(): - settings = xbmcaddon.Addon() - show_x_filtered_items = settings.getSetting("show_x_filtered_items") + item_limit = settings.getSetting("show_x_filtered_items") add_menu_directory_item("All Movies", 'plugin://plugin.video.jellycon/library/movies') - add_menu_directory_item(translate_string(30257) + " (" + show_x_filtered_items + ")", + add_menu_directory_item(translate_string(30257) + " (" + item_limit + ")", 'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=recent_movies') - add_menu_directory_item(translate_string(30258) + " (" + show_x_filtered_items + ")", + add_menu_directory_item(translate_string(30258) + " (" + item_limit + ")", 'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=inprogress_movies') - add_menu_directory_item(translate_string(30269) + " (" + show_x_filtered_items + ")", + add_menu_directory_item(translate_string(30269) + " (" + item_limit + ")", 'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=random_movies') - add_menu_directory_item(translate_string(30403) + " (" + show_x_filtered_items + ")", + add_menu_directory_item(translate_string(30403) + " (" + item_limit + ")", 'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=movie_recommendations') - add_menu_directory_item(translate_string(30287) + " (" + show_x_filtered_items + ")", + add_menu_directory_item(translate_string(30287) + " (" + item_limit + ")", 'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=recent_tvshows') - add_menu_directory_item(translate_string(30263) + " (" + show_x_filtered_items + ")", + add_menu_directory_item(translate_string(30263) + " (" + item_limit + ")", 'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=recent_episodes') - add_menu_directory_item(translate_string(30264) + " (" + show_x_filtered_items + ")", + add_menu_directory_item(translate_string(30264) + " (" + item_limit + ")", 'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=inprogress_episodes') - add_menu_directory_item(translate_string(30265) + " (" + show_x_filtered_items + ")", + add_menu_directory_item(translate_string(30265) + " (" + item_limit + ")", 'plugin://plugin.video.jellycon/?mode=WIDGET_CONTENT&type=nextup_episodes') xbmcplugin.endOfDirectory(int(sys.argv[1])) @@ -1108,14 +1101,14 @@ def set_library_window_values(force=False): home_window.clear_property("view_item.%i.thumb" % index) data_manager = DataManager() - url = "{server}/Users/{userid}/Views" + url = "/Users/{}/Views".format(user_id) result = data_manager.get_content(url) if result is None: return result = result.get("Items") - server = downloadUtils.get_server() + server = settings.getSetting('server_address') index = 0 for item in result: @@ -1138,7 +1131,7 @@ def set_library_window_values(force=False): home_window.set_property(prop_name, collection_type) log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}".format(prop_name, collection_type)) - thumb = downloadUtils.get_artwork(item, "Primary", server=server) + thumb = get_art_url(item, "Primary", server=server) prop_name = "view_item.%i.thumb" % index home_window.set_property(prop_name, thumb) log.debug("set_library_window_values: plugin.video.jellycon-{0}={1}".format(prop_name, thumb)) diff --git a/resources/lib/play_utils.py b/resources/lib/play_utils.py index 43c72b2..1639d55 100644 --- a/resources/lib/play_utils.py +++ b/resources/lib/play_utils.py @@ -12,10 +12,10 @@ import os import re from six.moves.urllib.parse import urlencode +from .api import API from .loghandler import LazyLogger -from .downloadutils import DownloadUtils from .dialogs import ResumeDialog -from .utils import send_event_notification, convert_size, get_device_id, translate_string +from .utils import send_event_notification, convert_size, get_device_id, translate_string, load_user_details from .kodi_utils import HomeWindow from .datamanager import DataManager, clear_old_cache_data from .item_functions import extract_item_info, add_gui_item, get_art @@ -25,13 +25,20 @@ from .tracking import timer from .playnext import PlayNextDialog log = LazyLogger(__name__) -download_utils = DownloadUtils() +user_details = load_user_details() +settings = xbmcaddon.Addon() + +api = API( + settings.getSetting('server_address'), + user_details.get('user_id'), + user_details.get('token') +) def play_all_files(items, play_items=True): home_window = HomeWindow() log.debug("playAllFiles called with items: {0}", items) - server = download_utils.get_server() + server = settings.getSetting('server_address') playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) playlist.clear() @@ -43,7 +50,7 @@ def play_all_files(items, play_items=True): item_id = item.get("Id") # get playback info - playback_info = download_utils.get_item_playback_info(item_id, False) + playback_info = get_item_playback_info(item_id, False) if playback_info is None: log.debug("playback_info was None, could not get MediaSources so can not play!") return @@ -115,8 +122,7 @@ def play_list_of_items(id_list): items = [] for item_id in id_list: - url = "{server}/Users/{userid}/Items/%s?format=json" - url = url % (item_id,) + url = "/Users/{}/Items/{}?format=json".format(api.user_id, item_id) result = data_manager.get_content(url) if result is None: log.debug("Playfile item was None, so can not play!") @@ -130,12 +136,11 @@ def add_to_playlist(play_info): log.debug("Adding item to playlist : {0}".format(play_info)) playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) - server = download_utils.get_server() + server = settings.getSetting('server_address') item_id = play_info.get("item_id") - url = "{server}/Users/{userid}/Items/%s?format=json" - url = url % (item_id,) + url = "/Users/{}/Items/{}?format=json".format(api.user_id, item_id) data_manager = DataManager() item = data_manager.get_content(url) if item is None: @@ -143,7 +148,7 @@ def add_to_playlist(play_info): return # get playback info - playback_info = download_utils.get_item_playback_info(item_id, False) + playback_info = get_item_playback_info(item_id, False) if playback_info is None: log.debug("playback_info was None, could not get MediaSources so can not play!") return @@ -201,7 +206,7 @@ def add_to_playlist(play_info): def get_playback_intros(item_id): log.debug("get_playback_intros") data_manager = DataManager() - url = "{server}/Users/{userid}/Items/%s/Intros" % item_id + url = "/Users/{}/Items/{}/Intros".format(api.user_id, item_id) intro_items = data_manager.get_content(url) if intro_items is None: @@ -242,15 +247,14 @@ def play_file(play_info): log.debug("playFile id({0}) resume({1}) force_transcode({2})".format(item_id, auto_resume, force_transcode)) - settings = xbmcaddon.Addon() addon_path = settings.getAddonInfo('path') force_auto_resume = settings.getSetting('forceAutoResume') == 'true' jump_back_amount = int(settings.getSetting("jump_back_amount")) play_cinema_intros = settings.getSetting('play_cinema_intros') == 'true' - server = download_utils.get_server() + server = settings.getSetting('server_address') - url = "{server}/Users/{userid}/Items/%s?format=json" % (item_id,) + url = "/Users/{}/Items/{}?format=json".format(api.user_id, item_id) data_manager = DataManager() result = data_manager.get_content(url) log.debug("Playfile item: {0}".format(result)) @@ -262,7 +266,7 @@ def play_file(play_info): # if this is a season, playlist or album then play all items in that parent if result.get("Type") in ["Season", "MusicAlbum", "Playlist"]: log.debug("PlayAllFiles for parent item id: {0}".format(item_id)) - url = ('{server}/Users/{userid}/items' + + url = ('/Users/{}/items'.format(api.user_id) + '?ParentId=%s' + '&Fields=MediaSources' + '&format=json') @@ -279,7 +283,7 @@ def play_file(play_info): # if this is a program from live tv epg then play the actual channel if result.get("Type") == "Program": channel_id = result.get("ChannelId") - url = "{server}/Users/{userid}/Items/%s?format=json" % (channel_id,) + url = "/Users/{}/Items/{}?format=json".format(api.user_id, channel_id) result = data_manager.get_content(url) item_id = result["Id"] @@ -294,7 +298,7 @@ def play_file(play_info): return # get playback info from the server using the device profile - playback_info = download_utils.get_item_playback_info(item_id, force_transcode) + playback_info = get_item_playback_info(item_id, force_transcode) if playback_info is None: log.debug("playback_info was None, could not get MediaSources so can not play!") return @@ -546,7 +550,7 @@ def get_next_episode(item): log.debug("No episode number, can not get next") return None - url = ('{server}/Users/{userid}/Items?' + + url = ('/Users/{}/Items?'.format(api.user_id) + '?Recursive=true' + '&ParentId=' + parent_id + '&IsVirtualUnaired=false' + @@ -580,7 +584,7 @@ def send_next_episode_details(item, next_episode): return gui_options = {} - gui_options["server"] = download_utils.get_server() + gui_options["server"] = settings.getSetting('server_address') gui_options["name_format"] = None gui_options["name_format_type"] = "" @@ -776,7 +780,7 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s # Load subtitles in the listitem if downloadable if select_subs_index in downloadable_streams: subtitle_url = "%s/Videos/%s/%s/Subtitles/%s/Stream.srt" - subtitle_url = subtitle_url % (download_utils.get_server(), item_id, source_id, select_subs_index) + subtitle_url = subtitle_url % (settings.getSetting('server_address'), item_id, source_id, select_subs_index) log.debug("Streaming subtitles url: {0} {1}".format(select_subs_index, subtitle_url)) list_item.setSubtitles([subtitle_url]) else: @@ -796,7 +800,7 @@ def audio_subs_pref(url, list_item, media_source, item_id, audio_stream_index, s # Load subtitles in the listitem if downloadable if select_subs_index in downloadable_streams: subtitle_url = "%s/Videos/%s/%s/Subtitles/%s/Stream.srt" - subtitle_url = subtitle_url % (download_utils.get_server(), item_id, source_id, select_subs_index) + subtitle_url = subtitle_url % (settings.getSetting('server_address'), item_id, source_id, select_subs_index) log.debug("Streaming subtitles url: {0} {1}".format(select_subs_index, subtitle_url)) list_item.setSubtitles([subtitle_url]) else: @@ -824,6 +828,10 @@ def external_subs(media_source, list_item, item_id): externalsubs = [] sub_names = [] + server = settings.getSetting('server_address') + user_details = load_user_details() + token = user_details.get('token') + for stream in media_streams: if (stream['Type'] == "Subtitle" @@ -833,8 +841,7 @@ def external_subs(media_source, list_item, item_id): index = stream['Index'] source_id = media_source['Id'] - server = download_utils.get_server() - token = download_utils.authenticate() + language = stream.get('Language', '') codec = stream.get('Codec', '') @@ -860,7 +867,6 @@ def external_subs(media_source, list_item, item_id): if len(externalsubs) == 0: return - settings = xbmcaddon.Addon() direct_stream_sub_select = settings.getSetting("direct_stream_sub_select") if direct_stream_sub_select == "0" or (len(externalsubs) == 1 and not direct_stream_sub_select == "2"): @@ -928,8 +934,8 @@ def send_progress(): log.debug("Sending POST progress started: {0}".format(postdata)) - url = "{server}/Sessions/Playing/Progress" - download_utils.download_url(url, post_body=postdata, method="POST") + url = "/Sessions/Playing/Progress" + api.post(url, postdata) def get_volume(): @@ -946,7 +952,6 @@ def get_volume(): def prompt_for_stop_actions(item_id, data): log.debug("prompt_for_stop_actions Called : {0}".format(data)) - settings = xbmcaddon.Addon() current_position = data.get("current_position", 0) duration = data.get("duration", 0) next_episode = data.get("next_episode") @@ -1040,7 +1045,7 @@ def stop_all_playback(): if jellyfin_item_id is not None and current_position >= 0: log.debug("Playback Stopped at: {0}".format(current_position)) - url = "{server}/Sessions/Playing/Stopped" + url = "/Sessions/Playing/Stopped" postdata = { 'ItemId': jellyfin_item_id, 'MediaSourceId': jellyfin_source_id, @@ -1048,7 +1053,7 @@ def stop_all_playback(): 'RunTimeTicks': int(duration * 10000000), 'PlaySessionId': play_session_id } - download_utils.download_url(url, post_body=postdata, method="POST") + api.post(url, postdata) data["currently_playing"] = False if data.get("play_action_type", "") == "play": @@ -1058,8 +1063,8 @@ def stop_all_playback(): if data.get('playback_type') == 'Transcode': device_id = get_device_id() - url = "{server}/Videos/ActiveEncodings?DeviceId=%s" % device_id - download_utils.download_url(url, method="DELETE") + url = "/Videos/ActiveEncodings?DeviceId=%s" % device_id + api.delete(url) for entry in clear_entries: del played_information[entry] @@ -1087,7 +1092,6 @@ def get_playing_data(): item_id = play_data.get("item_id") - settings = xbmcaddon.Addon() server = settings.getSetting('server_address') try: playing_file = player.getPlayingFile() @@ -1129,11 +1133,10 @@ def get_play_url(media_source, play_session_id): return playurl, "0", listitem_props # get all the options - addon_settings = xbmcaddon.Addon() - server = download_utils.get_server() - use_https = addon_settings.getSetting('protocol') == "1" - verify_cert = addon_settings.getSetting('verify_cert') == 'true' - allow_direct_file_play = addon_settings.getSetting('allow_direct_file_play') == 'true' + server = settings.getSetting('server_address') + use_https = settings.getSetting('protocol') == "1" + verify_cert = settings.getSetting('verify_cert') == 'true' + allow_direct_file_play = settings.getSetting('allow_direct_file_play') == 'true' can_direct_play = media_source["SupportsDirectPlay"] can_direct_stream = media_source["SupportsDirectStream"] @@ -1180,15 +1183,16 @@ def get_play_url(media_source, play_session_id): if can_transcode and playurl is None: item_id = media_source.get('Id') device_id = get_device_id() - user_token = download_utils.authenticate() - playback_bitrate = addon_settings.getSetting("force_max_stream_bitrate") + + user_token = user_details.get('token') + playback_bitrate = settings.getSetting("force_max_stream_bitrate") bitrate = int(playback_bitrate) * 1000 - playback_max_width = addon_settings.getSetting("playback_max_width") - audio_codec = addon_settings.getSetting("audio_codec") - audio_playback_bitrate = addon_settings.getSetting("audio_playback_bitrate") + playback_max_width = settings.getSetting("playback_max_width") + audio_codec = settings.getSetting("audio_codec") + audio_playback_bitrate = settings.getSetting("audio_playback_bitrate") audio_bitrate = int(audio_playback_bitrate) * 1000 - audio_max_channels = addon_settings.getSetting("audio_max_channels") - playback_video_force_8 = addon_settings.getSetting("playback_video_force_8") == "true" + audio_max_channels = settings.getSetting("audio_max_channels") + playback_video_force_8 = settings.getSetting("playback_video_force_8") == "true" transcode_params = { "MediaSourceId": item_id, @@ -1302,8 +1306,8 @@ class Service(xbmc.Player): log.debug("Sending POST play started: {0}".format(postdata)) - url = "{server}/Sessions/Playing" - download_utils.download_url(url, post_body=postdata, method="POST") + url = "/Sessions/Playing" + api.post(url, postdata) home_screen = HomeWindow() home_screen.set_property("currently_playing_id", str(jellyfin_item_id)) @@ -1389,7 +1393,6 @@ class PlaybackService(xbmc.Monitor): home_screen = HomeWindow() home_screen.clear_property("skip_select_user") - settings = xbmcaddon.Addon() stop_playback = settings.getSetting("stopPlaybackOnScreensaver") == 'true' if stop_playback: @@ -1415,7 +1418,6 @@ class PlaybackService(xbmc.Monitor): self.background_image_cache_thread.stop_activity() self.background_image_cache_thread = None - settings = xbmcaddon.Addon() show_change_user = settings.getSetting('changeUserOnScreenSaver') == 'true' if show_change_user: home_screen = HomeWindow() @@ -1423,3 +1425,188 @@ class PlaybackService(xbmc.Monitor): if skip_select_user is not None and skip_select_user == "true": return xbmc.executebuiltin("RunScript(plugin.video.jellycon,0,?mode=CHANGE_USER)") + + + +def get_item_playback_info(item_id, force_transcode): + + filtered_codecs = [] + if settings.getSetting("force_transcode_h265") == "true": + filtered_codecs.append("hevc") + filtered_codecs.append("h265") + if settings.getSetting("force_transcode_mpeg2") == "true": + filtered_codecs.append("mpeg2video") + if settings.getSetting("force_transcode_msmpeg4v3") == "true": + filtered_codecs.append("msmpeg4v3") + if settings.getSetting("force_transcode_mpeg4") == "true": + filtered_codecs.append("mpeg4") + + playback_bitrate = settings.getSetting("max_stream_bitrate") + force_playback_bitrate = settings.getSetting("force_max_stream_bitrate") + if force_transcode: + playback_bitrate = force_playback_bitrate + + audio_codec = settings.getSetting("audio_codec") + audio_playback_bitrate = settings.getSetting("audio_playback_bitrate") + audio_max_channels = settings.getSetting("audio_max_channels") + + audio_bitrate = int(audio_playback_bitrate) * 1000 + bitrate = int(playback_bitrate) * 1000 + + profile = { + "Name": "Kodi", + "MaxStaticBitrate": bitrate, + "MaxStreamingBitrate": bitrate, + "MusicStreamingTranscodingBitrate": audio_bitrate, + "TimelineOffsetSeconds": 5, + "TranscodingProfiles": [ + { + "Type": "Audio" + }, + { + "Container": "ts", + "Protocol": "hls", + "Type": "Video", + "AudioCodec": audio_codec, + "VideoCodec": "h264", + "MaxAudioChannels": audio_max_channels + }, + { + "Container": "jpeg", + "Type": "Photo" + } + ], + "DirectPlayProfiles": [ + { + "Type": "Video" + }, + { + "Type": "Audio" + }, + { + "Type": "Photo" + } + ], + "ResponseProfiles": [], + "ContainerProfiles": [], + "CodecProfiles": [], + "SubtitleProfiles": [ + { + "Format": "srt", + "Method": "External" + }, + { + "Format": "srt", + "Method": "Embed" + }, + { + "Format": "ass", + "Method": "External" + }, + { + "Format": "ass", + "Method": "Embed" + }, + { + "Format": "sub", + "Method": "Embed" + }, + { + "Format": "sub", + "Method": "External" + }, + { + "Format": "ssa", + "Method": "Embed" + }, + { + "Format": "ssa", + "Method": "External" + }, + { + "Format": "smi", + "Method": "Embed" + }, + { + "Format": "smi", + "Method": "External" + }, + { + "Format": "pgssub", + "Method": "Embed" + }, + { + "Format": "pgssub", + "Method": "External" + }, + { + "Format": "dvdsub", + "Method": "Embed" + }, + { + "Format": "dvdsub", + "Method": "External" + }, + { + "Format": "pgs", + "Method": "Embed" + }, + { + "Format": "pgs", + "Method": "External" + } + ] + } + + if len(filtered_codecs) > 0: + profile['DirectPlayProfiles'][0]['VideoCodec'] = "-%s" % ",".join(filtered_codecs) + + if force_transcode: + profile['DirectPlayProfiles'] = [] + + if settings.getSetting("playback_video_force_8") == "true": + profile['CodecProfiles'].append( + { + "Type": "Video", + "Codec": "h264", + "Conditions": [ + { + "Condition": "LessThanEqual", + "Property": "VideoBitDepth", + "Value": "8", + "IsRequired": False + } + ] + } + ) + profile['CodecProfiles'].append( + { + "Type": "Video", + "Codec": "h265,hevc", + "Conditions": [ + { + "Condition": "EqualsAny", + "Property": "VideoProfile", + "Value": "main" + } + ] + } + ) + + playback_info = { + 'UserId': api.user_id, + 'DeviceProfile': profile, + 'AutoOpenLiveStream': True + } + + if force_transcode: + url = "/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s&EnableDirectPlay=false&EnableDirectStream=false" % (item_id, bitrate) + else: + url = "/Items/%s/PlaybackInfo?MaxStreamingBitrate=%s" % (item_id, bitrate) + + log.debug("PlaybackInfo : {0}".format(url)) + log.debug("PlaybackInfo : {0}".format(profile)) + play_info_result = api.post(url, playback_info) + log.debug("PlaybackInfo : {0}".format(play_info_result)) + + return play_info_result diff --git a/resources/lib/server_detect.py b/resources/lib/server_detect.py index 62f952a..e62b8d5 100644 --- a/resources/lib/server_detect.py +++ b/resources/lib/server_detect.py @@ -3,7 +3,6 @@ from __future__ import division, absolute_import, print_function, unicode_litera import socket import json -import requests import time from datetime import datetime @@ -12,9 +11,9 @@ import xbmcgui import xbmc from .kodi_utils import HomeWindow -from .downloadutils import DownloadUtils +from .api import API from .loghandler import LazyLogger -from .utils import datetime_from_string, translate_string, get_version, load_user_details +from .utils import datetime_from_string, translate_string, save_user_details, load_user_details log = LazyLogger(__name__) @@ -26,26 +25,15 @@ def check_connection_speed(): log.debug("check_connection_speed") settings = xbmcaddon.Addon() - verify_cert = settings.getSetting('verify_cert') == 'true' - http_timeout = int(settings.getSetting("http_timeout")) speed_test_data_size = int(settings.getSetting("speed_test_data_size")) test_data_size = speed_test_data_size * 1000000 + user_details = load_user_details() - du = DownloadUtils() - server = du.get_server() - - url = server + "/playback/bitratetest?size=%s" % test_data_size - - head = du.get_auth_header(True) - head["User-Agent"] = "JellyCon-{}".format(get_version()) - - request_details = { - "stream": True, - "headers": head - } - - if not verify_cert: - request_details["verify"] = False + api = API( + settings.getSetting('server_address'), + user_details.get('user_id'), + user_details.get('token') + ) progress_dialog = xbmcgui.DialogProgress() message = 'Testing with {0} MB of data'.format(speed_test_data_size) @@ -53,8 +41,7 @@ def check_connection_speed(): start_time = time.time() log.debug("Starting Connection Speed Test") - - response = requests.get(url, **request_details) + response = api.speedtest(test_data_size) last_percentage_done = 0 total_data_read = 0 @@ -90,31 +77,6 @@ def check_connection_speed(): return speed -def check_safe_delete_available(): - log.debug("check_safe_delete_available") - - du = DownloadUtils() - result = du.download_url("{server}/Plugins") - if result: - log.debug("Server Plugin List: {0}".format(result)) - - safe_delete_found = False - for plugin in result: - if plugin["Name"] == "Safe Delete": - safe_delete_found = True - break - - log.debug("Safe Delete Plugin Available: {0}".format(safe_delete_found)) - home_window = HomeWindow() - if safe_delete_found: - home_window.set_property("safe_delete_plugin_available", "true") - else: - home_window.clear_property("safe_delete_plugin_available") - - else: - log.debug("Error getting server plugin list") - - def get_server_details(): log.debug("Getting Server Details from Network") servers = [] @@ -160,18 +122,17 @@ def check_server(force=False, change_user=False, notify=False): log.debug("checkServer Called") settings = xbmcaddon.Addon() - server_url = "" something_changed = False - du = DownloadUtils() + + # Initialize api object + api = API() if force is False: # if not forcing use server details from settings - svr = du.get_server() - if svr is not None: - server_url = svr + api.server = settings.getSetting('server_address') # if the server is not set then try to detect it - if server_url == "": + if not api.server: # scan for local server server_info = get_server_details() @@ -194,9 +155,9 @@ def check_server(force=False, change_user=False, notify=False): server_list, useDetails=True) if return_index != -1: - server_url = server_info[return_index]["Address"] + api.server = server_info[return_index]["Address"] - if not server_url: + if not api.server: return_index = xbmcgui.Dialog().yesno(__addon_name__, '{}\n{}'.format(translate_string(30282), translate_string(30370))) if not return_index: xbmc.executebuiltin("ActivateWindow(Home)") @@ -205,40 +166,38 @@ def check_server(force=False, change_user=False, notify=False): while True: kb = xbmc.Keyboard() kb.setHeading(translate_string(30372)) - if server_url: - kb.setDefault(server_url) + if api.server: + kb.setDefault(api.server) else: kb.setDefault("http://") kb.doModal() if kb.isConfirmed(): - server_url = kb.getText() + api.server = kb.getText() else: xbmc.executebuiltin("ActivateWindow(Home)") return - public_lookup_url = "%s/System/Info/Public?format=json" % (server_url) - - log.debug("Testing_Url: {0}".format(public_lookup_url)) progress = xbmcgui.DialogProgress() progress.create('{} : {}'.format(__addon_name__, translate_string(30376))) progress.update(0, translate_string(30377)) - result = du.download_url(public_lookup_url, authenticate=False) + api.server = api.server + result = api.get('/System/Info/Public') progress.close() if result: xbmcgui.Dialog().ok('{} : {}'.format(__addon_name__, translate_string(30167)), - server_url) + api.server) break else: return_index = xbmcgui.Dialog().yesno('{} : {}'.format(__addon_name__, translate_string(30135)), - server_url, + api.server, translate_string(30371)) if not return_index: xbmc.executebuiltin("ActivateWindow(Home)") return - log.debug("Selected server: {0}".format(server_url)) - settings.setSetting("server_address", server_url) + log.debug("Selected server: {0}".format(api.server)) + settings.setSetting("server_address", api.server) something_changed = True # do we need to change the user @@ -250,7 +209,7 @@ def check_server(force=False, change_user=False, notify=False): # stop playback when switching users xbmc.Player().stop() - users, user_selection = user_select(server_url, current_username) + users, user_selection = user_select(api, current_username) if user_selection > -1: @@ -288,33 +247,32 @@ def check_server(force=False, change_user=False, notify=False): if kb.isConfirmed(): password = kb.getText() - # TODO: Make the authenticate function operate normally. Network rework - home_window.set_property('password', password) - if something_changed: home_window = HomeWindow() home_window.clear_property("jellycon_widget_reload") - du.authenticate() + auth_payload = {'username': selected_user_name, 'pw': password} + user = api.authenticate(auth_payload) + token = user.get('AccessToken') + user_id = user.get('User').get('Id') + save_user_details(selected_user_name, user_id, token) xbmc.executebuiltin("ActivateWindow(Home)") if "estuary_jellycon" in xbmc.getSkinDir(): xbmc.executebuiltin("SetFocus(9000, 0, absolute)") xbmc.executebuiltin("ReloadSkin()") -def user_select(server_url, current_username): +def user_select(api, current_username): ''' Display user selection screen ''' - du = DownloadUtils() - # Retrieve list of public users from server - result = du.download_url('{}/Users/Public'.format(server_url), authenticate=False) + result = api.get('/Users/Public') # Build user display selected_id = -1 users = [] for user in result: - user_item = create_user_listitem(du, user) + user_item = create_user_listitem(api.server, user) if user_item: users.append(user_item) name = user.get("Name") @@ -346,7 +304,7 @@ def user_select(server_url, current_username): return (users, user_selection) -def create_user_listitem(du, user): +def create_user_listitem(server, user): ''' Create a user listitem for the user selection screen ''' @@ -378,9 +336,17 @@ def create_user_listitem(du, user): time_ago = "Active: {} ago".format(time_ago) user_item = xbmcgui.ListItem(name) - user_image = du.get_user_artwork(user, 'Primary') - if not user_image: + + # If the user doesn't have a profile image, user the default + if 'PrimaryImageTag' not in user: user_image = "DefaultUser.png" + else: + user_id = user.get('Id') + tag = user.get('PrimaryImageTag') + user_image = '{}/Users/{}/Images/Primary?Format=original&tag={}'.format( + server, user_id, tag + ) + art = {"Thumb": user_image} user_item.setArt(art) diff --git a/resources/lib/server_sessions.py b/resources/lib/server_sessions.py index 655578f..c48c572 100644 --- a/resources/lib/server_sessions.py +++ b/resources/lib/server_sessions.py @@ -4,7 +4,6 @@ import sys import xbmcgui import xbmcplugin -from .downloadutils import DownloadUtils from .loghandler import LazyLogger from .item_functions import get_art from .datamanager import DataManager @@ -16,10 +15,9 @@ def show_server_sessions(): log.debug("showServerSessions Called") handle = int(sys.argv[1]) - download_utils = DownloadUtils() data_manager = DataManager() - url = "{server}/Users/{userid}" + url = "/Users/{}".format(data_manager.api.user_id) results = data_manager.get_content(url) is_admin = results.get("Policy", {}).get("IsAdministrator", False) @@ -27,7 +25,7 @@ def show_server_sessions(): xbmcplugin.endOfDirectory(handle, cacheToDisc=False) return - url = "{server}/Sessions" + url = "/Sessions" results = data_manager.get_content(url) log.debug("session_info: {0}".format(results)) @@ -59,7 +57,7 @@ def show_server_sessions(): art = {} if now_playing: - server = download_utils.get_server() + server = settings.getSetting('server_address') art = get_art(now_playing, server) runtime = now_playing.get("RunTimeTicks", 0) diff --git a/resources/lib/utils.py b/resources/lib/utils.py index b872ac4..c05d0e5 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -29,13 +29,10 @@ throwaway = time.strptime('20110101', '%Y%m%d') log = LazyLogger(__name__) -def get_jellyfin_url(base_url, params): +def get_jellyfin_url(path, params): params["format"] = "json" url_params = urlencode(params) - # Filthy hack until I get around to reworking the network flow - # It relies on {thing} strings in downloadutils.py - url_params = url_params.replace('%7B', '{').replace('%7D', '}') - return base_url + "?" + url_params + return '{}?{}'.format(path, url_params) def get_checksum(item): @@ -210,3 +207,111 @@ def load_user_details(): else: return {} + + +def get_art_url(data, art_type, parent=False, index=0, server=None): + + item_id = data["Id"] + item_type = data["Type"] + + if item_type in ["Episode", "Season"]: + if art_type != "Primary" or parent is True: + item_id = data["SeriesId"] + + image_tag = "" + + # for episodes always use the parent BG + if item_type == "Episode" and art_type == "Backdrop": + item_id = data.get("ParentBackdropItemId") + bg_item_tags = data.get("ParentBackdropImageTags", []) + if bg_item_tags: + image_tag = bg_item_tags[0] + elif art_type == "Backdrop" and parent is True: + item_id = data.get("ParentBackdropItemId") + bg_item_tags = data.get("ParentBackdropImageTags", []) + if bg_item_tags: + image_tag = bg_item_tags[0] + elif art_type == "Backdrop": + bg_tags = data.get("BackdropImageTags", []) + if bg_tags: + image_tag = bg_tags[index] + elif parent is False: + image_tags = data.get("ImageTags", []) + if image_tags: + image_tag_type = image_tags.get(art_type) + if image_tag_type: + image_tag = image_tag_type + elif parent is True: + if (item_type == "Episode" or item_type == "Season") and art_type == 'Primary': + tag_name = 'SeriesPrimaryImageTag' + id_name = 'SeriesId' + else: + tag_name = 'Parent%sImageTag' % art_type + id_name = 'Parent%sItemId' % art_type + parent_image_id = data.get(id_name) + parent_image_tag = data.get(tag_name) + if parent_image_id is not None and parent_image_tag is not None: + item_id = parent_image_id + image_tag = parent_image_tag + + # ParentTag not passed for Banner and Art + if not image_tag and not ((art_type == 'Banner' or art_type == 'Art') and parent is True): + return "" + + artwork = "{}/Items/{}/Images/{}/{}?Format=original&Tag={}".format( + server, item_id, art_type, index, image_tag) + return artwork + + +def image_url(item_id, art_type, index, width, height, image_tag, server): + + # test imageTag e3ab56fe27d389446754d0fb04910a34 + artwork = "{}/Items/{}/Images/{}/{}?Format=original&Tag={}".format(server, item_id, art_type, index, image_tag) + if int(width) > 0: + artwork += '&MaxWidth={}'.format(width) + if int(height) > 0: + artwork += '&MaxHeight={}'.format(height) + + return artwork + + +def get_default_filters(): + + addon_settings = xbmcaddon.Addon() + include_media = addon_settings.getSetting("include_media") == "true" + include_people = addon_settings.getSetting("include_people") == "true" + include_overview = addon_settings.getSetting("include_overview") == "true" + + filer_list = [ + "DateCreated", + "EpisodeCount", + "SeasonCount", + "Path", + "Genres", + "Studios", + "Etag", + "Taglines", + "SortName", + "RecursiveItemCount", + "ChildCount", + "ProductionLocations", + "CriticRating", + "OfficialRating", + "CommunityRating", + "PremiereDate", + "ProductionYear", + "AirTime", + "Status", + "Tags" + ] + + if include_media: + filer_list.append("MediaStreams") + + if include_people: + filer_list.append("People") + + if include_overview: + filer_list.append("Overview") + + return ','.join(filer_list) diff --git a/resources/lib/websocket_client.py b/resources/lib/websocket_client.py index ec3ba54..8394b89 100644 --- a/resources/lib/websocket_client.py +++ b/resources/lib/websocket_client.py @@ -9,14 +9,14 @@ import websocket import time import xbmc +import xbmcaddon import xbmcgui from .functions import play_action from .loghandler import LazyLogger -from . import downloadutils from .jsonrpc import JsonRpc from .kodi_utils import HomeWindow -from .utils import get_device_id +from .utils import get_device_id, load_user_details log = LazyLogger(__name__) @@ -237,16 +237,16 @@ class WebSocketClient(threading.Thread): def run(self): - download_utils = downloadutils.DownloadUtils() - token = None while token is None or token == "": - token = download_utils.authenticate() + user_details = load_user_details() + token = user_details.get('token') if self.monitor.waitForAbort(10): return # Get the appropriate prefix for the websocket - server = download_utils.get_server() + settings = xbmcaddon.Addon() + server = settings.getSetting('server_address') if "https://" in server: server = server.replace('https://', 'wss://') else: @@ -290,5 +290,12 @@ class WebSocketClient(threading.Thread): def post_capabilities(self): - download_utils = downloadutils.DownloadUtils() - download_utils.post_capabilities() + user_details = load_user_details() + + api = API( + settings.getSetting('server_address'), + user_details.get('user_id'), + user_details.get('token') + ) + + api.post_capabilities() diff --git a/resources/lib/widgets.py b/resources/lib/widgets.py index 5226bdb..9c5a828 100644 --- a/resources/lib/widgets.py +++ b/resources/lib/widgets.py @@ -8,8 +8,8 @@ import hashlib import random import time -from .downloadutils import DownloadUtils -from .utils import get_jellyfin_url +from .api import API +from .utils import get_jellyfin_url, image_url, load_user_details, get_art_url, get_default_filters from .datamanager import DataManager from .loghandler import LazyLogger from .kodi_utils import HomeWindow @@ -17,7 +17,14 @@ from .dir_functions import process_directory from .tracking import timer log = LazyLogger(__name__) -downloadUtils = DownloadUtils() +settings = xbmcaddon.Addon() +user_details = load_user_details() +user_id = user_details.get('user_id') +api = API( + settings.getSetting('server_address'), + user_id, + user_details.get('token') +) dataManager = DataManager() kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2]) @@ -30,20 +37,21 @@ def set_random_movies(): log.debug("set_random_movies Called") settings = xbmcaddon.Addon() + item_limit = settings.getSetting("show_x_filtered_items") hide_watched = settings.getSetting("hide_watched") == "true" url_params = {} url_params["Recursive"] = True - url_params["limit"] = 20 + url_params["limit"] = item_limit if hide_watched: url_params["IsPlayed"] = False url_params["SortBy"] = "Random" url_params["IncludeItemTypes"] = "Movie" url_params["ImageTypeLimit"] = 0 - url = get_jellyfin_url("{server}/Users/{userid}/Items", url_params) + url = get_jellyfin_url("/Users/{}/Items".format(user_id), url_params) - results = downloadUtils.download_url(url, suppress=True) + results = api.get(url) randon_movies_list = [] if results is not None: @@ -70,6 +78,8 @@ def set_background_image(force=False): global background_current_item global background_items + server = settings.getSetting('server_address') + if force: background_current_item = 0 del background_items @@ -86,17 +96,16 @@ def set_background_image(force=False): url_params["IncludeItemTypes"] = "Movie,Series" url_params["ImageTypeLimit"] = 1 - url = get_jellyfin_url('{server}/Users/{userid}/Items', url_params) + url = get_jellyfin_url('/Users/{}/Items'.format(user_id), url_params) - server = downloadUtils.get_server() - results = downloadUtils.download_url(url, suppress=True) + results = api.get(url) if results is not None: items = results.get("Items", []) background_current_item = 0 background_items = [] for item in items: - bg_image = downloadUtils.get_artwork( + bg_image = get_art_url( item, "Backdrop", server=server) if bg_image: label = item.get("Name") @@ -148,9 +157,9 @@ def check_for_new_content(): url_params["IncludeItemTypes"] = "Movie,Episode" url_params["ImageTypeLimit"] = 0 - added_url = get_jellyfin_url('{server}/Users/{userid}/Items', url_params) + added_url = get_jellyfin_url('/Users/{}/Items'.format(user_id), url_params) - result = downloadUtils.download_url(added_url, suppress=True) + result = api.get(added_url) log.debug("LATEST_ADDED_ITEM: {0}".format(result)) last_added_date = "" @@ -170,9 +179,9 @@ def check_for_new_content(): url_params["IncludeItemTypes"] = "Movie,Episode" url_params["ImageTypeLimit"] = 0 - played_url = get_jellyfin_url('{server}/Users/{userid}/Items', url_params) + played_url = get_jellyfin_url('/Users/{}/Items'.format(user_id), url_params) - result = downloadUtils.download_url(played_url, suppress=True) + result = api.get(played_url) log.debug("LATEST_PLAYED_ITEM: {0}".format(result)) last_played_date = "" @@ -202,12 +211,12 @@ def check_for_new_content(): @timer def get_widget_content_cast(handle, params): log.debug("getWigetContentCast Called: {0}".format(params)) - server = downloadUtils.get_server() + server = settings.getSetting('server_address') item_id = params["id"] data_manager = DataManager() result = data_manager.get_content( - "{server}/Users/{userid}/Items/" + item_id) + "/Users/{}/Items/{}".format(user_id, item_id)) log.debug("ItemInfo: {0}".format(result)) if not result: @@ -233,7 +242,7 @@ def get_widget_content_cast(handle, params): person_tag = person.get("PrimaryImageTag") person_thumbnail = None if person_tag: - person_thumbnail = downloadUtils.image_url( + person_thumbnail = image_url( person_id, "Primary", 0, 400, 400, person_tag, server=server) if kodi_version > 17: @@ -269,6 +278,7 @@ def get_widget_content(handle, params): log.debug("getWigetContent Called: {0}".format(params)) settings = xbmcaddon.Addon() + item_limit = settings.getSetting("show_x_filtered_items") hide_watched = settings.getSetting("hide_watched") == "true" use_cached_widget_data = settings.getSetting( "use_cached_widget_data") == "true" @@ -280,10 +290,10 @@ def get_widget_content(handle, params): log.debug("widget_type: {0}".format(widget_type)) - url_verb = "{server}/Users/{userid}/Items" + url_verb = "/Users/{}/Items".format(user_id) url_params = {} - url_params["Limit"] = "{ItemLimit}" - url_params["Fields"] = "{field_filters}" + url_params["Limit"] = item_limit + url_params["Fields"] = get_default_filters() url_params["ImageTypeLimit"] = 1 url_params["IsMissing"] = False @@ -297,7 +307,7 @@ def get_widget_content(handle, params): url_params["IsPlayed"] = False url_params["IsVirtualUnaired"] = False url_params["IncludeItemTypes"] = "Movie" - url_params["Limit"] = 20 + url_params["Limit"] = item_limit elif widget_type == "inprogress_movies": xbmcplugin.setContent(handle, 'movies') @@ -307,27 +317,28 @@ def get_widget_content(handle, params): url_params["Filters"] = "IsResumable" url_params["IsVirtualUnaired"] = False url_params["IncludeItemTypes"] = "Movie" - url_params["Limit"] = 20 + url_params["Limit"] = item_limit elif widget_type == "random_movies": + home_window = HomeWindow() xbmcplugin.setContent(handle, 'movies') - url_params["Ids"] = "{random_movies}" + url_params["Ids"] = home_window.get_property("random-movies") elif widget_type == "recent_tvshows": xbmcplugin.setContent(handle, 'episodes') - url_verb = '{server}/Users/{userid}/Items/Latest' + url_verb = '/Users/{}/Items/Latest'.format(user_id) url_params["GroupItems"] = True url_params["Limit"] = 45 url_params["Recursive"] = True url_params["SortBy"] = "DateCreated" url_params["SortOrder"] = "Descending" - url_params["Fields"] = "{field_filters}" + url_params["Fields"] = get_default_filters() if hide_watched: url_params["IsPlayed"] = False url_params["IsVirtualUnaired"] = False url_params["IncludeItemTypes"] = "Episode" url_params["ImageTypeLimit"] = 1 - url_params["Limit"] = 20 + url_params["Limit"] = item_limit elif widget_type == "recent_episodes": xbmcplugin.setContent(handle, 'episodes') @@ -339,7 +350,7 @@ def get_widget_content(handle, params): url_params["IsPlayed"] = False url_params["IsVirtualUnaired"] = False url_params["IncludeItemTypes"] = "Episode" - url_params["Limit"] = 20 + url_params["Limit"] = item_limit elif widget_type == "inprogress_episodes": xbmcplugin.setContent(handle, 'episodes') @@ -349,18 +360,18 @@ def get_widget_content(handle, params): url_params["Filters"] = "IsResumable" url_params["IsVirtualUnaired"] = False url_params["IncludeItemTypes"] = "Episode" - url_params["Limit"] = 20 + url_params["Limit"] = item_limit elif widget_type == "nextup_episodes": xbmcplugin.setContent(handle, 'episodes') - url_verb = "{server}/Shows/NextUp" + url_verb = "/Shows/NextUp" url_params = url_params.copy() - url_params["Limit"] = "{ItemLimit}" - url_params["userid"] = "{userid}" + url_params["Limit"] = item_limit + url_params["userid"] = user_id url_params["Recursive"] = True url_params["ImageTypeLimit"] = 1 # Collect InProgress items to be combined with NextUp - inprogress_url_verb = "{server}/Users/{userid}/Items" + inprogress_url_verb = "/Users/{}/Items".format(user_id) inprogress_url_params = url_params.copy() inprogress_url_params["Recursive"] = True inprogress_url_params["SortBy"] = "DatePlayed" @@ -368,13 +379,13 @@ def get_widget_content(handle, params): inprogress_url_params["Filters"] = "IsResumable" inprogress_url_params["IsVirtualUnaired"] = False inprogress_url_params["IncludeItemTypes"] = "Episode" - inprogress_url_params["Limit"] = 20 + inprogress_url_params["Limit"] = item_limit elif widget_type == "movie_recommendations": suggested_items_url_params = {} - suggested_items_url_params["userId"] = "{userid}" + suggested_items_url_params["userId"] = user_id suggested_items_url_params["categoryLimit"] = 15 - suggested_items_url_params["ItemLimit"] = 20 + suggested_items_url_params["ItemLimit"] = item_limit suggested_items_url_params["ImageTypeLimit"] = 0 suggested_items_url = get_jellyfin_url( "{server}/Movies/Recommendations", suggested_items_url_params) @@ -383,7 +394,7 @@ def get_widget_content(handle, params): suggested_items = data_manager.get_content(suggested_items_url) ids = [] set_id = 0 - while len(ids) < 20 and suggested_items: + while len(ids) < item_limit and suggested_items: items = suggested_items[set_id] log.debug( "BaselineItemName : {0} - {1}".format(set_id, items.get("BaselineItemName"))) diff --git a/service.py b/service.py index 543cc1b..44af26c 100644 --- a/service.py +++ b/service.py @@ -8,14 +8,13 @@ import xbmc import xbmcaddon import xbmcgui -from resources.lib.downloadutils import DownloadUtils from resources.lib.loghandler import LazyLogger from resources.lib.play_utils import Service, PlaybackService, send_progress from resources.lib.kodi_utils import HomeWindow from resources.lib.widgets import set_background_image, set_random_movies from resources.lib.websocket_client import WebSocketClient from resources.lib.menu_functions import set_library_window_values -from resources.lib.server_detect import check_server, check_safe_delete_available, check_connection_speed +from resources.lib.server_detect import check_server, check_connection_speed from resources.lib.monitors import LibraryChangeMonitor, ContextMonitor from resources.lib.datamanager import clear_old_cache_data from resources.lib.tracking import set_timing_enabled @@ -52,16 +51,6 @@ while not monitor.abortRequested(): check_server() -download_utils = DownloadUtils() - -# auth the service -try: - download_utils.authenticate() - download_utils.get_user_id() -except Exception as error: - log.error("Error with initial service auth: {0}".format(error)) - - image_server = HttpImageServerThread() image_server.start() @@ -138,7 +127,8 @@ while home_window.get_property('exit') == 'False': if user_changed or first_run: settings = xbmcaddon.Addon() server_speed_check_data = settings.getSetting("server_speed_check_data") - server_host = download_utils.get_server() + server_speed_check_data = settings.getSetting("server_speed_check_data") + server_host = settings.getSetting('server_address') if server_host is not None and server_host != "" and server_host != "" and server_host not in server_speed_check_data: message = "This is the first time you have connected to this server.\nDo you want to run a connection speed test?" response = xbmcgui.Dialog().yesno("First Connection", message) @@ -167,10 +157,6 @@ while home_window.get_property('exit') == 'False': websocket_client = WebSocketClient(library_change_monitor) websocket_client.start() - if user_changed or not safe_delete_check: - check_safe_delete_available() - safe_delete_check = True - elif screen_saver_active: last_random_movie_update = time.time() - (random_movie_list_interval - 15) if background_interval != 0 and ((time.time() - last_background_update) > background_interval):