mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2025-04-14 14:33:48 +00:00
Most flake8 warnings corrected
This commit is contained in:
parent
e92d60f7c7
commit
00765c0a12
58 changed files with 1144 additions and 1262 deletions
|
@ -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(),
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -8,7 +8,7 @@ import queries as QU
|
|||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger("JELLYFIN."+__name__)
|
||||
LOG = logging.getLogger("JELLYFIN." + __name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
|
|
@ -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
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']
|
||||
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
#################################################################################################
|
||||
|
||||
|
||||
class LibraryException(Exception):
|
||||
# Jellyfin library sync exception
|
||||
def __init__(self, status):
|
||||
self.status = status
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -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")
|
||||
|
|
|
@ -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__)
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -10,10 +10,11 @@ from datetime import datetime
|
|||
|
||||
#################################################################################################
|
||||
|
||||
LOG = logging.getLogger('JELLYFIN.'+__name__)
|
||||
LOG = logging.getLogger('JELLYFIN.' + __name__)
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
||||
class Credentials(object):
|
||||
|
||||
credentials = None
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
#################################################################################################
|
||||
|
||||
|
||||
class HTTPException(Exception):
|
||||
# Jellyfin HTTP exception
|
||||
def __init__(self, status, message):
|
||||
self.status = status
|
||||
self.message = message
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ from ..resources import websocket
|
|||
|
||||
##################################################################################################
|
||||
|
||||
LOG = logging.getLogger('JELLYFIN.'+__name__)
|
||||
LOG = logging.getLogger('JELLYFIN.' + __name__)
|
||||
|
||||
##################################################################################################
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
||||
"""
|
||||
|
||||
|
|
|
@ -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.
|
||||
'''
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,))
|
||||
|
|
|
@ -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
|
@ -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
|
||||
"""
|
|
@ -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 = ?
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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('!'):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -8,7 +8,7 @@ from helper import JSONRPC
|
|||
|
||||
#################################################################################################
|
||||
|
||||
LOG = logging.getLogger("JELLYFIN."+__name__)
|
||||
LOG = logging.getLogger("JELLYFIN." + __name__)
|
||||
|
||||
#################################################################################################
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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"})
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue