Esempio n. 1
0
    def window_nodes(self):
        ''' Just read from the database and populate based on SortedViews
            Setup the window properties that reflect the jellyfin server views and more.
        '''
        self.window_clear()
        self.window_clear('Jellyfin.wnodes')

        with Database('jellyfin') as jellyfindb:
            libraries = jellyfin_db.JellyfinDatabase(
                jellyfindb.cursor).get_views()

        libraries = self.order_media_folders(libraries or [])
        index = 0
        windex = 0

        try:
            self.media_folders = self.get_libraries()
        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]
            }

            if library[0] in [
                    x.replace('Mixed:', "") for x in self.sync['Whitelist']
            ]:  # Synced libraries

                if view['Media'] in ('movies', 'tvshows', 'musicvideos',
                                     'mixed'):

                    if view['Media'] == 'mixed':
                        for media in ('movies', 'tvshows'):

                            for node in NODES[media]:

                                temp_view = dict(view)
                                temp_view['Media'] = media
                                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.
                                index += 1
                                windex += 1
                    else:
                        for node in NODES[view['Media']]:

                            self.window_node(index, view, *node)

                            if view['Media'] in ('movies', 'tvshows'):
                                self.window_wnode(windex, view, *node)

                        if view['Media'] in ('movies', 'tvshows'):
                            windex += 1

                elif view['Media'] == 'music':
                    self.window_node(index, view, 'music')
            else:  # Dynamic entry
                if view['Media'] in ('homevideos', 'books', 'playlists'):
                    self.window_wnode(windex, view, 'browse')
                    windex += 1

                self.window_node(index, view, 'browse')

            index += 1

        for single in [{
                'Name': _('fav_movies'),
                'Tag': "Favorite movies",
                'Media': "movies"
        }, {
                'Name': _('fav_tvshows'),
                'Tag': "Favorite tvshows",
                'Media': "tvshows"
        }, {
                'Name': _('fav_episodes'),
                'Tag': "Favorite episodes",
                'Media': "episodes"
        }]:

            self.window_single_node(index, "favorites", single)
            index += 1

        window('Jellyfin.nodes.total', str(index))
        window('Jellyfin.wnodes.total', str(windex))
Esempio n. 2
0
import xml.etree.ElementTree as etree

import xbmc
import xbmcvfs

import downloader as server
from database import Database, jellyfin_db, get_sync, save_sync
from objects.kodi import kodi
from helper import _, api, indent, write_xml, window, event
from jellyfin import Jellyfin

#################################################################################################

