From a009530434a25fb561ee8e1b2cbd847bb7dba84d Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 29 Oct 2020 11:54:10 +0100 Subject: [PATCH 01/10] Byebye TheVoid --- jellyfin_kodi/downloader.py | 40 ------ jellyfin_kodi/entrypoint/context.py | 21 +-- jellyfin_kodi/entrypoint/default.py | 89 ++++++------ jellyfin_kodi/helper/playutils.py | 28 ++-- jellyfin_kodi/jellyfin/__init__.py | 1 + jellyfin_kodi/jellyfin/connection_manager.py | 3 + jellyfin_kodi/monitor.py | 135 ++----------------- jellyfin_kodi/objects/actions.py | 11 +- 8 files changed, 89 insertions(+), 239 deletions(-) diff --git a/jellyfin_kodi/downloader.py b/jellyfin_kodi/downloader.py index 424650c6..af436191 100644 --- a/jellyfin_kodi/downloader.py +++ b/jellyfin_kodi/downloader.py @@ -381,43 +381,3 @@ class GetItemWorker(threading.Thread): if window('jellyfin_should_stop.bool'): break - - -class TheVoid(object): - - def __init__(self, method, data): - - ''' If you call get, this will block until response is received. - This is used to communicate between entrypoints. - ''' - if type(data) != dict: - raise Exception("unexpected data format") - - data['VoidName'] = str(create_id()) - LOG.info("---[ contact MU-TH-UR 6000/%s ]", method) - LOG.debug(data) - - event(method, data) - self.method = method - self.data = data - - def get(self): - - while True: - - response = window('jellyfin_%s.json' % self.data['VoidName']) - - if response != "": - - LOG.debug("--<[ nostromo/jellyfin_%s.json ]", self.data['VoidName']) - window('jellyfin_%s' % self.data['VoidName'], clear=True) - - return response - - if window('jellyfin_should_stop.bool'): - LOG.info("Abandon mission! A black hole just swallowed [ %s/%s ]", self.method, self.data['VoidName']) - - return - - xbmc.sleep(100) - LOG.info("--[ void/%s ]", self.data['VoidName']) diff --git a/jellyfin_kodi/entrypoint/context.py b/jellyfin_kodi/entrypoint/context.py index 53719060..ec655e25 100644 --- a/jellyfin_kodi/entrypoint/context.py +++ b/jellyfin_kodi/entrypoint/context.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import division, absolute_import, print_function, unicode_literals +from __future__ import division, absolute_import, print_function, unicode_literals, annotations ################################################################################################# @@ -11,8 +11,8 @@ from kodi_six import xbmc, xbmcaddon import database from dialogs import context from helper import translate, settings, dialog -from downloader import TheVoid from helper import LazyLogger +from jellyfin import Jellyfin ################################################################################################# @@ -39,10 +39,11 @@ class Context(object): try: self.kodi_id = sys.listitem.getVideoInfoTag().getDbId() or None self.media = self.get_media_type() - self.server = sys.listitem.getProperty('jellyfinserver') or None + self.server_id = sys.listitem.getProperty('jellyfinserver') or None + self.api_client = Jellyfin(self.server_id).get_client().jellyfin item_id = sys.listitem.getProperty('jellyfinid') except AttributeError: - self.server = None + self.server_id = None if xbmc.getInfoLabel('ListItem.Property(jellyfinid)'): item_id = xbmc.getInfoLabel('ListItem.Property(jellyfinid)') @@ -51,8 +52,8 @@ class Context(object): self.media = xbmc.getInfoLabel('ListItem.DBTYPE') item_id = None - if self.server or item_id: - self.item = TheVoid('GetItem', {'ServerId': self.server, 'Id': item_id}).get() + if self.server_id or item_id: + self.item = self.api_client.get_item(item_id) else: self.item = self.get_item_id() @@ -143,13 +144,13 @@ class Context(object): selected = self._selected_option if selected == OPTIONS['Refresh']: - TheVoid('RefreshItem', {'ServerId': self.server, 'Id': self.item['Id']}) + self.api_client.refresh_item(self.item['Id']) elif selected == OPTIONS['AddFav']: - TheVoid('FavoriteItem', {'ServerId': self.server, 'Id': self.item['Id'], 'Favorite': True}) + self.api_client.favorite(self.item['Id'], True) elif selected == OPTIONS['RemoveFav']: - TheVoid('FavoriteItem', {'ServerId': self.server, 'Id': self.item['Id'], 'Favorite': False}) + self.api_client.favorite(self.item['Id'], False) elif selected == OPTIONS['Addon']: xbmc.executebuiltin('Addon.OpenSettings(plugin.video.jellyfin)') @@ -159,7 +160,7 @@ class Context(object): def delete_item(self): if settings('skipContextMenu.bool') and dialog("yesno", "{jellyfin}", translate(33015)): - TheVoid('DeleteItem', {'ServerId': self.server, 'Id': self.item['Id']}) + self.api_client.delete_item(self.item['Id']) def transcode(self): filename = xbmc.getInfoLabel("ListItem.Filenameandpath") diff --git a/jellyfin_kodi/entrypoint/default.py b/jellyfin_kodi/entrypoint/default.py index 93de8d28..56f70aa1 100644 --- a/jellyfin_kodi/entrypoint/default.py +++ b/jellyfin_kodi/entrypoint/default.py @@ -11,12 +11,13 @@ from six.moves.urllib.parse import parse_qsl, urlencode from kodi_six import xbmc, xbmcvfs, xbmcgui, xbmcplugin, xbmcaddon import client +import downloader from database import reset, get_sync, Database, jellyfin_db, get_credentials from objects import Objects, Actions -from downloader import TheVoid from helper import translate, event, settings, window, dialog, api, JSONRPC from helper.utils import JsonDebugPrinter from helper import LazyLogger +from jellyfin import Jellyfin ################################################################################################# @@ -49,6 +50,8 @@ class Events(object): mode = params.get('mode') server = params.get('server') + jellyfin_client = Jellyfin(server).get_client() + api_client = jellyfin_client.jellyfin if server == 'None': server = None @@ -69,12 +72,16 @@ class Events(object): elif mode == 'play': - item = TheVoid('GetItem', {'Id': params['id'], 'ServerId': server}).get() + item = api_client.get_item(params['id']) item["resumePlayback"] = sys.argv[3].split(":")[1] == "true" Actions(server).play(item, params.get('dbid'), params.get('transcode') == 'true', playlist=params.get('playlist') == 'true') elif mode == 'playlist': - event('PlayPlaylist', {'Id': params['id'], 'ServerId': server}) + api_client.post_session(jellyfin_client.config.data['app.session'], "Playing", { + 'PlayCommand': "PlayNow", + 'ItemIds': params['id'], + 'StartPositionTicks': 0 + }) elif mode == 'deviceid': client.reset_device_id() elif mode == 'reset': @@ -253,6 +260,7 @@ def browse(media, view_id=None, folder=None, server_id=None): return folder = folder.lower() if folder else None + api_client = Jellyfin(server_id).get_client().jellyfin if folder is None and media in ('homevideos', 'movies', 'books', 'audiobooks'): return browse_subfolders(media, view_id, server_id) @@ -262,7 +270,7 @@ def browse(media, view_id=None, folder=None, server_id=None): if view_id: - view = TheVoid('GetItem', {'ServerId': server_id, 'Id': view_id}).get() + view = api_client.get_item(view_id) xbmcplugin.setPluginCategory(PROCESS_HANDLE, view['Name']) content_type = "files" @@ -277,47 +285,45 @@ def browse(media, view_id=None, folder=None, server_id=None): content_type = "artists" if folder == 'recentlyadded': - listing = TheVoid('RecentlyAdded', {'Id': view_id, 'ServerId': server_id}).get() + listing = api_client.get_recently_added(None, view_id, None) elif folder == 'genres': - listing = TheVoid('Genres', {'Id': view_id, 'ServerId': server_id}).get() + listing = api_client.get_genres(view_id) elif media == 'livetv': - listing = TheVoid('LiveTV', {'Id': view_id, 'ServerId': server_id}).get() + listing = api_client.get_channels() elif folder == 'unwatched': - listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Filters': ['IsUnplayed']}).get() + listing = downloader.get_filtered_section(view_id, None, None, None, None, None, ['IsUnplayed'], None, server_id) elif folder == 'favorite': - listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Filters': ['IsFavorite']}).get() + listing = downloader.get_filtered_section(view_id, None, None, None, None, None, ['IsFavorite'], None, server_id) elif folder == 'inprogress': - listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Filters': ['IsResumable']}).get() + listing = downloader.get_filtered_section(view_id, None, None, None, None, None, ['IsResumable'], None, server_id) elif folder == 'boxsets': - listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Media': get_media_type('boxsets'), 'Recursive': True}).get() + listing = downloader.get_filtered_section(view_id, get_media_type('boxsets'), None, True, None, None, None, None, server_id) elif folder == 'random': - listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Media': get_media_type(content_type), 'Sort': "Random", 'Limit': 25, 'Recursive': True}).get() + listing = downloader.get_filtered_section(view_id, get_media_type(content_type), 25, True, "Random", None, None, None, server_id) elif (folder or "").startswith('firstletter-'): - listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Media': get_media_type(content_type), 'Params': {'NameStartsWith': folder.split('-')[1]}}).get() + listing = downloader.get_filtered_section(view_id, get_media_type(content_type), None, None, None, None, None, {'NameStartsWith': folder.split('-')[1]}, server_id) elif (folder or "").startswith('genres-'): - listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Media': get_media_type(content_type), 'Params': {'GenreIds': folder.split('-')[1]}}).get() + listing = downloader.get_filtered_section(view_id, get_media_type(content_type), None, None, None, None, None, {'GenreIds': folder.split('-')[1]}, server_id) elif folder == 'favepisodes': - listing = TheVoid('Browse', {'Media': get_media_type(content_type), 'ServerId': server_id, 'Limit': 25, 'Filters': ['IsFavorite']}).get() + listing = downloader.get_filtered_section(None, get_media_type(content_type), 25, None, None, None, ['IsFavorite'], None, server_id) elif folder and media == 'playlists': - listing = TheVoid('Browse', {'Id': folder, 'ServerId': server_id, 'Recursive': False, 'Sort': 'None'}).get() + listing = downloader.get_filtered_section(folder, get_media_type(content_type), None, False, 'None', None, None, None, server_id) elif media == 'homevideos': - listing = TheVoid('Browse', {'Id': folder or view_id, 'Media': get_media_type(content_type), 'ServerId': server_id, 'Recursive': False}).get() - elif media == 'movies': - listing = TheVoid('Browse', {'Id': folder or view_id, 'Media': get_media_type(content_type), 'ServerId': server_id, 'Recursive': True}).get() + listing = downloader.get_filtered_section(folder or view_id, get_media_type(content_type), None, False, None, None, None, None, server_id) + elif media in ['movies', 'episodes']: + listing = downloader.get_filtered_section(folder or view_id, get_media_type(content_type), None, True, None, None, None, None, server_id) elif media in ('boxset', 'library'): - listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': True}).get() - elif media == 'episodes': - listing = TheVoid('Browse', {'Id': folder or view_id, 'Media': get_media_type(content_type), 'ServerId': server_id, 'Recursive': True}).get() + listing = downloader.get_filtered_section(folder or view_id, None, None, True, None, None, None, None, server_id) elif media == 'boxsets': - listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': False, 'Filters': ["Boxsets"]}).get() + listing = downloader.get_filtered_section(folder or view_id, None, None, False, None, None, ['Boxsets'], None, server_id) elif media == 'tvshows': - listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': True, 'Media': get_media_type(content_type)}).get() + listing = downloader.get_filtered_section(folder or view_id, get_media_type(content_type), None, True, None, None, None, None, server_id) elif media == 'seasons': - listing = TheVoid('BrowseSeason', {'Id': folder, 'ServerId': server_id}).get() + listing = api_client.get_seasons(folder) elif media != 'files': - listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': False, 'Media': get_media_type(content_type)}).get() + listing = downloader.get_filtered_section(folder or view_id, get_media_type(content_type), None, False, None, None, None, None, server_id) else: - listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': False}).get() + listing = downloader.get_filtered_section(folder or view_id, None, None, False, None, None, None, None, server_id) if listing: @@ -405,7 +411,7 @@ def browse_subfolders(media, view_id, server_id=None): ''' from views import DYNNODES - view = TheVoid('GetItem', {'ServerId': server_id, 'Id': view_id}).get() + view = Jellyfin(server_id).get_client().jellyfin.get_item(view_id) xbmcplugin.setPluginCategory(PROCESS_HANDLE, view['Name']) nodes = DYNNODES[media] @@ -431,7 +437,7 @@ def browse_letters(media, view_id, server_id=None): ''' letters = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ" - view = TheVoid('GetItem', {'ServerId': server_id, 'Id': view_id}).get() + view = Jellyfin(server_id).get_client().jellyfin.get_item(view_id) xbmcplugin.setPluginCategory(PROCESS_HANDLE, view['Name']) for node in letters: @@ -501,12 +507,13 @@ def get_fanart(item_id, path, server_id=None): objects = Objects() list_li = [] directory = xbmc.translatePath("special://thumbnails/jellyfin/%s/" % item_id) - server = TheVoid('GetServerAddress', {'ServerId': server_id}).get() + jellyfin_client = Jellyfin(server_id).get_client() + server = jellyfin_client.auth.get_server_info(self.server_id)['address'] if not xbmcvfs.exists(directory): xbmcvfs.mkdirs(directory) - item = TheVoid('GetItem', {'ServerId': server_id, 'Id': item_id}).get() + item = jellyfin_client.jellyfin.get_item(item_id) obj = objects.map(item, 'Artwork') backdrops = api.API(item, server).get_all_artwork(obj) tags = obj['BackdropTags'] @@ -542,8 +549,8 @@ def get_video_extras(item_id, path, server_id=None): if not item_id: return - TheVoid('GetItem', {'ServerId': server_id, 'Id': item_id}).get() - # TODO: Investigate the void (issue #228) + # TODO implement???? + # Jellyfin(server_id).get_client().jellyfin.get_item(item_id) """ def getVideoFiles(jellyfinId,jellyfinPath): @@ -737,8 +744,9 @@ def add_user(): if not window('jellyfin_online.bool'): return - session = TheVoid('GetSession', {}).get() - users = TheVoid('GetUsers', {'IsDisabled': False, 'IsHidden': False}).get() + jellyfin_client = Jellyfin().get_client() + session = jellyfin_client.get_device(jellyfin_client.config.app.device_id) + users = jellyfin_client.jellyfin.get_users() current = session[0]['AdditionalUsers'] result = dialog("select", translate(33061), [translate(33062), translate(33063)] if current else [translate(33062)]) @@ -796,18 +804,19 @@ def get_themes(): views = [x[0] for x in all_views if x[2] in ('movies', 'tvshows', 'mixed')] items = {} - server = TheVoid('GetServerAddress', {'ServerId': None}).get() - token = TheVoid('GetToken', {'ServerId': None}).get() + jellyfin_client = Jellyfin().get_client() + server = jellyfin_client.auth.get_server_address() + token = jellyfin_client.auth.jellyfin_token() for view in views: - result = TheVoid('GetThemes', {'Type': "Video", 'Id': view}).get() + result = jellyfin_client.jellyfin.get_items_theme_video(view) for item in result['Items']: folder = normalize_string(item['Name']) items[item['Id']] = folder - result = TheVoid('GetThemes', {'Type': "Song", 'Id': view}).get() + result = jellyfin_client.jellyfin.get_items_theme_song(view) for item in result['Items']: @@ -822,7 +831,7 @@ def get_themes(): if not xbmcvfs.exists(nfo_path): xbmcvfs.mkdir(nfo_path) - themes = TheVoid('GetTheme', {'Id': item}).get() + themes = jellyfin_client.jellyfin.get_themes(item) paths = [] for theme in themes['ThemeVideosResult']['Items'] + themes['ThemeSongsResult']['Items']: diff --git a/jellyfin_kodi/helper/playutils.py b/jellyfin_kodi/helper/playutils.py index 71df7b00..f5b92fc6 100644 --- a/jellyfin_kodi/helper/playutils.py +++ b/jellyfin_kodi/helper/playutils.py @@ -11,8 +11,8 @@ from kodi_six import xbmc, xbmcvfs import client import requests -from downloader import TheVoid from helper import LazyLogger +from jellyfin import Jellyfin from . import translate, settings, window, dialog, api @@ -60,23 +60,19 @@ class PlayUtils(object): ''' self.item = item self.item['PlaybackInfo'] = {} + self.jellyfin_client = Jellyfin(server_id).get_client() self.info = { 'ServerId': server_id, 'ServerAddress': server, 'ForceTranscode': force_transcode, - 'Token': token or TheVoid('GetToken', {'ServerId': server_id}).get() + 'Token': token or self.jellyfin_client.auth.jellyfin_token() } def get_sources(self, source_id=None): ''' Return sources based on the optional source_id or the device profile. ''' - params = { - 'ServerId': self.info['ServerId'], - 'Id': self.item['Id'], - 'Profile': self.get_device_profile() - } - info = TheVoid('GetPlaybackInfo', params).get() + info = self.jellyfin_client.jellyfin.get_play_info(self.item['Id'], self.get_device_profile()) LOG.info(info) self.info['PlaySessionId'] = info['PlaySessionId'] sources = [] @@ -217,14 +213,7 @@ class PlayUtils(object): ''' Get live stream media info. ''' - params = { - 'ServerId': self.info['ServerId'], - 'Id': self.item['Id'], - 'Profile': self.get_device_profile(), - 'PlaySessionId': self.info['PlaySessionId'], - 'Token': source['OpenToken'] - } - info = TheVoid('GetLiveStream', params).get() + info = self.jellyfin_client.jellyfin.get_live_stream(self.item['Id'], self.info['PlaySessionId'], source['OpenToken'], self.get_device_profile()) LOG.info(info) if info['MediaSource'].get('RequiresClosing'): @@ -509,7 +498,7 @@ class PlayUtils(object): mapping = {} kodi = 0 - server_settings = TheVoid('GetTranscodeOptions', {'ServerId': self.info['ServerId']}).get() + server_settings = self.jellyfin_client.jellyfin.get_transcode_settings() for stream in source['MediaStreams']: if stream['SupportsExternalStream'] and stream['Type'] == 'Subtitle' and stream['DeliveryMethod'] == 'External': @@ -598,7 +587,7 @@ class PlayUtils(object): subs_streams = collections.OrderedDict() streams = source['MediaStreams'] - server_settings = TheVoid('GetTranscodeOptions', {'ServerId': self.info['ServerId']}).get() + server_settings = self.jellyfin_client.jellyfin.get_transcode_settings() allow_burned_subs = settings('allowBurnedSubs.bool') for stream in streams: @@ -665,6 +654,7 @@ class PlayUtils(object): if subtitle: index = subtitle + server_settings = self.jellyfin_client.jellyfin.get_transcode_settings() stream = streams[index] if server_settings['EnableSubtitleExtraction'] and stream['SupportsExternalStream']: @@ -683,6 +673,8 @@ class PlayUtils(object): index = subs_streams[selection[resp]] if resp > -1 else source.get('DefaultSubtitleStreamIndex') if index is not None: + + server_settings = self.jellyfin_client.jellyfin.get_transcode_settings() stream = streams[index] if server_settings['EnableSubtitleExtraction'] and stream['SupportsExternalStream']: diff --git a/jellyfin_kodi/jellyfin/__init__.py b/jellyfin_kodi/jellyfin/__init__.py index 688a3c36..74178de7 100644 --- a/jellyfin_kodi/jellyfin/__init__.py +++ b/jellyfin_kodi/jellyfin/__init__.py @@ -54,6 +54,7 @@ class Jellyfin(object): self.server_id = server_id or "default" def get_client(self): + # type: () -> JellyfinClient return self.client[self.server_id] def close(self): diff --git a/jellyfin_kodi/jellyfin/connection_manager.py b/jellyfin_kodi/jellyfin/connection_manager.py index fa2ffc8c..e3a51b44 100644 --- a/jellyfin_kodi/jellyfin/connection_manager.py +++ b/jellyfin_kodi/jellyfin/connection_manager.py @@ -204,6 +204,9 @@ class ConnectionManager(object): if server['Id'] == server_id: return server + def get_server_address(self, server_id): + return self.get_server_info(server_id or self.server_id).get('address') + def get_public_users(self): return self.client.jellyfin.get_public_users() diff --git a/jellyfin_kodi/monitor.py b/jellyfin_kodi/monitor.py index e1f34cbb..b4444b46 100644 --- a/jellyfin_kodi/monitor.py +++ b/jellyfin_kodi/monitor.py @@ -53,12 +53,7 @@ class Monitor(xbmc.Monitor): if sender == 'plugin.video.jellyfin': method = method.split('.')[1] - if method not in ('GetItem', 'ReportProgressRequested', 'LoadServer', 'RandomItems', 'Recommended', - 'GetServerAddress', 'GetPlaybackInfo', 'Browse', 'GetImages', 'GetToken', - 'PlayPlaylist', 'Play', 'GetIntros', 'GetAdditionalParts', 'RefreshItem', 'Genres', - 'FavoriteItem', 'DeleteItem', 'AddUser', 'GetSession', 'GetUsers', 'GetThemes', - 'GetTheme', 'Playstate', 'GeneralCommand', 'GetTranscodeOptions', 'RecentlyAdded', - 'BrowseSeason', 'LiveTV', 'GetLiveStream'): + if method not in ('ReportProgressRequested', 'LoadServer', 'AddUser', 'PlayPlaylist', 'Play', 'Playstate', 'GeneralCommand'): return data = json.loads(data)[0] @@ -122,116 +117,17 @@ class Monitor(xbmc.Monitor): LOG.exception(error) server = Jellyfin() - if method == 'GetItem': + server = server.get_client() - item = server.jellyfin.get_item(data['Id']) - self.void_responder(data, item) + if method == 'Play': - elif method == 'GetAdditionalParts': + items = server.jellyfin.get_items(data['ItemIds']) - item = server.jellyfin.get_additional_parts(data['Id']) - self.void_responder(data, item) - - elif method == 'GetIntros': - - item = server.jellyfin.get_intros(data['Id']) - self.void_responder(data, item) - - elif method == 'GetImages': - - item = server.jellyfin.get_images(data['Id']) - self.void_responder(data, item) - - elif method == 'GetServerAddress': - - server_address = server.auth.get_server_info(server.auth.server_id)['address'] - self.void_responder(data, server_address) - - elif method == 'GetPlaybackInfo': - - sources = server.jellyfin.get_play_info(data['Id'], data['Profile']) - self.void_responder(data, sources) - - elif method == 'GetLiveStream': - - sources = server.jellyfin.get_live_stream(data['Id'], data['PlaySessionId'], data['Token'], data['Profile']) - self.void_responder(data, sources) - - elif method == 'GetToken': - - token = server.auth.jellyfin_token() - self.void_responder(data, token) - - elif method == 'GetSession': - - session = server.jellyfin.get_device(self.device_id) - self.void_responder(data, session) - - elif method == 'GetUsers': - - users = server.jellyfin.get_users() - self.void_responder(data, users) - - elif method == 'GetTranscodeOptions': - - result = server.jellyfin.get_transcode_settings() - self.void_responder(data, result) - - elif method == 'GetThemes': - - if data['Type'] == 'Video': - theme = server.jellyfin.get_items_theme_video(data['Id']) - else: - theme = server.jellyfin.get_items_theme_song(data['Id']) - - self.void_responder(data, theme) - - elif method == 'GetTheme': - - theme = server.jellyfin.get_themes(data['Id']) - self.void_responder(data, theme) - - elif method == 'Browse': - - result = downloader.get_filtered_section(data.get('Id'), data.get('Media'), data.get('Limit'), - data.get('Recursive'), data.get('Sort'), data.get('SortOrder'), - data.get('Filters'), data.get('Params'), data.get('ServerId')) - self.void_responder(data, result) - - elif method == 'BrowseSeason': - - result = server.jellyfin.get_seasons(data['Id']) - self.void_responder(data, result) - - elif method == 'LiveTV': - - result = server.jellyfin.get_channels() - self.void_responder(data, result) - - elif method == 'RecentlyAdded': - - result = server.jellyfin.get_recently_added(data.get('Media'), data.get('Id'), data.get('Limit')) - self.void_responder(data, result) - - elif method == 'Genres': - - result = server.jellyfin.get_genres(data.get('Id')) - self.void_responder(data, result) - - elif method == 'Recommended': - - result = server.jellyfin.get_recommendation(data.get('Id'), data.get('Limit')) - self.void_responder(data, result) - - elif method == 'RefreshItem': - server.jellyfin.refresh_item(data['Id']) - - elif method == 'FavoriteItem': - server.jellyfin.favorite(data['Id'], data['Favorite']) - - elif method == 'DeleteItem': - server.jellyfin.delete_item(data['Id']) + PlaylistWorker(data.get('ServerId'), items, data['PlayCommand'] == 'PlayNow', + data.get('StartPositionTicks', 0), data.get('AudioStreamIndex'), + data.get('SubtitleStreamIndex')).start() + # TODO no clue if this is called by anything elif method == 'PlayPlaylist': server.jellyfin.post_session(server.config.data['app.session'], "Playing", { @@ -240,14 +136,6 @@ class Monitor(xbmc.Monitor): 'StartPositionTicks': 0 }) - elif method == 'Play': - - items = server.jellyfin.get_items(data['ItemIds']) - - PlaylistWorker(data.get('ServerId'), items, data['PlayCommand'] == 'PlayNow', - data.get('StartPositionTicks', 0), data.get('AudioStreamIndex'), - data.get('SubtitleStreamIndex')).start() - elif method in ('ReportProgressRequested', 'Player.OnAVChange'): self.player.report_playback(data.get('Report', True)) @@ -270,14 +158,9 @@ class Monitor(xbmc.Monitor): elif method == 'VideoLibrary.OnUpdate': on_update(data, server) - def void_responder(self, data, result): - - window('jellyfin_%s.json' % data['VoidName'], result) - LOG.debug("--->[ nostromo/jellyfin_%s.json ] sent", data['VoidName']) - def server_instance(self, server_id=None): - server = Jellyfin(server_id) + server = Jellyfin(server_id).get_client() self.post_capabilities(server) if server_id is not None: diff --git a/jellyfin_kodi/objects/actions.py b/jellyfin_kodi/objects/actions.py index cc1019ae..bd8ad0fb 100644 --- a/jellyfin_kodi/objects/actions.py +++ b/jellyfin_kodi/objects/actions.py @@ -10,10 +10,10 @@ from datetime import timedelta from kodi_six import xbmc, xbmcgui, xbmcplugin, xbmcaddon import database -from downloader import TheVoid from helper import translate, playutils, api, window, settings, dialog from dialogs import resume from helper import LazyLogger +from jellyfin import Jellyfin from .obj import Objects @@ -29,7 +29,9 @@ class Actions(object): def __init__(self, server_id=None): self.server_id = server_id or None - self.server = TheVoid('GetServerAddress', {'ServerId': self.server_id}).get() + client = Jellyfin(self.server_id).get_client() + self.api_client = client.jellyfin + self.server = client.auth.get_server_address(self.server_id) self.stack = [] def get_playlist(self, item): @@ -112,7 +114,7 @@ class Actions(object): ''' if we have any play them when the movie/show is not being resumed. ''' - intros = TheVoid('GetIntros', {'ServerId': self.server_id, 'Id': item['Id']}).get() + intros = self.api_client.get_intros(item['Id']) if intros['Items']: enabled = True @@ -145,8 +147,7 @@ class Actions(object): ''' Create listitems and add them to the stack of playlist. ''' - parts = TheVoid('GetAdditionalParts', {'ServerId': self.server_id, 'Id': item_id}).get() - + parts = self.api_client.get_additional_parts(item_id) for part in parts['Items']: listitem = xbmcgui.ListItem() From 096680bc577ce046d99f1bd18a15dbc921de322e Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 29 Oct 2020 11:56:22 +0100 Subject: [PATCH 02/10] Remove unneeded import --- jellyfin_kodi/entrypoint/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jellyfin_kodi/entrypoint/context.py b/jellyfin_kodi/entrypoint/context.py index ec655e25..1ae8e835 100644 --- a/jellyfin_kodi/entrypoint/context.py +++ b/jellyfin_kodi/entrypoint/context.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import division, absolute_import, print_function, unicode_literals, annotations +from __future__ import division, absolute_import, print_function, unicode_literals ################################################################################################# From 21cf8bcb6862e4cf99bb2ab72164220dec8d73cb Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 29 Oct 2020 12:42:57 +0100 Subject: [PATCH 03/10] Fix some linting issues --- jellyfin_kodi/downloader.py | 2 +- jellyfin_kodi/entrypoint/default.py | 2 +- jellyfin_kodi/monitor.py | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/jellyfin_kodi/downloader.py b/jellyfin_kodi/downloader.py index af436191..c1dff193 100644 --- a/jellyfin_kodi/downloader.py +++ b/jellyfin_kodi/downloader.py @@ -11,7 +11,7 @@ from six.moves import range, queue as Queue from kodi_six import xbmc import requests -from helper import settings, stop, event, window, create_id +from helper import settings, stop, window from jellyfin import Jellyfin from jellyfin import api from helper.exceptions import HTTPException diff --git a/jellyfin_kodi/entrypoint/default.py b/jellyfin_kodi/entrypoint/default.py index 56f70aa1..153ddd80 100644 --- a/jellyfin_kodi/entrypoint/default.py +++ b/jellyfin_kodi/entrypoint/default.py @@ -508,7 +508,7 @@ def get_fanart(item_id, path, server_id=None): list_li = [] directory = xbmc.translatePath("special://thumbnails/jellyfin/%s/" % item_id) jellyfin_client = Jellyfin(server_id).get_client() - server = jellyfin_client.auth.get_server_info(self.server_id)['address'] + server = jellyfin_client.auth.get_server_info(server_id)['address'] if not xbmcvfs.exists(directory): diff --git a/jellyfin_kodi/monitor.py b/jellyfin_kodi/monitor.py index b4444b46..08756386 100644 --- a/jellyfin_kodi/monitor.py +++ b/jellyfin_kodi/monitor.py @@ -10,7 +10,6 @@ import threading from kodi_six import xbmc import connect -import downloader import player from client import get_device_id from objects import PlaylistWorker, on_play, on_update, special_listener From b6e2d0bbf349972d304895101d4470f24918391f Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 29 Oct 2020 12:52:42 +0100 Subject: [PATCH 04/10] Remove unused import --- jellyfin_kodi/downloader.py | 1 - 1 file changed, 1 deletion(-) diff --git a/jellyfin_kodi/downloader.py b/jellyfin_kodi/downloader.py index c1dff193..55e684d0 100644 --- a/jellyfin_kodi/downloader.py +++ b/jellyfin_kodi/downloader.py @@ -9,7 +9,6 @@ from datetime import date from six.moves import range, queue as Queue -from kodi_six import xbmc import requests from helper import settings, stop, window from jellyfin import Jellyfin From 4a437f7a2b2a5d57c3113687d02ab65f8ac699e3 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 16 Nov 2020 23:12:09 -0500 Subject: [PATCH 05/10] Make addon menu work without TheVoid --- jellyfin_kodi/downloader.py | 43 --------- jellyfin_kodi/entrypoint/default.py | 142 +++++++++++++++++++--------- jellyfin_kodi/objects/actions.py | 7 +- 3 files changed, 100 insertions(+), 92 deletions(-) diff --git a/jellyfin_kodi/downloader.py b/jellyfin_kodi/downloader.py index 55e684d0..22e533c4 100644 --- a/jellyfin_kodi/downloader.py +++ b/jellyfin_kodi/downloader.py @@ -33,13 +33,6 @@ def get_jellyfinserver_url(handler): return "{server}/%s" % handler -def browse_info(): - return ( - "DateCreated,EpisodeCount,SeasonCount,Path,Genres,Studios,Taglines,MediaStreams,Overview,Etag," - "ProductionLocations,Width,Height,RecursiveItemCount,ChildCount" - ) - - def _http(action, url, request=None, server_id=None): if request is None: @@ -88,42 +81,6 @@ def get_single_item(parent_id, media): }) -def get_filtered_section(parent_id=None, media=None, limit=None, recursive=None, sort=None, sort_order=None, - filters=None, extra=None, server_id=None): - - ''' Get dynamic listings. - ''' - params = { - 'ParentId': parent_id, - 'IncludeItemTypes': media, - 'IsMissing': False, - 'Recursive': recursive if recursive is not None else True, - 'Limit': limit, - 'SortBy': sort or "SortName", - 'SortOrder': sort_order or "Ascending", - 'ImageTypeLimit': 1, - 'IsVirtualUnaired': False, - 'Fields': browse_info() - } - if filters: - if 'Boxsets' in filters: - filters.remove('Boxsets') - params['CollapseBoxSetItems'] = settings('groupedSets.bool') - - params['Filters'] = ','.join(filters) - - if settings('getCast.bool'): - params['Fields'] += ",People" - - if media and 'Photo' in media: - params['Fields'] += ",Width,Height" - - if extra is not None: - params.update(extra) - - return _get("Users/{UserId}/Items", params, server_id) - - def get_movies_by_boxset(boxset_id): for items in get_items(boxset_id, "Movie"): diff --git a/jellyfin_kodi/entrypoint/default.py b/jellyfin_kodi/entrypoint/default.py index 153ddd80..7fc1b0dc 100644 --- a/jellyfin_kodi/entrypoint/default.py +++ b/jellyfin_kodi/entrypoint/default.py @@ -3,6 +3,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera ################################################################################################# +import json import sys import os @@ -11,7 +12,6 @@ from six.moves.urllib.parse import parse_qsl, urlencode from kodi_six import xbmc, xbmcvfs, xbmcgui, xbmcplugin, xbmcaddon import client -import downloader from database import reset, get_sync, Database, jellyfin_db, get_credentials from objects import Objects, Actions from helper import translate, event, settings, window, dialog, api, JSONRPC @@ -50,34 +50,48 @@ class Events(object): mode = params.get('mode') server = params.get('server') - jellyfin_client = Jellyfin(server).get_client() - api_client = jellyfin_client.jellyfin if server == 'None': server = None + jellyfin_client = Jellyfin(server).get_client() + api_client = jellyfin_client.jellyfin + + addon_data = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/data.json") + with open(addon_data, 'rb') as infile: + data = json.load(infile) + + try: + server_data = data['Servers'][0] + api_client.config.data['auth.server'] = server_data.get('address') + api_client.config.data['auth.server-name'] = server_data.get('Name') + api_client.config.data['auth.user_id'] = server_data.get('UserId') + api_client.config.data['auth.token'] = server_data.get('AccessToken') + except Exception as e: + LOG.warning('Addon appears to not be configured yet: {}'.format(e)) + LOG.info("path: %s params: %s", path, JsonDebugPrinter(params)) if '/extrafanart' in base_url: jellyfin_path = path[1:] jellyfin_id = params.get('id') - get_fanart(jellyfin_id, jellyfin_path, server) + get_fanart(jellyfin_id, jellyfin_path, server, api_client) elif '/Extras' in base_url or '/VideoFiles' in base_url: jellyfin_path = path[1:] jellyfin_id = params.get('id') - get_video_extras(jellyfin_id, jellyfin_path, server) + get_video_extras(jellyfin_id, jellyfin_path, server, api_client) elif mode == 'play': item = api_client.get_item(params['id']) item["resumePlayback"] = sys.argv[3].split(":")[1] == "true" - Actions(server).play(item, params.get('dbid'), params.get('transcode') == 'true', playlist=params.get('playlist') == 'true') + Actions(server, api_client).play(item, params.get('dbid'), params.get('transcode') == 'true', playlist=params.get('playlist') == 'true') elif mode == 'playlist': - api_client.post_session(jellyfin_client.config.data['app.session'], "Playing", { + api_client.post_session(api_client.config.data['app.session'], "Playing", { 'PlayCommand': "PlayNow", 'ItemIds': params['id'], 'StartPositionTicks': 0 @@ -93,7 +107,7 @@ class Events(object): elif mode == 'nextepisodes': get_next_episodes(params['id'], params['limit']) elif mode == 'browse': - browse(params.get('type'), params.get('id'), params.get('folder'), server) + browse(params.get('type'), params.get('id'), params.get('folder'), server, api_client) elif mode == 'synclib': event('SyncLibrary', {'Id': params.get('id')}) elif mode == 'updatelib': @@ -119,11 +133,11 @@ class Events(object): elif mode == 'settings': xbmc.executebuiltin('Addon.OpenSettings(plugin.video.jellyfin)') elif mode == 'adduser': - add_user() + add_user(api_client) elif mode == 'updatepassword': event('UpdatePassword') elif mode == 'thememedia': - get_themes() + get_themes(api_client) elif mode == 'managelibs': manage_libraries() elif mode == 'backup': @@ -239,7 +253,7 @@ def manage_libraries(): xbmcplugin.endOfDirectory(PROCESS_HANDLE) -def browse(media, view_id=None, folder=None, server_id=None): +def browse(media, view_id=None, folder=None, server_id=None, api_client=None): ''' Browse content dynamically. ''' @@ -260,7 +274,6 @@ def browse(media, view_id=None, folder=None, server_id=None): return folder = folder.lower() if folder else None - api_client = Jellyfin(server_id).get_client().jellyfin if folder is None and media in ('homevideos', 'movies', 'books', 'audiobooks'): return browse_subfolders(media, view_id, server_id) @@ -291,43 +304,43 @@ def browse(media, view_id=None, folder=None, server_id=None): elif media == 'livetv': listing = api_client.get_channels() elif folder == 'unwatched': - listing = downloader.get_filtered_section(view_id, None, None, None, None, None, ['IsUnplayed'], None, server_id) + listing = get_filtered_section(view_id, None, None, None, None, None, ['IsUnplayed'], None, server_id, api_client) elif folder == 'favorite': - listing = downloader.get_filtered_section(view_id, None, None, None, None, None, ['IsFavorite'], None, server_id) + listing = get_filtered_section(view_id, None, None, None, None, None, ['IsFavorite'], None, server_id, api_client) elif folder == 'inprogress': - listing = downloader.get_filtered_section(view_id, None, None, None, None, None, ['IsResumable'], None, server_id) + listing = get_filtered_section(view_id, None, None, None, None, None, ['IsResumable'], None, server_id, api_client) elif folder == 'boxsets': - listing = downloader.get_filtered_section(view_id, get_media_type('boxsets'), None, True, None, None, None, None, server_id) + listing = get_filtered_section(view_id, get_media_type('boxsets'), None, True, None, None, None, None, server_id, api_client) elif folder == 'random': - listing = downloader.get_filtered_section(view_id, get_media_type(content_type), 25, True, "Random", None, None, None, server_id) + listing = get_filtered_section(view_id, get_media_type(content_type), 25, True, "Random", None, None, None, server_id, api_client) elif (folder or "").startswith('firstletter-'): - listing = downloader.get_filtered_section(view_id, get_media_type(content_type), None, None, None, None, None, {'NameStartsWith': folder.split('-')[1]}, server_id) + listing = get_filtered_section(view_id, get_media_type(content_type), None, None, None, None, None, {'NameStartsWith': folder.split('-')[1]}, server_id, api_client) elif (folder or "").startswith('genres-'): - listing = downloader.get_filtered_section(view_id, get_media_type(content_type), None, None, None, None, None, {'GenreIds': folder.split('-')[1]}, server_id) + listing = get_filtered_section(view_id, get_media_type(content_type), None, None, None, None, None, {'GenreIds': folder.split('-')[1]}, server_id, api_client) elif folder == 'favepisodes': - listing = downloader.get_filtered_section(None, get_media_type(content_type), 25, None, None, None, ['IsFavorite'], None, server_id) + listing = get_filtered_section(None, get_media_type(content_type), 25, None, None, None, ['IsFavorite'], None, server_id, api_client) elif folder and media == 'playlists': - listing = downloader.get_filtered_section(folder, get_media_type(content_type), None, False, 'None', None, None, None, server_id) + listing = get_filtered_section(folder, get_media_type(content_type), None, False, 'None', None, None, None, server_id, api_client) elif media == 'homevideos': - listing = downloader.get_filtered_section(folder or view_id, get_media_type(content_type), None, False, None, None, None, None, server_id) + listing = get_filtered_section(folder or view_id, get_media_type(content_type), None, False, None, None, None, None, server_id, api_client) elif media in ['movies', 'episodes']: - listing = downloader.get_filtered_section(folder or view_id, get_media_type(content_type), None, True, None, None, None, None, server_id) + listing = get_filtered_section(folder or view_id, get_media_type(content_type), None, True, None, None, None, None, server_id, api_client) elif media in ('boxset', 'library'): - listing = downloader.get_filtered_section(folder or view_id, None, None, True, None, None, None, None, server_id) + listing = get_filtered_section(folder or view_id, None, None, True, None, None, None, None, server_id, api_client) elif media == 'boxsets': - listing = downloader.get_filtered_section(folder or view_id, None, None, False, None, None, ['Boxsets'], None, server_id) + listing = get_filtered_section(folder or view_id, None, None, False, None, None, ['Boxsets'], None, server_id, api_client) elif media == 'tvshows': - listing = downloader.get_filtered_section(folder or view_id, get_media_type(content_type), None, True, None, None, None, None, server_id) + listing = get_filtered_section(folder or view_id, get_media_type(content_type), None, True, None, None, None, None, server_id, api_client) elif media == 'seasons': listing = api_client.get_seasons(folder) elif media != 'files': - listing = downloader.get_filtered_section(folder or view_id, get_media_type(content_type), None, False, None, None, None, None, server_id) + listing = get_filtered_section(folder or view_id, get_media_type(content_type), None, False, None, None, None, None, server_id, api_client) else: - listing = downloader.get_filtered_section(folder or view_id, None, None, False, None, None, None, None, server_id) + listing = get_filtered_section(folder or view_id, None, None, False, None, None, None, None, server_id, api_client) if listing: - actions = Actions(server_id) + actions = Actions(server_id, api_client) list_li = [] listing = listing if type(listing) == list else listing.get('Items', []) @@ -492,7 +505,7 @@ def get_media_type(media): return "MusicArtist,MusicAlbum,Audio" -def get_fanart(item_id, path, server_id=None): +def get_fanart(item_id, path, server_id=None, api_client=None): ''' Get extra fanart for listitems. This is called by skinhelper. Images are stored locally, due to the Kodi caching system. @@ -507,13 +520,11 @@ def get_fanart(item_id, path, server_id=None): objects = Objects() list_li = [] directory = xbmc.translatePath("special://thumbnails/jellyfin/%s/" % item_id) - jellyfin_client = Jellyfin(server_id).get_client() - server = jellyfin_client.auth.get_server_info(server_id)['address'] if not xbmcvfs.exists(directory): xbmcvfs.mkdirs(directory) - item = jellyfin_client.jellyfin.get_item(item_id) + item = api_client.get_item(item_id) obj = objects.map(item, 'Artwork') backdrops = api.API(item, server).get_all_artwork(obj) tags = obj['BackdropTags'] @@ -538,7 +549,7 @@ def get_fanart(item_id, path, server_id=None): xbmcplugin.endOfDirectory(PROCESS_HANDLE) -def get_video_extras(item_id, path, server_id=None): +def get_video_extras(item_id, path, server_id=None, api_client=None): ''' Returns the video files for the item as plugin listing, can be used to browse actual files or video extras, etc. @@ -737,16 +748,15 @@ def create_listitem(item): return li -def add_user(): +def add_user(api_client): ''' Add or remove users from the default server session. ''' if not window('jellyfin_online.bool'): return - jellyfin_client = Jellyfin().get_client() - session = jellyfin_client.get_device(jellyfin_client.config.app.device_id) - users = jellyfin_client.jellyfin.get_users() + session = api_client.get_device(client.get_device_id()) + users = api_client.get_users() current = session[0]['AdditionalUsers'] result = dialog("select", translate(33061), [translate(33062), translate(33063)] if current else [translate(33062)]) @@ -773,7 +783,7 @@ def add_user(): event('AddUser', {'Id': user['UserId'], 'Add': False}) -def get_themes(): +def get_themes(api_client): ''' Add theme media locally, via strm. This is only for tv tunes. If another script is used, adjust this code. @@ -804,19 +814,18 @@ def get_themes(): views = [x[0] for x in all_views if x[2] in ('movies', 'tvshows', 'mixed')] items = {} - jellyfin_client = Jellyfin().get_client() - server = jellyfin_client.auth.get_server_address() - token = jellyfin_client.auth.jellyfin_token() + server = api_client.config.data['auth.server'] + token = api_client.config.data['auth.token'] for view in views: - result = jellyfin_client.jellyfin.get_items_theme_video(view) + result = api_client.get_items_theme_video(view) for item in result['Items']: folder = normalize_string(item['Name']) items[item['Id']] = folder - result = jellyfin_client.jellyfin.get_items_theme_song(view) + result = api_client.get_items_theme_song(view) for item in result['Items']: @@ -831,7 +840,7 @@ def get_themes(): if not xbmcvfs.exists(nfo_path): xbmcvfs.mkdir(nfo_path) - themes = jellyfin_client.jellyfin.get_themes(item) + themes = api_client.get_themes(item) paths = [] for theme in themes['ThemeVideosResult']['Items'] + themes['ThemeSongsResult']['Items']: @@ -911,3 +920,46 @@ def backup(): LOG.info("backup completed") dialog("ok", "{jellyfin}", "%s %s" % (translate(33091), backup)) + + +def get_filtered_section(parent_id=None, media=None, limit=None, recursive=None, sort=None, sort_order=None, + filters=None, extra=None, server_id=None, api_client=None): + + ''' Get dynamic listings. + ''' + params = { + 'ParentId': parent_id, + 'IncludeItemTypes': media, + 'IsMissing': False, + 'Recursive': recursive if recursive is not None else True, + 'Limit': limit, + 'SortBy': sort or "SortName", + 'SortOrder': sort_order or "Ascending", + 'ImageTypeLimit': 1, + 'IsVirtualUnaired': False, + 'Fields': browse_info() + } + if filters: + if 'Boxsets' in filters: + filters.remove('Boxsets') + params['CollapseBoxSetItems'] = settings('groupedSets.bool') + + params['Filters'] = ','.join(filters) + + if settings('getCast.bool'): + params['Fields'] += ",People" + + if media and 'Photo' in media: + params['Fields'] += ",Width,Height" + + if extra is not None: + params.update(extra) + + return api_client._get("Users/{UserId}/Items", params) + + +def browse_info(): + return ( + "DateCreated,EpisodeCount,SeasonCount,Path,Genres,Studios,Taglines,MediaStreams,Overview,Etag," + "ProductionLocations,Width,Height,RecursiveItemCount,ChildCount" + ) diff --git a/jellyfin_kodi/objects/actions.py b/jellyfin_kodi/objects/actions.py index bd8ad0fb..f7d708c2 100644 --- a/jellyfin_kodi/objects/actions.py +++ b/jellyfin_kodi/objects/actions.py @@ -26,12 +26,11 @@ LOG = LazyLogger(__name__) class Actions(object): - def __init__(self, server_id=None): + def __init__(self, server_id=None, api_client=None): self.server_id = server_id or None - client = Jellyfin(self.server_id).get_client() - self.api_client = client.jellyfin - self.server = client.auth.get_server_address(self.server_id) + self.api_client = api_client + self.server = self.api_client.config.data['auth.server'] self.stack = [] def get_playlist(self, item): From feea23e9daf6b74b0451aa06c81a70c7d3d870f0 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 17 Nov 2020 12:10:04 -0500 Subject: [PATCH 06/10] Voidless playback. Slightly important --- jellyfin_kodi/entrypoint/default.py | 2 +- jellyfin_kodi/helper/playutils.py | 19 +++++++++---------- jellyfin_kodi/objects/actions.py | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/jellyfin_kodi/entrypoint/default.py b/jellyfin_kodi/entrypoint/default.py index 7fc1b0dc..1b4a6d42 100644 --- a/jellyfin_kodi/entrypoint/default.py +++ b/jellyfin_kodi/entrypoint/default.py @@ -844,7 +844,7 @@ def get_themes(api_client): paths = [] for theme in themes['ThemeVideosResult']['Items'] + themes['ThemeSongsResult']['Items']: - putils = PlayUtils(theme, False, None, server, token) + putils = PlayUtils(theme, False, None, server, api_client) if play: paths.append(putils.direct_play(theme['MediaSources'][0])) diff --git a/jellyfin_kodi/helper/playutils.py b/jellyfin_kodi/helper/playutils.py index f5b92fc6..338e5415 100644 --- a/jellyfin_kodi/helper/playutils.py +++ b/jellyfin_kodi/helper/playutils.py @@ -12,7 +12,6 @@ from kodi_six import xbmc, xbmcvfs import client import requests from helper import LazyLogger -from jellyfin import Jellyfin from . import translate, settings, window, dialog, api @@ -53,26 +52,26 @@ def set_properties(item, method, server_id=None): class PlayUtils(object): - def __init__(self, item, force_transcode=False, server_id=None, server=None, token=None): + def __init__(self, item, force_transcode=False, server_id=None, server=None, api_client=None): ''' Item will be updated with the property PlaybackInfo, which holds all the playback information. ''' self.item = item self.item['PlaybackInfo'] = {} - self.jellyfin_client = Jellyfin(server_id).get_client() + self.api_client = api_client self.info = { 'ServerId': server_id, 'ServerAddress': server, 'ForceTranscode': force_transcode, - 'Token': token or self.jellyfin_client.auth.jellyfin_token() + 'Token': api_client.config.data['auth.token'] } def get_sources(self, source_id=None): ''' Return sources based on the optional source_id or the device profile. ''' - info = self.jellyfin_client.jellyfin.get_play_info(self.item['Id'], self.get_device_profile()) + info = self.api_client.get_play_info(self.item['Id'], self.get_device_profile()) LOG.info(info) self.info['PlaySessionId'] = info['PlaySessionId'] sources = [] @@ -213,7 +212,7 @@ class PlayUtils(object): ''' Get live stream media info. ''' - info = self.jellyfin_client.jellyfin.get_live_stream(self.item['Id'], self.info['PlaySessionId'], source['OpenToken'], self.get_device_profile()) + info = self.api_client.get_live_stream(self.item['Id'], self.info['PlaySessionId'], source['OpenToken'], self.get_device_profile()) LOG.info(info) if info['MediaSource'].get('RequiresClosing'): @@ -498,7 +497,7 @@ class PlayUtils(object): mapping = {} kodi = 0 - server_settings = self.jellyfin_client.jellyfin.get_transcode_settings() + server_settings = self.api_client.get_transcode_settings() for stream in source['MediaStreams']: if stream['SupportsExternalStream'] and stream['Type'] == 'Subtitle' and stream['DeliveryMethod'] == 'External': @@ -587,7 +586,7 @@ class PlayUtils(object): subs_streams = collections.OrderedDict() streams = source['MediaStreams'] - server_settings = self.jellyfin_client.jellyfin.get_transcode_settings() + server_settings = self.api_client.get_transcode_settings() allow_burned_subs = settings('allowBurnedSubs.bool') for stream in streams: @@ -654,7 +653,7 @@ class PlayUtils(object): if subtitle: index = subtitle - server_settings = self.jellyfin_client.jellyfin.get_transcode_settings() + server_settings = self.api_client.get_transcode_settings() stream = streams[index] if server_settings['EnableSubtitleExtraction'] and stream['SupportsExternalStream']: @@ -674,7 +673,7 @@ class PlayUtils(object): if index is not None: - server_settings = self.jellyfin_client.jellyfin.get_transcode_settings() + server_settings = self.api_client.get_transcode_settings() stream = streams[index] if server_settings['EnableSubtitleExtraction'] and stream['SupportsExternalStream']: diff --git a/jellyfin_kodi/objects/actions.py b/jellyfin_kodi/objects/actions.py index f7d708c2..108c5c81 100644 --- a/jellyfin_kodi/objects/actions.py +++ b/jellyfin_kodi/objects/actions.py @@ -51,7 +51,7 @@ class Actions(object): transcode = transcode or settings('playFromTranscode.bool') kodi_playlist = self.get_playlist(item) - play = playutils.PlayUtils(item, transcode, self.server_id, self.server) + play = playutils.PlayUtils(item, transcode, self.server_id, self.server, self.api_client) source = play.select_source(play.get_sources()) play.set_external_subs(source, listitem) From bf4c3852836492c31d204101e3e00d923b05d5e5 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 17 Nov 2020 19:06:24 -0500 Subject: [PATCH 07/10] Fix fanart --- jellyfin_kodi/entrypoint/default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jellyfin_kodi/entrypoint/default.py b/jellyfin_kodi/entrypoint/default.py index 1b4a6d42..7ec896ca 100644 --- a/jellyfin_kodi/entrypoint/default.py +++ b/jellyfin_kodi/entrypoint/default.py @@ -526,7 +526,7 @@ def get_fanart(item_id, path, server_id=None, api_client=None): xbmcvfs.mkdirs(directory) item = api_client.get_item(item_id) obj = objects.map(item, 'Artwork') - backdrops = api.API(item, server).get_all_artwork(obj) + backdrops = api.API(item).get_all_artwork(obj) tags = obj['BackdropTags'] for index, backdrop in enumerate(backdrops): From e487163ca22c6b262cbe510f88474c5a10f61992 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 17 Nov 2020 19:23:40 -0500 Subject: [PATCH 08/10] Make the linter happy --- jellyfin_kodi/entrypoint/default.py | 1 - jellyfin_kodi/objects/actions.py | 1 - 2 files changed, 2 deletions(-) diff --git a/jellyfin_kodi/entrypoint/default.py b/jellyfin_kodi/entrypoint/default.py index 7ec896ca..8a97c887 100644 --- a/jellyfin_kodi/entrypoint/default.py +++ b/jellyfin_kodi/entrypoint/default.py @@ -815,7 +815,6 @@ def get_themes(api_client): items = {} server = api_client.config.data['auth.server'] - token = api_client.config.data['auth.token'] for view in views: result = api_client.get_items_theme_video(view) diff --git a/jellyfin_kodi/objects/actions.py b/jellyfin_kodi/objects/actions.py index 108c5c81..c816af0c 100644 --- a/jellyfin_kodi/objects/actions.py +++ b/jellyfin_kodi/objects/actions.py @@ -13,7 +13,6 @@ import database from helper import translate, playutils, api, window, settings, dialog from dialogs import resume from helper import LazyLogger -from jellyfin import Jellyfin from .obj import Objects From a3ce3ae4fa403547e3bd749fef74c7fa6e348121 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 23 Nov 2020 20:38:24 -0500 Subject: [PATCH 09/10] Fix voidless intros --- jellyfin_kodi/objects/actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jellyfin_kodi/objects/actions.py b/jellyfin_kodi/objects/actions.py index c816af0c..0a6b7f21 100644 --- a/jellyfin_kodi/objects/actions.py +++ b/jellyfin_kodi/objects/actions.py @@ -131,7 +131,7 @@ class Actions(object): listitem = xbmcgui.ListItem() LOG.info("[ intro/%s ] %s", intro['Id'], intro['Name']) - play = playutils.PlayUtils(intro, False, self.server_id, self.server) + play = playutils.PlayUtils(intro, False, self.server_id, self.server, self.api_client) play.select_source(play.get_sources()) self.set_listitem(intro, listitem, intro=True) listitem.setPath(intro['PlaybackInfo']['Path']) From bceeff4754edf67947ccacbd84991a08a5ce7f4b Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 23 Nov 2020 20:41:32 -0500 Subject: [PATCH 10/10] Fix missing apiclient on other PlayUtils actions --- jellyfin_kodi/objects/actions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jellyfin_kodi/objects/actions.py b/jellyfin_kodi/objects/actions.py index 0a6b7f21..6ca92f24 100644 --- a/jellyfin_kodi/objects/actions.py +++ b/jellyfin_kodi/objects/actions.py @@ -151,7 +151,7 @@ class Actions(object): listitem = xbmcgui.ListItem() LOG.info("[ part/%s ] %s", part['Id'], part['Name']) - play = playutils.PlayUtils(part, False, self.server_id, self.server) + play = playutils.PlayUtils(part, False, self.server_id, self.server, self.api_client) source = play.select_source(play.get_sources()) play.set_external_subs(source, listitem) self.set_listitem(part, listitem) @@ -182,7 +182,7 @@ class Actions(object): listitem = xbmcgui.ListItem() LOG.info("[ playlist/%s ] %s", item['Id'], item['Name']) - play = playutils.PlayUtils(item, False, self.server_id, self.server) + play = playutils.PlayUtils(item, False, self.server_id, self.server, self.api_client) source = play.select_source(play.get_sources()) play.set_external_subs(source, listitem) @@ -212,7 +212,7 @@ class Actions(object): server_address, item['Id'], token) listitem.setPath(path) - play = playutils.PlayUtils(item, False, self.server_id, self.server) + play = playutils.PlayUtils(item, False, self.server_id, self.server, self.api_client) source = play.select_source(play.get_sources()) play.set_external_subs(source, listitem)