Most flake8 warnings corrected

This commit is contained in:
Odd Stråbø 2019-10-03 04:14:54 +02:00
parent e92d60f7c7
commit 00765c0a12
58 changed files with 1144 additions and 1262 deletions

View file

@ -15,19 +15,22 @@ from helper.utils import create_id
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
def get_addon_name():
''' Used for logging.
'''
return xbmcaddon.Addon(addon_id()).getAddonInfo('name').upper()
def get_version():
return xbmcaddon.Addon(addon_id()).getAddonInfo('version')
def get_platform():
if xbmc.getCondVisibility('system.platform.osx'):
@ -53,6 +56,7 @@ def get_platform():
else:
return "Unknown"
def get_device_name():
''' Detect the device name. If deviceNameOpt, then
@ -68,6 +72,7 @@ def get_device_name():
return device_name
def get_device_id(reset=False):
''' Return the device_id if already loaded.
@ -105,6 +110,7 @@ def get_device_id(reset=False):
return client_id
def reset_device_id():
window('jellyfin_deviceId', clear=True)
@ -112,6 +118,7 @@ def reset_device_id():
dialog("ok", heading="{jellyfin}", line1=_(33033))
xbmc.executebuiltin('RestartApp')
def get_info():
return {
'DeviceName': get_device_name(),

View file

@ -20,7 +20,7 @@ from jellyfin.core.exceptions import HTTPException
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
XML_PATH = (xbmcaddon.Addon(addon_id()).getAddonInfo('path'), "default", "1080i")
##################################################################################################
@ -42,7 +42,7 @@ class Connect(object):
if server_id is None and credentials['Servers']:
credentials['Servers'] = [credentials['Servers'][0]]
elif credentials['Servers']:
for server in credentials['Servers']:
@ -88,7 +88,7 @@ class Connect(object):
return client
def register_client(self, credentials=None, options=None, server_id=None, server_selection=False):
client = self.get_client(server_id)
self.client = client
self.connect_manager = client.auth
@ -102,7 +102,7 @@ class Connect(object):
if state['State'] == CONNECTION_STATE['SignedIn']:
client.callback_ws = event
if server_id is None: # Only assign for default server
if server_id is None: # Only assign for default server
client.callback = event
self.get_user(client)
@ -115,8 +115,7 @@ class Connect(object):
return state['Credentials']
elif (server_selection or state['State'] == CONNECTION_STATE['ServerSelection'] or
state['State'] == CONNECTION_STATE['Unavailable'] and not settings('SyncInstallRunDone.bool')):
elif (server_selection or state['State'] == CONNECTION_STATE['ServerSelection'] or state['State'] == CONNECTION_STATE['Unavailable'] and not settings('SyncInstallRunDone.bool')):
self.select_servers(state)
@ -143,7 +142,6 @@ class Connect(object):
return client.get_credentials()
def get_user(self, client):
''' Save user info.
@ -178,7 +176,8 @@ class Connect(object):
LOG.debug("Adding manual server")
try:
self.manual_server()
except RuntimeError: pass
except RuntimeError:
pass
else:
raise RuntimeError("No server selected")
@ -237,14 +236,16 @@ class Connect(object):
LOG.debug("User has password, present manual login")
try:
return self.login_manual(username)
except RuntimeError: pass
except RuntimeError:
pass
else:
return self.connect_manager.login(server, username)
elif dialog.is_manual_login():
try:
return self.login_manual()
except RuntimeError: pass
except RuntimeError:
pass
else:
raise RuntimeError("No user selected")
@ -267,7 +268,7 @@ class Connect(object):
save_credentials(credentials)
def login_manual(self, user=None, manager=None):
''' Return manual login user authenticated or raise error.
'''
dialog = LoginManual("script-jellyfin-connect-login-manual.xml", *XML_PATH)
@ -294,4 +295,3 @@ class Connect(object):
save_credentials(credentials)
LOG.info("[ remove server ] %s", server_id)

View file

@ -18,7 +18,7 @@ from objects import obj
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
@ -51,7 +51,7 @@ class Database(object):
self.cursor = self.conn.cursor()
if self.db_file in ('video', 'music', 'texture', 'jellyfin'):
self.conn.execute("PRAGMA journal_mode=WAL") # to avoid writing conflict with kodi
self.conn.execute("PRAGMA journal_mode=WAL") # to avoid writing conflict with kodi
LOG.debug("--->[ database: %s ] %s", self.db_file, id(self.conn))
@ -105,8 +105,7 @@ class Database(object):
for file in reversed(files):
if (file.startswith(database) and not file.endswith('-wal') and
not file.endswith('-shm') and not file.endswith('db-journal')):
if (file.startswith(database) and not file.endswith('-wal') and not file.endswith('-shm') and not file.endswith('db-journal')):
st = xbmcvfs.Stat(databases + file.decode('utf-8'))
modified_int = st.st_mtime()
@ -147,7 +146,7 @@ class Database(object):
loaded = self._get_database(databases[alt_file])
break
except KeyError: # No other db options
except KeyError: # No other db options
loaded = None
break
@ -172,7 +171,7 @@ class Database(object):
'''
changes = self.conn.total_changes
if exc_type is not None: # errors raised
if exc_type is not None: # errors raised
LOG.error("type: %s value: %s", exc_type, exc_val)
if self.commit_close and changes:
@ -184,6 +183,7 @@ class Database(object):
self.cursor.close()
self.conn.close()
def jellyfin_tables(cursor):
''' Create the tables for the jellyfin database.
@ -205,6 +205,7 @@ def jellyfin_tables(cursor):
LOG.info("Add missing column jellyfin_parent_id")
cursor.execute("ALTER TABLE jellyfin ADD COLUMN jellyfin_parent_id 'TEXT'")
def reset():
''' Reset both the jellyfin database and the kodi database.
@ -257,6 +258,7 @@ def reset():
dialog("ok", heading="{jellyfin}", line1=_(33088))
xbmc.executebuiltin('RestartApp')
def reset_kodi():
with Database() as videodb:
@ -281,6 +283,7 @@ def reset_kodi():
LOG.info("[ reset kodi ]")
def reset_jellyfin():
with Database('jellyfin') as jellyfindb:
@ -298,6 +301,7 @@ def reset_jellyfin():
LOG.info("[ reset jellyfin ]")
def reset_artwork():
''' Remove all existing texture.
@ -325,6 +329,7 @@ def reset_artwork():
LOG.info("[ reset artwork ]")
def get_sync():
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
@ -345,6 +350,7 @@ def get_sync():
return sync
def save_sync(sync):
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
@ -358,6 +364,7 @@ def save_sync(sync):
data = json.dumps(sync, sort_keys=True, indent=4, ensure_ascii=False)
outfile.write(unicode(data))
def get_credentials():
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
@ -383,19 +390,21 @@ def get_credentials():
return credentials
def save_credentials(credentials):
credentials = credentials or {}
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
if not xbmcvfs.exists(path):
xbmcvfs.mkdirs(path)
try:
try:
with open(os.path.join(path, 'data.json'), 'w', encoding='utf8') as outfile:
data = json.dumps(credentials, sort_keys=True, indent=4, ensure_ascii=False)
outfile.write(unicode(data))
except Exception as e:
LOG.error("Failed to save credentials: {}".format(e))
def get_item(kodi_id, media):
''' Get jellyfin item based on kodi id and media.
@ -409,4 +418,3 @@ def get_item(kodi_id, media):
return
return item

View file

@ -8,7 +8,7 @@ import queries as QU
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################

View file

@ -1,182 +1,142 @@
get_item = """ SELECT kodi_id, kodi_fileid, kodi_pathid, parent_id, media_type,
jellyfin_type, media_folder, jellyfin_parent_id
FROM jellyfin
WHERE jellyfin_id = ?
"""
get_item_obj = [ "{Id}"
]
get_item_series_obj = [ "{SeriesId}"
]
get_item_song_obj = [ "{SongAlbumId}"
]
get_item_id_by_parent = """ SELECT jellyfin_id, kodi_id
FROM jellyfin
WHERE parent_id = ?
AND media_type = ?
"""
get_item_id_by_parent_boxset_obj = [ "{SetId}","movie"
]
get_item_by_parent = """ SELECT jellyfin_id, kodi_id, kodi_fileid
FROM jellyfin
WHERE parent_id = ?
AND media_type = ?
get_item = """ SELECT kodi_id, kodi_fileid, kodi_pathid, parent_id, media_type,
jellyfin_type, media_folder, jellyfin_parent_id
FROM jellyfin
WHERE jellyfin_id = ?
"""
get_item_obj = ["{Id}"]
get_item_series_obj = ["{SeriesId}"]
get_item_song_obj = ["{SongAlbumId}"]
get_item_id_by_parent = """ SELECT jellyfin_id, kodi_id
FROM jellyfin
WHERE parent_id = ?
AND media_type = ?
"""
get_item_by_media_folder = """ SELECT jellyfin_id, jellyfin_type
FROM jellyfin
WHERE media_folder = ?
get_item_id_by_parent_boxset_obj = ["{SetId}", "movie"]
get_item_by_parent = """ SELECT jellyfin_id, kodi_id, kodi_fileid
FROM jellyfin
WHERE parent_id = ?
AND media_type = ?
"""
get_item_by_media_folder = """ SELECT jellyfin_id, jellyfin_type
FROM jellyfin
WHERE media_folder = ?
"""
get_item_by_parent_movie_obj = [ "{KodiId}","movie"
]
get_item_by_parent_tvshow_obj = [ "{ParentId}","tvshow"
]
get_item_by_parent_season_obj = [ "{ParentId}","season"
]
get_item_by_parent_episode_obj = [ "{ParentId}","episode"
]
get_item_by_parent_album_obj = [ "{ParentId}","album"
]
get_item_by_parent_song_obj = [ "{ParentId}","song"
]
get_item_by_wild = """ SELECT kodi_id, media_type
FROM jellyfin
WHERE jellyfin_id LIKE ?
"""
get_item_by_wild_obj = [ "{Id}"
]
get_item_by_kodi = """ SELECT jellyfin_id, parent_id, media_folder, jellyfin_type, checksum
FROM jellyfin
WHERE kodi_id = ?
AND media_type = ?
"""
get_checksum = """ SELECT jellyfin_id, checksum
FROM jellyfin
WHERE jellyfin_type = ?
"""
get_view_name = """ SELECT view_name
FROM view
WHERE view_id = ?
get_item_by_parent_movie_obj = ["{KodiId}", "movie"]
get_item_by_parent_tvshow_obj = ["{ParentId}", "tvshow"]
get_item_by_parent_season_obj = ["{ParentId}", "season"]
get_item_by_parent_episode_obj = ["{ParentId}", "episode"]
get_item_by_parent_album_obj = ["{ParentId}", "album"]
get_item_by_parent_song_obj = ["{ParentId}", "song"]
get_item_by_wild = """ SELECT kodi_id, media_type
FROM jellyfin
WHERE jellyfin_id LIKE ?
"""
get_media_by_id = """ SELECT jellyfin_type
FROM jellyfin
WHERE jellyfin_id = ?
get_item_by_wild_obj = ["{Id}"]
get_item_by_kodi = """ SELECT jellyfin_id, parent_id, media_folder, jellyfin_type, checksum
FROM jellyfin
WHERE kodi_id = ?
AND media_type = ?
"""
get_media_by_parent_id = """ SELECT jellyfin_id, jellyfin_type, kodi_id, kodi_fileid
FROM jellyfin
WHERE jellyfin_parent_id = ?
"""
get_view = """ SELECT view_name, media_type
FROM view
get_checksum = """ SELECT jellyfin_id, checksum
FROM jellyfin
WHERE jellyfin_type = ?
"""
get_view_name = """ SELECT view_name
FROM view
WHERE view_id = ?
"""
get_views = """ SELECT *
get_media_by_id = """ SELECT jellyfin_type
FROM jellyfin
WHERE jellyfin_id = ?
"""
get_media_by_parent_id = """ SELECT jellyfin_id, jellyfin_type, kodi_id, kodi_fileid
FROM jellyfin
WHERE jellyfin_parent_id = ?
"""
get_view = """ SELECT view_name, media_type
FROM view
WHERE view_id = ?
"""
get_views = """ SELECT *
FROM view
"""
get_views_by_media = """ SELECT *
FROM view
WHERE media_type = ?
"""
get_items_by_media = """ SELECT jellyfin_id
FROM jellyfin
get_views_by_media = """ SELECT *
FROM view
WHERE media_type = ?
"""
get_version = """ SELECT idVersion
get_items_by_media = """ SELECT jellyfin_id
FROM jellyfin
WHERE media_type = ?
"""
get_version = """ SELECT idVersion
FROM version
"""
add_reference = """ INSERT OR REPLACE INTO jellyfin(jellyfin_id, kodi_id, kodi_fileid, kodi_pathid, jellyfin_type,
media_type, parent_id, checksum, media_folder, jellyfin_parent_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
add_reference_movie_obj = [ "{Id}","{MovieId}","{FileId}","{PathId}","Movie","movie", None,"{Checksum}","{LibraryId}",
"{JellyfinParentId}"
]
add_reference_boxset_obj = [ "{Id}","{SetId}",None,None,"BoxSet","set",None,"{Checksum}",None,None
]
add_reference_tvshow_obj = [ "{Id}","{ShowId}",None,"{PathId}","Series","tvshow",None,"{Checksum}","{LibraryId}",
"{JellyfinParentId}"
]
add_reference_season_obj = [ "{Id}","{SeasonId}",None,None,"Season","season","{ShowId}",None,None,None
]
add_reference_pool_obj = [ "{SeriesId}","{ShowId}",None,"{PathId}","Series","tvshow",None,"{Checksum}","{LibraryId}",None
]
add_reference_episode_obj = [ "{Id}","{EpisodeId}","{FileId}","{PathId}","Episode","episode","{SeasonId}","{Checksum}",
None,"{JellyfinParentId}"
]
add_reference_mvideo_obj = [ "{Id}","{MvideoId}","{FileId}","{PathId}","MusicVideo","musicvideo",None,"{Checksum}",
"{LibraryId}","{JellyfinParentId}"
]
add_reference_artist_obj = [ "{Id}","{ArtistId}",None,None,"{ArtistType}","artist",None,"{Checksum}","{LibraryId}",
"{JellyfinParentId}"
]
add_reference_album_obj = [ "{Id}","{AlbumId}",None,None,"MusicAlbum","album",None,"{Checksum}",None,"{JellyfinParentId}"
]
add_reference_song_obj = [ "{Id}","{SongId}",None,"{PathId}","Audio","song","{AlbumId}","{Checksum}",
None,"{JellyfinParentId}"
]
add_view = """ INSERT OR REPLACE INTO view(view_id, view_name, media_type)
add_reference = """ INSERT OR REPLACE INTO jellyfin(jellyfin_id, kodi_id, kodi_fileid, kodi_pathid, jellyfin_type,
media_type, parent_id, checksum, media_folder, jellyfin_parent_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
add_reference_movie_obj = ["{Id}", "{MovieId}", "{FileId}", "{PathId}", "Movie", "movie", None, "{Checksum}", "{LibraryId}", "{JellyfinParentId}"]
add_reference_boxset_obj = ["{Id}", "{SetId}", None, None, "BoxSet", "set", None, "{Checksum}", None, None]
add_reference_tvshow_obj = ["{Id}", "{ShowId}", None, "{PathId}", "Series", "tvshow", None, "{Checksum}", "{LibraryId}", "{JellyfinParentId}"]
add_reference_season_obj = ["{Id}", "{SeasonId}", None, None, "Season", "season", "{ShowId}", None, None, None]
add_reference_pool_obj = ["{SeriesId}", "{ShowId}", None, "{PathId}", "Series", "tvshow", None, "{Checksum}", "{LibraryId}", None]
add_reference_episode_obj = ["{Id}", "{EpisodeId}", "{FileId}", "{PathId}", "Episode", "episode", "{SeasonId}", "{Checksum}", None, "{JellyfinParentId}"]
add_reference_mvideo_obj = ["{Id}", "{MvideoId}", "{FileId}", "{PathId}", "MusicVideo", "musicvideo", None, "{Checksum}", "{LibraryId}", "{JellyfinParentId}"]
add_reference_artist_obj = ["{Id}", "{ArtistId}", None, None, "{ArtistType}", "artist", None, "{Checksum}", "{LibraryId}", "{JellyfinParentId}"]
add_reference_album_obj = ["{Id}", "{AlbumId}", None, None, "MusicAlbum", "album", None, "{Checksum}", None, "{JellyfinParentId}"]
add_reference_song_obj = ["{Id}", "{SongId}", None, "{PathId}", "Audio", "song", "{AlbumId}", "{Checksum}", None, "{JellyfinParentId}"]
add_view = """ INSERT OR REPLACE INTO view(view_id, view_name, media_type)
VALUES (?, ?, ?)
"""
add_version = """ INSERT OR REPLACE INTO version(idVersion)
add_version = """ INSERT OR REPLACE INTO version(idVersion)
VALUES (?)
"""
update_reference = """ UPDATE jellyfin
SET checksum = ?
WHERE jellyfin_id = ?
"""
update_reference_obj = [ "{Checksum}", "{Id}"
]
update_parent = """ UPDATE jellyfin
SET parent_id = ?
WHERE jellyfin_id = ?
"""
update_parent_movie_obj = [ "{SetId}","{Id}"
]
update_parent_episode_obj = [ "{SeasonId}","{Id}"
]
update_parent_album_obj = [ "{ArtistId}","{AlbumId}"]
update_reference = """ UPDATE jellyfin
SET checksum = ?
WHERE jellyfin_id = ?
"""
update_reference_obj = ["{Checksum}", "{Id}"]
update_parent = """ UPDATE jellyfin
SET parent_id = ?
WHERE jellyfin_id = ?
"""
update_parent_movie_obj = ["{SetId}", "{Id}"]
update_parent_episode_obj = ["{SeasonId}", "{Id}"]
update_parent_album_obj = ["{ArtistId}", "{AlbumId}"]
delete_item = """ DELETE FROM jellyfin
WHERE jellyfin_id = ?
"""
delete_item_obj = [ "{Id}"
]
delete_item_by_parent = """ DELETE FROM jellyfin
WHERE parent_id = ?
AND media_type = ?
"""
delete_item_by_parent_tvshow_obj = [ "{ParentId}","tvshow"
]
delete_item_by_parent_season_obj = [ "{ParentId}","season"
]
delete_item_by_parent_episode_obj = [ "{ParentId}","episode"
]
delete_item_by_parent_song_obj = [ "{ParentId}","song"
]
delete_item_by_parent_artist_obj = [ "{ParentId}","artist"
]
delete_item_by_parent_album_obj = [ "{KodiId}","album"
]
delete_item_by_kodi = """ DELETE FROM jellyfin
WHERE kodi_id = ?
AND media_type = ?
"""
delete_item_by_wild = """ DELETE FROM jellyfin
WHERE jellyfin_id LIKE ?
"""
delete_view = """ DELETE FROM view
delete_item = """ DELETE FROM jellyfin
WHERE jellyfin_id = ?
"""
delete_item_obj = ["{Id}"]
delete_item_by_parent = """ DELETE FROM jellyfin
WHERE parent_id = ?
AND media_type = ?
"""
delete_item_by_parent_tvshow_obj = ["{ParentId}", "tvshow"]
delete_item_by_parent_season_obj = ["{ParentId}", "season"]
delete_item_by_parent_episode_obj = ["{ParentId}", "episode"]
delete_item_by_parent_song_obj = ["{ParentId}", "song"]
delete_item_by_parent_artist_obj = ["{ParentId}", "artist"]
delete_item_by_parent_album_obj = ["{KodiId}", "album"]
delete_item_by_kodi = """ DELETE FROM jellyfin
WHERE kodi_id = ?
AND media_type = ?
"""
delete_item_by_wild = """ DELETE FROM jellyfin
WHERE jellyfin_id LIKE ?
"""
delete_view = """ DELETE FROM view
WHERE view_id = ?
"""
delete_parent_boxset_obj = [ None, "{Movie}"
]
delete_media_by_parent_id = """ DELETE FROM jellyfin
WHERE jellyfin_parent_id = ?
delete_parent_boxset_obj = [None, "{Movie}"]
delete_media_by_parent_id = """ DELETE FROM jellyfin
WHERE jellyfin_parent_id = ?
"""
delete_version = """ DELETE FROM version
delete_version = """ DELETE FROM version
"""

View file

@ -12,7 +12,7 @@ from helper import window, addon_id
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
ACTION_PARENT_DIR = 9
ACTION_PREVIOUS_MENU = 10
ACTION_BACK = 92
@ -29,7 +29,6 @@ class ContextMenu(xbmcgui.WindowXMLDialog):
_options = []
selected_option = None
def __init__(self, *args, **kwargs):
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)

View file

@ -12,7 +12,7 @@ from helper import _, addon_id
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
ACTION_PARENT_DIR = 9
ACTION_PREVIOUS_MENU = 10
ACTION_BACK = 92
@ -31,7 +31,6 @@ class LoginManual(xbmcgui.WindowXMLDialog):
error = None
username = None
def __init__(self, *args, **kwargs):
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)

View file

@ -10,7 +10,7 @@ import xbmcaddon
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
ACTION_PARENT_DIR = 9
ACTION_PREVIOUS_MENU = 10
ACTION_BACK = 92
@ -43,7 +43,7 @@ class ResumeDialog(xbmcgui.WindowXMLDialog):
self.getControl(START_BEGINNING).setLabel(xbmc.getLocalizedString(12021))
def onAction(self, action):
if action in (ACTION_BACK, ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU):
self.close()
@ -52,7 +52,7 @@ class ResumeDialog(xbmcgui.WindowXMLDialog):
if controlID == RESUME:
self.selected_option = 1
self.close()
if controlID == START_BEGINNING:
self.selected_option = 0
self.close()

View file

@ -12,7 +12,7 @@ from jellyfin.core.connection_manager import CONNECTION_STATE
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
ACTION_PARENT_DIR = 9
ACTION_PREVIOUS_MENU = 10
ACTION_BACK = 92
@ -38,7 +38,6 @@ class ServerConnect(xbmcgui.WindowXMLDialog):
_connect_login = False
_manual_server = False
def __init__(self, *args, **kwargs):
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)

View file

@ -13,7 +13,7 @@ from jellyfin.core.connection_manager import CONNECTION_STATE
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
ACTION_PARENT_DIR = 9
ACTION_PREVIOUS_MENU = 10
ACTION_BACK = 92
@ -34,7 +34,6 @@ class ServerManual(xbmcgui.WindowXMLDialog):
_server = None
error = None
def __init__(self, *args, **kwargs):
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)

View file

@ -9,7 +9,7 @@ import xbmcgui
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
ACTION_PARENT_DIR = 9
ACTION_PREVIOUS_MENU = 10
ACTION_BACK = 92
@ -27,7 +27,6 @@ class UsersConnect(xbmcgui.WindowXMLDialog):
_user = None
_manual_login = False
def __init__(self, *args, **kwargs):
self.kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2])

View file

@ -15,7 +15,7 @@ from jellyfin.core.exceptions import HTTPException
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
LIMIT = min(int(settings('limitIndex') or 50), 50)
#################################################################################################
@ -35,13 +35,14 @@ def browse_info():
return (
"DateCreated,EpisodeCount,SeasonCount,Path,Genres,Studios,Taglines,MediaStreams,Overview,Etag,"
"ProductionLocations,Width,Height,RecursiveItemCount,ChildCount"
)
)
def _http(action, url, request={}, server_id=None):
request.update({'url': url, 'type': action})
return Jellyfin(server_id).http.request(request)
def _get(handler, params=None, server_id=None):
return _http("GET", get_jellyfinserver_url(handler), {'params': params}, server_id)
@ -61,10 +62,10 @@ def validate_view(library_id, item_id):
'''
try:
result = _get("Users/{UserId}/Items", {
'ParentId': library_id,
'Recursive': True,
'Ids': item_id
})
'ParentId': library_id,
'Recursive': True,
'Ids': item_id
})
except Exception as error:
LOG.exception(error)
return False

View file

@ -17,7 +17,7 @@ from objects import Actions
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
XML_PATH = (xbmcaddon.Addon('plugin.video.jellyfin').getAddonInfo('path'), "default", "1080i")
OPTIONS = {
'Refresh': _(30410),

View file

@ -8,7 +8,6 @@ import sys
import urlparse
import urllib
import os
import sys
import xbmc
import xbmcvfs
@ -24,14 +23,13 @@ from helper import _, event, settings, window, dialog, api, JSONRPC
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
class Events(object):
def __init__(self):
''' Parse the parameters. Reroute to our service.py
@ -65,7 +63,7 @@ class Events(object):
jellyfin_id = params.get('id')
get_video_extras(jellyfin_id, jellyfin_path, server)
elif mode =='play':
elif mode == 'play':
item = TheVoid('GetItem', {'Id': params['id'], 'ServerId': server}).get()
Actions(server).play(item, params.get('dbid'), params.get('transcode') == 'true', playlist=params.get('playlist') == 'true')
@ -180,7 +178,6 @@ def listing():
else:
directory(server['Name'], "plugin://plugin.video.jellyfin/?mode=browse&server=%s" % server['Id'], context=context)
directory(_(33194), "plugin://plugin.video.jellyfin/?mode=managelibs", True)
directory(_(33134), "plugin://plugin.video.jellyfin/?mode=addserver", False)
directory(_(33054), "plugin://plugin.video.jellyfin/?mode=adduser", False)
@ -194,6 +191,7 @@ def listing():
xbmcplugin.setContent(int(sys.argv[1]), 'files')
xbmcplugin.endOfDirectory(int(sys.argv[1]))
def directory(label, path, folder=True, artwork=None, fanart=None, context=None):
''' Add directory listitem. context should be a list of tuples [(label, action)*]
@ -207,6 +205,7 @@ def directory(label, path, folder=True, artwork=None, fanart=None, context=None)
return li
def dir_listitem(label, path, artwork=None, fanart=None):
''' Gets the icon paths for default node listings
@ -218,6 +217,7 @@ def dir_listitem(label, path, artwork=None, fanart=None):
return li
def manage_libraries():
directory(_(33098), "plugin://plugin.video.jellyfin/?mode=refreshboxsets", False)
@ -230,6 +230,7 @@ def manage_libraries():
xbmcplugin.setContent(int(sys.argv[1]), 'files')
xbmcplugin.endOfDirectory(int(sys.argv[1]))
def browse(media, view_id=None, folder=None, server_id=None):
''' Browse content dynamically.
@ -274,7 +275,6 @@ def browse(media, view_id=None, folder=None, server_id=None):
elif media == 'music':
content_type = "artists"
if folder == 'recentlyadded':
listing = TheVoid('RecentlyAdded', {'Id': view_id, 'ServerId': server_id}).get()
elif folder == 'genres':
@ -316,7 +316,6 @@ def browse(media, view_id=None, folder=None, server_id=None):
else:
listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': False}).get()
if listing:
actions = Actions(server_id)
@ -339,7 +338,7 @@ def browse(media, view_id=None, folder=None, server_id=None):
'folder': item['Id'],
'server': server_id
}
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
context = []
if item['Type'] in ('Series', 'Season', 'Playlist'):
@ -362,7 +361,7 @@ def browse(media, view_id=None, folder=None, server_id=None):
'folder': 'genres-%s' % item['Id'],
'server': server_id
}
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
list_li.append((path, li, True))
else:
@ -396,6 +395,7 @@ def browse(media, view_id=None, folder=None, server_id=None):
xbmcplugin.setContent(int(sys.argv[1]), content_type)
xbmcplugin.endOfDirectory(int(sys.argv[1]))
def browse_subfolders(media, view_id, server_id=None):
''' Display submenus for jellyfin views.
@ -415,12 +415,13 @@ def browse_subfolders(media, view_id, server_id=None):
'folder': view_id if node[0] == 'all' else node[0],
'server': server_id
}
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
directory(node[1] or view['Name'], path)
xbmcplugin.setContent(int(sys.argv[1]), 'files')
xbmcplugin.endOfDirectory(int(sys.argv[1]))
def browse_letters(media, view_id, server_id=None):
''' Display letters as options.
@ -439,12 +440,13 @@ def browse_letters(media, view_id, server_id=None):
'folder': 'firstletter-%s' % node,
'server': server_id
}
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
directory(node, path)
xbmcplugin.setContent(int(sys.argv[1]), 'files')
xbmcplugin.endOfDirectory(int(sys.argv[1]))
def get_folder_type(item, content_type=None):
media = item['Type']
@ -480,6 +482,7 @@ def get_media_type(media):
elif media == 'music':
return "MusicArtist,MusicAlbum,Audio"
def get_fanart(item_id, path, server_id=None):
''' Get extra fanart for listitems. This is called by skinhelper.
@ -524,6 +527,7 @@ def get_fanart(item_id, path, server_id=None):
xbmcplugin.addDirectoryItems(int(sys.argv[1]), list_li, len(list_li))
xbmcplugin.endOfDirectory(int(sys.argv[1]))
def get_video_extras(item_id, path, server_id=None):
''' Returns the video files for the item as plugin listing, can be used
@ -565,6 +569,7 @@ def get_video_extras(item_id, path, server_id=None):
#xbmcplugin.endOfDirectory(int(sys.argv[1]))
"""
def get_next_episodes(item_id, limit):
''' Only for synced content.
@ -578,14 +583,14 @@ def get_next_episodes(item_id, limit):
return
result = JSONRPC('VideoLibrary.GetTVShows').execute({
'sort': {'order': "descending", 'method': "lastplayed"},
'filter': {
'and': [
{'operator': "true", 'field': "inprogress", 'value': ""},
{'operator': "is", 'field': "tag", 'value': "%s" % library}
]},
'properties': ['title', 'studio', 'mpaa', 'file', 'art']
})
'sort': {'order': "descending", 'method': "lastplayed"},
'filter': {
'and': [
{'operator': "true", 'field': "inprogress", 'value': ""},
{'operator': "is", 'field': "tag", 'value': "%s" % library}
]},
'properties': ['title', 'studio', 'mpaa', 'file', 'art']
})
try:
items = result['result']['tvshows']
@ -603,7 +608,7 @@ def get_next_episodes(item_id, limit):
'and': [
{'operator': "lessthan", 'field': "playcount", 'value': "1"},
{'operator': "greaterthan", 'field': "season", 'value': "0"}
]},
]},
'properties': [
"title", "playcount", "season", "episode", "showtitle",
"plot", "file", "rating", "resume", "tvshowid", "art",
@ -645,6 +650,7 @@ def get_next_episodes(item_id, limit):
xbmcplugin.setContent(int(sys.argv[1]), 'episodes')
xbmcplugin.endOfDirectory(int(sys.argv[1]))
def create_listitem(item):
''' Listitem based on jsonrpc items.
@ -656,7 +662,7 @@ def create_listitem(item):
metadata = {
'Title': title,
'duration': str(item['runtime']/60),
'duration': str(item['runtime'] / 60),
'Plot': item['plot'],
'Playcount': item['playcount']
}
@ -688,7 +694,7 @@ def create_listitem(item):
metadata['Premiered'] = item['firstaired']
if "rating" in item:
metadata['Rating'] = str(round(float(item['rating']),1))
metadata['Rating'] = str(round(float(item['rating']), 1))
if "director" in item:
metadata['Director'] = " / ".join(item['director'])
@ -711,10 +717,10 @@ def create_listitem(item):
li.setProperty('resumetime', str(item['resume']['position']))
li.setProperty('totaltime', str(item['resume']['total']))
li.setArt(item['art'])
li.setThumbnailImage(item['art'].get('thumb',''))
li.setThumbnailImage(item['art'].get('thumb', ''))
li.setIconImage('DefaultTVShows.png')
li.setProperty('dbid', str(item['episodeid']))
li.setProperty('fanart_image', item['art'].get('tvshow.fanart',''))
li.setProperty('fanart_image', item['art'].get('tvshow.fanart', ''))
for key, value in item['streamdetails'].iteritems():
for stream in value:
@ -722,6 +728,7 @@ def create_listitem(item):
return li
def add_user():
''' Add or remove users from the default server session.
@ -738,7 +745,7 @@ def add_user():
if result < 0:
return
if not result: # Add user
if not result: # Add user
eligible = [x for x in users if x['Id'] not in [current_user['UserId'] for current_user in current]]
resp = dialog("select", _(33064), [x['Name'] for x in eligible])
@ -747,7 +754,7 @@ def add_user():
user = eligible[resp]
event('AddUser', {'Id': user['Id'], 'Add': True})
else: # Remove user
else: # Remove user
resp = dialog("select", _(33064), [x['UserName'] for x in current])
if resp < 0:
@ -756,6 +763,7 @@ def add_user():
user = current[resp]
event('AddUser', {'Id': user['UserId'], 'Add': False})
def get_themes():
''' Add theme media locally, via strm. This is only for tv tunes.
@ -786,7 +794,6 @@ def get_themes():
all_views = jellyfin_db.JellyfinDatabase(jellyfindb.cursor).get_views()
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()
@ -829,6 +836,7 @@ def get_themes():
dialog("notification", heading="{jellyfin}", message=_(33153), icon="{jellyfin}", time=1000, sound=False)
def delete_item():
''' Delete keymap action.
@ -837,6 +845,7 @@ def delete_item():
context.Context(delete=True)
def backup():
''' Jellyfin backup.

View file

@ -2,7 +2,7 @@
#################################################################################################
import _strptime # Workaround for threads using datetime: _striptime is locked
import _strptime # Workaround for threads using datetime: _striptime is locked
import json
import logging
import sys
@ -24,7 +24,7 @@ from database import Database, jellyfin_db, reset
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
@ -38,7 +38,6 @@ class Service(xbmc.Monitor):
warn = True
settings = {'last_progress': datetime.today(), 'last_progress_report': datetime.today()}
def __init__(self):
window('jellyfin_should_stop', clear=True)
@ -209,7 +208,7 @@ class Service(xbmc.Monitor):
users = [user for user in (settings('additionalUsers') or "").decode('utf-8').split(',') if user]
users.insert(0, settings('username').decode('utf-8'))
dialog("notification", heading="{jellyfin}", message="%s %s" % (_(33000), ", ".join(users)),
icon="{jellyfin}", time=1500, sound=False)
icon="{jellyfin}", time=1500, sound=False)
if self.library_thread is None:
@ -352,7 +351,7 @@ class Service(xbmc.Monitor):
return
LOG.info("--<[ sleep ]")
xbmc.sleep(10000)# Allow network to wake up
xbmc.sleep(10000) # Allow network to wake up
self.monitor.sleep = False
window('jellyfin_should_stop', clear=True)
@ -444,7 +443,7 @@ class Service(xbmc.Monitor):
LOG.info("---<[ EXITING ]")
window('jellyfin_should_stop.bool', True)
properties = [ # TODO: review
properties = [ # TODO: review
"jellyfin_state", "jellyfin_serverStatus", "jellyfin_currUser",
"jellyfin_play", "jellyfin_online", "jellyfin.connected", "jellyfin.resume", "jellyfin_startup",

View file

@ -19,7 +19,7 @@ from views import Views
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
@ -36,7 +36,6 @@ class FullSync(object):
running = False
screensaver = None
def __init__(self, library, server):
''' You can call all big syncing methods here.
@ -69,7 +68,6 @@ class FullSync(object):
return self
def libraries(self, library_id=None, update=False):
''' Map the syncing process and start the sync. Ensure only one sync is running.
@ -179,7 +177,6 @@ class FullSync(object):
return [libraries[x - 1] for x in selection]
def start(self):
''' Main sync process.
@ -278,7 +275,7 @@ class FullSync(object):
for index, movie in enumerate(items['Items']):
dialog.update(int((float(start_index + index) / float(items['TotalRecordCount']))*100),
dialog.update(int((float(start_index + index) / float(items['TotalRecordCount'])) * 100),
heading="%s: %s" % (_('addon_name'), library['Name']),
message=movie['Name'])
obj.movie(movie, library=library)
@ -318,11 +315,11 @@ class FullSync(object):
for index, show in enumerate(items['Items']):
percent = int((float(start_index + index) / float(items['TotalRecordCount']))*100)
percent = int((float(start_index + index) / float(items['TotalRecordCount'])) * 100)
message = show['Name']
dialog.update(percent, heading="%s: %s" % (_('addon_name'), library['Name']), message=message)
if obj.tvshow(show, library=library) != False:
if obj.tvshow(show, library=library):
for episodes in server.get_episode_by_show(show['Id']):
for episode in episodes['Items']:
@ -368,7 +365,7 @@ class FullSync(object):
for index, mvideo in enumerate(items['Items']):
dialog.update(int((float(start_index + index) / float(items['TotalRecordCount']))*100),
dialog.update(int((float(start_index + index) / float(items['TotalRecordCount'])) * 100),
heading="%s: %s" % (_('addon_name'), library['Name']),
message=mvideo['Name'])
obj.musicvideo(mvideo, library=library)
@ -408,7 +405,7 @@ class FullSync(object):
for index, artist in enumerate(items['Items']):
percent = int((float(start_index + index) / float(items['TotalRecordCount']))*100)
percent = int((float(start_index + index) / float(items['TotalRecordCount'])) * 100)
message = artist['Name']
dialog.update(percent, heading="%s: %s" % (_('addon_name'), library['Name']), message=message)
obj.artist(artist, library=library)
@ -431,7 +428,6 @@ class FullSync(object):
dialog.update(percent, message="%s/%s" % (message, song['Name']))
obj.song(song)
if self.update_library:
self.music_compare(library, obj, jellyfindb)
@ -470,7 +466,7 @@ class FullSync(object):
for index, boxset in enumerate(items['Items']):
dialog.update(int((float(start_index + index) / float(items['TotalRecordCount']))*100),
dialog.update(int((float(start_index + index) / float(items['TotalRecordCount'])) * 100),
heading="%s: %s" % (_('addon_name'), _('boxsets')),
message=boxset['Name'])
obj.boxset(boxset)
@ -524,7 +520,7 @@ class FullSync(object):
for item in movies:
obj(item[0])
dialog.update(int((float(count) / float(len(items))*100)), heading="%s: %s" % (_('addon_name'), library[0]))
dialog.update(int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0]))
count += 1
obj = TVShows(self.server, jellyfindb, kodidb, direct_path).remove
@ -532,7 +528,7 @@ class FullSync(object):
for item in tvshows:
obj(item[0])
dialog.update(int((float(count) / float(len(items))*100)), heading="%s: %s" % (_('addon_name'), library[0]))
dialog.update(int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0]))
count += 1
else:
# from mcarlton: I'm not sure what triggers this.
@ -547,7 +543,7 @@ class FullSync(object):
for item in items:
obj(item[0])
dialog.update(int((float(count) / float(len(items))*100)), heading="%s: %s" % (_('addon_name'), library[0]))
dialog.update(int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0]))
count += 1
self.sync = get_sync()
@ -560,7 +556,6 @@ class FullSync(object):
save_sync(self.sync)
def __exit__(self, exc_type, exc_val, exc_tb):
''' Exiting sync

View file

@ -8,14 +8,14 @@ from . import settings
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
class API(object):
def __init__(self, item, server=None):
''' Get item information in special cases.
server is the server address, provide if your functions requires it.
'''
@ -225,7 +225,7 @@ class API(object):
return path
def get_user_artwork(self, user_id):
''' Get jellyfin user profile picture.
'''
return "%s/emby/Users/%s/Images/Primary?Format=original" % (self.server, user_id)
@ -286,7 +286,7 @@ class API(object):
if obj.get('SeriesTag'):
all_artwork['Series.Primary'] = self.get_artwork(obj['SeriesId'], "Primary", obj['SeriesTag'], query)
if not all_artwork['Primary']:
all_artwork['Primary'] = all_artwork['Series.Primary']

View file

@ -2,9 +2,8 @@
#################################################################################################
class LibraryException(Exception):
# Jellyfin library sync exception
def __init__(self, status):
self.status = status

View file

@ -20,7 +20,7 @@ from downloader import TheVoid
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
@ -52,8 +52,8 @@ def set_properties(item, method, server_id=None):
window('jellyfin_play.json', current)
class PlayUtils(object):
class PlayUtils(object):
def __init__(self, item, force_transcode=False, server_id=None, server=None, token=None):
@ -236,7 +236,7 @@ class PlayUtils(object):
def transcode(self, source, audio=None, subtitle=None):
if not 'TranscodingUrl' in source:
if 'TranscodingUrl' not in source:
raise Exception("use get_sources to get transcoding url")
self.info['Method'] = "Transcode"
@ -248,7 +248,7 @@ class PlayUtils(object):
url_parsed = params.split('&')
for i in url_parsed:
if 'AudioStreamIndex' in i or 'AudioBitrate' in i or 'SubtitleStreamIndex' in i: # handle manually
if 'AudioStreamIndex' in i or 'AudioBitrate' in i or 'SubtitleStreamIndex' in i: # handle manually
url_parsed.remove(i)
params = "%s%s" % ('&'.join(url_parsed), self.get_audio_subs(source, audio, subtitle))
@ -275,13 +275,19 @@ class PlayUtils(object):
self.info['Method'] = "DirectStream"
if self.item['Type'] == "Audio":
self.info['Path'] = ("%s/emby/Audio/%s/stream.%s?static=true&api_key=%s" %
(self.info['ServerAddress'], self.item['Id'],
source.get('Container', "mp4").split(',')[0],
self.info['Token']))
self.info['Path'] = "%s/emby/Audio/%s/stream.%s?static=true&api_key=%s" % (
self.info['ServerAddress'],
self.item['Id'],
source.get('Container', "mp4").split(',')[0],
self.info['Token']
)
else:
self.info['Path'] = ("%s/emby/Videos/%s/stream?static=true&MediaSourceId=%s&api_key=%s" %
(self.info['ServerAddress'], self.item['Id'], source['Id'], self.info['Token']))
self.info['Path'] = "%s/emby/Videos/%s/stream?static=true&MediaSourceId=%s&api_key=%s" % (
self.info['ServerAddress'],
self.item['Id'],
source['Id'],
self.info['Token']
)
return self.info['Path']
@ -495,7 +501,6 @@ class PlayUtils(object):
listitem.setSubtitles(subs)
self.item['PlaybackInfo']['Subtitles'] = mapping
@classmethod
def download_external_subs(cls, src, filename):
@ -579,7 +584,7 @@ class PlayUtils(object):
selection = list(audio_streams.keys())
resp = dialog("select", _(33013), selection)
audio_selected = audio_streams[selection[resp]] if resp else source['DefaultAudioStreamIndex']
else: # Only one choice
else: # Only one choice
audio_selected = audio_streams[next(iter(audio_streams))]
else:
audio_selected = source['DefaultAudioStreamIndex']
@ -628,7 +633,13 @@ class PlayUtils(object):
if stream['IsTextSubtitleStream'] and 'DeliveryUrl' in stream and stream['DeliveryUrl'].lower().startswith('/videos'):
url = "%s/emby%s" % (self.info['ServerAddress'], stream['DeliveryUrl'])
else:
url = ("%s/emby/Videos/%s/%s/Subtitles/%s/Stream.%s?api_key=%s" %
(self.info['ServerAddress'], self.item['Id'], source['Id'], index, stream['Codec'], self.info['Token']))
url = "%s/emby/Videos/%s/%s/Subtitles/%s/Stream.%s?api_key=%s" % (
self.info['ServerAddress'],
self.item['Id'],
source['Id'],
index,
stream['Codec'],
self.info['Token']
)
return url

View file

@ -11,10 +11,11 @@ import xbmcaddon
##################################################################################################
LOG = logging.getLogger('JELLYFIN.'+__name__)
LOG = logging.getLogger('JELLYFIN.' + __name__)
##################################################################################################
def _(string):
''' Get add-on string. Returns in unicode.
@ -26,7 +27,7 @@ def _(string):
if not result:
result = xbmc.getLocalizedString(string)
return result

View file

@ -22,16 +22,19 @@ from dateutil import tz, parser
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
def addon_id():
return "plugin.video.jellyfin"
def kodi_version():
return xbmc.getInfoLabel('System.BuildVersion')[:2]
def window(key, value=None, clear=False, window_id=10000):
''' Get or set window properties.
@ -65,6 +68,7 @@ def window(key, value=None, clear=False, window_id=10000):
return result
def settings(setting, value=None):
''' Get or add add-on settings.
@ -87,9 +91,11 @@ def settings(setting, value=None):
return result
def create_id():
return uuid4()
def compare_version(a, b):
''' -1 a is smaller
@ -107,6 +113,7 @@ def compare_version(a, b):
return 0
def find(dict, item):
''' Find value in dictionary.
@ -114,11 +121,12 @@ def find(dict, item):
if item in dict:
return dict[item]
for key,value in sorted(dict.iteritems(), key=lambda (k,v): (v,k)):
for key, value in sorted(dict.iteritems(), key=lambda (k, v): (v, k)):
if re.match(key, item, re.I):
return dict[key]
def event(method, data=None, sender=None, hexlify=False):
''' Data is a dictionary.
@ -134,13 +142,16 @@ def event(method, data=None, sender=None, hexlify=False):
xbmc.executebuiltin('NotifyAll(%s, %s, %s)' % (sender, method, data))
LOG.debug("---[ event: %s/%s ] %s", sender, method, data)
def dialog(dialog_type, *args, **kwargs):
d = xbmcgui.Dialog()
if "icon" in kwargs:
kwargs['icon'] = kwargs['icon'].replace("{jellyfin}",
"special://home/addons/plugin.video.jellyfin/resources/icon.png")
kwargs['icon'] = kwargs['icon'].replace(
"{jellyfin}",
"special://home/addons/plugin.video.jellyfin/resources/icon.png"
)
if "heading" in kwargs:
kwargs['heading'] = kwargs['heading'].replace("{jellyfin}", _('addon_name'))
@ -155,6 +166,7 @@ def dialog(dialog_type, *args, **kwargs):
}
return types[dialog_type](*args, **kwargs)
def should_stop():
''' Checkpoint during the sync process.
@ -171,6 +183,7 @@ def should_stop():
return False
def get_screensaver():
''' Get the current screensaver value.
@ -181,6 +194,7 @@ def get_screensaver():
except KeyError:
return ""
def set_screensaver(value):
''' Toggle the screensaver
@ -192,6 +206,7 @@ def set_screensaver(value):
result = JSONRPC('Settings.setSettingValue').execute(params)
LOG.info("---[ screensaver/%s ] %s", value, result)
class JSONRPC(object):
version = 1
@ -221,6 +236,7 @@ class JSONRPC(object):
self.params = params
return json.loads(xbmc.executeJSONRPC(self._query()))
def validate(path):
''' Verify if path is accessible.
@ -241,6 +257,7 @@ def validate(path):
return True
def values(item, keys):
''' Grab the values in the item for a list of keys {key},{key1}....
@ -248,6 +265,7 @@ def values(item, keys):
'''
return (item[key.replace('{', "").replace('}', "")] if type(key) == str and key.startswith('{') else key for key in keys)
def indent(elem, level=0):
''' Prettify xml docs.
@ -256,20 +274,21 @@ def indent(elem, level=0):
i = "\n" + level * " "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
elem.tail = i
for elem in elem:
indent(elem, level + 1)
indent(elem, level + 1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
elem.tail = i
except Exception as error:
LOG.exception(error)
return
def write_xml(content, file):
with open(file, 'w') as infile:
@ -332,6 +351,7 @@ def unzip(path, dest, folder=None):
LOG.info("Unzipped %s", path)
def unzip_recursive(path, dirs, dest):
for directory in dirs:
@ -348,6 +368,7 @@ def unzip_recursive(path, dirs, dest):
for file in files:
unzip_file(os.path.join(dirs_dir, file.decode('utf-8')), os.path.join(dest_dir, file.decode('utf-8')))
def unzip_file(path, dest):
''' Unzip specific file. Path should start with zip://
@ -355,6 +376,7 @@ def unzip_file(path, dest):
xbmcvfs.copy(path, dest)
LOG.debug("unzip: %s to %s", path, dest)
def get_zip_directory(path, folder):
dirs, files = xbmcvfs.listdir(path)
@ -367,6 +389,7 @@ def get_zip_directory(path, folder):
if result:
return result
def copytree(path, dest):
''' Copy folder content from one to another.
@ -384,6 +407,7 @@ def copytree(path, dest):
LOG.info("Copied %s", path)
def copy_recursive(path, dirs, dest):
for directory in dirs:
@ -400,6 +424,7 @@ def copy_recursive(path, dirs, dest):
for file in files:
copy_file(os.path.join(dirs_dir, file.decode('utf-8')), os.path.join(dest_dir, file.decode('utf-8')))
def copy_file(path, dest):
''' Copy specific file.
@ -410,6 +435,7 @@ def copy_file(path, dest):
xbmcvfs.copy(path, dest)
LOG.debug("copy: %s to %s", path, dest)
def normalize_string(text):
''' For theme media, do not modify unless modified in TV Tunes.
@ -431,11 +457,13 @@ def normalize_string(text):
return text
def split_list(itemlist, size):
''' Split up list in pieces of size. Will generate a list of lists
'''
return [itemlist[i:i+size] for i in range(0, len(itemlist), size)]
return [itemlist[i:i + size] for i in range(0, len(itemlist), size)]
def convert_to_local(date):

View file

@ -11,10 +11,11 @@ from utils import should_stop
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
def progress(message=None):
''' Will start and close the progress dialog.
@ -61,6 +62,7 @@ def catch(errors=(Exception,)):
return wrapper
return decorator
def silent_catch(errors=(Exception,)):
''' Wrapper to catch exceptions and ignore them
@ -76,6 +78,7 @@ def silent_catch(errors=(Exception,)):
return wrapper
return decorator
def stop(default=None):
''' Wrapper to catch exceptions and return using catch
@ -100,6 +103,7 @@ def stop(default=None):
return wrapper
return decorator
def jellyfin_item():
''' Wrapper to retrieve the jellyfin_db item.
@ -113,6 +117,7 @@ def jellyfin_item():
return wrapper
return decorator
def library_check():
''' Wrapper to retrieve the library
@ -148,7 +153,7 @@ def library_check():
return
view = {'Id': views[0], 'Name': views[1]}
else: # Grab the first music library
else: # Grab the first music library
return
else:
for ancestor in ancestors:

View file

@ -13,10 +13,11 @@ from . import _, indent, write_xml, dialog, settings
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
def sources():
''' Create master lock compatible sources.
@ -77,6 +78,7 @@ def sources():
indent(xml)
write_xml(etree.tostring(xml, 'UTF-8'), file)
def tvtunes_nfo(path, urls):
''' Create tvtunes.nfo
@ -96,6 +98,7 @@ def tvtunes_nfo(path, urls):
indent(xml)
write_xml(etree.tostring(xml, 'UTF-8'), path)
def advanced_settings():
''' Track the existence of <cleanonupdate>true</cleanonupdate>

View file

@ -9,21 +9,25 @@ from helpers import has_attribute
#################################################################################################
class NullHandler(logging.Handler):
def emit(self, record):
print(self.format(record))
loghandler = NullHandler
LOG = logging.getLogger('Jellyfin')
#################################################################################################
def config(level=logging.INFO):
logger = logging.getLogger('Jellyfin')
logger.addHandler(Jellyfin.loghandler())
logger.setLevel(level)
def ensure_client():
def decorator(func):
@ -109,7 +113,7 @@ class Jellyfin(object):
@ensure_client()
def __getattr__(self, name):
return getattr(self.client[self.server_id], name)
def construct(self):
self.client[self.server_id] = JellyfinClient()

View file

@ -12,10 +12,11 @@ from core.connection_manager import ConnectionManager, CONNECTION_STATE
#################################################################################################
LOG = logging.getLogger('JELLYFIN.'+__name__)
LOG = logging.getLogger('JELLYFIN.' + __name__)
#################################################################################################
def callback(message, data):
''' Callback function should received message, data

View file

@ -1 +0,0 @@

View file

@ -225,15 +225,15 @@ class API(object):
def get_themes(self, item_id):
return self.items("/%s/ThemeMedia" % item_id, params={
'UserId': "{UserId}",
'InheritFromParent': True
})
'UserId': "{UserId}",
'InheritFromParent': True
})
def get_items_theme_song(self, parent_id):
return self.users("/Items", params={
'HasThemeSong': True,
'ParentId': parent_id
})
'HasThemeSong': True,
'ParentId': parent_id
})
def get_plugins(self):
return self._get("Plugins")