LOG = logging.getLogger("JELLYFIN." + __name__)
NODES = {
    'tvshows': [('all', None), ('recent', _(30170)),
                ('recentepisodes', _(30175)), ('inprogress', _(30171)),
                ('inprogressepisodes', _(30178)), ('nextepisodes', _(30179)),
                ('genres', 135), ('random', _(30229)),
                ('recommended', _(30230))],
    'movies': [('all', None), ('recent', _(30174)), ('inprogress', _(30177)),
               ('unwatched', _(30189)), ('sets', 20434), ('genres', 135),
               ('random', _(30229)), ('recommended', _(30230))],
    'musicvideos': [('all', None), ('recent', _(30256)),
                    ('inprogress', _(30257)), ('unwatched', _(30258))]
}
DYNNODES = {
    'tvshows': [('all', None), ('RecentlyAdded', _(30170)),
                ('recentepisodes', _(30175)), ('InProgress', _(30171)),
                ('inprogressepisodes', _(30178)), ('nextepisodes', _(30179)),
                ('Genres', _(135)), ('Random', _(30229)),
Esempio n. 3
0
    def get_nodes(self):
        ''' Set up playlists, video nodes, window prop.
        '''
        node_path = xbmc.translatePath(
            "special://profile/library/video").decode('utf-8')
        playlist_path = xbmc.translatePath(
            "special://profile/playlists/video").decode('utf-8')
        index = 0

        with Database('jellyfin') as jellyfindb:
            db = jellyfin_db.JellyfinDatabase(jellyfindb.cursor)

            for library in self.sync['Whitelist']:

                library = library.replace('Mixed:', "")
                view = db.get_view(library)

                if view:
                    view = {
                        'Id': library,
                        'Name': view[0],
                        'Tag': view[0],
                        'Media': view[1]
                    }

                    if view['Media'] == 'mixed':
                        for media in ('movies', 'tvshows'):

                            temp_view = dict(view)
                            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.
                            index += 1
                    else:
                        if view['Media'] in ('movies', 'tvshows',
                                             'musicvideos'):
                            self.add_playlist(playlist_path, view)

                        if view['Media'] not in ('music', ):
                            self.add_nodes(node_path, view)

                    index += 1

        for single in [{
                'Name': _('fav_movies'),
                'Tag': "Favorite movies",
                'Media': "movies"
        }, {
                'Name': _('fav_tvshows'),
                'Tag': "Favorite tvshows",
                'Media': "tvshows"
        }, {
                'Name': _('fav_episodes'),
                'Tag': "Favorite episodes",
                'Media': "episodes"
        }]:

            self.add_single_node(node_path, index, "favorites", single)
            index += 1

        self.window_nodes()
    def fast_sync(self):
        ''' Movie and userdata not provided by server yet.
        '''
        last_sync = settings('LastIncrementalSync')
        filters = ["tvshows", "boxsets", "musicvideos", "music", "movies"]
        sync = get_sync()
        LOG.info("--[ retrieve changes ] %s", last_sync)
        """
        for library in sync['Whitelist']:

            data = self.server.jellyfin.get_date_modified(last_sync, library.replace('Mixed:', ""), "Series,Episode,BoxSet,Movie,MusicVideo,MusicArtist,MusicAlbum,Audio")
            [self.updated_output[query['Type']].put(query) for query in data['Items']]
        """
        try:
            updated = []
            userdata = []
            removed = []

            for media in filters:
                result = self.server.jellyfin.get_sync_queue(
                    last_sync, ",".join([x for x in filters if x != media]))
                updated.extend(result['ItemsAdded'])
                updated.extend(result['ItemsUpdated'])
                userdata.extend(result['UserDataChanged'])
                removed.extend(result['ItemsRemoved'])

            total = len(updated) + len(userdata)

            if total > int(settings('syncIndicator') or 99):
                ''' Inverse yes no, in case the dialog is forced closed by Kodi.
                '''
                if dialog("yesno",
                          heading="{jellyfin}",
                          line1=_(33172).replace('{number}', str(total)),
                          nolabel=_(107),
                          yeslabel=_(106)):
                    LOG.warn("Large updates skipped.")

                    return True

            self.updated(updated)
            self.userdata(userdata)
            self.removed(removed)
            """
            result = self.server.jellyfin.get_sync_queue(last_sync)
            self.userdata(result['UserDataChanged'])
            self.removed(result['ItemsRemoved'])


            filters.extend(["tvshows", "boxsets", "musicvideos", "music"])

            # Get only movies.
            result = self.server.jellyfin.get_sync_queue(last_sync, ",".join(filters))
            self.updated(result['ItemsAdded'])
            self.updated(result['ItemsUpdated'])
            self.userdata(result['UserDataChanged'])
            self.removed(result['ItemsRemoved'])
            """

        except Exception as error:
            LOG.exception(error)

            return False

        return True
Esempio n. 5
0
    def onNotification(self, sender, method, data):
        ''' All notifications are sent via NotifyAll built-in or Kodi.
            Central hub.
        '''
        if sender.lower() not in ('plugin.video.emby', 'xbmc'):
            return

        if sender == 'plugin.video.emby':
            method = method.split('.')[1]

            if method not in ('ServerUnreachable', 'ServerShuttingDown',
                              'UserDataChanged', 'ServerConnect',
                              'LibraryChanged', 'ServerOnline', 'SyncLibrary',
                              'RepairLibrary', 'RemoveLibrary', 'EmbyConnect',
                              'SyncLibrarySelection', 'RepairLibrarySelection',
                              'AddServer', 'Unauthorized', 'UpdateServer',
                              'UserConfigurationUpdated', 'ServerRestarting',
                              'RemoveServer', 'AddLibrarySelection',
                              'CheckUpdate', 'RemoveLibrarySelection',
                              'PatchMusic', 'WebSocketRestarting',
                              'ResetUpdate', 'UserPolicyUpdated',
                              'SetServerSSL'):
                return

            data = json.loads(data)[0]
        else:
            if method not in ('System.OnQuit', 'System.OnSleep',
                              'System.OnWake', 'GUI.OnScreensaverDeactivated'):
                return

            data = json.loads(data)

        LOG.info("[ onNotification/%s/%s ]", sender, method)
        LOG.debug("[ %s: %s ] %s", sender, method, json.dumps(data, indent=4))

        if method == 'ServerOnline':
            if data.get('ServerId') is None:

                window('emby_online.bool', True)
                self['auth_check'] = True
                self['warn'] = True

                if settings('connectMsg.bool'):

                    users = Emby()['api'].get_device(
                        window('emby_deviceId'))[0]['AdditionalUsers']
                    users = [user['UserName'] for user in users]
                    users.insert(0, settings('username').decode('utf-8'))
                    dialog("notification",
                           heading="{emby}",
                           message="%s %s" % (_(33000), ", ".join(users)),
                           icon="{emby}",
                           time=1500,
                           sound=False)

        elif method in ('ServerUnreachable', 'ServerShuttingDown'):

            if self['warn'] or data.get('ServerId'):

                self['warn'] = data.get('ServerId') is not None
                dialog("notification",
                       heading="{emby}",
                       message=_(33146)
                       if data.get('ServerId') is None else _(33149),
                       icon=xbmcgui.NOTIFICATION_ERROR)

            if data.get('ServerId') is None:
                self._server(20)

        elif method == 'Unauthorized':
            dialog("notification",
                   heading="{emby}",
                   message=_(33147) if data['ServerId'] is None else _(33148),
                   icon=xbmcgui.NOTIFICATION_ERROR)

            if data.get('ServerId') is None and self['auth_check']:

                self['auth_check'] = False
                self._server(120)

        elif method == 'ServerRestarting':
            if data.get('ServerId'):
                return

            if settings('restartMsg.bool'):
                dialog("notification",
                       heading="{emby}",
                       message=_(33006),
                       icon="{emby}")

            self._server(20)

        elif method == 'ServerConnect':
            self['connect'].register(data['Id'])
            xbmc.executebuiltin("Container.Refresh")

        elif method == 'EmbyConnect':
            self['connect'].setup_login_connect()

        elif method == 'AddServer':

            self['connect'].setup_manual_server()
            xbmc.executebuiltin("Container.Refresh")

        elif method == 'RemoveServer':

            self['connect'].remove_server(data['Id'])
            xbmc.executebuiltin("Container.Refresh")

        elif method == 'UpdateServer':

            dialog("ok", heading="{emby}", line1=_(33151))
            self['connect'].setup_manual_server()

        elif method == 'UserDataChanged':
            if not self['library'] and data.get(
                    'ServerId') or not self['library'].started:
                return

            if data.get('UserId') != Emby()['auth/user-id']:
                return

            LOG.info("[ UserDataChanged ] %s", data)
            self['library'].userdata(data['UserDataList'])

        elif method == 'LibraryChanged' and self['library'].started:
            if data.get('ServerId') or not self['library'].started:
                return

            LOG.info("[ LibraryChanged ] %s", data)
            self['library'].updated(data['ItemsUpdated'] + data['ItemsAdded'])
            self['library'].removed(data['ItemsRemoved'])
            self['library'].delay_verify(data.get('ItemsVerify', []))

        elif method == 'WebSocketRestarting':

            if self['library']:
                try:
                    self['library'].get_fast_sync()
                except Exception as error:
                    LOG.error(error)

        elif method == 'System.OnQuit':
            window('emby_should_stop.bool', True)
            self.running = False

        elif method in ('SyncLibrarySelection', 'RepairLibrarySelection',
                        'AddLibrarySelection', 'RemoveLibrarySelection'):
            self['library'].select_libraries(method)

        elif method == 'SyncLibrary':
            if not data.get('Id'):
                return

            self['library'].add_library(data['Id'], data.get('Update', False))

        elif method == 'RepairLibrary':
            if not data.get('Id'):
                return

            libraries = data['Id'].split(',')

            for lib in libraries:
                self['library'].remove_library(lib)

            self['library'].add_library(data['Id'])

        elif method == 'RemoveLibrary':
            libraries = data['Id'].split(',')

            for lib in libraries:
                self['library'].remove_library(lib)

        elif method == 'System.OnSleep':

            LOG.info("-->[ sleep ]")
            window('emby_should_stop.bool', True)
            self._server(close=True)

            Emby.close_all()
            self['monitor'].server = []
            self['monitor'].sleep = True

        elif method == 'System.OnWake':

            if not self['monitor'].sleep:
                LOG.warn("System.OnSleep was never called, skip System.OnWake")

                return

            LOG.info("--<[ sleep ]")
            xbmc.sleep(10000)  # Allow network to wake up
            self['monitor'].sleep = False
            window('emby_should_stop', clear=True)
            self._server()

        elif method == 'GUI.OnScreensaverDeactivated':

            LOG.info("--<[ screensaver ]")
            xbmc.sleep(5000)

            if self['library'] is not None:
                self['library'].get_fast_sync()

        elif method in ('UserConfigurationUpdated', 'UserPolicyUpdated'):

            if data.get('ServerId') is None:
                Views().get_views()

        elif method == 'CheckUpdate':

            if not PATCH.check_update(True):
                dialog("notification",
                       heading="{emby}",
                       message=_(21341),
                       icon="{emby}",
                       sound=False)
            else:
                dialog("notification",
                       heading="{emby}",
                       message=_(33181),
                       icon="{emby}",
                       sound=False)
                window('emby.restart.bool', True)

        elif method == 'ResetUpdate':
            PATCH.reset()

        elif method == 'PatchMusic':
            self['library'].run_library_task(method,
                                             data.get('Notification', True))

        elif method == 'SetServerSSL':
            self['connect'].set_ssl(data['Id'])
    def service(self):
        ''' If error is encountered, it will rerun this function.
            Start new "daemon threads" to process library updates.
            (actual daemon thread is not supported in Kodi)
        '''
        for threads in (self.download_threads, self.writer_threads['updated'],
                        self.writer_threads['userdata'],
                        self.writer_threads['removed']):
            for thread in threads:
                if thread.is_done:
                    threads.remove(thread)

        if not self.player.isPlayingVideo() or settings(
                'syncDuringPlay.bool') or xbmc.getCondVisibility(
                    'VideoPlayer.Content(livetv)'):

            self.worker_downloads()
            self.worker_sort()

            self.worker_updates()
            self.worker_userdata()
            self.worker_remove()
            self.worker_notify()

        if self.pending_refresh:
            window('jellyfin_sync.bool', True)

            if self.total_updates > self.progress_display:
                queue_size = self.worker_queue_size()

                if self.progress_updates is None:

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

                xbmc.executebuiltin('InhibitIdleShutdown(true)')
                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']):
            self.pending_refresh = False
            self.save_last_sync()
            self.total_updates = 0
            window('jellyfin_sync', clear=True)

            if self.progress_updates:

                self.progress_updates.close()
                self.progress_updates = None

            if not settings(
                    'dbSyncScreensaver.bool') and self.screensaver is not None:

                xbmc.executebuiltin('InhibitIdleShutdown(false)')
                set_screensaver(value=self.screensaver)
                self.screensaver = None

            if xbmc.getCondVisibility('Container.Content(musicvideos)'
                                      ):  # Prevent cursor from moving
                xbmc.executebuiltin('Container.Refresh')
            else:  # Update widgets
                xbmc.executebuiltin('UpdateLibrary(video)')

                if xbmc.getCondVisibility('Window.IsMedia'):
                    xbmc.executebuiltin('Container.Refresh')
    def startup(self):
        ''' Run at startup.
            Check databases.
            Check for the server plugin.
        '''
        self.test_databases()

        Views().get_views()
        Views().get_nodes()

        try:
            if get_sync()['Libraries']:

                try:
                    with FullSync(self, self.server) as sync:
                        sync.libraries()

                    Views().get_nodes()
                except Exception as error:
                    LOG.exception(error)

            elif not settings('SyncInstallRunDone.bool'):

                with FullSync(self, self.server) as sync:
                    sync.libraries()

                Views().get_nodes()

                return True

            if settings('SyncInstallRunDone.bool'):
                if settings('kodiCompanion.bool'):

                    for plugin in self.server.jellyfin.get_plugins():
                        if plugin['Name'] in ("Jellyfin.Kodi Sync Queue",
                                              "Kodi companion",
                                              "Kodi Sync Queue"):

                            if not self.fast_sync():
                                dialog("ok",
                                       heading="{jellyfin}",
                                       line1=_(33128))

                                raise Exception(
                                    "Failed to retrieve latest updates")

                            LOG.info("--<[ retrieve changes ]")

                            break
                    else:
                        raise LibraryException('CompanionMissing')

            return True
        except LibraryException as error:
            LOG.error(error.status)

            if error.status in 'SyncLibraryLater':

                dialog("ok", heading="{jellyfin}", line1=_(33129))
                settings('SyncInstallRunDone.bool', True)
                sync = get_sync()
                sync['Libraries'] = []
                save_sync(sync)

                return True

            elif error.status == 'CompanionMissing':

                dialog("ok", heading="{jellyfin}", line1=_(33099))
                settings('kodiCompanion.bool', False)

                return True

        except Exception as error:
            LOG.exception(error)

        return False
Esempio n. 8
0
    def onNotification(self, sender, method, data):
        ''' All notifications are sent via NotifyAll built-in or Kodi.
            Central hub.
        '''
        if sender.lower() not in ('plugin.video.jellyfin', 'xbmc'):
            return

        if sender == 'plugin.video.jellyfin':
            method = method.split('.')[1]

            if method not in ('ServerUnreachable', 'ServerShuttingDown',
                              'UserDataChanged', 'ServerConnect',
                              'LibraryChanged', 'ServerOnline', 'SyncLibrary',
                              'RepairLibrary', 'RemoveLibrary',
                              'SyncLibrarySelection', 'RepairLibrarySelection',
                              'AddServer', 'Unauthorized', 'UpdateServer',
                              'UserConfigurationUpdated', 'ServerRestarting',
                              'RemoveServer', 'AddLibrarySelection',
                              'RemoveLibrarySelection'):
                return

            data = json.loads(data)[0]
        else:
            if method not in ('System.OnQuit', 'System.OnSleep',
                              'System.OnWake'):
                return

            data = json.loads(data)

        LOG.debug("[ %s: %s ] %s", sender, method, json.dumps(data, indent=4))

        if method == 'ServerOnline':
            if data.get('ServerId') is None:

                window('jellyfin_online.bool', True)
                self.settings['auth_check'] = True
                self.warn = True

                if settings('connectMsg.bool'):

                    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)

                if self.library_thread is None:

                    self.library_thread = library.Library(self)
                    self.library_thread.start()

        elif method in ('ServerUnreachable', 'ServerShuttingDown'):

            if self.warn or data.get('ServerId'):

                self.warn = data.get('ServerId') is not None
                dialog("notification",
                       heading="{jellyfin}",
                       message=_(33146)
                       if data.get('ServerId') is None else _(33149),
                       icon=xbmcgui.NOTIFICATION_ERROR)

            if data.get('ServerId') is None:
                self.stop_default()

                if self.waitForAbort(120):
                    return

                self.start_default()

        elif method == 'Unauthorized':
            dialog("notification",
                   heading="{jellyfin}",
                   message=_(33147) if data['ServerId'] is None else _(33148),
                   icon=xbmcgui.NOTIFICATION_ERROR)

            if data.get('ServerId') is None and self.settings['auth_check']:

                self.settings['auth_check'] = False
                self.stop_default()

                if self.waitForAbort(5):
                    return

                self.start_default()

        elif method == 'ServerRestarting':
            if data.get('ServerId'):
                return

            if settings('restartMsg.bool'):
                dialog("notification",
                       heading="{jellyfin}",
                       message=_(33006),
                       icon="{jellyfin}")

            self.stop_default()

            if self.waitForAbort(15):
                return

            self.start_default()

        elif method == 'ServerConnect':
            self.connect.register(data['Id'])
            xbmc.executebuiltin("Container.Refresh")

        elif method == 'AddServer':

            self.connect.setup_manual_server()
            xbmc.executebuiltin("Container.Refresh")

        elif method == 'RemoveServer':

            self.connect.remove_server(data['Id'])
            xbmc.executebuiltin("Container.Refresh")

        elif method == 'UpdateServer':

            dialog("ok", heading="{jellyfin}", line1=_(33151))
            self.connect.setup_manual_server()

        elif method == 'UserDataChanged' and self.library_thread:
            if data.get('ServerId') or not window('jellyfin_startup.bool'):
                return

            LOG.info("[ UserDataChanged ] %s", data)
            self.library_thread.userdata(data['UserDataList'])

        elif method == 'LibraryChanged' and self.library_thread:
            if data.get('ServerId') or not window('jellyfin_startup.bool'):
                return

            LOG.info("[ LibraryChanged ] %s", data)
            self.library_thread.updated(data['ItemsUpdated'] +
                                        data['ItemsAdded'])
            self.library_thread.removed(data['ItemsRemoved'])

        elif method == 'System.OnQuit':
            window('jellyfin_should_stop.bool', True)
            self.running = False

        elif method in ('SyncLibrarySelection', 'RepairLibrarySelection',
                        'AddLibrarySelection', 'RemoveLibrarySelection'):
            self.library_thread.select_libraries(method)

        elif method == 'SyncLibrary':
            if not data.get('Id'):
                return

            self.library_thread.add_library(data['Id'],
                                            data.get('Update', False))
            xbmc.executebuiltin("Container.Refresh")

        elif method == 'RepairLibrary':
            if not data.get('Id'):
                return

            libraries = data['Id'].split(',')

            for lib in libraries:

                if not self.library_thread.remove_library(lib):
                    return

            self.library_thread.add_library(data['Id'])
            xbmc.executebuiltin("Container.Refresh")

        elif method == 'RemoveLibrary':
            libraries = data['Id'].split(',')

            for lib in libraries:

                if not self.library_thread.remove_library(lib):
                    return

            xbmc.executebuiltin("Container.Refresh")

        elif method == 'System.OnSleep':

            LOG.info("-->[ sleep ]")
            window('jellyfin_should_stop.bool', True)

            if self.library_thread is not None:

                self.library_thread.stop_client()
                self.library_thread = None

            Jellyfin.close_all()
            self.monitor.server = []
            self.monitor.sleep = True

        elif method == 'System.OnWake':

            if not self.monitor.sleep:
                LOG.warning(
                    "System.OnSleep was never called, skip System.OnWake")

                return

            LOG.info("--<[ sleep ]")
            xbmc.sleep(10000)  # Allow network to wake up
            self.monitor.sleep = False
            window('jellyfin_should_stop', clear=True)

            try:
                self.connect.register()
            except Exception as error:
                LOG.exception(error)

        elif method == 'GUI.OnScreensaverDeactivated':

            LOG.info("--<[ screensaver ]")
            xbmc.sleep(5000)

            if self.library_thread is not None:
                self.library_thread.fast_sync()

        elif method == 'UserConfigurationUpdated':

            if data.get('ServerId') is None:
                Views().get_views()
Esempio n. 9
0
    def stop_playback(self):
        ''' Stop all playback. Check for external player for positionticks.
        '''
        if not self.played:
            return

        LOG.info("Played info: %s", self.played)

        for file in self.played:
            item = self.get_file_info(file)

            window('jellyfin.skip.%s.bool' % item['Id'], True)

            if window('jellyfin.external.bool'):
                window('jellyfin.external', clear=True)

                if int(item['CurrentPosition']) == 1:
                    item['CurrentPosition'] = int(item['Runtime'])

            data = {
                'ItemId': item['Id'],
                'MediaSourceId': item['MediaSourceId'],
                'PositionTicks': int(item['CurrentPosition'] * 10000000),
                'PlaySessionId': item['PlaySessionId']
            }
            item['Server']['api'].session_stop(data)

            if item.get('LiveStreamId'):

                LOG.info("<[ livestream/%s ]", item['LiveStreamId'])
                item['Server']['api'].close_live_stream(item['LiveStreamId'])

            elif item['PlayMethod'] == 'Transcode':

                LOG.info("<[ transcode/%s ]", item['Id'])
                item['Server']['api'].close_transcode(item['DeviceId'])

            path = xbmc.translatePath(
                "special://profile/addon_data/plugin.video.jellyfin/temp/"
            ).decode('utf-8')

            if xbmcvfs.exists(path):
                dirs, files = xbmcvfs.listdir(path)

                for file in files:
                    xbmcvfs.delete(os.path.join(path, file.decode('utf-8')))

            result = item['Server']['api'].get_item(item['Id']) or {}

            if 'UserData' in result and result['UserData']['Played']:
                delete = False

                if result['Type'] == 'Episode' and settings('deleteTV.bool'):
                    delete = True
                elif result['Type'] == 'Movie' and settings(
                        'deleteMovies.bool'):
                    delete = True

                if not settings('offerDelete.bool'):
                    delete = False

                if delete:
                    LOG.info("Offer delete option")

                    if dialog("yesno",
                              heading=_(30091),
                              line1=_(33015),
                              autoclose=120000):
                        item['Server']['api'].delete_item(item['Id'])

            window('jellyfin.external_check', clear=True)

        self.played.clear()
def get_themes():

    ''' Add theme media locally, via strm. This is only for tv tunes.
        If another script is used, adjust this code.
    '''
    from helper.utils import normalize_string
    from helper.playutils import PlayUtils
    from helper.xmls import tvtunes_nfo

    library = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/library").decode('utf-8')
    play = settings('useDirectPaths') == "1"

    if not xbmcvfs.exists(library + '/'):
        xbmcvfs.mkdir(library)

    if xbmc.getCondVisibility('System.HasAddon(script.tvtunes)'):

        tvtunes = xbmcaddon.Addon(id="script.tvtunes")
        tvtunes.setSetting('custom_path_enable', "true")
        tvtunes.setSetting('custom_path', library)
        LOG.info("TV Tunes custom path is enabled and set.")
    else:
        dialog("ok", heading="{jellyfin}", line1=_(33152))

        return

    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')]


    items = {}
    server = TheVoid('GetServerAddress', {'ServerId': None}).get()
    token = TheVoid('GetToken', {'ServerId': None}).get()

    for view in views:
        result = TheVoid('GetThemes', {'Type': "Video", 'Id': view}).get()

        for item in result['Items']:

            folder = normalize_string(item['Name'].encode('utf-8'))
            items[item['Id']] = folder

        result = TheVoid('GetThemes', {'Type': "Song", 'Id': view}).get()

        for item in result['Items']:

            folder = normalize_string(item['Name'].encode('utf-8'))
            items[item['Id']] = folder

    for item in items:

        nfo_path = os.path.join(library, items[item])
        nfo_file = os.path.join(nfo_path, "tvtunes.nfo")

        if not xbmcvfs.exists(nfo_path):
            xbmcvfs.mkdir(nfo_path)

        themes = TheVoid('GetTheme', {'Id': item}).get()
        paths = []

        for theme in themes['ThemeVideosResult']['Items'] + themes['ThemeSongsResult']['Items']:
            putils = PlayUtils(theme, False, None, server, token)

            if play:
                paths.append(putils.direct_play(theme['MediaSources'][0]).encode('utf-8'))
            else:
                paths.append(putils.direct_url(theme['MediaSources'][0]).encode('utf-8'))

        tvtunes_nfo(nfo_file, paths)

    dialog("notification", heading="{jellyfin}", message=_(33153), icon="{jellyfin}", time=1000, sound=False)
def browse(media, view_id=None, folder=None, server_id=None):

    ''' Browse content dynamically.
    '''
    LOG.info("--[ v:%s/%s ] %s", view_id, media, folder)

    if not window('jellyfin_online.bool') and server_id is None:

        monitor = xbmc.Monitor()

        for i in range(300):
            if window('jellyfin_online.bool'):
                break
            elif monitor.waitForAbort(0.1):
                return
        else:
            LOG.error("Default server is not online.")

            return

    folder = folder.lower() if folder else None

    if folder is None and media in ('homevideos', 'movies', 'books', 'audiobooks'):
        return browse_subfolders(media, view_id, server_id)

    if folder and folder == 'firstletter':
        return browse_letters(media, view_id, server_id)

    if view_id:

        view = TheVoid('GetItem', {'ServerId': server_id, 'Id': view_id}).get()
        xbmcplugin.setPluginCategory(int(sys.argv[1]), view['Name'])

    content_type = "files"

    if media in ('tvshows', 'seasons', 'episodes', 'movies', 'musicvideos', 'songs', 'albums'):
        content_type = media
    elif media in ('homevideos', 'photos'):
        content_type = "images"
    elif media in ('books', 'audiobooks'):
        content_type = "videos"
    elif media == 'music':
        content_type = "artists"


    if folder == 'recentlyadded':
        listing = TheVoid('RecentlyAdded', {'Id': view_id, 'ServerId': server_id}).get()
    elif folder == 'genres':
        listing = TheVoid('Genres', {'Id': view_id, 'ServerId': server_id}).get()
    elif media == 'livetv':
        listing = TheVoid('LiveTV', {'Id': view_id, 'ServerId': server_id}).get()
    elif folder == 'unwatched':
        listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Filters': ['IsUnplayed']}).get()
    elif folder == 'favorite':
        listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Filters': ['IsFavorite']}).get()
    elif folder == 'inprogress':
        listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Filters': ['IsResumable']}).get()
    elif folder == 'boxsets':
        listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Media': get_media_type('boxsets'), 'Recursive': True}).get()
    elif folder == 'random':
        listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Media': get_media_type(content_type), 'Sort': "Random", 'Limit': 25, 'Recursive': True}).get()
    elif (folder or "").startswith('firstletter-'):
        listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Media': get_media_type(content_type), 'Params': {'NameStartsWith': folder.split('-')[1]}}).get()
    elif (folder or "").startswith('genres-'):
        listing = TheVoid('Browse', {'Id': view_id, 'ServerId': server_id, 'Media': get_media_type(content_type), 'Params': {'GenreIds': folder.split('-')[1]}}).get()
    elif folder == 'favepisodes':
        listing = TheVoid('Browse', {'Media': get_media_type(content_type), 'ServerId': server_id, 'Limit': 25, 'Filters': ['IsFavorite']}).get()
    elif media == 'homevideos':
        listing = TheVoid('Browse', {'Id': folder or view_id, 'Media': get_media_type(content_type), 'ServerId': server_id, 'Recursive': False}).get()
    elif media == 'movies':
        listing = TheVoid('Browse', {'Id': folder or view_id, 'Media': get_media_type(content_type), 'ServerId': server_id, 'Recursive': True}).get()
    elif media in ('boxset', 'library'):
        listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': True}).get()
    elif media == 'episodes':
        listing = TheVoid('Browse', {'Id': folder or view_id, 'Media': get_media_type(content_type), 'ServerId': server_id, 'Recursive': True}).get()
    elif media == 'boxsets':
        listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': False, 'Filters': ["Boxsets"]}).get()
    elif media == 'tvshows':
        listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': True, 'Media': get_media_type(content_type)}).get()
    elif media == 'seasons':
        listing = TheVoid('BrowseSeason', {'Id': folder, 'ServerId': server_id}).get()
    elif media != 'files':
        listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': False, 'Media': get_media_type(content_type)}).get()
    else:
        listing = TheVoid('Browse', {'Id': folder or view_id, 'ServerId': server_id, 'Recursive': False}).get()


    if listing:

        actions = Actions(server_id)
        list_li = []
        listing = listing if type(listing) == list else listing.get('Items', [])

        for item in listing:

            li = xbmcgui.ListItem()
            li.setProperty('jellyfinid', item['Id'])
            li.setProperty('jellyfinserver', server_id)
            actions.set_listitem(item, li)

            if item.get('IsFolder'):

                params = {
                    'id': view_id or item['Id'],
                    'mode': "browse",
                    'type': get_folder_type(item, media) or media,
                    'folder': item['Id'],
                    'server': server_id
                }
                path = "%s?%s" % ("plugin://plugin.video.jellyfin/",  urllib.urlencode(params))
                context = []

                if item['Type'] in ('Series', 'Season', 'Playlist'):
                    context.append(("Play", "RunPlugin(plugin://plugin.video.jellyfin/?mode=playlist&id=%s&server=%s)" % (item['Id'], server_id)))

                if item['UserData']['Played']:
                    context.append((_(16104), "RunPlugin(plugin://plugin.video.jellyfin/?mode=unwatched&id=%s&server=%s)" % (item['Id'], server_id)))
                else:
                    context.append((_(16103), "RunPlugin(plugin://plugin.video.jellyfin/?mode=watched&id=%s&server=%s)" % (item['Id'], server_id)))

                li.addContextMenuItems(context)
                list_li.append((path, li, True))

            elif item['Type'] == 'Genre':

                params = {
                    'id': view_id or item['Id'],
                    'mode': "browse",
                    'type': get_folder_type(item, media) or media,
                    'folder': 'genres-%s' % item['Id'],
                    'server': server_id
                }
                path = "%s?%s" % ("plugin://plugin.video.jellyfin/",  urllib.urlencode(params))
                list_li.append((path, li, True))

            else:
                if item['Type'] not in ('Photo', 'PhotoAlbum'):
                    params = {
                        'id': item['Id'],
                        'mode': "play",
                        'server': server_id
                    }
                    path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
                    li.setProperty('path', path)
                    context = [(_(13412), "RunPlugin(plugin://plugin.video.jellyfin/?mode=playlist&id=%s&server=%s)" % (item['Id'], server_id))]

                    if item['UserData']['Played']:
                        context.append((_(16104), "RunPlugin(plugin://plugin.video.jellyfin/?mode=unwatched&id=%s&server=%s)" % (item['Id'], server_id)))
                    else:
                        context.append((_(16103), "RunPlugin(plugin://plugin.video.jellyfin/?mode=watched&id=%s&server=%s)" % (item['Id'], server_id)))

                    li.addContextMenuItems(context)

                list_li.append((li.getProperty('path'), li, False))

        xbmcplugin.addDirectoryItems(int(sys.argv[1]), list_li, len(list_li))

    if content_type == 'images':
        xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE)
        xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE)
        xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RATING)
        xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RUNTIME)

    xbmcplugin.setContent(int(sys.argv[1]), content_type)
    xbmcplugin.endOfDirectory(int(sys.argv[1]))
