mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2025-10-14 12:52:04 +00:00
Merge 49f6546aca
into 33bd7b8192
This commit is contained in:
commit
aca89a9974
35 changed files with 134 additions and 137 deletions
6
build.py
6
build.py
|
@ -41,8 +41,11 @@ def create_addon_xml(config: dict, source: str, py_version: str) -> None:
|
||||||
|
|
||||||
# Populate dependencies in template
|
# Populate dependencies in template
|
||||||
dependencies = config['dependencies'].get(py_version)
|
dependencies = config['dependencies'].get(py_version)
|
||||||
|
requires_el = root.find('requires')
|
||||||
|
assert isinstance(requires_el, ET.Element), "Unable to find requires element in template"
|
||||||
|
|
||||||
for dep in dependencies:
|
for dep in dependencies:
|
||||||
ET.SubElement(root.find('requires'), 'import', attrib=dep)
|
ET.SubElement(requires_el, 'import', attrib=dep)
|
||||||
|
|
||||||
# Populate version string
|
# Populate version string
|
||||||
addon_version = config.get('version')
|
addon_version = config.get('version')
|
||||||
|
@ -110,7 +113,6 @@ def folder_filter(folder_name: str) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser(description='Build flags:')
|
parser = argparse.ArgumentParser(description='Build flags:')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
|
@ -7,7 +7,7 @@ import os
|
||||||
|
|
||||||
from kodi_six import xbmc, xbmcaddon, xbmcvfs
|
from kodi_six import xbmc, xbmcaddon, xbmcvfs
|
||||||
|
|
||||||
from .helper import translate, window, settings, addon_id, dialog, LazyLogger
|
from .helper import translate, window, settings, ADDON_ID, dialog, LazyLogger
|
||||||
from .helper.utils import create_id, translate_path
|
from .helper.utils import create_id, translate_path
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
@ -21,11 +21,11 @@ def get_addon_name():
|
||||||
|
|
||||||
''' Used for logging.
|
''' Used for logging.
|
||||||
'''
|
'''
|
||||||
return xbmcaddon.Addon(addon_id()).getAddonInfo('name').upper()
|
return xbmcaddon.Addon(ADDON_ID).getAddonInfo('name').upper()
|
||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
return xbmcaddon.Addon(addon_id()).getAddonInfo('version')
|
return xbmcaddon.Addon(ADDON_ID).getAddonInfo('version')
|
||||||
|
|
||||||
|
|
||||||
def get_platform():
|
def get_platform():
|
||||||
|
|
|
@ -8,7 +8,7 @@ from kodi_six import xbmc, xbmcaddon
|
||||||
from . import client
|
from . import client
|
||||||
from .database import get_credentials, save_credentials
|
from .database import get_credentials, save_credentials
|
||||||
from .dialogs import ServerConnect, UsersConnect, LoginManual, ServerManual
|
from .dialogs import ServerConnect, UsersConnect, LoginManual, ServerManual
|
||||||
from .helper import settings, addon_id, event, api, window, LazyLogger
|
from .helper import settings, ADDON_ID, event, api, window, LazyLogger
|
||||||
from .jellyfin import Jellyfin
|
from .jellyfin import Jellyfin
|
||||||
from .jellyfin.connection_manager import CONNECTION_STATE
|
from .jellyfin.connection_manager import CONNECTION_STATE
|
||||||
from .helper.exceptions import HTTPException
|
from .helper.exceptions import HTTPException
|
||||||
|
@ -16,7 +16,7 @@ from .helper.exceptions import HTTPException
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
LOG = LazyLogger(__name__)
|
LOG = LazyLogger(__name__)
|
||||||
XML_PATH = (xbmcaddon.Addon(addon_id()).getAddonInfo('path'), "default", "1080i")
|
XML_PATH = (xbmcaddon.Addon(ADDON_ID).getAddonInfo('path'), "default", "1080i")
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ class Connect(object):
|
||||||
except RuntimeError as error:
|
except RuntimeError as error:
|
||||||
|
|
||||||
LOG.exception(error)
|
LOG.exception(error)
|
||||||
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addon_id())
|
xbmc.executebuiltin('Addon.OpenSettings(%s)' % ADDON_ID)
|
||||||
|
|
||||||
raise Exception('User sign in interrupted')
|
raise Exception('User sign in interrupted')
|
||||||
|
|
||||||
|
@ -144,17 +144,17 @@ class Connect(object):
|
||||||
|
|
||||||
''' Save user info.
|
''' Save user info.
|
||||||
'''
|
'''
|
||||||
self.user = client.jellyfin.get_user()
|
user = client.jellyfin.get_user()
|
||||||
settings('username', self.user['Name'])
|
settings('username', user['Name'])
|
||||||
|
|
||||||
if 'PrimaryImageTag' in self.user:
|
if 'PrimaryImageTag' in user:
|
||||||
server_address = client.auth.get_server_info(client.auth.server_id)['address']
|
server_address = client.auth.get_server_info(client.auth.server_id)['address']
|
||||||
window('JellyfinUserImage', api.API(self.user, server_address).get_user_artwork(self.user['Id']))
|
window('JellyfinUserImage', api.API(user, server_address).get_user_artwork(user['Id']))
|
||||||
|
|
||||||
def select_servers(self, state=None):
|
def select_servers(self, state=None):
|
||||||
|
|
||||||
state = state or self.connect_manager.connect({'enableAutoLogin': False})
|
state = state or self.connect_manager.connect({'enableAutoLogin': False})
|
||||||
user = {}
|
user = {} # TODO: Fixme: content of this dict is used below, but can never contain anything
|
||||||
|
|
||||||
dialog = ServerConnect("script-jellyfin-connect-server.xml", *XML_PATH)
|
dialog = ServerConnect("script-jellyfin-connect-server.xml", *XML_PATH)
|
||||||
dialog.set_args(
|
dialog.set_args(
|
||||||
|
|
|
@ -8,6 +8,7 @@ import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
from kodi_six import xbmc, xbmcvfs
|
from kodi_six import xbmc, xbmcvfs
|
||||||
from six import text_type
|
from six import text_type
|
||||||
|
@ -36,7 +37,7 @@ class Database(object):
|
||||||
'''
|
'''
|
||||||
timeout = 120
|
timeout = 120
|
||||||
discovered = False
|
discovered = False
|
||||||
discovered_file = None
|
discovered_file: str = None
|
||||||
|
|
||||||
def __init__(self, db_file=None, commit_close=True):
|
def __init__(self, db_file=None, commit_close=True):
|
||||||
|
|
||||||
|
@ -319,7 +320,7 @@ def reset_artwork():
|
||||||
LOG.info("[ reset artwork ]")
|
LOG.info("[ reset artwork ]")
|
||||||
|
|
||||||
|
|
||||||
def get_sync():
|
def get_sync() -> dict:
|
||||||
if (3, 0) <= sys.version_info < (3, 6):
|
if (3, 0) <= sys.version_info < (3, 6):
|
||||||
LOG.error("Python versions 3.0-3.5 are NOT supported.")
|
LOG.error("Python versions 3.0-3.5 are NOT supported.")
|
||||||
|
|
||||||
|
@ -343,7 +344,7 @@ def get_sync():
|
||||||
return sync
|
return sync
|
||||||
|
|
||||||
|
|
||||||
def save_sync(sync):
|
def save_sync(sync: Dict[str, Any]):
|
||||||
|
|
||||||
if not xbmcvfs.exists(ADDON_DATA):
|
if not xbmcvfs.exists(ADDON_DATA):
|
||||||
xbmcvfs.mkdirs(ADDON_DATA)
|
xbmcvfs.mkdirs(ADDON_DATA)
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
|
from sqlite3 import Cursor
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
from . import queries as QU
|
from . import queries as QU
|
||||||
|
@ -14,9 +17,10 @@ LOG = LazyLogger(__name__)
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
||||||
class JellyfinDatabase():
|
class JellyfinDatabase:
|
||||||
|
cursor: Cursor
|
||||||
|
|
||||||
def __init__(self, cursor):
|
def __init__(self, cursor: Cursor) -> None:
|
||||||
self.cursor = cursor
|
self.cursor = cursor
|
||||||
cursor.row_factory = sqlite_namedtuple_factory
|
cursor.row_factory = sqlite_namedtuple_factory
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,12 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from typing import List, cast
|
||||||
|
|
||||||
from kodi_six import xbmcgui, xbmcaddon
|
from kodi_six import xbmcgui, xbmcaddon
|
||||||
from six import ensure_text
|
from six import ensure_text
|
||||||
|
|
||||||
from ..helper import window, addon_id
|
from ..helper import window, ADDON_ID
|
||||||
from ..helper import LazyLogger
|
from ..helper import LazyLogger
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
@ -27,7 +28,7 @@ USER_IMAGE = 150
|
||||||
|
|
||||||
class ContextMenu(xbmcgui.WindowXMLDialog):
|
class ContextMenu(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
_options = []
|
_options: List[str] = []
|
||||||
selected_option = None
|
selected_option = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -51,7 +52,7 @@ class ContextMenu(xbmcgui.WindowXMLDialog):
|
||||||
self.getControl(USER_IMAGE).setImage(window('JellyfinUserImage'))
|
self.getControl(USER_IMAGE).setImage(window('JellyfinUserImage'))
|
||||||
|
|
||||||
LOG.info("options: %s", self._options)
|
LOG.info("options: %s", self._options)
|
||||||
self.list_ = self.getControl(LIST)
|
self.list_ = cast(xbmcgui.ControlList, self.getControl(LIST))
|
||||||
|
|
||||||
for option in self._options:
|
for option in self._options:
|
||||||
self.list_.addItem(self._add_listitem(option))
|
self.list_.addItem(self._add_listitem(option))
|
||||||
|
@ -73,7 +74,7 @@ class ContextMenu(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
def _add_editcontrol(self, x, y, height, width, password=0):
|
def _add_editcontrol(self, x, y, height, width, password=0):
|
||||||
|
|
||||||
media = os.path.join(xbmcaddon.Addon(addon_id()).getAddonInfo('path'), 'resources', 'skins', 'default', 'media')
|
media = os.path.join(xbmcaddon.Addon(ADDON_ID).getAddonInfo('path'), 'resources', 'skins', 'default', 'media')
|
||||||
control = xbmcgui.ControlImage(0, 0, 0, 0,
|
control = xbmcgui.ControlImage(0, 0, 0, 0,
|
||||||
filename=os.path.join(media, "white.png"),
|
filename=os.path.join(media, "white.png"),
|
||||||
aspectRatio=0,
|
aspectRatio=0,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
from kodi_six import xbmc, xbmcgui
|
from kodi_six import xbmc, xbmcgui
|
||||||
|
@ -22,7 +24,7 @@ START_BEGINNING = 3011
|
||||||
class ResumeDialog(xbmcgui.WindowXMLDialog):
|
class ResumeDialog(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
_resume_point = None
|
_resume_point = None
|
||||||
selected_option = None
|
selected_option: Optional[int] = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)
|
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
|
@ -32,7 +34,7 @@ MANUAL_SERVER = 206
|
||||||
class ServerConnect(xbmcgui.WindowXMLDialog):
|
class ServerConnect(xbmcgui.WindowXMLDialog):
|
||||||
|
|
||||||
user_image = None
|
user_image = None
|
||||||
servers = []
|
servers: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
_selected_server = None
|
_selected_server = None
|
||||||
_connect_login = False
|
_connect_login = False
|
||||||
|
|
|
@ -345,7 +345,7 @@ def browse(media, view_id=None, folder=None, server_id=None, api_client=None):
|
||||||
|
|
||||||
actions = Actions(server_id, api_client)
|
actions = Actions(server_id, api_client)
|
||||||
list_li = []
|
list_li = []
|
||||||
listing = listing if type(listing) == list else listing.get('Items', [])
|
listing = listing if isinstance(listing, list) else listing.get('Items', [])
|
||||||
|
|
||||||
for item in listing:
|
for item in listing:
|
||||||
|
|
||||||
|
@ -885,7 +885,7 @@ def backup():
|
||||||
if xbmcvfs.exists(backup + '/'):
|
if xbmcvfs.exists(backup + '/'):
|
||||||
if not dialog("yesno", "{jellyfin}", translate(33090)):
|
if not dialog("yesno", "{jellyfin}", translate(33090)):
|
||||||
|
|
||||||
return backup()
|
return backup
|
||||||
|
|
||||||
delete_folder(backup)
|
delete_folder(backup)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
# Workaround for threads using datetime: _striptime is locked
|
# Workaround for threads using datetime: _striptime is locked
|
||||||
import _strptime # noqa:F401
|
import _strptime # noqa:F401
|
||||||
|
@ -37,7 +38,7 @@ class Service(xbmc.Monitor):
|
||||||
monitor = None
|
monitor = None
|
||||||
play_event = None
|
play_event = None
|
||||||
warn = True
|
warn = True
|
||||||
settings = {'last_progress': datetime.today(), 'last_progress_report': datetime.today()}
|
settings: Dict[str, Any] = {'last_progress': datetime.today(), 'last_progress_report': datetime.today()}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
@ -264,17 +265,17 @@ class Service(xbmc.Monitor):
|
||||||
window('jellyfin_should_stop.bool', True)
|
window('jellyfin_should_stop.bool', True)
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
elif method in ('SyncLibrarySelection', 'RepairLibrarySelection', 'AddLibrarySelection', 'RemoveLibrarySelection'):
|
elif self.library_thread is not None and method in ('SyncLibrarySelection', 'RepairLibrarySelection', 'AddLibrarySelection', 'RemoveLibrarySelection'):
|
||||||
self.library_thread.select_libraries(method)
|
self.library_thread.select_libraries(method)
|
||||||
|
|
||||||
elif method == 'SyncLibrary':
|
elif self.library_thread is not None and method == 'SyncLibrary':
|
||||||
if not data.get('Id'):
|
if not data.get('Id'):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.library_thread.add_library(data['Id'], data.get('Update', False))
|
self.library_thread.add_library(data['Id'], data.get('Update', False))
|
||||||
xbmc.executebuiltin("Container.Refresh")
|
xbmc.executebuiltin("Container.Refresh")
|
||||||
|
|
||||||
elif method == 'RepairLibrary':
|
elif self.library_thread is not None and method == 'RepairLibrary':
|
||||||
if not data.get('Id'):
|
if not data.get('Id'):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -288,7 +289,7 @@ class Service(xbmc.Monitor):
|
||||||
self.library_thread.add_library(data['Id'])
|
self.library_thread.add_library(data['Id'])
|
||||||
xbmc.executebuiltin("Container.Refresh")
|
xbmc.executebuiltin("Container.Refresh")
|
||||||
|
|
||||||
elif method == 'RemoveLibrary':
|
elif self.library_thread is not None and method == 'RemoveLibrary':
|
||||||
libraries = data['Id'].split(',')
|
libraries = data['Id'].split(',')
|
||||||
|
|
||||||
for lib in libraries:
|
for lib in libraries:
|
||||||
|
@ -309,10 +310,11 @@ class Service(xbmc.Monitor):
|
||||||
self.library_thread = None
|
self.library_thread = None
|
||||||
|
|
||||||
Jellyfin.close_all()
|
Jellyfin.close_all()
|
||||||
self.monitor.server = []
|
if self.monitor is not None:
|
||||||
self.monitor.sleep = True
|
self.monitor.server = []
|
||||||
|
self.monitor.sleep = True
|
||||||
|
|
||||||
elif method == 'System.OnWake':
|
elif self.monitor is not None and method == 'System.OnWake':
|
||||||
|
|
||||||
if not self.monitor.sleep:
|
if not self.monitor.sleep:
|
||||||
LOG.warning("System.OnSleep was never called, skip System.OnWake")
|
LOG.warning("System.OnSleep was never called, skip System.OnWake")
|
||||||
|
@ -365,7 +367,7 @@ class Service(xbmc.Monitor):
|
||||||
self.settings['enable_context_transcode'] = settings('enableContextTranscode.bool')
|
self.settings['enable_context_transcode'] = settings('enableContextTranscode.bool')
|
||||||
LOG.info("New context transcode setting: %s", self.settings['enable_context_transcode'])
|
LOG.info("New context transcode setting: %s", self.settings['enable_context_transcode'])
|
||||||
|
|
||||||
if settings('useDirectPaths') != self.settings['mode'] and self.library_thread.started:
|
if self.library_thread is not None and settings('useDirectPaths') != self.settings['mode'] and self.library_thread.started:
|
||||||
|
|
||||||
self.settings['mode'] = settings('useDirectPaths')
|
self.settings['mode'] = settings('useDirectPaths')
|
||||||
LOG.info("New playback mode setting: %s", self.settings['mode'])
|
LOG.info("New playback mode setting: %s", self.settings['mode'])
|
||||||
|
|
|
@ -5,6 +5,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
||||||
|
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
import datetime
|
import datetime
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
from kodi_six import xbmc
|
from kodi_six import xbmc
|
||||||
|
|
||||||
|
@ -29,8 +30,8 @@ class FullSync(object):
|
||||||
sync.libraries()
|
sync.libraries()
|
||||||
'''
|
'''
|
||||||
# Borg - multiple instances, shared state
|
# Borg - multiple instances, shared state
|
||||||
_shared_state = {}
|
_shared_state: Dict[str, Any] = {}
|
||||||
sync = None
|
sync = get_sync()
|
||||||
running = False
|
running = False
|
||||||
screensaver = None
|
screensaver = None
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from .lazylogger import LazyLogger
|
||||||
|
|
||||||
from .translate import translate
|
from .translate import translate
|
||||||
|
|
||||||
from .utils import addon_id
|
from .utils import ADDON_ID
|
||||||
from .utils import window
|
from .utils import window
|
||||||
from .utils import settings
|
from .utils import settings
|
||||||
from .utils import kodi_version
|
from .utils import kodi_version
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
from . import settings, LazyLogger
|
from . import settings, LazyLogger
|
||||||
|
@ -42,7 +44,7 @@ class API(object):
|
||||||
return self.item['Name']
|
return self.item['Name']
|
||||||
|
|
||||||
def get_actors(self):
|
def get_actors(self):
|
||||||
cast = []
|
cast: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
if 'People' in self.item:
|
if 'People' in self.item:
|
||||||
self.get_people_artwork(self.item['People'])
|
self.get_people_artwork(self.item['People'])
|
||||||
|
|
|
@ -6,6 +6,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
from six import ensure_text
|
from six import ensure_text
|
||||||
from kodi_six import xbmc, xbmcaddon
|
from kodi_six import xbmc, xbmcaddon
|
||||||
|
@ -36,7 +37,7 @@ class LogHandler(logging.StreamHandler):
|
||||||
logging.StreamHandler.__init__(self)
|
logging.StreamHandler.__init__(self)
|
||||||
self.setFormatter(MyFormatter())
|
self.setFormatter(MyFormatter())
|
||||||
|
|
||||||
self.sensitive = {'Token': [], 'Server': []}
|
self.sensitive: Dict[str, List[str]] = {'Token': [], 'Server': []}
|
||||||
|
|
||||||
for server in database.get_credentials()['Servers']:
|
for server in database.get_credentials()['Servers']:
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ def translate(string):
|
||||||
|
|
||||||
''' Get add-on string. Returns in unicode.
|
''' Get add-on string. Returns in unicode.
|
||||||
'''
|
'''
|
||||||
if type(string) != int:
|
if not isinstance(string, int):
|
||||||
string = STRINGS[string]
|
string = STRINGS[string]
|
||||||
|
|
||||||
result = xbmcaddon.Addon('plugin.video.jellyfin').getLocalizedString(string)
|
result = xbmcaddon.Addon('plugin.video.jellyfin').getLocalizedString(string)
|
||||||
|
|
|
@ -29,11 +29,10 @@ LOG = LazyLogger(__name__)
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
|
||||||
def addon_id():
|
ADDON_ID = "plugin.video.jellyfin"
|
||||||
return "plugin.video.jellyfin"
|
|
||||||
|
|
||||||
|
|
||||||
def kodi_version():
|
def kodi_version() -> int:
|
||||||
# Kodistubs returns empty string, causing Python 3 tests to choke on int()
|
# Kodistubs returns empty string, causing Python 3 tests to choke on int()
|
||||||
# TODO: Make Kodistubs version configurable for testing purposes
|
# TODO: Make Kodistubs version configurable for testing purposes
|
||||||
if sys.version_info.major == 2:
|
if sys.version_info.major == 2:
|
||||||
|
@ -84,7 +83,7 @@ def settings(setting, value=None):
|
||||||
''' Get or add add-on settings.
|
''' Get or add add-on settings.
|
||||||
getSetting returns unicode object.
|
getSetting returns unicode object.
|
||||||
'''
|
'''
|
||||||
addon = xbmcaddon.Addon(addon_id())
|
addon = xbmcaddon.Addon(ADDON_ID)
|
||||||
|
|
||||||
if value is not None:
|
if value is not None:
|
||||||
if setting.endswith('.bool'):
|
if setting.endswith('.bool'):
|
||||||
|
@ -154,7 +153,8 @@ def event(method, data=None, sender=None, hexlify=False):
|
||||||
xbmc.executebuiltin('NotifyAll(%s, %s, %s)' % (sender, method, data))
|
xbmc.executebuiltin('NotifyAll(%s, %s, %s)' % (sender, method, data))
|
||||||
|
|
||||||
|
|
||||||
def dialog(dialog_type, *args, **kwargs):
|
def dialog(dialog_type: str, *args: str, **kwargs):
|
||||||
|
arg_list = list(args)
|
||||||
|
|
||||||
d = xbmcgui.Dialog()
|
d = xbmcgui.Dialog()
|
||||||
|
|
||||||
|
@ -166,9 +166,8 @@ def dialog(dialog_type, *args, **kwargs):
|
||||||
if "heading" in kwargs:
|
if "heading" in kwargs:
|
||||||
kwargs['heading'] = kwargs['heading'].replace("{jellyfin}", translate('addon_name'))
|
kwargs['heading'] = kwargs['heading'].replace("{jellyfin}", translate('addon_name'))
|
||||||
|
|
||||||
if args:
|
if arg_list:
|
||||||
args = list(args)
|
arg_list[0] = arg_list[0].replace("{jellyfin}", translate('addon_name'))
|
||||||
args[0] = args[0].replace("{jellyfin}", translate('addon_name'))
|
|
||||||
|
|
||||||
types = {
|
types = {
|
||||||
'yesno': d.yesno,
|
'yesno': d.yesno,
|
||||||
|
@ -179,7 +178,7 @@ def dialog(dialog_type, *args, **kwargs):
|
||||||
'numeric': d.numeric,
|
'numeric': d.numeric,
|
||||||
'multi': d.multiselect
|
'multi': d.multiselect
|
||||||
}
|
}
|
||||||
return types[dialog_type](*args, **kwargs)
|
return types[dialog_type](*arg_list, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def should_stop():
|
def should_stop():
|
||||||
|
|
|
@ -27,7 +27,7 @@ def progress(message=None):
|
||||||
|
|
||||||
dialog = xbmcgui.DialogProgressBG()
|
dialog = xbmcgui.DialogProgressBG()
|
||||||
|
|
||||||
if item and type(item) == dict:
|
if item and isinstance(item, dict):
|
||||||
|
|
||||||
dialog.create(translate('addon_name'), "%s %s" % (translate('gathering'), item['Name']))
|
dialog.create(translate('addon_name'), "%s %s" % (translate('gathering'), item['Name']))
|
||||||
LOG.info("Processing %s: %s", item['Name'], item['Id'])
|
LOG.info("Processing %s: %s", item['Name'], item['Id'])
|
||||||
|
@ -72,7 +72,7 @@ def jellyfin_item(func):
|
||||||
''' Wrapper to retrieve the jellyfin_db item.
|
''' Wrapper to retrieve the jellyfin_db item.
|
||||||
'''
|
'''
|
||||||
def wrapper(self, item, *args, **kwargs):
|
def wrapper(self, item, *args, **kwargs):
|
||||||
e_item = self.jellyfin_db.get_item_by_id(item['Id'] if type(item) == dict else item)
|
e_item = self.jellyfin_db.get_item_by_id(item['Id'] if isinstance(item, dict) else item)
|
||||||
|
|
||||||
return func(self, item, e_item=e_item, *args, **kwargs)
|
return func(self, item, e_item=e_item, *args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ def tvtunes_nfo(path, urls):
|
||||||
except Exception:
|
except Exception:
|
||||||
xml = etree.Element('tvtunes')
|
xml = etree.Element('tvtunes')
|
||||||
|
|
||||||
for elem in xml.getiterator('tvtunes'):
|
for elem in xml.iter('tvtunes'):
|
||||||
for file in list(elem):
|
for file in list(elem):
|
||||||
elem.remove(file)
|
elem.remove(file)
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ def advanced_settings():
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def verify_kodi_defaults():
|
def verify_kodi_defaults():
|
||||||
''' Make sure we have the kodi default folder in place.
|
''' Make sure we have the kodi default folder in place.
|
||||||
'''
|
'''
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
from ..helper import has_attribute, LazyLogger
|
from ..helper import has_attribute, LazyLogger
|
||||||
|
@ -43,16 +45,15 @@ class Jellyfin(object):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Borg - multiple instances, shared state
|
# Borg - multiple instances, shared state
|
||||||
_shared_state = {}
|
_shared_state: Dict[str, Any] = {}
|
||||||
client = {}
|
client: Dict[str, JellyfinClient] = {}
|
||||||
server_id = "default"
|
server_id: str = "default"
|
||||||
|
|
||||||
def __init__(self, server_id=None):
|
def __init__(self, server_id=None):
|
||||||
self.__dict__ = self._shared_state
|
self.__dict__ = self._shared_state
|
||||||
self.server_id = server_id or "default"
|
self.server_id = server_id or "default"
|
||||||
|
|
||||||
def get_client(self):
|
def get_client(self) -> JellyfinClient:
|
||||||
# type: () -> JellyfinClient
|
|
||||||
return self.client[self.server_id]
|
return self.client[self.server_id]
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
@ -71,7 +72,7 @@ class Jellyfin(object):
|
||||||
for client in cls.client:
|
for client in cls.client:
|
||||||
cls.client[client].stop()
|
cls.client[client].stop()
|
||||||
|
|
||||||
cls.client = {}
|
cls.client.clear()
|
||||||
LOG.info("---[ STOPPED ALL JELLYFINCLIENTS ]---")
|
LOG.info("---[ STOPPED ALL JELLYFINCLIENTS ]---")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -8,6 +8,7 @@ import socket
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
import traceback
|
import traceback
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import urllib3
|
import urllib3
|
||||||
|
|
||||||
|
@ -31,8 +32,7 @@ CONNECTION_STATE = {
|
||||||
|
|
||||||
class ConnectionManager(object):
|
class ConnectionManager(object):
|
||||||
|
|
||||||
user = {}
|
server_id: Optional[str] = None
|
||||||
server_id = None
|
|
||||||
|
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ def clean_none_dict_values(obj):
|
||||||
if mutable:
|
if mutable:
|
||||||
# Remove keys with None value
|
# Remove keys with None value
|
||||||
for key in remove:
|
for key in remove:
|
||||||
item.pop(key)
|
item.pop(key) # typing: ignore [attr-defined]
|
||||||
|
|
||||||
elif isinstance(item, collections_abc.Iterable):
|
elif isinstance(item, collections_abc.Iterable):
|
||||||
for value in item:
|
for value in item:
|
||||||
|
|
|
@ -14,7 +14,7 @@ from ..helper import LazyLogger, settings
|
||||||
# If numpy is installed, the websockets library tries to use it, and then
|
# If numpy is installed, the websockets library tries to use it, and then
|
||||||
# kodi hard crashes for reasons I don't even want to pretend to understand
|
# kodi hard crashes for reasons I don't even want to pretend to understand
|
||||||
import sys # noqa: E402,I100
|
import sys # noqa: E402,I100
|
||||||
sys.modules['numpy'] = None
|
sys.modules['numpy'] = None # type: ignore [assignment]
|
||||||
import websocket # noqa: E402,I201
|
import websocket # noqa: E402,I201
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
|
@ -5,6 +5,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
from six.moves import queue as Queue
|
from six.moves import queue as Queue
|
||||||
|
|
||||||
|
@ -48,18 +49,18 @@ class Library(threading.Thread):
|
||||||
self.monitor = monitor
|
self.monitor = monitor
|
||||||
self.player = monitor.monitor.player
|
self.player = monitor.monitor.player
|
||||||
self.server = Jellyfin().get_client()
|
self.server = Jellyfin().get_client()
|
||||||
self.updated_queue = Queue.Queue()
|
self.updated_queue: Queue.Queue[str] = Queue.Queue()
|
||||||
self.userdata_queue = Queue.Queue()
|
self.userdata_queue: Queue.Queue[str] = Queue.Queue()
|
||||||
self.removed_queue = Queue.Queue()
|
self.removed_queue: Queue.Queue[str] = Queue.Queue()
|
||||||
self.updated_output = self.__new_queues__()
|
self.updated_output = self.__new_queues__()
|
||||||
self.userdata_output = self.__new_queues__()
|
self.userdata_output = self.__new_queues__()
|
||||||
self.removed_output = self.__new_queues__()
|
self.removed_output = self.__new_queues__()
|
||||||
self.notify_output = Queue.Queue()
|
self.notify_output: Queue.Queue[Tuple[str, str]] = Queue.Queue()
|
||||||
|
|
||||||
self.jellyfin_threads = []
|
self.jellyfin_threads = []
|
||||||
self.download_threads = []
|
self.download_threads = []
|
||||||
self.notify_threads = []
|
self.notify_threads = []
|
||||||
self.writer_threads = {'updated': [], 'userdata': [], 'removed': []}
|
self.writer_threads: Dict[str, List[BaseWorker]] = {'updated': [], 'userdata': [], 'removed': []}
|
||||||
self.database_lock = threading.Lock()
|
self.database_lock = threading.Lock()
|
||||||
self.music_database_lock = threading.Lock()
|
self.music_database_lock = threading.Lock()
|
||||||
|
|
||||||
|
@ -231,10 +232,10 @@ class Library(threading.Thread):
|
||||||
|
|
||||||
''' Get items from jellyfin and place them in the appropriate queues.
|
''' Get items from jellyfin and place them in the appropriate queues.
|
||||||
'''
|
'''
|
||||||
for queue in ((self.updated_queue, self.updated_output), (self.userdata_queue, self.userdata_output)):
|
for input_queue, output_queue in ((self.updated_queue, self.updated_output), (self.userdata_queue, self.userdata_output)):
|
||||||
if queue[0].qsize() and len(self.download_threads) < DTHREADS:
|
if input_queue.qsize() and len(self.download_threads) < DTHREADS:
|
||||||
|
|
||||||
new_thread = GetItemWorker(self.server, queue[0], queue[1])
|
new_thread = GetItemWorker(self.server, input_queue, output_queue)
|
||||||
new_thread.start()
|
new_thread.start()
|
||||||
LOG.info("-->[ q:download/%s ]", id(new_thread))
|
LOG.info("-->[ q:download/%s ]", id(new_thread))
|
||||||
self.download_threads.append(new_thread)
|
self.download_threads.append(new_thread)
|
||||||
|
@ -600,7 +601,11 @@ class Library(threading.Thread):
|
||||||
LOG.info("---[ removed:%s ]", len(data))
|
LOG.info("---[ removed:%s ]", len(data))
|
||||||
|
|
||||||
|
|
||||||
class UpdateWorker(threading.Thread):
|
class BaseWorker(threading.Thread):
|
||||||
|
is_done: bool
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateWorker(BaseWorker):
|
||||||
|
|
||||||
is_done = False
|
is_done = False
|
||||||
|
|
||||||
|
@ -676,7 +681,7 @@ class UpdateWorker(threading.Thread):
|
||||||
self.is_done = True
|
self.is_done = True
|
||||||
|
|
||||||
|
|
||||||
class UserDataWorker(threading.Thread):
|
class UserDataWorker(BaseWorker):
|
||||||
|
|
||||||
is_done = False
|
is_done = False
|
||||||
|
|
||||||
|
@ -739,7 +744,7 @@ class UserDataWorker(threading.Thread):
|
||||||
self.is_done = True
|
self.is_done = True
|
||||||
|
|
||||||
|
|
||||||
class SortWorker(threading.Thread):
|
class SortWorker(BaseWorker):
|
||||||
|
|
||||||
is_done = False
|
is_done = False
|
||||||
|
|
||||||
|
@ -786,7 +791,7 @@ class SortWorker(threading.Thread):
|
||||||
self.is_done = True
|
self.is_done = True
|
||||||
|
|
||||||
|
|
||||||
class RemovedWorker(threading.Thread):
|
class RemovedWorker(BaseWorker):
|
||||||
|
|
||||||
is_done = False
|
is_done = False
|
||||||
|
|
||||||
|
@ -847,7 +852,7 @@ class RemovedWorker(threading.Thread):
|
||||||
self.is_done = True
|
self.is_done = True
|
||||||
|
|
||||||
|
|
||||||
class NotifyWorker(threading.Thread):
|
class NotifyWorker(BaseWorker):
|
||||||
|
|
||||||
is_done = False
|
is_done = False
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
||||||
import binascii
|
import binascii
|
||||||
import json
|
import json
|
||||||
import threading
|
import threading
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from kodi_six import xbmc
|
from kodi_six import xbmc
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ LOG = LazyLogger(__name__)
|
||||||
|
|
||||||
class Monitor(xbmc.Monitor):
|
class Monitor(xbmc.Monitor):
|
||||||
|
|
||||||
servers = []
|
servers: List[str] = []
|
||||||
sleep = False
|
sleep = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -116,11 +117,11 @@ class Monitor(xbmc.Monitor):
|
||||||
LOG.exception(error)
|
LOG.exception(error)
|
||||||
server = Jellyfin()
|
server = Jellyfin()
|
||||||
|
|
||||||
server = server.get_client()
|
server_client = server.get_client()
|
||||||
|
|
||||||
if method == 'Play':
|
if method == 'Play':
|
||||||
|
|
||||||
items = server.jellyfin.get_items(data['ItemIds'])
|
items = server_client.jellyfin.get_items(data['ItemIds'])
|
||||||
|
|
||||||
PlaylistWorker(data.get('ServerId'), items, data['PlayCommand'] == 'PlayNow',
|
PlaylistWorker(data.get('ServerId'), items, data['PlayCommand'] == 'PlayNow',
|
||||||
data.get('StartPositionTicks', 0), data.get('AudioStreamIndex'),
|
data.get('StartPositionTicks', 0), data.get('AudioStreamIndex'),
|
||||||
|
@ -129,7 +130,7 @@ class Monitor(xbmc.Monitor):
|
||||||
# TODO no clue if this is called by anything
|
# TODO no clue if this is called by anything
|
||||||
elif method == 'PlayPlaylist':
|
elif method == 'PlayPlaylist':
|
||||||
|
|
||||||
server.jellyfin.post_session(server.config.data['app.session'], "Playing", {
|
server_client.jellyfin.post_session(server_client.config.data['app.session'], "Playing", {
|
||||||
'PlayCommand': "PlayNow",
|
'PlayCommand': "PlayNow",
|
||||||
'ItemIds': data['Id'],
|
'ItemIds': data['Id'],
|
||||||
'StartPositionTicks': 0
|
'StartPositionTicks': 0
|
||||||
|
@ -148,14 +149,14 @@ class Monitor(xbmc.Monitor):
|
||||||
self.server_instance(data['ServerId'])
|
self.server_instance(data['ServerId'])
|
||||||
|
|
||||||
elif method == 'AddUser':
|
elif method == 'AddUser':
|
||||||
server.jellyfin.session_add_user(server.config.data['app.session'], data['Id'], data['Add'])
|
server_client.jellyfin.session_add_user(server_client.config.data['app.session'], data['Id'], data['Add'])
|
||||||
self.additional_users(server)
|
self.additional_users(server_client)
|
||||||
|
|
||||||
elif method == 'Player.OnPlay':
|
elif method == 'Player.OnPlay':
|
||||||
on_play(data, server)
|
on_play(data, server_client)
|
||||||
|
|
||||||
elif method == 'VideoLibrary.OnUpdate':
|
elif method == 'VideoLibrary.OnUpdate':
|
||||||
on_update(data, server)
|
on_update(data, server_client)
|
||||||
|
|
||||||
def server_instance(self, server_id=None):
|
def server_instance(self, server_id=None):
|
||||||
|
|
||||||
|
@ -178,7 +179,6 @@ class Monitor(xbmc.Monitor):
|
||||||
|
|
||||||
self.additional_users(server)
|
self.additional_users(server)
|
||||||
|
|
||||||
|
|
||||||
def additional_users(self, server):
|
def additional_users(self, server):
|
||||||
|
|
||||||
''' Setup additional users images.
|
''' Setup additional users images.
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
|
from abc import ABCMeta
|
||||||
|
from sqlite3 import Cursor
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
from ...helper import values, LazyLogger, kodi_version
|
from ...helper import values, LazyLogger, kodi_version
|
||||||
|
@ -15,7 +18,8 @@ LOG = LazyLogger(__name__)
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
||||||
class Kodi(object):
|
class Kodi(metaclass=ABCMeta):
|
||||||
|
cursor: Cursor
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.artwork = artwork.Artwork(self.cursor)
|
self.artwork = artwork.Artwork(self.cursor)
|
||||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from ..database import jellyfin_db, queries as QUEM
|
from ..database import jellyfin_db, queries as QUEM
|
||||||
from ..helper import api, stop, validate, jellyfin_item, values, Local, LazyLogger
|
from ..helper import api, stop, validate, jellyfin_item, values, Local, LazyLogger
|
||||||
|
@ -551,7 +552,7 @@ class Music(KodiDb):
|
||||||
''' Get all child elements from tv show jellyfin id.
|
''' Get all child elements from tv show jellyfin id.
|
||||||
'''
|
'''
|
||||||
obj = {'Id': item_id}
|
obj = {'Id': item_id}
|
||||||
child = []
|
child: List[str] = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
obj['KodiId'] = e_item[0]
|
obj['KodiId'] = e_item[0]
|
||||||
|
|
|
@ -5,6 +5,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from six.moves.urllib.parse import urlencode
|
from six.moves.urllib.parse import urlencode
|
||||||
from kodi_six.utils import py2_encode
|
from kodi_six.utils import py2_encode
|
||||||
|
@ -122,7 +123,7 @@ class MusicVideos(KodiDb):
|
||||||
if search:
|
if search:
|
||||||
obj['Index'] = search.group()
|
obj['Index'] = search.group()
|
||||||
|
|
||||||
tags = []
|
tags: List[str] = []
|
||||||
tags.extend(obj['Tags'] or [])
|
tags.extend(obj['Tags'] or [])
|
||||||
tags.append(obj['LibraryName'])
|
tags.append(obj['LibraryName'])
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
from six import iteritems, ensure_text
|
from six import iteritems, ensure_text
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ LOG = LazyLogger(__name__)
|
||||||
class Objects(object):
|
class Objects(object):
|
||||||
|
|
||||||
# Borg - multiple instances, shared state
|
# Borg - multiple instances, shared state
|
||||||
_shared_state = {}
|
_shared_state: Dict[str, Any] = {}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
@ -107,7 +108,7 @@ class Objects(object):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if obj_key:
|
if obj_key:
|
||||||
obj = [d[obj_key] for d in obj if d.get(obj_key)] if type(obj) == list else obj.get(obj_key)
|
obj = [d[obj_key] for d in obj if d.get(obj_key)] if isinstance(obj, list) else obj.get(obj_key)
|
||||||
|
|
||||||
self.mapped_item[key] = obj
|
self.mapped_item[key] = obj
|
||||||
break
|
break
|
||||||
|
|
|
@ -5,6 +5,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from ntpath import dirname
|
from ntpath import dirname
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from six.moves.urllib.parse import urlencode
|
from six.moves.urllib.parse import urlencode
|
||||||
from kodi_six.utils import py2_encode
|
from kodi_six.utils import py2_encode
|
||||||
|
@ -101,7 +102,7 @@ class TVShows(KodiDb):
|
||||||
if obj['Premiere']:
|
if obj['Premiere']:
|
||||||
obj['Premiere'] = str(Local(obj['Premiere'])).split('.')[0].replace('T', " ")
|
obj['Premiere'] = str(Local(obj['Premiere'])).split('.')[0].replace('T', " ")
|
||||||
|
|
||||||
tags = []
|
tags: List[str] = []
|
||||||
tags.extend(obj['Tags'] or [])
|
tags.extend(obj['Tags'] or [])
|
||||||
tags.append(obj['LibraryName'])
|
tags.append(obj['LibraryName'])
|
||||||
|
|
||||||
|
@ -656,7 +657,7 @@ class TVShows(KodiDb):
|
||||||
''' Get all child elements from tv show jellyfin id.
|
''' Get all child elements from tv show jellyfin id.
|
||||||
'''
|
'''
|
||||||
obj = {'Id': item_id}
|
obj = {'Id': item_id}
|
||||||
child = []
|
child: List[str] = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
obj['KodiId'] = e_item[0]
|
obj['KodiId'] = e_item[0]
|
||||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import division, absolute_import, print_function, unicode_litera
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
from kodi_six import xbmc, xbmcvfs
|
from kodi_six import xbmc, xbmcvfs
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ LOG = LazyLogger(__name__)
|
||||||
|
|
||||||
class Player(xbmc.Player):
|
class Player(xbmc.Player):
|
||||||
|
|
||||||
played = {}
|
played: Dict[str, dict] = {}
|
||||||
up_next = False
|
up_next = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -411,7 +411,7 @@ class Views(object):
|
||||||
etree.SubElement(xml, 'content')
|
etree.SubElement(xml, 'content')
|
||||||
|
|
||||||
label = xml.find('label')
|
label = xml.find('label')
|
||||||
label.text = str(name) if type(name) == int else name
|
label.text = str(name) if isinstance(name, int) else name
|
||||||
|
|
||||||
content = xml.find('content')
|
content = xml.find('content')
|
||||||
content.text = view['Media']
|
content.text = view['Media']
|
||||||
|
@ -772,7 +772,7 @@ class Views(object):
|
||||||
else:
|
else:
|
||||||
window_path = "ActivateWindow(Videos,%s,return)" % path
|
window_path = "ActivateWindow(Videos,%s,return)" % path
|
||||||
|
|
||||||
node_label = translate(node_label) if type(node_label) == int else node_label
|
node_label = translate(node_label) if isinstance(node_label, int) else node_label
|
||||||
node_label = node_label or view['Name']
|
node_label = node_label or view['Name']
|
||||||
|
|
||||||
if node in ('all', 'music'):
|
if node in ('all', 'music'):
|
||||||
|
@ -824,7 +824,7 @@ class Views(object):
|
||||||
else:
|
else:
|
||||||
window_path = "ActivateWindow(Videos,%s,return)" % path
|
window_path = "ActivateWindow(Videos,%s,return)" % path
|
||||||
|
|
||||||
node_label = translate(node_label) if type(node_label) == int else node_label
|
node_label = translate(node_label) if isinstance(node_label, int) else node_label
|
||||||
node_label = node_label or view['Name']
|
node_label = node_label or view['Name']
|
||||||
|
|
||||||
if node == 'all':
|
if node == 'all':
|
||||||
|
|
4
mypy.ini
Normal file
4
mypy.ini
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[mypy]
|
||||||
|
check_untyped_defs = True
|
||||||
|
warn_unused_configs = True
|
||||||
|
files = .
|
2
tox.ini
2
tox.ini
|
@ -1,7 +1,7 @@
|
||||||
[flake8]
|
[flake8]
|
||||||
max-line-length = 9999
|
max-line-length = 9999
|
||||||
import-order-style = pep8
|
import-order-style = pep8
|
||||||
exclude = .git,.vscode,libraries,build.py,.github
|
exclude = .git,.vscode,libraries,.github
|
||||||
extend-ignore =
|
extend-ignore =
|
||||||
I202
|
I202
|
||||||
per-file-ignores =
|
per-file-ignores =
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
from sqlite3 import Cursor
|
|
||||||
from typing import Any, List, Optional, NamedTuple
|
|
||||||
|
|
||||||
|
|
||||||
class ViewRow(NamedTuple):
|
|
||||||
view_id: str
|
|
||||||
view_name: str
|
|
||||||
media_type: str
|
|
||||||
|
|
||||||
|
|
||||||
class JellyfinDatabase:
|
|
||||||
cursor: Cursor = ...
|
|
||||||
def __init__(self, cursor: Cursor) -> None: ...
|
|
||||||
def get_view(self, *args: Any) -> Optional[ViewRow]: ...
|
|
||||||
def get_views(self) -> List[ViewRow]: ...
|
|
||||||
|
|
||||||
# def get_item_by_id(self, *args: Any): ...
|
|
||||||
# def add_reference(self, *args: Any) -> None: ...
|
|
||||||
# def update_reference(self, *args: Any) -> None: ...
|
|
||||||
# def update_parent_id(self, *args: Any) -> None: ...
|
|
||||||
# def get_item_id_by_parent_id(self, *args: Any): ...
|
|
||||||
# def get_item_by_parent_id(self, *args: Any): ...
|
|
||||||
# def get_item_by_media_folder(self, *args: Any): ...
|
|
||||||
# def get_item_by_wild_id(self, item_id: Any): ...
|
|
||||||
# def get_checksum(self, *args: Any): ...
|
|
||||||
# def get_item_by_kodi_id(self, *args: Any): ...
|
|
||||||
# def get_full_item_by_kodi_id(self, *args: Any): ...
|
|
||||||
# def get_media_by_id(self, *args: Any): ...
|
|
||||||
# def get_media_by_parent_id(self, *args: Any): ...
|
|
||||||
# def remove_item(self, *args: Any) -> None: ...
|
|
||||||
# def remove_items_by_parent_id(self, *args: Any) -> None: ...
|
|
||||||
# def remove_item_by_kodi_id(self, *args: Any) -> None: ...
|
|
||||||
# def remove_wild_item(self, item_id: Any) -> None: ...
|
|
||||||
# def get_view_name(self, item_id: Any): ...
|
|
||||||
# def add_view(self, *args: Any) -> None: ...
|
|
||||||
# def remove_view(self, *args: Any) -> None: ...
|
|
||||||
# def get_views_by_media(self, *args: Any): ...
|
|
||||||
# def get_items_by_media(self, *args: Any): ...
|
|
||||||
# def remove_media_by_parent_id(self, *args: Any) -> None: ...
|
|
|
@ -1 +0,0 @@
|
||||||
def kodi_version(self) -> int: ...
|
|
Loading…
Add table
Add a link
Reference in a new issue