View file

@ -12,7 +12,7 @@ import logging
DEFAULT_HTTP_MAX_RETRIES = 3
DEFAULT_HTTP_TIMEOUT = 30
LOG = logging.getLogger('JELLYFIN.'+__name__)
LOG = logging.getLogger('JELLYFIN.' + __name__)
#################################################################################################

View file

@ -16,7 +16,7 @@ from http import HTTP
#################################################################################################
LOG = logging.getLogger('JELLYFIN.'+__name__)
LOG = logging.getLogger('JELLYFIN.' + __name__)
CONNECTION_STATE = {
'Unavailable': 0,
'ServerSelection': 1,
@ -31,6 +31,7 @@ CONNECTION_MODE = {
#################################################################################################
def get_server_address(server, mode):
modes = {
@ -86,7 +87,7 @@ class ConnectionManager(object):
credentials = self.credentials.get_credentials()
found_servers = self._find_servers(self._server_discovery())
if not found_servers and not credentials['Servers']: # back out right away, no point in continuing
if not found_servers and not credentials['Servers']: # back out right away, no point in continuing
LOG.info("Found no servers")
return list()
@ -178,7 +179,7 @@ class ConnectionManager(object):
LOG.info("beginning connection tests")
return self._test_next_connection_mode(tests, 0, server, options)
def get_server_address(self, server, mode): #TODO: De-duplicated (Duplicated from above when getting rid of shortcuts)
def get_server_address(self, server, mode): # TODO: De-duplicated (Duplicated from above when getting rid of shortcuts)
modes = {
CONNECTION_MODE['Local']: server.get('LocalAddress'),
@ -239,8 +240,10 @@ class ConnectionManager(object):
request.pop('dataType')
headers['X-Application'] = self._add_app_info()
headers['Content-type'] = request.get('contentType',
'application/x-www-form-urlencoded; charset=UTF-8')
headers['Content-type'] = request.get(
'contentType',
'application/x-www-form-urlencoded; charset=UTF-8'
)
def _connect_to_servers(self, servers, options):
@ -379,7 +382,7 @@ class ConnectionManager(object):
MESSAGE = "who is JellyfinServer?"
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(1.0) # This controls the socket.timeout exception
sock.settimeout(1.0) # This controls the socket.timeout exception
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 20)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
@ -399,7 +402,7 @@ class ConnectionManager(object):
while True:
try:
data, addr = sock.recvfrom(1024) # buffer size
data, addr = sock.recvfrom(1024) # buffer size
servers.append(json.loads(data))
except socket.timeout:
@ -446,7 +449,7 @@ class ConnectionManager(object):
'Id': found_server['Id'],
'LocalAddress': server or found_server['Address'],
'Name': found_server['Name']
} #TODO
} # TODO
info['LastConnectionMode'] = CONNECTION_MODE['Manual'] if info.get('ManualAddress') else CONNECTION_MODE['Local']
servers.append(info)
@ -461,7 +464,7 @@ class ConnectionManager(object):
# Determine the port, if any
parts = info['Address'].split(':')
if len(parts) > 1:
port_string = parts[len(parts)-1]
port_string = parts[len(parts) - 1]
try:
address += ":%s" % int(port_string)
@ -496,7 +499,7 @@ class ConnectionManager(object):
def _after_connect_validated(self, server, credentials, system_info, connection_mode, verify_authentication, options):
if options.get('enableAutoLogin') == False:
if not options.get('enableAutoLogin'):
self.config.data['auth.user_id'] = server.pop('UserId', None)
self.config.data['auth.token'] = server.pop('AccessToken', None)
@ -581,7 +584,8 @@ class ConnectionManager(object):
if server['Id'] == result['ServerId']:
found_server = server
break
else: return # No server found
else:
return # No server found
if options.get('updateDateLastAccessed') is not False:
found_server['DateLastAccessed'] = datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ')

View file

@ -10,10 +10,11 @@ from datetime import datetime
#################################################################################################
LOG = logging.getLogger('JELLYFIN.'+__name__)
LOG = logging.getLogger('JELLYFIN.' + __name__)
#################################################################################################
class Credentials(object):
credentials = None

View file

@ -2,10 +2,9 @@
#################################################################################################
class HTTPException(Exception):
# Jellyfin HTTP exception
def __init__(self, status, message):
self.status = status
self.message = message

View file

@ -11,7 +11,7 @@ from exceptions import HTTPException
#################################################################################################
LOG = logging.getLogger('Jellyfin.'+__name__)
LOG = logging.getLogger('Jellyfin.' + __name__)
#################################################################################################
@ -127,7 +127,7 @@ class HTTP(object):
raise HTTPException("AccessRestricted", error)
else:
self.client.callback("Unauthorized", {'ServerId': self.config.data['auth.server-id']})
self.client.auth.revoke_token()
self.client.auth.revoke_token()
raise HTTPException("Unauthorized", error)

View file

@ -13,7 +13,7 @@ from ..resources import websocket
##################################################################################################
LOG = logging.getLogger('JELLYFIN.'+__name__)
LOG = logging.getLogger('JELLYFIN.' + __name__)
##################################################################################################

View file

@ -7,9 +7,10 @@ from uuid import uuid4
#################################################################################################
LOG = logging.getLogger('JELLYFIN.'+__name__)
LOG = logging.getLogger('JELLYFIN.' + __name__)
#################################################################################################
def generate_client_id():
return str("%012X" % uuid4())

View file

@ -43,8 +43,6 @@ import base64
import threading
import time
import logging
import traceback
import sys
"""
websocket python client.
@ -89,12 +87,14 @@ class WebSocketConnectionClosedException(WebSocketException):
"""
pass
class WebSocketTimeoutException(WebSocketException):
"""
WebSocketTimeoutException will be raised at socket timeout during read/write data.
"""
pass
default_timeout = None
traceEnabled = False
@ -135,8 +135,10 @@ def _wrap_sni_socket(sock, sslopt, hostname):
if sslopt.get('cert_reqs', ssl.CERT_NONE) != ssl.CERT_NONE:
capath = ssl.get_default_verify_paths().capath
context.load_verify_locations(cafile=sslopt.get('ca_certs', None),
capath=sslopt.get('ca_cert_path', capath))
context.load_verify_locations(
cafile=sslopt.get('ca_certs', None),
capath=sslopt.get('ca_cert_path', capath)
)
return context.wrap_socket(
sock,
@ -217,9 +219,10 @@ def create_connection(url, timeout=None, **options):
websock.connect(url, **options)
return websock
_MAX_INTEGER = (1 << 32) -1
_MAX_INTEGER = (1 << 32) - 1
_AVAILABLE_KEY_CHARS = range(0x21, 0x2f + 1) + range(0x3a, 0x7e + 1)
_MAX_CHAR_BYTE = (1<<8) -1
_MAX_CHAR_BYTE = (1 << 8) - 1
# ref. Websocket gets an update, and it breaks stuff.
# http://axod.blogspot.com/2010/06/websocket-gets-update-and-it-breaks.html
@ -233,7 +236,7 @@ def _create_sec_websocket_key():
_HEADERS_TO_CHECK = {
"upgrade": "websocket",
"connection": "upgrade",
}
}
class ABNF(object):
@ -244,16 +247,16 @@ class ABNF(object):
"""
# operation code values.
OPCODE_CONT = 0x0
OPCODE_TEXT = 0x1
OPCODE_CONT = 0x0
OPCODE_TEXT = 0x1
OPCODE_BINARY = 0x2
OPCODE_CLOSE = 0x8
OPCODE_PING = 0x9
OPCODE_PONG = 0xa
OPCODE_CLOSE = 0x8
OPCODE_PING = 0x9
OPCODE_PONG = 0xa
# available operation code value tuple
OPCODES = (OPCODE_CONT, OPCODE_TEXT, OPCODE_BINARY, OPCODE_CLOSE,
OPCODE_PING, OPCODE_PONG)
OPCODE_PING, OPCODE_PONG)
# opcode human readable string
OPCODE_MAP = {
@ -263,10 +266,10 @@ class ABNF(object):
OPCODE_CLOSE: "close",
OPCODE_PING: "ping",
OPCODE_PONG: "pong"
}
}
# data length threashold.
LENGTH_7 = 0x7d
LENGTH_7 = 0x7d
LENGTH_16 = 1 << 16
LENGTH_63 = 1 << 63
@ -287,8 +290,8 @@ class ABNF(object):
def __str__(self):
return "fin=" + str(self.fin) \
+ " opcode=" + str(self.opcode) \
+ " data=" + str(self.data)
+ " opcode=" + str(self.opcode) \
+ " data=" + str(self.data)
@staticmethod
def create_frame(data, opcode):
@ -318,9 +321,7 @@ class ABNF(object):
if length >= ABNF.LENGTH_63:
raise ValueError("data is too long")
frame_header = chr(self.fin << 7
| self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4
| self.opcode)
frame_header = chr(self.fin << 7 | self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4 | self.opcode)
if length < ABNF.LENGTH_7:
frame_header += chr(self.mask << 7 | length)
elif length < ABNF.LENGTH_16:
@ -582,8 +583,7 @@ class WebSocket(object):
if traceEnabled:
logger.debug("send: " + repr(data))
while data:
l = self._send(data)
data = data[l:]
data = data[self._send(data):]
return length
def send_binary(self, payload):
@ -685,7 +685,6 @@ class WebSocket(object):
self._frame_mask = None
return ABNF(fin, rsv1, rsv2, rsv3, opcode, has_mask, payload)
def send_close(self, status=STATUS_NORMAL, reason=""):
"""
send close data to the server.
@ -709,7 +708,7 @@ class WebSocket(object):
try:
self.sock.shutdown(socket.SHUT_RDWR)
except:
except: # noqa: E722
pass
'''
@ -766,7 +765,6 @@ class WebSocket(object):
raise WebSocketConnectionClosedException()
return bytes
def _recv_strict(self, bufsize):
shortage = bufsize - sum(len(x) for x in self._recv_buffer)
while shortage > 0:
@ -781,7 +779,6 @@ class WebSocket(object):
self._recv_buffer = [unified[bufsize:]]
return unified[:bufsize]
def _recv_line(self):
line = []
while True:
@ -844,7 +841,7 @@ class WebSocketApp(object):
close websocket connection.
"""
self.keep_running = False
if(self.sock != None):
if self.sock is None:
self.sock.close()
def _send_ping(self, interval):
@ -890,7 +887,7 @@ class WebSocketApp(object):
try:
data = self.sock.recv()
if data is None or self.keep_running == False:
if data is None or not self.keep_running:
break
self._callback(self.on_message, data)

View file

@ -22,7 +22,7 @@ from jellyfin import Jellyfin
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
LIMIT = min(int(settings('limitIndex') or 50), 50)
DTHREADS = int(settings('limitThreads') or 3)
MEDIA = {
@ -41,7 +41,6 @@ MEDIA = {
##################################################################################################
class Library(threading.Thread):
started = False
@ -52,7 +51,6 @@ class Library(threading.Thread):
progress_updates = None
total_updates = 0
def __init__(self, monitor):
self.media = {'Movies': Movies, 'TVShows': TVShows, 'MusicVideos': MusicVideos, 'Music': Music}
@ -159,11 +157,11 @@ class Library(threading.Thread):
self.progress_updates = xbmcgui.DialogProgressBG()
self.progress_updates.create(_('addon_name'), _(33178))
self.progress_updates.update(int((float(self.total_updates - queue_size) / float(self.total_updates))*100), message="%s: %s" % (_(33178), queue_size))
self.progress_updates.update(int((float(self.total_updates - queue_size) / float(self.total_updates)) * 100), message="%s: %s" % (_(33178), queue_size))
elif queue_size:
self.progress_updates.update(int((float(self.total_updates - queue_size) / float(self.total_updates))*100), message="%s: %s" % (_(33178), queue_size))
self.progress_updates.update(int((float(self.total_updates - queue_size) / float(self.total_updates)) * 100), message="%s: %s" % (_(33178), queue_size))
else:
self.progress_updates.update(int((float(self.total_updates - queue_size) / float(self.total_updates))*100), message=_(33178))
self.progress_updates.update(int((float(self.total_updates - queue_size) / float(self.total_updates)) * 100), message=_(33178))
if not settings('dbSyncScreensaver.bool') and self.screensaver is None:
@ -171,8 +169,7 @@ class Library(threading.Thread):
self.screensaver = get_screensaver()
set_screensaver(value="")
if (self.pending_refresh and not self.download_threads and not self.writer_threads['updated'] and
not self.writer_threads['userdata'] and not self.writer_threads['removed']):
if (self.pending_refresh and not self.download_threads and not self.writer_threads['updated'] and not self.writer_threads['userdata'] and not self.writer_threads['removed']):
self.pending_refresh = False
self.save_last_sync()
self.total_updates = 0
@ -189,9 +186,9 @@ class Library(threading.Thread):
set_screensaver(value=self.screensaver)
self.screensaver = None
if xbmc.getCondVisibility('Container.Content(musicvideos)'): # Prevent cursor from moving
if xbmc.getCondVisibility('Container.Content(musicvideos)'): # Prevent cursor from moving
xbmc.executebuiltin('Container.Refresh')
else: # Update widgets
else: # Update widgets
xbmc.executebuiltin('UpdateLibrary(video)')
if xbmc.getCondVisibility('Window.IsMedia'):
@ -313,7 +310,6 @@ class Library(threading.Thread):
LOG.info("-->[ q:notify/%s ]", id(new_thread))
self.notify_threads.append(new_thread)
def startup(self):
''' Run at startup.
@ -491,7 +487,7 @@ class Library(threading.Thread):
available = [x for x in sync['SortedViews'] if x not in whitelist]
for library in available:
name, media = db.get_view(library)
name, media = db.get_view(library)
if media in ('movies', 'tvshows', 'musicvideos', 'mixed', 'music'):
libraries.append({'Id': library, 'Name': name})
@ -546,7 +542,6 @@ class Library(threading.Thread):
return True
def userdata(self, data):
''' Add item_id to userdata queue.
@ -654,6 +649,7 @@ class UpdatedWorker(threading.Thread):
LOG.info("--<[ q:updated/%s ]", id(self))
self.is_done = True
class UserDataWorker(threading.Thread):
is_done = False
@ -697,6 +693,7 @@ class UserDataWorker(threading.Thread):
LOG.info("--<[ q:userdata/%s ]", id(self))
self.is_done = True
class SortWorker(threading.Thread):
is_done = False
@ -742,6 +739,7 @@ class SortWorker(threading.Thread):
LOG.info("--<[ q:sort/%s ]", id(self))
self.is_done = True
class RemovedWorker(threading.Thread):
is_done = False
@ -789,6 +787,7 @@ class RemovedWorker(threading.Thread):
LOG.info("--<[ q:removed/%s ]", id(self))
self.is_done = True
class NotifyWorker(threading.Thread):
is_done = False

View file

@ -22,7 +22,7 @@ from webservice import WebService
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
@ -84,7 +84,7 @@ class Monitor(xbmc.Monitor):
Otherwise the next played item will be added the previous queue.
'''
if method == "Player.OnStop":
xbmc.sleep(3000) # let's wait for the player so we don't clear the canceled playlist by mistake.
xbmc.sleep(3000) # let's wait for the player so we don't clear the canceled playlist by mistake.
if xbmc.getCondVisibility("!Player.HasMedia + !Window.IsVisible(busydialog)"):
@ -144,7 +144,7 @@ class Monitor(xbmc.Monitor):
self.void_responder(data, item)
elif method == 'GetServerAddress':
server_data = server.auth.get_server_info(server.auth.server_id)
server_address = server.auth.get_server_address(server_data, server_data['LastConnectionMode'])
self.void_responder(data, server_address)
@ -392,7 +392,7 @@ class Monitor(xbmc.Monitor):
elif command == 'DisplayMessage':
dialog("notification", heading=args['Header'], message=args['Text'],
icon="{jellyfin}", time=int(settings('displayMessage'))*1000)
icon="{jellyfin}", time=int(settings('displayMessage')) * 1000)
elif command == 'SendString':
JSONRPC('Input.SendText').execute({'text': args['String'], 'done': False})