def listing():

    ''' Display all jellyfin nodes and dynamic entries when appropriate.
    '''
    total = int(window('Jellyfin.nodes.total') or 0)
    sync = get_sync()
    whitelist = [x.replace('Mixed:', "") for x in sync['Whitelist']]
    servers = get_credentials()['Servers'][1:]

    for i in range(total):

        window_prop = "Jellyfin.nodes.%s" % i
        path = window('%s.index' % window_prop)

        if not path:
            path = window('%s.content' % window_prop) or window('%s.path' % window_prop)

        label = window('%s.title' % window_prop)
        node = window('%s.type' % window_prop)
        artwork = window('%s.artwork' % window_prop)
        view_id = window('%s.id' % window_prop)
        context = []

        if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music', 'mixed') and view_id not in whitelist:
            label = "%s %s" % (label.decode('utf-8'), _(33166))
            context.append((_(33123), "RunPlugin(plugin://plugin.video.jellyfin/?mode=synclib&id=%s)" % view_id))

        if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music') and view_id in whitelist:

            context.append((_(33136), "RunPlugin(plugin://plugin.video.jellyfin/?mode=updatelib&id=%s)" % view_id))
            context.append((_(33132), "RunPlugin(plugin://plugin.video.jellyfin/?mode=repairlib&id=%s)" % view_id))
            context.append((_(33133), "RunPlugin(plugin://plugin.video.jellyfin/?mode=removelib&id=%s)" % view_id))

        LOG.debug("--[ listing/%s/%s ] %s", node, label, path)

        if path:
            if xbmc.getCondVisibility('Window.IsActive(Pictures)') and node in ('photos', 'homevideos'):
                directory(label, path, artwork=artwork)
            elif xbmc.getCondVisibility('Window.IsActive(Videos)') and node not in ('photos', 'music', 'audiobooks'):
                directory(label, path, artwork=artwork, context=context)
            elif xbmc.getCondVisibility('Window.IsActive(Music)') and node in ('music'):
                directory(label, path, artwork=artwork, context=context)
            elif not xbmc.getCondVisibility('Window.IsActive(Videos) | Window.IsActive(Pictures) | Window.IsActive(Music)'):
                directory(label, path, artwork=artwork)

    for server in servers:
        context = []

        if server.get('ManualAddress'):
            context.append((_(33141), "RunPlugin(plugin://plugin.video.jellyfin/?mode=removeserver&server=%s)" % server['Id']))

        if 'AccessToken' not in server:
            directory("%s (%s)" % (server['Name'], _(30539)), "plugin://plugin.video.jellyfin/?mode=login&server=%s" % server['Id'], False, context=context)
        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)
    directory(_(5), "plugin://plugin.video.jellyfin/?mode=settings", False)
    directory(_(33058), "plugin://plugin.video.jellyfin/?mode=reset", False)
    directory(_(33192), "plugin://plugin.video.jellyfin/?mode=restartservice", False)

    if settings('backupPath'):
        directory(_(33092), "plugin://plugin.video.jellyfin/?mode=backup", False)

    xbmcplugin.setContent(int(sys.argv[1]), 'files')
    xbmcplugin.endOfDirectory(int(sys.argv[1]))