Merge pull request #425 from mcarlton00/BEGONE-SATAN

Remove TheVoid
This commit is contained in:
Odd Stråbø 2020-11-26 16:55:27 +01:00 committed by GitHub
commit a7e4fa8a5c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 158 additions and 305 deletions

View file

@ -9,9 +9,8 @@ from datetime import date
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
@ -34,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:
@ -89,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"):
@ -381,43 +337,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'])

View file

@ -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")

View file

@ -3,6 +3,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
#################################################################################################
import json
import sys
import os
@ -13,10 +14,10 @@ from kodi_six import xbmc, xbmcvfs, xbmcgui, xbmcplugin, xbmcaddon
import client
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
#################################################################################################
@ -53,28 +54,48 @@ class Events(object):
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 = 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')
Actions(server, api_client).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(api_client.config.data['app.session'], "Playing", {
'PlayCommand': "PlayNow",
'ItemIds': params['id'],
'StartPositionTicks': 0
})
elif mode == 'deviceid':
client.reset_device_id()
elif mode == 'reset':
@ -86,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':
@ -112,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':
@ -232,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.
'''
@ -262,7 +283,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,51 +298,49 @@ 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 = get_filtered_section(view_id, None, None, None, None, None, ['IsUnplayed'], None, server_id, api_client)
elif folder == 'favorite':
listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Filters': ['IsFavorite']}).get()
listing = get_filtered_section(view_id, None, None, None, None, None, ['IsFavorite'], None, server_id, api_client)
elif folder == 'inprogress':
listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Filters': ['IsResumable']}).get()
listing = get_filtered_section(view_id, None, None, None, None, None, ['IsResumable'], None, server_id, api_client)
elif folder == 'boxsets':
listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Media': get_media_type('boxsets'), 'Recursive': True}).get()
listing = get_filtered_section(view_id, get_media_type('boxsets'), None, True, None, None, None, None, server_id, api_client)
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 = 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 = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Media': get_media_type(content_type), 'Params': {'NameStartsWith': folder.split('-')[1]}}).get()
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 = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Media': get_media_type(content_type), 'Params': {'GenreIds': folder.split('-')[1]}}).get()
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 = TheVoid('Browse', {'Media': get_media_type(content_type), 'ServerId': server_id, 'Limit': 25, 'Filters': ['IsFavorite']}).get()
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 = TheVoid('Browse', {'Id': folder, 'ServerId': server_id, 'Recursive': False, 'Sort': 'None'}).get()
listing = get_filtered_section(folder, get_media_type(content_type), None, False, 'None', None, None, None, server_id, api_client)
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 = 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 = 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 = 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 = get_filtered_section(folder or view_id, None, None, True, None, None, None, None, server_id, api_client)
elif media == 'boxsets':
listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': False, 'Filters': ["Boxsets"]}).get()
listing = get_filtered_section(folder or view_id, None, None, False, None, None, ['Boxsets'], None, server_id, api_client)
elif media == 'tvshows':
listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': True, 'Media': get_media_type(content_type)}).get()
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 = 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 = get_filtered_section(folder or view_id, get_media_type(content_type), None, False, None, None, None, None, server_id, api_client)
else:
listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': False}).get()
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', [])
@ -405,7 +424,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 +450,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:
@ -486,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.
@ -501,14 +520,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()
if not xbmcvfs.exists(directory):
xbmcvfs.mkdirs(directory)
item = TheVoid('GetItem', {'ServerId': server_id, 'Id': item_id}).get()
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):
@ -531,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.
@ -542,8 +560,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):
@ -730,15 +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
session = TheVoid('GetSession', {}).get()
users = TheVoid('GetUsers', {'IsDisabled': False, 'IsHidden': False}).get()
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)])
@ -765,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.
@ -796,18 +814,17 @@ 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()
server = api_client.config.data['auth.server']
for view in views:
result = TheVoid('GetThemes', {'Type': "Video", 'Id': view}).get()
result = api_client.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 = api_client.get_items_theme_song(view)
for item in result['Items']:
@ -822,11 +839,11 @@ def get_themes():
if not xbmcvfs.exists(nfo_path):
xbmcvfs.mkdir(nfo_path)
themes = TheVoid('GetTheme', {'Id': item}).get()
themes = api_client.get_themes(item)
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]))
@ -902,3 +919,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"
)

View file

@ -11,7 +11,6 @@ from kodi_six import xbmc, xbmcvfs
import client
import requests
from downloader import TheVoid
from helper import LazyLogger
from . import translate, settings, window, dialog, api
@ -53,30 +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.api_client = api_client
self.info = {
'ServerId': server_id,
'ServerAddress': server,
'ForceTranscode': force_transcode,
'Token': token or TheVoid('GetToken', {'ServerId': server_id}).get()
'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.
'''
params = {
'ServerId': self.info['ServerId'],
'Id': self.item['Id'],
'Profile': self.get_device_profile()
}
info = TheVoid('GetPlaybackInfo', params).get()
info = self.api_client.get_play_info(self.item['Id'], self.get_device_profile())
LOG.info(info)
self.info['PlaySessionId'] = info['PlaySessionId']
sources = []
@ -217,14 +212,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.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'):
@ -509,7 +497,7 @@ class PlayUtils(object):
mapping = {}
kodi = 0
server_settings = TheVoid('GetTranscodeOptions', {'ServerId': self.info['ServerId']}).get()
server_settings = self.api_client.get_transcode_settings()
for stream in source['MediaStreams']:
if stream['SupportsExternalStream'] and stream['Type'] == 'Subtitle' and stream['DeliveryMethod'] == 'External':
@ -598,7 +586,7 @@ class PlayUtils(object):
subs_streams = collections.OrderedDict()
streams = source['MediaStreams']
server_settings = TheVoid('GetTranscodeOptions', {'ServerId': self.info['ServerId']}).get()
server_settings = self.api_client.get_transcode_settings()
allow_burned_subs = settings('allowBurnedSubs.bool')
for stream in streams:
@ -665,6 +653,7 @@ class PlayUtils(object):
if subtitle:
index = subtitle
server_settings = self.api_client.get_transcode_settings()
stream = streams[index]
if server_settings['EnableSubtitleExtraction'] and stream['SupportsExternalStream']:
@ -683,6 +672,8 @@ class PlayUtils(object):
index = subs_streams[selection[resp]] if resp > -1 else source.get('DefaultSubtitleStreamIndex')
if index is not None:
server_settings = self.api_client.get_transcode_settings()
stream = streams[index]
if server_settings['EnableSubtitleExtraction'] and stream['SupportsExternalStream']:

View file

@ -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):

View file

@ -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()

View file

@ -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
@ -53,12 +52,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 +116,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 +135,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 +157,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:

View file

@ -10,7 +10,6 @@ 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
@ -26,10 +25,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
self.server = TheVoid('GetServerAddress', {'ServerId': self.server_id}).get()
self.api_client = api_client
self.server = self.api_client.config.data['auth.server']
self.stack = []
def get_playlist(self, item):
@ -50,7 +50,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)
@ -112,7 +112,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
@ -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'])
@ -145,14 +145,13 @@ 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()
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)
@ -183,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)
@ -213,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)