View file

@ -22,7 +22,7 @@ from utils import get_play_action
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
@ -58,7 +58,7 @@ class Actions(object):
play.set_external_subs(source, listitem)
self.set_playlist(item, listitem, db_id, transcode)
index = max(kodi_playlist.getposition(), 0) + 1 # Can return -1
index = max(kodi_playlist.getposition(), 0) + 1 # Can return -1
force_play = False
self.stack[0][1].setPath(self.stack[0][0])
@ -79,7 +79,8 @@ class Actions(object):
index += 1
if force_play:
if len(sys.argv) > 1: xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, self.stack[0][1])
if len(sys.argv) > 1:
xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, self.stack[0][1])
xbmc.Player().play(kodi_playlist, windowed=False)
def set_playlist(self, item, listitem, db_id=None, transcode=False):
@ -177,7 +178,7 @@ class Actions(object):
playlist = self.get_playlist(item)
player = xbmc.Player()
#xbmc.executebuiltin("Playlist.Clear") # Clear playlist to remove the previous item from playlist position no.2
# xbmc.executebuiltin("Playlist.Clear") # Clear playlist to remove the previous item from playlist position no.2
if clear:
if player.isPlaying():
@ -186,7 +187,7 @@ class Actions(object):
xbmc.executebuiltin('ActivateWindow(busydialognocancel)')
index = 0
else:
index = max(playlist.getposition(), 0) + 1 # Can return -1
index = max(playlist.getposition(), 0) + 1 # Can return -1
listitem = xbmcgui.ListItem()
LOG.info("[ playlist/%s ] %s", item['Id'], item['Name'])
@ -282,7 +283,7 @@ class Actions(object):
''' Set listitem for video content. That also include streams.
'''
API = api.API(item, self.server)
is_video = obj['MediaType'] in ('Video', 'Audio') # audiobook
is_video = obj['MediaType'] in ('Video', 'Audio') # audiobook
obj['Genres'] = " / ".join(obj['Genres'] or [])
obj['Studios'] = [API.validate_studio(studio) for studio in (obj['Studios'] or [])]
@ -312,21 +313,21 @@ class Actions(object):
if not intro and not obj['Type'] == 'Trailer':
obj['Artwork']['Primary'] = obj['Artwork']['Primary'] \
or "special://home/addons/plugin.video.jellyfin/resources/icon.png"
or "special://home/addons/plugin.video.jellyfin/resources/icon.png"
else:
obj['Artwork']['Primary'] = obj['Artwork']['Primary'] \
or obj['Artwork']['Thumb'] \
or (obj['Artwork']['Backdrop'][0] \
if len(obj['Artwork']['Backdrop']) \
or obj['Artwork']['Thumb'] \
or (obj['Artwork']['Backdrop'][0]
if len(obj['Artwork']['Backdrop'])
else "special://home/addons/plugin.video.jellyfin/resources/fanart.png")
obj['Artwork']['Primary'] += "&KodiTrailer=true" \
if obj['Type'] == 'Trailer' else "&KodiCinemaMode=true"
if obj['Type'] == 'Trailer' else "&KodiCinemaMode=true"
obj['Artwork']['Backdrop'] = [obj['Artwork']['Primary']]
self.set_artwork(obj['Artwork'], listitem, obj['Type'])
if intro or obj['Type'] == 'Trailer':
listitem.setArt({'poster': ""}) # Clear the poster value for intros / trailers to prevent issues in skins
listitem.setArt({'poster': ""}) # Clear the poster value for intros / trailers to prevent issues in skins
listitem.setIconImage('DefaultVideo.png')
listitem.setThumbnailImage(obj['Artwork']['Primary'])
@ -442,9 +443,9 @@ class Actions(object):
listitem.setProperty('IsPlayable', 'true')
listitem.setProperty('IsFolder', 'false')
if obj['Resume'] and seektime != False:
if obj['Resume'] and seektime:
listitem.setProperty('resumetime', str(obj['Resume']))
listitem.setProperty('StartPercent', str(((obj['Resume']/obj['Runtime']) * 100) - 0.40))
listitem.setProperty('StartPercent', str(((obj['Resume'] / obj['Runtime']) * 100) - 0.40))
else:
listitem.setProperty('resumetime', '0')
@ -478,11 +479,11 @@ class Actions(object):
obj['PlayCount'] = API.get_playcount(obj['Played'], obj['PlayCount']) or 0
obj['Overlay'] = 7 if obj['Played'] else 6
obj['Artwork']['Primary'] = obj['Artwork']['Primary'] \
or "special://home/addons/plugin.video.jellyfin/resources/icon.png"
or "special://home/addons/plugin.video.jellyfin/resources/icon.png"
obj['Artwork']['Thumb'] = obj['Artwork']['Thumb'] \
or "special://home/addons/plugin.video.jellyfin/resources/fanart.png"
or "special://home/addons/plugin.video.jellyfin/resources/fanart.png"
obj['Artwork']['Backdrop'] = obj['Artwork']['Backdrop'] \
or ["special://home/addons/plugin.video.jellyfin/resources/fanart.png"]
or ["special://home/addons/plugin.video.jellyfin/resources/fanart.png"]
metadata = {
'title': obj['Title'],
@ -625,7 +626,7 @@ class Actions(object):
'clearlogo': "Logo",
'discart': "Disc",
'fanart': "Backdrop",
'fanart_image': "Backdrop", # in case
'fanart_image': "Backdrop", # in case
'thumb': "Primary"
}
else:
@ -671,9 +672,9 @@ class Actions(object):
dialog.doModal()
if dialog.is_selected():
if not dialog.get_selected(): # Start from beginning selected.
if not dialog.get_selected(): # Start from beginning selected.
return False
else: # User backed out
else: # User backed out
LOG.info("User exited without a selection.")
return
@ -688,9 +689,7 @@ class Actions(object):
return False
if (not xbmc.getCondVisibility('Window.IsMedia') and
((item['Type'] == 'Audio' and not xbmc.getCondVisibility('Integer.IsGreater(Playlist.Length(music),1)')) or
not xbmc.getCondVisibility('Integer.IsGreater(Playlist.Length(video),1)'))):
if (not xbmc.getCondVisibility('Window.IsMedia') and ((item['Type'] == 'Audio' and not xbmc.getCondVisibility('Integer.IsGreater(Playlist.Length(music),1)')) or not xbmc.getCondVisibility('Integer.IsGreater(Playlist.Length(video),1)'))):
return True
@ -733,6 +732,7 @@ def on_update(data, server):
window('jellyfin.skip.%s' % item[0], clear=True)
def on_play(data, server):
''' Setup progress for jellyfin playback.
@ -781,6 +781,7 @@ def on_play(data, server):
item['PlaybackInfo'] = {'Path': file}
playutils.set_properties(item, 'DirectStream' if settings('useDirectPaths') == '0' else 'DirectPlay')
def special_listener():
''' Corner cases that needs to be listened to.
@ -790,12 +791,11 @@ def special_listener():
isPlaying = player.isPlaying()
count = int(window('jellyfin.external_count') or 0)
if (not isPlaying and xbmc.getCondVisibility('Window.IsVisible(DialogContextMenu.xml)') and
xbmc.getInfoLabel('Control.GetLabel(1002)') == xbmc.getLocalizedString(12021)):
if (not isPlaying and xbmc.getCondVisibility('Window.IsVisible(DialogContextMenu.xml)') and xbmc.getInfoLabel('Control.GetLabel(1002)') == xbmc.getLocalizedString(12021)):
control = int(xbmcgui.Window(10106).getFocusId())
if control == 1002: # Start from beginning
if control == 1002: # Start from beginning
LOG.info("Resume dialog: Start from beginning selected.")
window('jellyfin.resume.bool', False)
@ -806,7 +806,7 @@ def special_listener():
elif isPlaying and not window('jellyfin.external_check'):
time = player.getTime()
if time > 1: # Not external player.
if time > 1: # Not external player.
window('jellyfin.external_check', value="true")
window('jellyfin.external_count', value="0")

View file

@ -17,7 +17,7 @@ import requests
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
@ -37,7 +37,6 @@ class Artwork(object):
'port': settings('webServerPort')
}
def update(self, image_url, kodi_id, media, image):
''' Update artwork in the video database.
@ -210,7 +209,7 @@ class GetArtworkWorker(threading.Thread):
prep = req.prepare()
prep.url = "http://%s:%s/image/image://%s" % (self.kodi['host'], self.kodi['port'], url)
s.send(prep, timeout=(0.01, 0.01))
s.content # release the connection
s.content # release the connection
except Exception as error:
LOG.exception(error)
@ -220,8 +219,6 @@ class GetArtworkWorker(threading.Thread):
break
"""
# -*- coding: utf-8 -*-
@ -381,4 +378,3 @@ class Artwork(object):
count += 1
"""

View file

@ -12,14 +12,13 @@ from helper import values
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
class Kodi(object):
def __init__(self):
self.artwork = artwork.Artwork(self.cursor)
@ -215,7 +214,7 @@ class Kodi(object):
return self.add_studio(*args)
def add_streams(self, file_id, streams, runtime):
''' First remove any existing entries
Then re-add video, audio and subtitles.
'''

View file

@ -9,14 +9,13 @@ import queries as QU
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
class Movies(Kodi):
def __init__(self, cursor):
self.cursor = cursor
@ -29,7 +28,7 @@ class Movies(Kodi):
def create_entry_rating(self):
self.cursor.execute(QU.create_rating)
return self.cursor.fetchone()[0] + 1
def create_entry(self):

View file

@ -9,7 +9,7 @@ from kodi import Kodi
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
@ -34,17 +34,17 @@ class Music(Kodi):
def create_entry_album(self):
self.cursor.execute(QU.create_album)
return self.cursor.fetchone()[0] + 1
def create_entry_song(self):
self.cursor.execute(QU.create_song)
return self.cursor.fetchone()[0] + 1
def create_entry_genre(self):
self.cursor.execute(QU.create_genre)
return self.cursor.fetchone()[0] + 1
def update_path(self, *args):
@ -212,7 +212,7 @@ class Music(Kodi):
''' Add genres, but delete current genres first.
Album_genres was removed in kodi 18
'''
if media == 'album' and self.version_id < 72 :
if media == 'album' and self.version_id < 72:
self.cursor.execute(QU.delete_genres_album, (kodi_id,))
for genre in genres:
@ -258,11 +258,11 @@ class Music(Kodi):
return self.cursor.fetchone()[0]
#current bug in Kodi 18 that will ask for a scan of music tags unless this is set without a lastscanned
# current bug in Kodi 18 that will ask for a scan of music tags unless this is set without a lastscanned
def update_versiontagscan(self):
if self.version_id < 72:
return
else:
self.cursor.execute(QU.get_versiontagcount)
if self.cursor.fetchone()[0] == 0:
self.cursor.execute(QU.update_versiontag, (self.version_id,))
self.cursor.execute(QU.update_versiontag, (self.version_id,))

View file

@ -9,14 +9,13 @@ from kodi import Kodi
##################################################################################################
log = logging.getLogger("JELLYFIN."+__name__)
log = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
class MusicVideos(Kodi):
def __init__(self, cursor):
self.cursor = cursor
@ -31,7 +30,7 @@ class MusicVideos(Kodi):
try:
self.cursor.execute(QU.get_musicvideo, args)
return self.cursor.fetchone()[0]
except TypeError:
return

File diff suppressed because it is too large Load diff

View file

@ -1,238 +1,212 @@
create_artist = """ SELECT coalesce(max(idArtist), 1)
FROM artist
"""
create_album = """ SELECT coalesce(max(idAlbum), 0)
FROM album
"""
create_song = """ SELECT coalesce(max(idSong), 0)
FROM song
"""
create_genre = """ SELECT coalesce(max(idGenre), 0)
FROM genre
"""
get_artist = """ SELECT idArtist, strArtist
FROM artist
WHERE strMusicBrainzArtistID = ?
"""
get_artist_obj = [ "{ArtistId}","{Name}","{UniqueId}"
]
get_artist_by_name = """ SELECT idArtist
FROM artist
WHERE strArtist = ?
COLLATE NOCASE
"""
get_artist_by_id = """ SELECT *
FROM artist
WHERE idArtist = ?
"""
get_artist_by_id_obj = [ "{ArtistId}"
]
get_album_by_id = """ SELECT *
FROM album
WHERE idAlbum = ?
"""
get_album_by_id_obj = [ "{AlbumId}"
]
get_song_by_id = """ SELECT *
FROM song
WHERE idSong = ?
"""
get_song_by_id_obj = [ "{SongId}"
]
get_album = """ SELECT idAlbum
FROM album
WHERE strMusicBrainzAlbumID = ?
"""
get_album_obj = [ "{AlbumId}","{Title}","{UniqueId}","{Artists}","album"
]
get_album_by_name = """ SELECT idAlbum, strArtists
FROM album
WHERE strAlbum = ?
"""
get_album_by_name72 = """ SELECT idAlbum, strArtistDisp
FROM album
WHERE strAlbum = ?
"""
get_album_artist = """ SELECT strArtists
FROM album
WHERE idAlbum = ?
"""
get_album_artist72 = """ SELECT strArtistDisp
FROM album
WHERE idAlbum = ?
"""
get_album_artist_obj = [ "{AlbumId}","{strAlbumArtists}"
]
get_genre = """ SELECT idGenre
FROM genre
WHERE strGenre = ?
COLLATE NOCASE
"""
get_total_episodes = """ SELECT totalCount
FROM tvshowcounts
WHERE idShow = ?
"""
add_artist = """ INSERT INTO artist(idArtist, strArtist, strMusicBrainzArtistID)
VALUES (?, ?, ?)
"""
add_album = """ INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strReleaseType)
VALUES (?, ?, ?, ?)
"""
add_album72 = """ INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strReleaseType, bScrapedMBID)
VALUES (?, ?, ?, ?, 1)
"""
add_single = """ INSERT INTO album(idAlbum, strGenres, iYear, strReleaseType)
VALUES (?, ?, ?, ?)
"""
add_single_obj = [ "{AlbumId}","{Genre}","{Year}","single"
]
add_song = """ INSERT INTO song(idSong, idAlbum, idPath, strArtists, strGenres, strTitle, iTrack,
iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed,
rating, comment, dateAdded)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
add_song72 = """ INSERT INTO song(idSong, idAlbum, idPath, strArtistDisp, strGenres, strTitle, iTrack,
iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed,
rating, comment, dateAdded)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
add_song_obj = [ "{SongId}","{AlbumId}","{PathId}","{Artists}","{Genre}","{Title}","{Index}",
"{Runtime}","{Year}","{Filename}","{UniqueId}","{PlayCount}","{DatePlayed}","{Rating}",
"{Comment}","{DateAdded}"
]
add_genre = """ INSERT INTO genre(idGenre, strGenre)
VALUES (?, ?)
"""
add_genres_obj = [ "{AlbumId}","{Genres}","album"
]
update_path = """ UPDATE path
SET strPath = ?
WHERE idPath = ?
"""
update_path_obj = [ "{Path}","{PathId}"
]
update_role = """ INSERT OR REPLACE INTO role(idRole, strRole)
VALUES (?, ?)
"""
update_role_obj = [ 1,"Composer"
]
update_artist_name = """ UPDATE artist
SET strArtist = ?
WHERE idArtist = ?
"""
update_artist_name_obj = [ "{Name}","{ArtistId}"
]
update_artist = """ UPDATE artist
SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?, lastScraped = ?
WHERE idArtist = ?
"""
update_link = """ INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
VALUES (?, ?, ?)
"""
update_link_obj = [ "{ArtistId}","{AlbumId}","{Name}"
]
update_discography = """ INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
VALUES (?, ?, ?)
"""
update_discography_obj = [ "{ArtistId}","{Title}","{Year}"
]
update_album = """ UPDATE album
SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,
iUserrating = ?, lastScraped = ?, strReleaseType = ?
WHERE idAlbum = ?
"""
update_album72 = """ UPDATE album
SET strArtistDisp = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,
iUserrating = ?, lastScraped = ?, bScrapedMBID = 1, strReleaseType = ?
WHERE idAlbum = ?
"""
update_album_obj = [ "{Artists}","{Year}","{Genre}","{Bio}","{Thumb}","{Rating}","{LastScraped}",
"album","{AlbumId}"
]
update_album_artist = """ UPDATE album
SET strArtists = ?
WHERE idAlbum = ?
"""
update_album_artist72 = """ UPDATE album
SET strArtistDisp = ?
WHERE idAlbum = ?
"""
update_song = """ UPDATE song
SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?,
iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,
rating = ?, comment = ?, dateAdded = ?
WHERE idSong = ?
"""
update_song72 = """ UPDATE song
SET idAlbum = ?, strArtistDisp = ?, strGenres = ?, strTitle = ?, iTrack = ?,
iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,
rating = ?, comment = ?, dateAdded = ?
WHERE idSong = ?
"""
update_song_obj = [ "{AlbumId}","{Artists}","{Genre}","{Title}","{Index}","{Runtime}","{Year}",
"{Filename}","{PlayCount}","{DatePlayed}","{Rating}","{Comment}",
"{DateAdded}","{SongId}"
]
update_song_artist = """ INSERT OR REPLACE INTO song_artist(idArtist, idSong, idRole, iOrder, strArtist)
VALUES (?, ?, ?, ?, ?)
"""
update_song_artist_obj = [ "{ArtistId}","{SongId}",1,"{Index}","{Name}"
]
update_song_album = """ INSERT OR REPLACE INTO albuminfosong(idAlbumInfoSong, idAlbumInfo, iTrack,
strTitle, iDuration)
VALUES (?, ?, ?, ?, ?)
"""
update_song_album_obj = [ "{SongId}","{AlbumId}","{Index}","{Title}","{Runtime}"
]
update_song_rating = """ UPDATE song
SET iTimesPlayed = ?, lastplayed = ?, rating = ?
WHERE idSong = ?
"""
update_song_rating_obj = [ "{PlayCount}","{DatePlayed}","{Rating}","{KodiId}"
]
update_genre_album = """ INSERT OR REPLACE INTO album_genre(idGenre, idAlbum)
VALUES (?, ?)
"""
update_genre_song = """ INSERT OR REPLACE INTO song_genre(idGenre, idSong)
VALUES (?, ?)
"""
update_genre_song_obj = [ "{SongId}","{Genres}","song"
]
delete_genres_album = """ DELETE FROM album_genre
WHERE idAlbum = ?
"""
delete_genres_song = """ DELETE FROM song_genre
WHERE idSong = ?
"""
delete_artist = """ DELETE FROM artist
WHERE idArtist = ?
"""
delete_album = """ DELETE FROM album
WHERE idAlbum = ?
"""
delete_song = """ DELETE FROM song
WHERE idSong = ?
"""
get_version = """ SELECT idVersion
FROM version
"""
update_versiontag = """ INSERT OR REPLACE INTO versiontagscan(idVersion, iNeedsScan)
VALUES (?, 0)
create_artist = """ SELECT coalesce(max(idArtist), 1)
FROM artist
"""
create_album = """ SELECT coalesce(max(idAlbum), 0)
FROM album
"""
create_song = """ SELECT coalesce(max(idSong), 0)
FROM song
"""
create_genre = """ SELECT coalesce(max(idGenre), 0)
FROM genre
"""
get_artist = """ SELECT idArtist, strArtist
FROM artist
WHERE strMusicBrainzArtistID = ?
"""
get_artist_obj = ["{ArtistId}", "{Name}", "{UniqueId}"]
get_artist_by_name = """ SELECT idArtist
FROM artist
WHERE strArtist = ?
COLLATE NOCASE
"""
get_artist_by_id = """ SELECT *
FROM artist
WHERE idArtist = ?
"""
get_artist_by_id_obj = ["{ArtistId}"]
get_album_by_id = """ SELECT *
FROM album
WHERE idAlbum = ?
"""
get_album_by_id_obj = ["{AlbumId}"]
get_song_by_id = """ SELECT *
FROM song
WHERE idSong = ?
"""
get_song_by_id_obj = ["{SongId}"]
get_album = """ SELECT idAlbum
FROM album
WHERE strMusicBrainzAlbumID = ?
"""
get_album_obj = ["{AlbumId}", "{Title}", "{UniqueId}", "{Artists}", "album"]
get_album_by_name = """ SELECT idAlbum, strArtists
FROM album
WHERE strAlbum = ?
"""
get_album_by_name72 = """ SELECT idAlbum, strArtistDisp
FROM album
WHERE strAlbum = ?
"""
get_album_artist = """ SELECT strArtists
FROM album
WHERE idAlbum = ?
"""
get_album_artist72 = """ SELECT strArtistDisp
FROM album
WHERE idAlbum = ?
"""
get_album_artist_obj = ["{AlbumId}", "{strAlbumArtists}"]
get_genre = """ SELECT idGenre
FROM genre
WHERE strGenre = ?
COLLATE NOCASE
"""
get_total_episodes = """ SELECT totalCount
FROM tvshowcounts
WHERE idShow = ?
"""
add_artist = """ INSERT INTO artist(idArtist, strArtist, strMusicBrainzArtistID)
VALUES (?, ?, ?)
"""
add_album = """ INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strReleaseType)
VALUES (?, ?, ?, ?)
"""
add_album72 = """ INSERT INTO album(idAlbum, strAlbum, strMusicBrainzAlbumID, strReleaseType, bScrapedMBID)
VALUES (?, ?, ?, ?, 1)
"""
add_single = """ INSERT INTO album(idAlbum, strGenres, iYear, strReleaseType)
VALUES (?, ?, ?, ?)
"""
add_single_obj = ["{AlbumId}", "{Genre}", "{Year}", "single"]
add_song = """ INSERT INTO song(idSong, idAlbum, idPath, strArtists, strGenres, strTitle, iTrack,
iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed,
rating, comment, dateAdded)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
add_song72 = """ INSERT INTO song(idSong, idAlbum, idPath, strArtistDisp, strGenres, strTitle, iTrack,
iDuration, iYear, strFileName, strMusicBrainzTrackID, iTimesPlayed, lastplayed,
rating, comment, dateAdded)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
add_song_obj = ["{SongId}", "{AlbumId}", "{PathId}", "{Artists}", "{Genre}", "{Title}", "{Index}",
"{Runtime}", "{Year}", "{Filename}", "{UniqueId}", "{PlayCount}", "{DatePlayed}", "{Rating}",
"{Comment}", "{DateAdded}"]
add_genre = """ INSERT INTO genre(idGenre, strGenre)
VALUES (?, ?)
"""
add_genres_obj = ["{AlbumId}", "{Genres}", "album"]
update_path = """ UPDATE path
SET strPath = ?
WHERE idPath = ?
"""
update_path_obj = ["{Path}", "{PathId}"]
update_role = """ INSERT OR REPLACE INTO role(idRole, strRole)
VALUES (?, ?)
"""
update_role_obj = [1, "Composer"]
update_artist_name = """ UPDATE artist
SET strArtist = ?
WHERE idArtist = ?
"""
update_artist_name_obj = ["{Name}", "{ArtistId}"]
update_artist = """ UPDATE artist
SET strGenres = ?, strBiography = ?, strImage = ?, strFanart = ?, lastScraped = ?
WHERE idArtist = ?
"""
update_link = """ INSERT OR REPLACE INTO album_artist(idArtist, idAlbum, strArtist)
VALUES (?, ?, ?)
"""
update_link_obj = ["{ArtistId}", "{AlbumId}", "{Name}"]
update_discography = """ INSERT OR REPLACE INTO discography(idArtist, strAlbum, strYear)
VALUES (?, ?, ?)
"""
update_discography_obj = ["{ArtistId}", "{Title}", "{Year}"]
update_album = """ UPDATE album
SET strArtists = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,
iUserrating = ?, lastScraped = ?, strReleaseType = ?
WHERE idAlbum = ?
"""
update_album72 = """ UPDATE album
SET strArtistDisp = ?, iYear = ?, strGenres = ?, strReview = ?, strImage = ?,
iUserrating = ?, lastScraped = ?, bScrapedMBID = 1, strReleaseType = ?
WHERE idAlbum = ?
"""
update_album_obj = ["{Artists}", "{Year}", "{Genre}", "{Bio}", "{Thumb}", "{Rating}", "{LastScraped}", "album", "{AlbumId}"]
update_album_artist = """ UPDATE album
SET strArtists = ?
WHERE idAlbum = ?
"""
update_album_artist72 = """ UPDATE album
SET strArtistDisp = ?
WHERE idAlbum = ?
"""
update_song = """ UPDATE song
SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?,
iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,
rating = ?, comment = ?, dateAdded = ?
WHERE idSong = ?
"""
update_song72 = """ UPDATE song
SET idAlbum = ?, strArtistDisp = ?, strGenres = ?, strTitle = ?, iTrack = ?,
iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,
rating = ?, comment = ?, dateAdded = ?
WHERE idSong = ?
"""
update_song_obj = ["{AlbumId}", "{Artists}", "{Genre}", "{Title}", "{Index}", "{Runtime}", "{Year}",
"{Filename}", "{PlayCount}", "{DatePlayed}", "{Rating}", "{Comment}",
"{DateAdded}", "{SongId}"]
update_song_artist = """ INSERT OR REPLACE INTO song_artist(idArtist, idSong, idRole, iOrder, strArtist)
VALUES (?, ?, ?, ?, ?)
"""
update_song_artist_obj = ["{ArtistId}", "{SongId}", 1, "{Index}", "{Name}"]
update_song_album = """ INSERT OR REPLACE INTO albuminfosong(idAlbumInfoSong, idAlbumInfo, iTrack,
strTitle, iDuration)
VALUES (?, ?, ?, ?, ?)
"""
update_song_album_obj = ["{SongId}", "{AlbumId}", "{Index}", "{Title}", "{Runtime}"]
update_song_rating = """ UPDATE song
SET iTimesPlayed = ?, lastplayed = ?, rating = ?
WHERE idSong = ?
"""
update_song_rating_obj = ["{PlayCount}", "{DatePlayed}", "{Rating}", "{KodiId}"]
update_genre_album = """ INSERT OR REPLACE INTO album_genre(idGenre, idAlbum)
VALUES (?, ?)
"""
update_genre_song = """ INSERT OR REPLACE INTO song_genre(idGenre, idSong)
VALUES (?, ?)
"""
update_genre_song_obj = ["{SongId}", "{Genres}", "song"]
delete_genres_album = """ DELETE FROM album_genre
WHERE idAlbum = ?
"""
delete_genres_song = """ DELETE FROM song_genre
WHERE idSong = ?
"""
delete_artist = """ DELETE FROM artist
WHERE idArtist = ?
"""
delete_album = """ DELETE FROM album
WHERE idAlbum = ?
"""
delete_song = """ DELETE FROM song
WHERE idSong = ?
"""
get_version = """ SELECT idVersion
FROM version
"""
update_versiontag = """ INSERT OR REPLACE INTO versiontagscan(idVersion, iNeedsScan)
VALUES (?, 0)
"""
get_versiontagcount = """ SELECT COUNT(*)
FROM versiontagscan
"""
get_versiontagcount = """ SELECT COUNT(*)
FROM versiontagscan
"""

View file

@ -1,11 +1,10 @@
get_cache = """ SELECT cachedurl
FROM texture
WHERE url = ?
"""
get_cache = """ SELECT cachedurl
FROM texture
WHERE url = ?
"""
delete_cache = """ DELETE FROM texture
WHERE url = ?
"""
delete_cache = """ DELETE FROM texture
WHERE url = ?
"""

View file

@ -9,14 +9,13 @@ from kodi import Kodi
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
class TVShows(Kodi):
def __init__(self, cursor):
self.cursor = cursor
@ -24,7 +23,7 @@ class TVShows(Kodi):
def create_entry_unique_id(self):
self.cursor.execute(QU.create_unique_id)
return self.cursor.fetchone()[0] + 1
def create_entry_rating(self):
@ -39,12 +38,12 @@ class TVShows(Kodi):
def create_entry_season(self):
self.cursor.execute(QU.create_season)
return self.cursor.fetchone()[0] + 1
def create_entry_episode(self):
self.cursor.execute(QU.create_episode)
return self.cursor.fetchone()[0] + 1
def get(self, *args):
@ -69,7 +68,7 @@ class TVShows(Kodi):
try:
self.cursor.execute(QU.get_rating, args)
return self.cursor.fetchone()[0]
except TypeError:
return
@ -93,7 +92,7 @@ class TVShows(Kodi):
try:
self.cursor.execute(QU.get_unique_id, args)
return self.cursor.fetchone()[0]
except TypeError:
return

View file

@ -14,7 +14,7 @@ from helper import api, catch, stop, validate, jellyfin_item, library_check, val
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
@ -103,13 +103,11 @@ class Movies(KodiDb):
obj['Tags'] = tags
if update:
self.movie_update(obj)
else:
self.movie_add(obj)
self.update_path(*values(obj, QU.update_path_movie_obj))
self.update_file(*values(obj, QU.update_file_obj))
self.add_tags(*values(obj, QU.add_tags_movie_obj))
@ -192,7 +190,6 @@ class Movies(KodiDb):
}
obj['Filename'] = "%s?%s" % (obj['Path'], urllib.urlencode(params))
@stop()
@jellyfin_item()
def boxset(self, item, e_item):

View file

@ -14,7 +14,7 @@ from helper import api, catch, stop, validate, jellyfin_item, values, library_ch
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
@ -77,13 +77,11 @@ class Music(KodiDb):
if obj['Backdrops']:
obj['Backdrops'] = "<fanart>%s</fanart>" % obj['Backdrops'][0]
if update:
self.artist_update(obj)
else:
self.artist_add(obj)
self.update(obj['Genre'], obj['Bio'], obj['Thumb'], obj['Backdrops'], obj['LastScraped'], obj['ArtistId'])
self.artwork.add(obj['Artwork'], obj['ArtistId'], "artist")
self.item_ids.append(obj['Id'])
@ -106,7 +104,6 @@ class Music(KodiDb):
self.jellyfin_db.update_reference(*values(obj, QUEM.update_reference_obj))
LOG.info("UPDATE artist [%s] %s: %s", obj['ArtistId'], obj['Name'], obj['Id'])
@stop()
@jellyfin_item()
def album(self, item, e_item):
@ -144,13 +141,11 @@ class Music(KodiDb):
if obj['Thumb']:
obj['Thumb'] = "<thumb>%s</thumb>" % obj['Thumb']
if update:
self.album_update(obj)
else:
self.album_add(obj)
self.artist_link(obj)
self.artist_discography(obj)
self.update_album(*values(obj, QU.update_album_obj))
@ -217,7 +212,6 @@ class Music(KodiDb):
self.link(*values(temp_obj, QU.update_link_obj))
self.item_ids.append(temp_obj['Id'])
@stop()
@jellyfin_item()
def song(self, item, e_item):
@ -269,15 +263,13 @@ class Music(KodiDb):
if obj['Disc'] != 1:
obj['Index'] = obj['Disc'] * 2 ** 16 + obj['Index']
if update:
self.song_update(obj)
else:
self.song_add(obj)
self.link_song_album(*values(obj, QU.update_song_album_obj))
self.add_role(*values(obj, QU.update_role_obj)) # defaultt role
self.add_role(*values(obj, QU.update_role_obj)) # defaultt role
self.song_artist_link(obj)
self.song_artist_discography(obj)
@ -415,7 +407,6 @@ class Music(KodiDb):
obj['AlbumId'] = self.create_entry_album()
self.add_single(*values(obj, QU.add_single_obj))
@stop()
@jellyfin_item()
def userdata(self, item, e_item):

View file

@ -14,7 +14,7 @@ from helper import api, catch, stop, validate, library_check, jellyfin_item, val
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
@ -114,13 +114,11 @@ class MusicVideos(KodiDb):
obj['Tags'] = tags
if update:
self.musicvideo_update(obj)
else:
self.musicvideo_add(obj)
self.update_path(*values(obj, QU.update_path_mvideo_obj))
self.update_file(*values(obj, QU.update_file_obj))
self.add_tags(*values(obj, QU.add_tags_mvideo_obj))
@ -176,7 +174,6 @@ class MusicVideos(KodiDb):
}
obj['Filename'] = "%s?%s" % (obj['Path'], urllib.urlencode(params))
@stop()
@jellyfin_item()
def userdata(self, item, e_item):
@ -185,7 +182,7 @@ class MusicVideos(KodiDb):
Poster with progress bar
'''
server_data = self.server.auth.get_server_info(self.server.auth.server_id)
server_address = self.server.auth.get_server_address(server_data, server_data['LastConnectionMode'])
server_address = self.server.auth.get_server_address(server_data, server_data['LastConnectionMode'])
API = api.API(item, server_address)
obj = self.objects.map(item, 'MusicVideoUserData')

