diff --git a/jellyfin_kodi/database/jellyfin_db.py b/jellyfin_kodi/database/jellyfin_db.py index 92b6d69f..2036a25d 100644 --- a/jellyfin_kodi/database/jellyfin_db.py +++ b/jellyfin_kodi/database/jellyfin_db.py @@ -5,6 +5,8 @@ from __future__ import division, absolute_import, print_function, unicode_litera from database import queries as QU from helper import LazyLogger +from jellyfin.utils import sqlite_namedtuple_factory + ################################################################################################## LOG = LazyLogger(__name__) @@ -16,6 +18,7 @@ class JellyfinDatabase(): def __init__(self, cursor): self.cursor = cursor + cursor.row_factory = sqlite_namedtuple_factory def get_item_by_id(self, *args): self.cursor.execute(QU.get_item, args) @@ -124,8 +127,8 @@ class JellyfinDatabase(): def remove_view(self, *args): self.cursor.execute(QU.delete_view, args) - def get_views(self, *args): - self.cursor.execute(QU.get_views, args) + def get_views(self): + self.cursor.execute(QU.get_views) return self.cursor.fetchall() diff --git a/jellyfin_kodi/database/queries.py b/jellyfin_kodi/database/queries.py index b80b792b..e1c3eb69 100644 --- a/jellyfin_kodi/database/queries.py +++ b/jellyfin_kodi/database/queries.py @@ -66,7 +66,7 @@ FROM jellyfin WHERE jellyfin_parent_id = ? """ get_view = """ -SELECT view_name, media_type +SELECT * FROM view WHERE view_id = ? """ diff --git a/jellyfin_kodi/entrypoint/default.py b/jellyfin_kodi/entrypoint/default.py index 2e002d06..cbede539 100644 --- a/jellyfin_kodi/entrypoint/default.py +++ b/jellyfin_kodi/entrypoint/default.py @@ -811,7 +811,7 @@ def get_themes(api_client): with Database('jellyfin') as jellyfindb: all_views = jellyfin_db.JellyfinDatabase(jellyfindb.cursor).get_views() - views = [x[0] for x in all_views if x[2] in ('movies', 'tvshows', 'mixed')] + views = [x.view_id for x in all_views if x.media_type in ('movies', 'tvshows', 'mixed')] items = {} server = api_client.config.data['auth.server'] diff --git a/jellyfin_kodi/full_sync.py b/jellyfin_kodi/full_sync.py index b28ade1c..36ceb64b 100644 --- a/jellyfin_kodi/full_sync.py +++ b/jellyfin_kodi/full_sync.py @@ -86,9 +86,9 @@ class FullSync(object): if library: - self.sync['Libraries'].append("Mixed:%s" % selected if library[1] == 'mixed' else selected) + self.sync['Libraries'].append("Mixed:%s" % selected) - if library[1] in ('mixed', 'movies'): + if library.media_type in ('mixed', 'movies'): self.sync['Libraries'].append('Boxsets:%s' % selected) else: self.sync['Libraries'].append(selected) @@ -129,9 +129,8 @@ class FullSync(object): libraries = [] for library in self.get_libraries(): - - if library[2] in ('movies', 'tvshows', 'musicvideos', 'music', 'mixed'): - libraries.append({'Id': library[0], 'Name': library[1], 'Media': library[2]}) + if library.media_type in ('movies', 'tvshows', 'musicvideos', 'music', 'mixed'): + libraries.append({'Id': library.view_id, 'Name': library.view_name, 'Media': library.media_type}) libraries = self.select_libraries(libraries) @@ -223,16 +222,12 @@ class FullSync(object): if not sync_id or sync_id == 'Refresh': libraries = self.get_libraries() else: - res = self.get_library(sync_id) - if res is not None: - # FIXME: This is a hack. Fix plz. - libraries = [(sync_id,) + res] - else: - libraries = [] + _lib = self.get_library(sync_id) + libraries = [_lib] if _lib else [] for entry in libraries: - if entry[2] == 'boxsets': - boxset_library = {'Id': entry[0], 'Name': entry[1]} + if entry.media_type == 'boxsets': + boxset_library = {'Id': entry.view_id, 'Name': entry.view_name} break if boxset_library: @@ -529,7 +524,7 @@ class FullSync(object): db = jellyfin_db.JellyfinDatabase(jellyfindb.cursor) library = db.get_view(library_id.replace('Mixed:', "")) items = db.get_item_by_media_folder(library_id.replace('Mixed:', "")) - media = 'music' if library[1] == 'music' else 'video' + media = 'music' if library.media_type == 'music' else 'video' if media == 'music': settings('MusicRescan.bool', False) @@ -540,7 +535,7 @@ class FullSync(object): with self.library.music_database_lock if media == 'music' else self.library.database_lock: with Database(media) as kodidb: - if library[1] == 'mixed': + if library.media_type == 'mixed': movies = [x for x in items if x[1] == 'Movie'] tvshows = [x for x in items if x[1] == 'Series'] @@ -550,7 +545,7 @@ class FullSync(object): for item in movies: obj(item[0]) - dialog.update(int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (translate('addon_name'), library[0])) + dialog.update(int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (translate('addon_name'), library.view_name)) count += 1 obj = TVShows(self.server, jellyfindb, kodidb, direct_path, library).remove @@ -558,7 +553,7 @@ class FullSync(object): for item in tvshows: obj(item[0]) - dialog.update(int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (translate('addon_name'), library[0])) + dialog.update(int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (translate('addon_name'), library.view_name)) count += 1 else: default_args = (self.server, jellyfindb, kodidb, direct_path) diff --git a/jellyfin_kodi/jellyfin/utils.py b/jellyfin_kodi/jellyfin/utils.py index a2b8f572..ad976c78 100644 --- a/jellyfin_kodi/jellyfin/utils.py +++ b/jellyfin_kodi/jellyfin/utils.py @@ -1,5 +1,6 @@ from six import string_types from six.moves import collections_abc +from collections import namedtuple def clean_none_dict_values(obj): @@ -41,3 +42,15 @@ def clean_none_dict_values(obj): queue.append(value) return obj + + +def sqlite_namedtuple_factory(cursor, row): + """ + Usage: + con.row_factory = namedtuple_factory + + http://peter-hoffmann.com/2010/python-sqlite-namedtuple-factory.html + """ + fields = [col[0] for col in cursor.description] + Row = namedtuple("Row", fields) + return Row(*row) diff --git a/jellyfin_kodi/library.py b/jellyfin_kodi/library.py index edd0d112..c55d4256 100644 --- a/jellyfin_kodi/library.py +++ b/jellyfin_kodi/library.py @@ -471,10 +471,10 @@ 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) + view = db.get_view(library) - if media in ('movies', 'tvshows', 'musicvideos', 'mixed', 'music'): - libraries.append({'Id': library, 'Name': name}) + if view.media_type in ('movies', 'tvshows', 'musicvideos', 'mixed', 'music'): + libraries.append({'Id': view.view_id, 'Name': view.view_name}) choices = [x['Name'] for x in libraries] choices.insert(0, translate(33121)) diff --git a/jellyfin_kodi/views.py b/jellyfin_kodi/views.py index 0070fe16..aa07aca7 100644 --- a/jellyfin_kodi/views.py +++ b/jellyfin_kodi/views.py @@ -174,9 +174,8 @@ class Views(object): removed = [] for view in views: - - if view[0] not in self.sync['SortedViews']: - removed.append(view[0]) + if view.view_id not in self.sync['SortedViews']: + removed.append(view.view_id) if removed: event('RemoveLibrary', {'Id': ','.join(removed)}) @@ -204,7 +203,7 @@ class Views(object): view = db.get_view(library) if view: - view = {'Id': library, 'Name': view[0], 'Tag': view[0], 'Media': view[1]} + view = {'Id': library, 'Name': view.view_name, 'Tag': view.view_name, 'Media': view.media_type} if view['Media'] == 'mixed': for media in ('movies', 'tvshows'): @@ -697,10 +696,10 @@ class Views(object): except IndexError as error: LOG.exception(error) - for library in (libraries or []): - view = {'Id': library[0], 'Name': library[1], 'Tag': library[1], 'Media': library[2]} + for library in libraries: + view = {'Id': library.view_id, 'Name': library.view_name, 'Tag': library.view_name, 'Media': library.media_type} - if library[0] in [x.replace('Mixed:', "") for x in self.sync['Whitelist']]: # Synced libraries + if library.view_id in [x.replace('Mixed:', "") for x in self.sync['Whitelist']]: # Synced libraries if view['Media'] in ('movies', 'tvshows', 'musicvideos', 'mixed'): diff --git a/typings/database/jellyfin_db.pyi b/typings/database/jellyfin_db.pyi new file mode 100644 index 00000000..2cfc2a3d --- /dev/null +++ b/typings/database/jellyfin_db.pyi @@ -0,0 +1,39 @@ +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: ...