View file

@ -8,7 +8,7 @@ import os
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
@ -44,7 +44,7 @@ class Objects(object):
"$": lead the key name with $. Only one key value can be requested per element.
":": indicates it's a list of elements [], i.e. MediaSources/0/MediaStreams:?$Name
MediaStreams is a list.
"/": indicates where to go directly
"/": indicates where to go directly
'''
self.mapped_item = {}
@ -145,7 +145,7 @@ class Objects(object):
result = False
for key, value in filters.iteritems():
inverse = False
if value.startswith('!'):

View file

@ -16,7 +16,7 @@ from helper import api, catch, stop, validate, jellyfin_item, library_check, set
##################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
##################################################################################################
@ -76,7 +76,6 @@ class TVShows(KodiDb):
update = False
LOG.info("ShowId %s missing from kodi. repairing the entry.", obj['ShowId'])
obj['Path'] = API.get_file_path(obj['Path'])
obj['LibraryId'] = library['Id']
obj['LibraryName'] = library['Name']
@ -107,13 +106,11 @@ class TVShows(KodiDb):
obj['Tags'] = tags
if update:
self.tvshow_update(obj)
else:
self.tvshow_add(obj)
self.link(*values(obj, QU.update_tvshow_link_obj))
self.update_path(*values(obj, QU.update_path_tvshow_obj))
self.add_tags(*values(obj, QU.add_tags_tvshow_obj))
@ -161,10 +158,10 @@ class TVShows(KodiDb):
''' Add object to kodi.
'''
obj['RatingId'] = self.create_entry_rating()
obj['RatingId'] = self.create_entry_rating()
self.add_ratings(*values(obj, QU.add_rating_tvshow_obj))
obj['Unique'] = self.create_entry_unique_id()
obj['Unique'] = self.create_entry_unique_id()
self.add_unique_id(*values(obj, QU.add_unique_id_tvshow_obj))
obj['TopPathId'] = self.add_path(obj['TopLevel'])
@ -180,10 +177,10 @@ class TVShows(KodiDb):
''' Update object to kodi.
'''
obj['RatingId'] = self.get_rating_id(*values(obj, QU.get_unique_id_tvshow_obj))
obj['RatingId'] = self.get_rating_id(*values(obj, QU.get_unique_id_tvshow_obj))
self.update_ratings(*values(obj, QU.update_rating_tvshow_obj))
obj['Unique'] = self.get_unique_id(*values(obj, QU.get_unique_id_tvshow_obj))
obj['Unique'] = self.get_unique_id(*values(obj, QU.get_unique_id_tvshow_obj))
self.update_unique_id(*values(obj, QU.update_unique_id_tvshow_obj))
self.update(*values(obj, QU.update_tvshow_obj))
@ -209,7 +206,6 @@ class TVShows(KodiDb):
obj['TopLevel'] = "plugin://plugin.video.jellyfin/"
obj['Path'] = "%s%s/" % (obj['TopLevel'], obj['Id'])
@stop()
def season(self, item, show_id=None):
@ -244,7 +240,6 @@ class TVShows(KodiDb):
self.artwork.add(obj['Artwork'], obj['SeasonId'], "season")
LOG.info("UPDATE season [%s/%s] %s: %s", obj['ShowId'], obj['SeasonId'], obj['Title'] or obj['Index'], obj['Id'])
@stop()
@jellyfin_item()
def episode(self, item, e_item):
@ -286,7 +281,6 @@ class TVShows(KodiDb):
update = False
LOG.info("EpisodeId %s missing from kodi. repairing the entry.", obj['EpisodeId'])
obj['Path'] = API.get_file_path(obj['Path'])
obj['Index'] = obj['Index'] or -1
obj['Writers'] = " / ".join(obj['Writers'] or [])
@ -319,7 +313,7 @@ class TVShows(KodiDb):
if obj['AirsAfterSeason']:
obj['AirsBeforeSeason'] = obj['AirsAfterSeason']
obj['AirsBeforeEpisode'] = 4096 # Kodi default number for afterseason ordering
obj['AirsBeforeEpisode'] = 4096 # Kodi default number for afterseason ordering
if obj['MultiEpisode']:
obj['Title'] = "| %02d | %s" % (obj['MultiEpisode'], obj['Title'])
@ -329,13 +323,11 @@ class TVShows(KodiDb):
obj['SeasonId'] = self.get_season(*values(obj, QU.get_season_episode_obj))
if update:
self.episode_update(obj)
else:
self.episode_add(obj)
self.update_path(*values(obj, QU.update_path_episode_obj))
self.update_file(*values(obj, QU.update_file_obj))
self.add_people(*values(obj, QU.add_people_episode_obj))
@ -359,10 +351,10 @@ class TVShows(KodiDb):
''' Add object to kodi.
'''
obj['RatingId'] = self.create_entry_rating()
obj['RatingId'] = self.create_entry_rating()
self.add_ratings(*values(obj, QU.add_rating_episode_obj))
obj['Unique'] = self.create_entry_unique_id()
obj['Unique'] = self.create_entry_unique_id()
self.add_unique_id(*values(obj, QU.add_unique_id_episode_obj))
obj['PathId'] = self.add_path(*values(obj, QU.add_path_obj))
@ -387,7 +379,7 @@ class TVShows(KodiDb):
obj['RatingId'] = self.get_rating_id(*values(obj, QU.get_rating_episode_obj))
self.update_ratings(*values(obj, QU.update_rating_episode_obj))
obj['Unique'] = self.get_unique_id(*values(obj, QU.get_unique_id_episode_obj))
obj['Unique'] = self.get_unique_id(*values(obj, QU.get_unique_id_episode_obj))
self.update_unique_id(*values(obj, QU.update_unique_id_episode_obj))
self.update_episode(*values(obj, QU.update_episode_obj))
@ -440,7 +432,6 @@ class TVShows(KodiDb):
return True
@stop()
@jellyfin_item()
def userdata(self, item, e_item):

View file

@ -8,7 +8,7 @@ from helper import JSONRPC
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################

View file

@ -15,7 +15,7 @@ from jellyfin import Jellyfin
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
@ -326,7 +326,7 @@ class Player(xbmc.Player):
try:
played = float(item['CurrentPosition'] * 10000000) / int(item['Runtime']) * 100
except ZeroDivisionError: # Runtime is 0.
except ZeroDivisionError: # Runtime is 0.
played = 0
if played > 2.0 and not self.up_next:
@ -338,7 +338,6 @@ class Player(xbmc.Player):
return
result = JSONRPC('Application.GetProperties').execute({'properties': ["volume", "muted"]})
result = result.get('result', {})
item['Volume'] = result.get('volume')
@ -415,7 +414,6 @@ class Player(xbmc.Player):
LOG.info("<[ transcode/%s ]", item['Id'])
item['Server'].jellyfin.close_transcode(item['DeviceId'])
path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/temp/").decode('utf-8')
if xbmcvfs.exists(path):

View file

@ -10,7 +10,7 @@ from helper import _, settings, dialog, JSONRPC, compare_version
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
@ -43,7 +43,7 @@ class Setup(object):
settings('enableTextureCache.bool', False)
dialog("ok", heading="{jellyfin}", line1=_(33103))
return
result = get_setting.execute({'setting': "services.webserverport"})

View file

@ -19,7 +19,7 @@ from jellyfin import Jellyfin
#################################################################################################
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
NODES = {
'tvshows': [
('all', None),
@ -70,7 +70,7 @@ DYNNODES = {
('FirstLetter', _(33171)),
('Genres', _(135)),
('Random', _(30229)),
#('Recommended', _(30230))
# ('Recommended', _(30230))
],
'musicvideos': [
('all', None),
@ -136,6 +136,7 @@ def verify_kodi_defaults():
if not xbmcvfs.exists(playlist_path):
xbmcvfs.mkdirs(playlist_path)
class Views(object):
sync = None
@ -246,7 +247,7 @@ class Views(object):
temp_view['Media'] = media
self.add_playlist(playlist_path, temp_view, True)
self.add_nodes(node_path, temp_view, True)
else: # Compensate for the duplicate.
else: # Compensate for the duplicate.
index += 1
else:
if view['Media'] in ('movies', 'tvshows', 'musicvideos'):
@ -421,7 +422,6 @@ class Views(object):
etree.SubElement(xml, 'match')
etree.SubElement(xml, 'content')
label = xml.find('label')
label.text = str(name) if type(name) == int else name
@ -438,7 +438,7 @@ class Views(object):
rule = etree.SubElement(xml, 'rule', {'field': "tag", 'operator': "is"})
etree.SubElement(rule, 'value').text = view['Tag']
getattr(self, 'node_' + node)(xml) # get node function based on node type
getattr(self, 'node_' + node)(xml) # get node function based on node type
indent(xml)
write_xml(etree.tostring(xml, 'UTF-8'), file)
@ -642,7 +642,7 @@ class Views(object):
if rule.attrib['field'] == 'inprogress':
break
else:
etree.SubElement(root, 'rule', {'field': "inprogress", 'operator':"true"})
etree.SubElement(root, 'rule', {'field': "inprogress", 'operator': "true"})
content = root.find('content')
content.text = "episodes"
@ -661,7 +661,6 @@ class Views(object):
else:
etree.SubElement(root, 'content').text = "episodes"
def order_media_folders(self, folders):
''' Returns a list of sorted media folders based on the Jellyfin views.
@ -704,7 +703,7 @@ class Views(object):
for library in (libraries or []):
view = {'Id': library[0], 'Name': library[1], 'Tag': library[1], 'Media': library[2]}
if library[0] in [x.replace('Mixed:', "") for x in self.sync['Whitelist']]: # Synced libraries
if library[0] in [x.replace('Mixed:', "") for x in self.sync['Whitelist']]: # Synced libraries
if view['Media'] in ('movies', 'tvshows', 'musicvideos', 'mixed'):
@ -718,7 +717,7 @@ class Views(object):
temp_view['Name'] = "%s (%s)" % (view['Name'], _(media))
self.window_node(index, temp_view, *node)
self.window_wnode(windex, temp_view, *node)
else: # Add one to compensate for the duplicate.
else: # Add one to compensate for the duplicate.
index += 1
windex += 1
else:
@ -734,7 +733,7 @@ class Views(object):
elif view['Media'] == 'music':
self.window_node(index, view, 'music')
else: # Dynamic entry
else: # Dynamic entry
if view['Media'] in ('homevideos', 'books', 'playlists'):
self.window_wnode(windex, view, 'browse')
windex += 1
@ -781,7 +780,7 @@ class Views(object):
if node in ('all', 'music'):
window_prop = "Jellyfin.nodes.%s" % index
window('%s.index' % window_prop, path.replace('all.xml', "")) # dir
window('%s.index' % window_prop, path.replace('all.xml', "")) # dir
window('%s.title' % window_prop, view['Name'].encode('utf-8'))
window('%s.content' % window_prop, path)
@ -833,7 +832,7 @@ class Views(object):
if node == 'all':
window_prop = "Jellyfin.wnodes.%s" % index
window('%s.index' % window_prop, path.replace('all.xml', "")) # dir
window('%s.index' % window_prop, path.replace('all.xml', "")) # dir
window('%s.title' % window_prop, view['Name'].encode('utf-8'))
window('%s.content' % window_prop, path)
@ -909,16 +908,16 @@ class Views(object):
total = int(window((name or 'Jellyfin.nodes') + '.total') or 0)
props = [
"index","id","path","artwork","title","content","type"
"inprogress.content","inprogress.title",
"inprogress.content","inprogress.path",
"nextepisodes.title","nextepisodes.content",
"nextepisodes.path","unwatched.title",
"unwatched.content","unwatched.path",
"recent.title","recent.content","recent.path",
"recentepisodes.title","recentepisodes.content",
"recentepisodes.path","inprogressepisodes.title",
"inprogressepisodes.content","inprogressepisodes.path"
"index", "id", "path", "artwork", "title", "content", "type"
"inprogress.content", "inprogress.title",
"inprogress.content", "inprogress.path",
"nextepisodes.title", "nextepisodes.content",
"nextepisodes.path", "unwatched.title",
"unwatched.content", "unwatched.path",
"recent.title", "recent.content", "recent.path",
"recentepisodes.title", "recentepisodes.content",
"recentepisodes.path", "inprogressepisodes.title",
"inprogressepisodes.content", "inprogressepisodes.path"
]
for i in range(total):
for prop in props:

View file

@ -13,10 +13,11 @@ import xbmc
#################################################################################################
PORT = 57578
LOG = logging.getLogger("JELLYFIN."+__name__)
LOG = logging.getLogger("JELLYFIN." + __name__)
#################################################################################################
class WebService(threading.Thread):
''' Run a webservice to trigger playback.
@ -127,7 +128,7 @@ class requestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
% (params.get('Id'), params.get('KodiId'), params.get('Name'), params.get('transcode') or False))
self.send_response(200)
self.send_header('Content-type','text/html')
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(path)
@ -142,4 +143,3 @@ class requestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
self.send_error(500, "Exception occurred: %s" % error)
return