Beispiel #1
0
    def only_Shanka(self):
        '''Denote the Shankarappa region.
 
        Returns:
            index: boolean index with the Shankarappa region marked by 'True's.

        *Note*: The region is marked by 'EEE' at the beginning and 'RPGGG' at the end.
        '''

        cons = self.get_consensus_initial()
        is_Shanka = np.zeros(len(cons), bool)
        consaa = ''.join(translate(cons))
        is_Shanka[consaa.find(self.aaS[0]) * 3:consaa.find(self.aaS[1]) *
                  3] = True
        return is_Shanka
Beispiel #2
0
    def boxsets(self, library_id=None, dialog=None):
        ''' Process all boxsets.
        '''
        Movies = self.library.media['Movies']

        for items in server.get_items(library_id, "BoxSet", False,
                                      self.sync['RestorePoint'].get('params')):

            with self.video_database_locks() as (videodb, jellyfindb):
                obj = Movies(self.server, jellyfindb, videodb,
                             self.direct_path)

                self.sync['RestorePoint'] = items['RestorePoint']
                start_index = items['RestorePoint']['params']['StartIndex']

                for index, boxset in enumerate(items['Items']):

                    dialog.update(
                        int((float(start_index + index) /
                             float(items['TotalRecordCount'])) * 100),
                        heading="%s: %s" %
                        (translate('addon_name'), translate('boxsets')),
                        message=boxset['Name'])
                    obj.boxset(boxset)
Beispiel #3
0
    def service(self):

        ''' Keeps the service monitor going.
            Exit on Kodi shutdown or profile switch.

            if profile switch happens more than once,
            Threads depending on abortRequest will not trigger.
        '''
        self.monitor = monitor.Monitor()
        player = self.monitor.player
        self.connect = connect.Connect()
        self.start_default()

        self.settings['mode'] = settings('useDirectPaths')

        while self.running:
            if window('jellyfin_online.bool'):

                if self.settings['profile'] != window('jellyfin_kodiProfile'):
                    LOG.info("[ profile switch ] %s", self.settings['profile'])

                    break

                if player.isPlaying() and player.is_playing_file(player.get_playing_file()):
                    difference = datetime.today() - self.settings['last_progress']

                    if difference.seconds > 10:
                        self.settings['last_progress'] = datetime.today()

                        update = (datetime.today() - self.settings['last_progress_report']).seconds > 250
                        event('ReportProgressRequested', {'Report': update})

                        if update:
                            self.settings['last_progress_report'] = datetime.today()

            if window('jellyfin.restart.bool'):

                window('jellyfin.restart', clear=True)
                dialog("notification", heading="{jellyfin}", message=translate(33193), icon="{jellyfin}", time=1000, sound=False)

                raise Exception('RestartService')

            if self.waitForAbort(1):
                break

        self.shutdown()

        raise Exception("ExitService")
Beispiel #4
0
    def window_node(self, index, view, node=None, node_label=None):
        ''' Leads to another listing of nodes.
        '''
        if view['Media'] in ('homevideos', 'photos'):
            path = self.window_browse(
                view, None if node in ('all', 'browse') else node)
        elif node == 'nextepisodes':
            path = self.window_nextepisodes(view)
        elif node == 'music':
            path = self.window_music(view)
        elif node == 'browse':
            path = self.window_browse(view)
        else:
            path = self.window_path(view, node)

        if node == 'music':
            window_path = "ActivateWindow(Music,%s,return)" % path
        elif node in ('browse', 'homevideos', 'photos'):
            window_path = path
        else:
            window_path = "ActivateWindow(Videos,%s,return)" % path

        node_label = translate(node_label) if type(
            node_label) == int else node_label
        node_label = node_label or view['Name']

        if node in ('all', 'music'):

            window_prop = "Jellyfin.nodes.%s" % index
            window('%s.index' % window_prop, path.replace('all.xml',
                                                          ""))  # dir
            window('%s.title' % window_prop, view['Name'])
            window('%s.content' % window_prop, path)

        elif node == 'browse':

            window_prop = "Jellyfin.nodes.%s" % index
            window('%s.title' % window_prop, view['Name'])
        else:
            window_prop = "Jellyfin.nodes.%s.%s" % (index, node)
            window('%s.title' % window_prop, node_label)
            window('%s.content' % window_prop, path)

        window('%s.id' % window_prop, view['Id'])
        window('%s.path' % window_prop, window_path)
        window('%s.type' % window_prop, view['Media'])
        self.window_artwork(window_prop, view['Id'])
Beispiel #5
0
def manage_libraries():

    directory(translate(33098), "plugin://plugin.video.jellyfin/?mode=refreshboxsets", False)
    directory(translate(33154), "plugin://plugin.video.jellyfin/?mode=addlibs", False)
    directory(translate(33139), "plugin://plugin.video.jellyfin/?mode=updatelibs", False)
    directory(translate(33140), "plugin://plugin.video.jellyfin/?mode=repairlibs", False)
    directory(translate(33184), "plugin://plugin.video.jellyfin/?mode=removelibs", False)
    directory(translate(33060), "plugin://plugin.video.jellyfin/?mode=thememedia", False)

    xbmcplugin.setContent(PROCESS_HANDLE, 'files')
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)
Beispiel #6
0
    def window_wnode(self, index, view, node=None, node_label=None):
        ''' Similar to window_node, but does not contain music, musicvideos.
            Contains books, audiobooks.
        '''
        if view['Media'] in ('homevideos', 'photos', 'books', 'playlists'):
            path = self.window_browse(
                view, None if node in ('all', 'browse') else node)
        else:
            path = self.window_path(view, node)

        if node in ('browse', 'homevideos', 'photos', 'books', 'playlists'):
            window_path = path
        else:
            window_path = "ActivateWindow(Videos,%s,return)" % path

        node_label = translate(node_label) if type(
            node_label) == int else node_label
        node_label = node_label or view['Name']

        if node == 'all':

            window_prop = "Jellyfin.wnodes.%s" % index
            window('%s.index' % window_prop, path.replace('all.xml',
                                                          ""))  # dir
            window('%s.title' % window_prop, view['Name'])
            window('%s.content' % window_prop, path)

        elif node == 'browse':

            window_prop = "Jellyfin.wnodes.%s" % index
            window('%s.title' % window_prop, view['Name'])
            window('%s.content' % window_prop, path)
        else:
            window_prop = "Jellyfin.wnodes.%s.%s" % (index, node)
            window('%s.title' % window_prop, node_label)
            window('%s.content' % window_prop, path)

        window('%s.id' % window_prop, view['Id'])
        window('%s.path' % window_prop, window_path)
        window('%s.type' % window_prop, view['Media'])
        self.window_artwork(window_prop, view['Id'])

        LOG.debug("--[ wnode/%s/%s ] %s", index,
                  window('%s.title' % window_prop),
                  window('%s.artwork' % window_prop))
Beispiel #7
0
    def tvshows(self, library, dialog):
        ''' Process tvshows and episodes from a single library.
        '''
        TVShows = self.library.media['TVShows']

        with self.library.database_lock:
            with Database() as videodb:
                with Database('jellyfin') as jellyfindb:
                    obj = TVShows(self.server, jellyfindb, videodb,
                                  self.direct_path, True)

                    for items in server.get_items(
                            library['Id'], "Series", False,
                            self.sync['RestorePoint'].get('params')):

                        self.sync['RestorePoint'] = items['RestorePoint']
                        start_index = items['RestorePoint']['params'][
                            'StartIndex']

                        for index, show in enumerate(items['Items']):

                            percent = int(
                                (float(start_index + index) /
                                 float(items['TotalRecordCount'])) * 100)
                            message = show['Name']
                            dialog.update(
                                percent,
                                heading="%s: %s" %
                                (translate('addon_name'), library['Name']),
                                message=message)

                            if obj.tvshow(show, library=library) is not False:

                                for episodes in server.get_episode_by_show(
                                        show['Id']):
                                    for episode in episodes['Items']:

                                        dialog.update(
                                            percent,
                                            message="%s/%s" %
                                            (message, episode['Name'][:10]))
                                        obj.episode(episode)

                    if self.update_library:
                        self.tvshows_compare(library, obj, jellyfindb)
Beispiel #8
0
    def onClick(self, control):

        if control == CONNECT:
            self._disable_error()

            server = self.host_field.getText()

            if not server:
                # Display error
                self._error(ERROR['Empty'], translate('empty_server'))
                LOG.error("Server cannot be null")

            elif self._connect_to_server(server):
                self.close()

        elif control == CANCEL:
            # Remind me later
            self.close()
Beispiel #9
0
    def node_index(self, folder, view, mixed=False):

        file = os.path.join(folder, "index.xml")
        index = self.sync['SortedViews'].index(view['Id'])

        try:
            xml = etree.parse(file).getroot()
            xml.set('order', str(index))
        except Exception as error:
            LOG.exception(error)
            xml = self.node_root('main', index)
            etree.SubElement(xml, 'label')

        label = xml.find('label')
        label.text = view['Name'] if not mixed else "%s (%s)" % (
            view['Name'], translate(view['Media']))

        indent(xml)
        write_xml(etree.tostring(xml, 'UTF-8'), file)
Beispiel #10
0
    def onClick(self, control):

        if control == SIGN_IN:
            self._disable_error()

            user = self.user_field.getText()
            password = self.password_field.getText()

            if not user:
                # Display error
                self._error(ERROR['Empty'], translate('empty_user'))
                LOG.error("Username cannot be null")

            elif self._login(user, password):
                self.close()

        elif control == CANCEL:
            # Remind me later
            self.close()
Beispiel #11
0
    def music(self, library, dialog):

        ''' Process artists, album, songs from a single library.
        '''
        Music = self.library.media['Music']

        with self.library.music_database_lock:
            with Database('music') as musicdb:
                with Database('jellyfin') as jellyfindb:
                    obj = Music(self.server, jellyfindb, musicdb, self.direct_path)

                    for items in server.get_artists(library['Id'], False, self.sync['RestorePoint'].get('params')):

                        self.sync['RestorePoint'] = items['RestorePoint']
                        start_index = items['RestorePoint']['params']['StartIndex']

                        for index, artist in enumerate(items['Items']):

                            percent = int((float(start_index + index) / float(items['TotalRecordCount'])) * 100)
                            message = artist['Name']
                            dialog.update(percent, heading="%s: %s" % (translate('addon_name'), library['Name']), message=message)
                            obj.artist(artist, library=library)

                            for albums in server.get_albums_by_artist(artist['Id']):

                                for album in albums['Items']:
                                    obj.album(album)

                                    for songs in server.get_items(album['Id'], "Audio"):
                                        for song in songs['Items']:

                                            dialog.update(percent,
                                                          message="%s/%s/%s" % (message, album['Name'][:7], song['Name'][:7]))
                                            obj.song(song)

                            for songs in server.get_songs_by_artist(artist['Id']):
                                for song in songs['Items']:

                                    dialog.update(percent, message="%s/%s" % (message, song['Name']))
                                    obj.song(song)

                    if self.update_library:
                        self.music_compare(library, obj, jellyfindb)
Beispiel #12
0
    def _set_intros(self, item):
        ''' if we have any play them when the movie/show is not being resumed.
        '''
        intros = TheVoid('GetIntros', {
            'ServerId': self.server_id,
            'Id': item['Id']
        }).get()

        if intros['Items']:
            enabled = True

            if settings('askCinema') == "true":

                resp = dialog("yesno",
                              heading="{jellyfin}",
                              line1=translate(33016))
                if not resp:

                    enabled = False
                    LOG.info("Skip trailers.")

            if enabled:
                for intro in intros['Items']:

                    listitem = xbmcgui.ListItem()
                    LOG.info("[ intro/%s ] %s", intro['Id'], intro['Name'])

                    play = playutils.PlayUtils(intro, False, self.server_id,
                                               self.server)
                    source = play.select_source(play.get_sources())
                    self.set_listitem(intro, listitem, intro=True)
                    listitem.setPath(intro['PlaybackInfo']['Path'])
                    playutils.set_properties(intro,
                                             intro['PlaybackInfo']['Method'],
                                             self.server_id)

                    self.stack.append(
                        [intro['PlaybackInfo']['Path'], listitem])

                window('jellyfin.skip.%s' % intro['Id'], value="true")
Beispiel #13
0
    def run(self):

        while True:

            try:
                item = self.queue.get(timeout=3)
            except Queue.Empty:
                break

            time = self.music_time if item[0] == 'Audio' else self.video_time

            if time and (not self.player.isPlayingVideo() or xbmc.getCondVisibility('VideoPlayer.Content(livetv)')):
                dialog("notification", heading="%s %s" % (translate(33049), item[0]), message=item[1],
                       icon="{jellyfin}", time=time, sound=False)

            self.queue.task_done()

            if window('jellyfin_should_stop.bool'):
                break

        LOG.info("--<[ q:notify/%s ]", id(self))
        self.is_done = True
Beispiel #14
0
    def set_web_server(self):
        ''' Enable the webserver if not enabled. This is used to cache artwork.
            Will only test once, if it fails, user will be notified only once.
        '''
        if settings('enableTextureCache.bool'):

            get_setting = JSONRPC('Settings.GetSettingValue')

            if not self.get_web_server():

                set_setting = JSONRPC('Settings.SetSetingValue')
                set_setting.execute({
                    'setting': "services.webserverport",
                    'value': 8080
                })
                set_setting.execute({
                    'setting': "services.webserver",
                    'value': True
                })

                if not self.get_web_server():

                    settings('enableTextureCache.bool', False)
                    dialog("ok", heading="{jellyfin}", line1=translate(33103))

                    return

            result = get_setting.execute({'setting': "services.webserverport"})
            settings('webServerPort', str(result['result']['value'] or ""))
            result = get_setting.execute(
                {'setting': "services.webserverusername"})
            settings('webServerUser', str(result['result']['value'] or ""))
            result = get_setting.execute(
                {'setting': "services.webserverpassword"})
            settings('webServerPass', str(result['result']['value'] or ""))
            settings('useWebServer.bool', True)
Beispiel #15
0
def add_user(api_client):
    ''' Add or remove users from the default server session.
    '''
    if not window('jellyfin_online.bool'):
        return

    session = api_client.get_device(client.get_device_id())
    users = api_client.get_users()
    current = session[0]['AdditionalUsers']

    result = dialog("select", translate(33061),
                    [translate(33062), translate(33063)]
                    if current else [translate(33062)])

    if result < 0:
        return

    if not result:  # Add user
        eligible = [
            x for x in users if x['Id'] not in
            [current_user['UserId'] for current_user in current]
        ]
        resp = dialog("select", translate(33064),
                      [x['Name'] for x in eligible])

        if resp < 0:
            return

        user = eligible[resp]
        event('AddUser', {'Id': user['Id'], 'Add': True})
    else:  # Remove user
        resp = dialog("select", translate(33064),
                      [x['UserName'] for x in current])

        if resp < 0:
            return

        user = current[resp]
        event('AddUser', {'Id': user['UserId'], 'Add': False})
Beispiel #16
0
def browse(media, view_id=None, folder=None, server_id=None, api_client=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 = api_client.get_item(view_id)
        xbmcplugin.setPluginCategory(PROCESS_HANDLE, 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 = api_client.get_recently_added(None, view_id, None)
    elif folder == 'genres':
        listing = api_client.get_genres(view_id)
    elif media == 'livetv':
        listing = api_client.get_channels()
    elif folder == 'unwatched':
        listing = get_filtered_section(view_id, None, None, None, None, None,
                                       ['IsUnplayed'], None, server_id,
                                       api_client)
    elif folder == 'favorite':
        listing = get_filtered_section(view_id, None, None, None, None, None,
                                       ['IsFavorite'], None, server_id,
                                       api_client)
    elif folder == 'inprogress':
        listing = get_filtered_section(view_id, None, None, None, None, None,
                                       ['IsResumable'], None, server_id,
                                       api_client)
    elif folder == 'boxsets':
        listing = get_filtered_section(view_id, get_media_type('boxsets'),
                                       None, True, None, None, None, None,
                                       server_id, api_client)
    elif folder == 'random':
        listing = get_filtered_section(view_id, get_media_type(content_type),
                                       25, True, "Random", None, None, None,
                                       server_id, api_client)
    elif (folder or "").startswith('firstletter-'):
        listing = get_filtered_section(
            view_id, get_media_type(content_type), None, None, None, None,
            None, {'NameStartsWith': folder.split('-')[1]}, server_id,
            api_client)
    elif (folder or "").startswith('genres-'):
        listing = get_filtered_section(view_id, get_media_type(content_type),
                                       None, None, None, None, None,
                                       {'GenreIds': folder.split('-')[1]},
                                       server_id, api_client)
    elif folder == 'favepisodes':
        listing = get_filtered_section(None, get_media_type(content_type), 25,
                                       None, None, None, ['IsFavorite'], None,
                                       server_id, api_client)
    elif folder and media == 'playlists':
        listing = get_filtered_section(folder, get_media_type(content_type),
                                       None, False, 'None', None, None, None,
                                       server_id, api_client)
    elif media == 'homevideos':
        listing = get_filtered_section(folder or view_id,
                                       get_media_type(content_type), None,
                                       False, None, None, None, None,
                                       server_id, api_client)
    elif media in ['movies', 'episodes']:
        listing = get_filtered_section(folder or view_id,
                                       get_media_type(content_type), None,
                                       True, None, None, None, None, server_id,
                                       api_client)
    elif media in ('boxset', 'library'):
        listing = get_filtered_section(folder or view_id, None, None, True,
                                       None, None, None, None, server_id,
                                       api_client)
    elif media == 'boxsets':
        listing = get_filtered_section(folder or view_id, None, None, False,
                                       None, None, ['Boxsets'], None,
                                       server_id, api_client)
    elif media == 'tvshows':
        listing = get_filtered_section(folder or view_id,
                                       get_media_type(content_type), None,
                                       True, None, None, None, None, server_id,
                                       api_client)
    elif media == 'seasons':
        listing = api_client.get_seasons(folder)
    elif media != 'files':
        listing = get_filtered_section(folder or view_id,
                                       get_media_type(content_type), None,
                                       False, None, None, None, None,
                                       server_id, api_client)
    else:
        listing = get_filtered_section(folder or view_id, None, None, False,
                                       None, None, None, None, server_id,
                                       api_client)

    if listing:

        actions = Actions(server_id, api_client)
        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/",
                                  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((translate(
                        16104
                    ), "RunPlugin(plugin://plugin.video.jellyfin/?mode=unwatched&id=%s&server=%s)"
                                    % (item['Id'], server_id)))
                else:
                    context.append((translate(
                        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/",
                                  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/",
                                      urlencode(params))
                    li.setProperty('path', path)
                    context = [(translate(
                        13412
                    ), "RunPlugin(plugin://plugin.video.jellyfin/?mode=playlist&id=%s&server=%s)"
                                % (item['Id'], server_id))]

                    if item['UserData']['Played']:
                        context.append((translate(
                            16104
                        ), "RunPlugin(plugin://plugin.video.jellyfin/?mode=unwatched&id=%s&server=%s)"
                                        % (item['Id'], server_id)))
                    else:
                        context.append((translate(
                            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(PROCESS_HANDLE, list_li, len(list_li))

    if content_type == 'images':
        xbmcplugin.addSortMethod(PROCESS_HANDLE,
                                 xbmcplugin.SORT_METHOD_VIDEO_TITLE)
        xbmcplugin.addSortMethod(PROCESS_HANDLE, xbmcplugin.SORT_METHOD_DATE)
        xbmcplugin.addSortMethod(PROCESS_HANDLE,
                                 xbmcplugin.SORT_METHOD_VIDEO_RATING)
        xbmcplugin.addSortMethod(PROCESS_HANDLE,
                                 xbmcplugin.SORT_METHOD_VIDEO_RUNTIME)

    xbmcplugin.setContent(PROCESS_HANDLE, content_type)
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)
Beispiel #17
0
    def fast_sync(self):
        ''' Movie and userdata not provided by server yet.
        '''
        last_sync = settings('LastIncrementalSync')
        include = []
        filters = ["tvshows", "boxsets", "musicvideos", "music", "movies"]
        sync = get_sync()
        whitelist = [x.replace('Mixed:', "") for x in sync['Whitelist']]
        LOG.info("--[ retrieve changes ] %s", last_sync)

        # Get the item type of each synced library and build list of types to request
        for item_id in whitelist:
            library = self.server.jellyfin.get_item(item_id)
            library_type = library.get('CollectionType')
            if library_type in filters:
                include.append(library_type)

        # Include boxsets if movies are synced
        if 'movies' in include:
            include.append('boxsets')

        # Filter down to the list of library types we want to exclude
        query_filter = list(set(filters) - set(include))

        try:
            # Get list of updates from server for synced library types and populate work queues
            result = self.server.jellyfin.get_sync_queue(
                last_sync, ",".join([x for x in query_filter]))

            if result is None:
                return True

            updated = []
            userdata = []
            removed = []

            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",
                          "{jellyfin}",
                          translate(33172).replace('{number}', str(total)),
                          nolabel=translate(107),
                          yeslabel=translate(106)):
                    LOG.warning("Large updates skipped.")

                    return True

            self.updated(updated)
            self.userdata(userdata)
            self.removed(removed)

        except Exception as error:
            LOG.exception(error)

            return False

        return True
Beispiel #18
0
def backup():
    ''' Jellyfin backup.
    '''
    from helper.utils import delete_folder, copytree

    path = settings('backupPath')
    folder_name = "Kodi%s.%s" % (xbmc.getInfoLabel('System.BuildVersion')[:2],
                                 xbmc.getInfoLabel('System.Date(dd-mm-yy)'))
    folder_name = dialog("input",
                         heading=translate(33089),
                         defaultt=folder_name)

    if not folder_name:
        return

    backup = os.path.join(path, folder_name)

    if xbmcvfs.exists(backup + '/'):
        if not dialog("yesno", "{jellyfin}", translate(33090)):

            return backup()

        delete_folder(backup)

    addon_data = xbmc.translatePath(
        "special://profile/addon_data/plugin.video.jellyfin")
    destination_data = os.path.join(backup, "addon_data",
                                    "plugin.video.jellyfin")
    destination_databases = os.path.join(backup, "Database")

    if not xbmcvfs.mkdirs(path) or not xbmcvfs.mkdirs(destination_databases):

        LOG.info("Unable to create all directories")
        dialog("notification",
               heading="{jellyfin}",
               icon="{jellyfin}",
               message=translate(33165),
               sound=False)

        return

    copytree(addon_data, destination_data)

    databases = Objects().objects

    db = xbmc.translatePath(databases['jellyfin'])
    xbmcvfs.copy(db, os.path.join(destination_databases,
                                  db.rsplit('\\', 1)[1]))
    LOG.info("copied jellyfin.db")

    db = xbmc.translatePath(databases['video'])
    filename = db.rsplit('\\', 1)[1]
    xbmcvfs.copy(db, os.path.join(destination_databases, filename))
    LOG.info("copied %s", filename)

    if settings('enableMusic.bool'):

        db = xbmc.translatePath(databases['music'])
        filename = db.rsplit('\\', 1)[1]
        xbmcvfs.copy(db, os.path.join(destination_databases, filename))
        LOG.info("copied %s", filename)

    LOG.info("backup completed")
    dialog("ok", "{jellyfin}", "%s %s" % (translate(33091), backup))
Beispiel #19
0
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, translate(33166))
            context.append((
                translate(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((translate(
                33136
            ), "RunPlugin(plugin://plugin.video.jellyfin/?mode=updatelib&id=%s)"
                            % view_id))
            context.append((translate(
                33132
            ), "RunPlugin(plugin://plugin.video.jellyfin/?mode=repairlib&id=%s)"
                            % view_id))
            context.append((translate(
                33133
            ), "RunPlugin(plugin://plugin.video.jellyfin/?mode=removelib&id=%s)"
                            % view_id))

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

        if path:
            directory(label, path, artwork=artwork, context=context)

    for server in servers:
        context = []

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

        if 'AccessToken' not in server:
            directory("%s (%s)" % (server['Name'], translate(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(translate(33194),
              "plugin://plugin.video.jellyfin/?mode=managelibs", True)
    directory(translate(33134),
              "plugin://plugin.video.jellyfin/?mode=addserver", False)
    directory(translate(33054), "plugin://plugin.video.jellyfin/?mode=adduser",
              False)
    directory(translate(5), "plugin://plugin.video.jellyfin/?mode=settings",
              False)
    directory(translate(33161),
              "plugin://plugin.video.jellyfin/?mode=updatepassword", False)
    directory(translate(33058), "plugin://plugin.video.jellyfin/?mode=reset",
              False)
    directory(translate(33180),
              "plugin://plugin.video.jellyfin/?mode=restartservice", False)

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

    xbmcplugin.setContent(PROCESS_HANDLE, 'files')
    xbmcplugin.endOfDirectory(PROCESS_HANDLE)
Beispiel #20
0
import sys

from kodi_six import xbmc, xbmcaddon

import database
from dialogs import context
from helper import translate, settings, dialog
from downloader import TheVoid
from helper import LazyLogger

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

LOG = LazyLogger(__name__)
XML_PATH = (xbmcaddon.Addon('plugin.video.jellyfin').getAddonInfo('path'), "default", "1080i")
OPTIONS = {
    'Refresh': translate(30410),
    'Delete': translate(30409),
    'Addon': translate(30408),
    'AddFav': translate(30405),
    'RemoveFav': translate(30406),
    'Transcode': translate(30412)
}

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


class Context(object):

    _selected_option = None

    def __init__(self, transcode=False, delete=False):
Beispiel #21
0
    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)

        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=translate(33172).replace(
                              '{number}', str(total)),
                          nolabel=translate(107),
                          yeslabel=translate(106)):
                    LOG.warning("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
Beispiel #22
0
class FullSync(object):
    ''' This should be called like a context.
        i.e. with FullSync('jellyfin') as sync:
            sync.libraries()
    '''
    # Borg - multiple instances, shared state
    _shared_state = {}
    sync = None
    running = False
    screensaver = None

    def __init__(self, library, server):
        ''' You can call all big syncing methods here.
            Initial, update, repair, remove.
        '''
        self.__dict__ = self._shared_state

        if self.running:
            dialog("ok", heading="{jellyfin}", line1=translate(33197))

            raise Exception("Sync is already running.")

        self.library = library
        self.server = server

    def __enter__(self):
        ''' Do everything we need before the sync
        '''
        LOG.info("-->[ fullsync ]")

        if not settings('dbSyncScreensaver.bool'):

            xbmc.executebuiltin('InhibitIdleShutdown(true)')
            self.screensaver = get_screensaver()
            set_screensaver(value="")

        self.running = True
        window('jellyfin_sync.bool', True)

        return self

    def libraries(self, library_id=None, update=False):
        ''' Map the syncing process and start the sync. Ensure only one sync is running.
        '''
        self.direct_path = settings('useDirectPaths') == "1"
        self.update_library = update
        self.sync = get_sync()

        if library_id:
            libraries = library_id.split(',')

            for selected in libraries:

                if selected not in [
                        x.replace('Mixed:', "") for x in self.sync['Libraries']
                ]:
                    library = self.get_libraries(selected)

                    if library:

                        self.sync['Libraries'].append("Mixed:%s" %
                                                      selected if library[1] ==
                                                      'mixed' else selected)

                        if library[1] in ('mixed', 'movies'):
                            self.sync['Libraries'].append('Boxsets:%s' %
                                                          selected)
                    else:
                        self.sync['Libraries'].append(selected)
        else:
            self.mapping()

        xmls.sources()

        if not xmls.advanced_settings() and self.sync['Libraries']:
            self.start()

    def get_libraries(self, library_id=None):

        with Database('jellyfin') as jellyfindb:
            if library_id is None:
                return jellyfin_db.JellyfinDatabase(
                    jellyfindb.cursor).get_views()
            else:
                return jellyfin_db.JellyfinDatabase(
                    jellyfindb.cursor).get_view(library_id)

    def mapping(self):
        ''' Load the mapping of the full sync.
            This allows us to restore a previous sync.
        '''
        if self.sync['Libraries']:

            if not dialog(
                    "yesno", heading="{jellyfin}", line1=translate(33102)):

                if not dialog(
                        "yesno", heading="{jellyfin}", line1=translate(33173)):
                    dialog("ok", heading="{jellyfin}", line1=translate(33122))

                    raise LibraryException("ProgressStopped")
                else:
                    self.sync['Libraries'] = []
                    self.sync['RestorePoint'] = {}
        else:
            LOG.info("generate full sync")
            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]
                    })

            libraries = self.select_libraries(libraries)

            if [
                    x['Media'] for x in libraries
                    if x['Media'] in ('movies', 'mixed')
            ]:
                self.sync['Libraries'].append("Boxsets:")

        save_sync(self.sync)

    def select_libraries(self, libraries):
        ''' Select all or certain libraries to be whitelisted.
        '''

        choices = [x['Name'] for x in libraries]
        choices.insert(0, translate(33121))
        selection = dialog("multi", translate(33120), choices)

        if selection is None:
            raise LibraryException('LibrarySelection')
        elif not selection:
            LOG.info("Nothing was selected.")

            raise LibraryException('SyncLibraryLater')

        if 0 in selection:
            selection = list(range(1, len(libraries) + 1))

        selected_libraries = []

        for x in selection:
            library = libraries[x - 1]

            if library['Media'] != 'mixed':
                selected_libraries.append(library['Id'])
            else:
                selected_libraries.append("Mixed:%s" % library['Id'])

        self.sync['Libraries'] = selected_libraries

        return [libraries[x - 1] for x in selection]

    def start(self):
        ''' Main sync process.
        '''
        LOG.info("starting sync with %s", self.sync['Libraries'])
        save_sync(self.sync)
        start_time = datetime.datetime.now()

        for library in list(self.sync['Libraries']):

            self.process_library(library)

            if not library.startswith(
                    'Boxsets:') and library not in self.sync['Whitelist']:
                self.sync['Whitelist'].append(library)

            self.sync['Libraries'].pop(self.sync['Libraries'].index(library))
            self.sync['RestorePoint'] = {}

        elapsed = datetime.datetime.now() - start_time
        settings('SyncInstallRunDone.bool', True)
        self.library.save_last_sync()
        save_sync(self.sync)

        xbmc.executebuiltin('UpdateLibrary(video)')
        dialog("notification",
               heading="{jellyfin}",
               message="%s %s" %
               (translate(33025), str(elapsed).split('.')[0]),
               icon="{jellyfin}",
               sound=False)
        LOG.info("Full sync completed in: %s", str(elapsed).split('.')[0])

    def process_library(self, library_id):
        ''' Add a library by it's id. Create a node and a playlist whenever appropriate.
        '''
        media = {
            'movies': self.movies,
            'musicvideos': self.musicvideos,
            'tvshows': self.tvshows,
            'music': self.music
        }
        try:
            if library_id.startswith('Boxsets:'):

                if library_id.endswith('Refresh'):
                    self.refresh_boxsets()
                else:
                    self.boxsets(
                        library_id.split('Boxsets:')[1]
                        if len(library_id) > len('Boxsets:') else None)

                return

            library = self.server.jellyfin.get_item(
                library_id.replace('Mixed:', ""))

            if library_id.startswith('Mixed:'):
                for mixed in ('movies', 'tvshows'):

                    media[mixed](library)
                    self.sync['RestorePoint'] = {}
            else:
                if library['CollectionType']:
                    settings('enableMusic.bool', True)

                media[library['CollectionType']](library)
        except LibraryException as error:

            if error.status == 'StopCalled':
                save_sync(self.sync)

                raise

        except Exception as error:
            LOG.exception(error)

            if 'Failed to validate path' not in error:

                dialog("ok", heading="{jellyfin}", line1=translate(33119))
                LOG.error("full sync exited unexpectedly")
                save_sync(self.sync)

            raise

    @contextmanager
    def video_database_locks(self):
        with self.library.database_lock:
            with Database() as videodb:
                with Database('jellyfin') as jellyfindb:
                    yield videodb, jellyfindb

    @progress()
    def movies(self, library, dialog):
        ''' Process movies from a single library.
        '''
        processed_ids = []

        for items in server.get_items(library['Id'], "Movie", False,
                                      self.sync['RestorePoint'].get('params')):

            with self.video_database_locks() as (videodb, jellyfindb):
                obj = Movies(self.server, jellyfindb, videodb,
                             self.direct_path)

                self.sync['RestorePoint'] = items['RestorePoint']
                start_index = items['RestorePoint']['params']['StartIndex']

                for index, movie in enumerate(items['Items']):

                    dialog.update(int(
                        (float(start_index + index) /
                         float(items['TotalRecordCount'])) * 100),
                                  heading="%s: %s" %
                                  (translate('addon_name'), library['Name']),
                                  message=movie['Name'])
                    obj.movie(movie, library=library)
                    processed_ids.append(movie['Id'])

        with self.video_database_locks() as (videodb, jellyfindb):
            obj = Movies(self.server, jellyfindb, videodb, self.direct_path)
            obj.item_ids = processed_ids

            if self.update_library:
                self.movies_compare(library, obj, jellyfindb)

    def movies_compare(self, library, obj, jellyfinydb):
        ''' Compare entries from library to what's in the jellyfindb. Remove surplus
        '''
        db = jellyfin_db.JellyfinDatabase(jellyfinydb.cursor)

        items = db.get_item_by_media_folder(library['Id'])
        current = obj.item_ids

        for x in items:
            if x[0] not in current and x[1] == 'Movie':
                obj.remove(x[0])

    @progress()
    def tvshows(self, library, dialog):
        ''' Process tvshows and episodes from a single library.
        '''
        processed_ids = []

        for items in server.get_items(library['Id'], "Series", False,
                                      self.sync['RestorePoint'].get('params')):

            with self.video_database_locks() as (videodb, jellyfindb):
                obj = TVShows(self.server, jellyfindb, videodb,
                              self.direct_path, True)

                self.sync['RestorePoint'] = items['RestorePoint']
                start_index = items['RestorePoint']['params']['StartIndex']

                for index, show in enumerate(items['Items']):

                    percent = int((float(start_index + index) /
                                   float(items['TotalRecordCount'])) * 100)
                    message = show['Name']
                    dialog.update(percent,
                                  heading="%s: %s" %
                                  (translate('addon_name'), library['Name']),
                                  message=message)

                    if obj.tvshow(show, library=library) is not False:

                        for episodes in server.get_episode_by_show(show['Id']):
                            for episode in episodes['Items']:

                                dialog.update(percent,
                                              message="%s/%s" %
                                              (message, episode['Name'][:10]))
                                obj.episode(episode)
                    processed_ids.append(show['Id'])

        with self.video_database_locks() as (videodb, jellyfindb):
            obj = TVShows(self.server, jellyfindb, videodb, self.direct_path,
                          True)
            obj.item_ids = processed_ids
            if self.update_library:
                self.tvshows_compare(library, obj, jellyfindb)

    def tvshows_compare(self, library, obj, jellyfindb):
        ''' Compare entries from library to what's in the jellyfindb. Remove surplus
        '''
        db = jellyfin_db.JellyfinDatabase(jellyfindb.cursor)

        items = db.get_item_by_media_folder(library['Id'])
        for x in list(items):
            items.extend(obj.get_child(x[0]))

        current = obj.item_ids

        for x in items:
            if x[0] not in current and x[1] == 'Series':
                obj.remove(x[0])

    @progress()
    def musicvideos(self, library, dialog):
        ''' Process musicvideos from a single library.
        '''
        processed_ids = []

        for items in server.get_items(library['Id'], "MusicVideo", False,
                                      self.sync['RestorePoint'].get('params')):

            with self.video_database_locks() as (videodb, jellyfindb):
                obj = MusicVideos(self.server, jellyfindb, videodb,
                                  self.direct_path)

                self.sync['RestorePoint'] = items['RestorePoint']
                start_index = items['RestorePoint']['params']['StartIndex']

                for index, mvideo in enumerate(items['Items']):

                    dialog.update(int(
                        (float(start_index + index) /
                         float(items['TotalRecordCount'])) * 100),
                                  heading="%s: %s" %
                                  (translate('addon_name'), library['Name']),
                                  message=mvideo['Name'])
                    obj.musicvideo(mvideo, library=library)
                    processed_ids.append(mvideo['Id'])

        with self.video_database_locks() as (videodb, jellyfindb):
            obj = MusicVideos(self.server, jellyfindb, videodb,
                              self.direct_path)
            obj.item_ids = processed_ids
            if self.update_library:
                self.musicvideos_compare(library, obj, jellyfindb)

    def musicvideos_compare(self, library, obj, jellyfindb):
        ''' Compare entries from library to what's in the jellyfindb. Remove surplus
        '''
        db = jellyfin_db.JellyfinDatabase(jellyfindb.cursor)

        items = db.get_item_by_media_folder(library['Id'])
        current = obj.item_ids

        for x in items:
            if x[0] not in current and x[1] == 'MusicVideo':
                obj.remove(x[0])

    @progress()
    def music(self, library, dialog):
        ''' Process artists, album, songs from a single library.
        '''
        with self.library.music_database_lock:
            with Database('music') as musicdb:
                with Database('jellyfin') as jellyfindb:
                    obj = Music(self.server, jellyfindb, musicdb,
                                self.direct_path)

                    for items in server.get_artists(
                            library['Id'], False,
                            self.sync['RestorePoint'].get('params')):

                        self.sync['RestorePoint'] = items['RestorePoint']
                        start_index = items['RestorePoint']['params'][
                            'StartIndex']

                        for index, artist in enumerate(items['Items']):

                            percent = int(
                                (float(start_index + index) /
                                 float(items['TotalRecordCount'])) * 100)
                            message = artist['Name']
                            dialog.update(
                                percent,
                                heading="%s: %s" %
                                (translate('addon_name'), library['Name']),
                                message=message)
                            obj.artist(artist, library=library)

                            for albums in server.get_albums_by_artist(
                                    artist['Id']):

                                for album in albums['Items']:
                                    obj.album(album)

                                    for songs in server.get_items(
                                            album['Id'], "Audio"):
                                        for song in songs['Items']:

                                            dialog.update(
                                                percent,
                                                message="%s/%s/%s" %
                                                (message, album['Name'][:7],
                                                 song['Name'][:7]))
                                            obj.song(song)

                            for songs in server.get_songs_by_artist(
                                    artist['Id']):
                                for song in songs['Items']:

                                    dialog.update(percent,
                                                  message="%s/%s" %
                                                  (message, song['Name']))
                                    obj.song(song)

                    if self.update_library:
                        self.music_compare(library, obj, jellyfindb)

    def music_compare(self, library, obj, jellyfindb):
        ''' Compare entries from library to what's in the jellyfindb. Remove surplus
        '''
        db = jellyfin_db.JellyfinDatabase(jellyfindb.cursor)

        items = db.get_item_by_media_folder(library['Id'])
        for x in list(items):
            items.extend(obj.get_child(x[0]))

        current = obj.item_ids

        for x in items:
            if x[0] not in current and x[1] == 'MusicArtist':
                obj.remove(x[0])

    @progress(translate(33018))
    def boxsets(self, library_id=None, dialog=None):
        ''' Process all boxsets.
        '''
        for items in server.get_items(library_id, "BoxSet", False,
                                      self.sync['RestorePoint'].get('params')):

            with self.video_database_locks() as (videodb, jellyfindb):
                obj = Movies(self.server, jellyfindb, videodb,
                             self.direct_path)

                self.sync['RestorePoint'] = items['RestorePoint']
                start_index = items['RestorePoint']['params']['StartIndex']

                for index, boxset in enumerate(items['Items']):

                    dialog.update(
                        int((float(start_index + index) /
                             float(items['TotalRecordCount'])) * 100),
                        heading="%s: %s" %
                        (translate('addon_name'), translate('boxsets')),
                        message=boxset['Name'])
                    obj.boxset(boxset)

    def refresh_boxsets(self):
        ''' Delete all exisitng boxsets and re-add.
        '''
        with self.video_database_locks() as (videodb, jellyfindb):
            obj = Movies(self.server, jellyfindb, videodb, self.direct_path)
            obj.boxsets_reset()

        self.boxsets(None)

    @progress(translate(33144))
    def remove_library(self, library_id, dialog):
        ''' Remove library by their id from the Kodi database.
        '''
        direct_path = self.library.direct_path

        with Database('jellyfin') as jellyfindb:

            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'

            if media == 'music':
                settings('MusicRescan.bool', False)

            if items:
                count = 0

                with self.library.music_database_lock if media == 'music' else self.library.database_lock:
                    with Database(media) as kodidb:

                        if library[1] == 'mixed':

                            movies = [x for x in items if x[1] == 'Movie']
                            tvshows = [x for x in items if x[1] == 'Series']

                            obj = Movies(self.server, jellyfindb, kodidb,
                                         direct_path).remove

                            for item in movies:

                                obj(item[0])
                                dialog.update(
                                    int((float(count) / float(len(items)) *
                                         100)),
                                    heading="%s: %s" %
                                    (translate('addon_name'), library[0]))
                                count += 1

                            obj = TVShows(self.server, jellyfindb, kodidb,
                                          direct_path).remove

                            for item in tvshows:

                                obj(item[0])
                                dialog.update(
                                    int((float(count) / float(len(items)) *
                                         100)),
                                    heading="%s: %s" %
                                    (translate('addon_name'), library[0]))
                                count += 1
                        else:
                            default_args = (self.server, jellyfindb, kodidb,
                                            direct_path)
                            for item in items:
                                if item[1] in ('Series', 'Season', 'Episode'):
                                    TVShows(*default_args).remove(item[0])
                                elif item[1] in ('Movie', 'BoxSet'):
                                    Movies(*default_args).remove(item[0])
                                elif item[1] in ('MusicAlbum', 'MusicArtist',
                                                 'AlbumArtist', 'Audio'):
                                    Music(*default_args).remove(item[0])
                                elif item[1] == 'MusicVideo':
                                    MusicVideos(*default_args).remove(item[0])

                                dialog.update(
                                    int((float(count) / float(len(items)) *
                                         100)),
                                    heading="%s: %s" %
                                    (translate('addon_name'), library[0]))
                                count += 1

        self.sync = get_sync()

        if library_id in self.sync['Whitelist']:
            self.sync['Whitelist'].remove(library_id)

        elif 'Mixed:%s' % library_id in self.sync['Whitelist']:
            self.sync['Whitelist'].remove('Mixed:%s' % library_id)

        save_sync(self.sync)

    def __exit__(self, exc_type, exc_val, exc_tb):
        ''' Exiting sync
        '''
        self.running = False
        window('jellyfin_sync', clear=True)

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

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

        LOG.info("--<[ fullsync ]")
Beispiel #23
0
    def remove_library(self, library_id, dialog):
        ''' Remove library by their id from the Kodi database.
        '''
        direct_path = self.library.direct_path

        with Database('jellyfin') as jellyfindb:

            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'

            if media == 'music':
                settings('MusicRescan.bool', False)

            if items:
                count = 0

                with self.library.music_database_lock if media == 'music' else self.library.database_lock:
                    with Database(media) as kodidb:

                        if library[1] == 'mixed':

                            movies = [x for x in items if x[1] == 'Movie']
                            tvshows = [x for x in items if x[1] == 'Series']

                            obj = Movies(self.server, jellyfindb, kodidb,
                                         direct_path).remove

                            for item in movies:

                                obj(item[0])
                                dialog.update(
                                    int((float(count) / float(len(items)) *
                                         100)),
                                    heading="%s: %s" %
                                    (translate('addon_name'), library[0]))
                                count += 1

                            obj = TVShows(self.server, jellyfindb, kodidb,
                                          direct_path).remove

                            for item in tvshows:

                                obj(item[0])
                                dialog.update(
                                    int((float(count) / float(len(items)) *
                                         100)),
                                    heading="%s: %s" %
                                    (translate('addon_name'), library[0]))
                                count += 1
                        else:
                            default_args = (self.server, jellyfindb, kodidb,
                                            direct_path)
                            for item in items:
                                if item[1] in ('Series', 'Season', 'Episode'):
                                    TVShows(*default_args).remove(item[0])
                                elif item[1] in ('Movie', 'BoxSet'):
                                    Movies(*default_args).remove(item[0])
                                elif item[1] in ('MusicAlbum', 'MusicArtist',
                                                 'AlbumArtist', 'Audio'):
                                    Music(*default_args).remove(item[0])
                                elif item[1] == 'MusicVideo':
                                    MusicVideos(*default_args).remove(item[0])

                                dialog.update(
                                    int((float(count) / float(len(items)) *
                                         100)),
                                    heading="%s: %s" %
                                    (translate('addon_name'), library[0]))
                                count += 1

        self.sync = get_sync()

        if library_id in self.sync['Whitelist']:
            self.sync['Whitelist'].remove(library_id)

        elif 'Mixed:%s' % library_id in self.sync['Whitelist']:
            self.sync['Whitelist'].remove('Mixed:%s' % library_id)

        save_sync(self.sync)
Beispiel #24
0
def get_themes(api_client):
    ''' 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")
    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", "{jellyfin}", translate(33152))

        return

    with Database('jellyfin') as jellyfindb:
        all_views = jellyfin_db.JellyfinDatabase(jellyfindb.cursor).get_views()
        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']

    for view in views:
        result = api_client.get_items_theme_video(view)

        for item in result['Items']:

            folder = normalize_string(item['Name'])
            items[item['Id']] = folder

        result = api_client.get_items_theme_song(view)

        for item in result['Items']:

            folder = normalize_string(item['Name'])
            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 = api_client.get_themes(item)
        paths = []

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

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

        tvtunes_nfo(nfo_file, paths)

    dialog("notification",
           heading="{jellyfin}",
           message=translate(33153),
           icon="{jellyfin}",
           time=1000,
           sound=False)
Beispiel #25
0
    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'):

                    if self.server.jellyfin.check_companion_installed():

                        if not self.fast_sync():
                            dialog("ok", "{jellyfin}", translate(33128))

                            raise Exception(
                                "Failed to retrieve latest updates")

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

                    else:
                        raise LibraryException('CompanionMissing')

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

            if error.status in 'SyncLibraryLater':

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

                return True

            elif error.status == 'CompanionMissing':

                dialog("ok", "{jellyfin}", translate(33099))
                settings('kodiCompanion.bool', False)

                return True

        except Exception as error:
            LOG.exception(error)

        return False
Beispiel #26
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 "").split(',')
                        if user
                    ]
                    users.insert(0, settings('username'))
                    dialog("notification",
                           heading="{jellyfin}",
                           message="%s %s" %
                           (translate(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=translate(33146)
                       if data.get('ServerId') is None else translate(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=translate(33147)
                   if data['ServerId'] is None else translate(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=translate(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=translate(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()
Beispiel #27
0
    def select_libraries(self, mode=None):
        ''' Select from libraries synced. Either update or repair libraries.
            Send event back to service.py
        '''
        modes = {
            'SyncLibrarySelection': 'SyncLibrary',
            'RepairLibrarySelection': 'RepairLibrary',
            'AddLibrarySelection': 'SyncLibrary',
            'RemoveLibrarySelection': 'RemoveLibrary'
        }
        sync = get_sync()
        whitelist = [x.replace('Mixed:', "") for x in sync['Whitelist']]
        libraries = []

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

            if mode in ('SyncLibrarySelection', 'RepairLibrarySelection',
                        'RemoveLibrarySelection'):
                for library in sync['Whitelist']:

                    name = db.get_view_name(library.replace('Mixed:', ""))
                    libraries.append({'Id': library, 'Name': name})
            else:
                available = [
                    x for x in sync['SortedViews'] if x not in whitelist
                ]

                for library in available:
                    name, media = db.get_view(library)

                    if media in ('movies', 'tvshows', 'musicvideos', 'mixed',
                                 'music'):
                        libraries.append({'Id': library, 'Name': name})

        choices = [x['Name'] for x in libraries]
        choices.insert(0, translate(33121))

        titles = {
            "RepairLibrarySelection": 33199,
            "SyncLibrarySelection": 33198,
            "RemoveLibrarySelection": 33200,
            "AddLibrarySelection": 33120
        }
        title = titles.get(mode, "Failed to get title {}".format(mode))

        selection = dialog("multi", translate(title), choices)

        if selection is None:
            return

        if 0 in selection:
            selection = list(range(1, len(libraries) + 1))

        selected_libraries = []

        for x in selection:

            library = libraries[x - 1]
            selected_libraries.append(library['Id'])

        event(
            modes[mode], {
                'Id': ','.join([libraries[x - 1]['Id'] for x in selection]),
                'Update': mode == 'SyncLibrarySelection'
            })
Beispiel #28
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'], translate(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': translate('fav_movies'),
                'Tag': "Favorite movies",
                'Media': "movies"
        }, {
                'Name': translate('fav_tvshows'),
                'Tag': "Favorite tvshows",
                'Media': "tvshows"
        }, {
                'Name': translate('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))
Beispiel #29
0
    def onNotification(self, sender, method, data):

        if sender.lower() not in ('plugin.video.jellyfin', 'xbmc',
                                  'upnextprovider.signal'):
            return

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

            if method not in ('GetItem', 'ReportProgressRequested',
                              'LoadServer', 'RandomItems', 'Recommended',
                              'GetServerAddress', 'GetPlaybackInfo', 'Browse',
                              'GetImages', 'GetToken', 'PlayPlaylist', 'Play',
                              'GetIntros', 'GetAdditionalParts', 'RefreshItem',
                              'Genres', 'FavoriteItem', 'DeleteItem',
                              'AddUser', 'GetSession', 'GetUsers', 'GetThemes',
                              'GetTheme', 'Playstate', 'GeneralCommand',
                              'GetTranscodeOptions', 'RecentlyAdded',
                              'BrowseSeason', 'LiveTV', 'GetLiveStream'):
                return

            data = json.loads(data)[0]

        elif sender.startswith('upnextprovider'):
            LOG.info('Attempting to play the next episode via upnext')
            method = method.split('.', 1)[1]

            if method not in ('plugin.video.jellyfin_play_action', ):
                LOG.info('Received invalid upnext method: %s', method)
                return

            data = json.loads(data)
            method = "Play"

            if data:
                data = json.loads(binascii.unhexlify(data[0]))
        else:
            if method not in ('Player.OnPlay', 'VideoLibrary.OnUpdate',
                              'Player.OnAVChange'):
                ''' We have to clear the playlist if it was stopped before it has been played completely.
                    Otherwise the next played item will be added the previous queue.
                '''
                if method == "Player.OnStop":
                    xbmc.sleep(
                        3000
                    )  # let's wait for the player so we don't clear the canceled playlist by mistake.

                    if xbmc.getCondVisibility(
                            "!Player.HasMedia + !Window.IsVisible(busydialog)"
                    ):

                        xbmc.executebuiltin("Playlist.Clear")
                        LOG.info("[ playlist ] cleared")

                return

            data = json.loads(data)

        LOG.debug("[ %s: %s ] %s", sender, method, JsonDebugPrinter(data))

        if self.sleep:
            LOG.info("System.OnSleep detected, ignore monitor request.")

            return

        try:
            if not data.get('ServerId'):
                server = Jellyfin()
            else:
                if method != 'LoadServer' and data[
                        'ServerId'] not in self.servers:

                    try:
                        connect.Connect().register(data['ServerId'])
                        self.server_instance(data['ServerId'])
                    except Exception as error:

                        LOG.exception(error)
                        dialog("ok",
                               heading="{jellyfin}",
                               line1=translate(33142))

                        return

                server = Jellyfin(data['ServerId'])
        except Exception as error:
            LOG.exception(error)
            server = Jellyfin()

        if method == 'GetItem':

            item = server.jellyfin.get_item(data['Id'])
            self.void_responder(data, item)

        elif method == 'GetAdditionalParts':

            item = server.jellyfin.get_additional_parts(data['Id'])
            self.void_responder(data, item)

        elif method == 'GetIntros':

            item = server.jellyfin.get_intros(data['Id'])
            self.void_responder(data, item)

        elif method == 'GetImages':

            item = server.jellyfin.get_images(data['Id'])
            self.void_responder(data, item)

        elif method == 'GetServerAddress':

            server_address = server.auth.get_server_info(
                server.auth.server_id)['address']
            self.void_responder(data, server_address)

        elif method == 'GetPlaybackInfo':

            sources = server.jellyfin.get_play_info(data['Id'],
                                                    data['Profile'])
            self.void_responder(data, sources)

        elif method == 'GetLiveStream':

            sources = server.jellyfin.get_live_stream(data['Id'],
                                                      data['PlaySessionId'],
                                                      data['Token'],
                                                      data['Profile'])
            self.void_responder(data, sources)

        elif method == 'GetToken':

            token = server.auth.jellyfin_token()
            self.void_responder(data, token)

        elif method == 'GetSession':

            session = server.jellyfin.get_device(self.device_id)
            self.void_responder(data, session)

        elif method == 'GetUsers':

            users = server.jellyfin.get_users()
            self.void_responder(data, users)

        elif method == 'GetTranscodeOptions':

            result = server.jellyfin.get_transcode_settings()
            self.void_responder(data, result)

        elif method == 'GetThemes':

            if data['Type'] == 'Video':
                theme = server.jellyfin.get_items_theme_video(data['Id'])
            else:
                theme = server.jellyfin.get_items_theme_song(data['Id'])

            self.void_responder(data, theme)

        elif method == 'GetTheme':

            theme = server.jellyfin.get_themes(data['Id'])
            self.void_responder(data, theme)

        elif method == 'Browse':

            result = downloader.get_filtered_section(data.get('Id'),
                                                     data.get('Media'),
                                                     data.get('Limit'),
                                                     data.get('Recursive'),
                                                     data.get('Sort'),
                                                     data.get('SortOrder'),
                                                     data.get('Filters'),
                                                     data.get('Params'),
                                                     data.get('ServerId'))
            self.void_responder(data, result)

        elif method == 'BrowseSeason':

            result = server.jellyfin.get_seasons(data['Id'])
            self.void_responder(data, result)

        elif method == 'LiveTV':

            result = server.jellyfin.get_channels()
            self.void_responder(data, result)

        elif method == 'RecentlyAdded':

            result = server.jellyfin.get_recently_added(
                data.get('Media'), data.get('Id'), data.get('Limit'))
            self.void_responder(data, result)

        elif method == 'Genres':

            result = server.jellyfin.get_genres(data.get('Id'))
            self.void_responder(data, result)

        elif method == 'Recommended':

            result = server.jellyfin.get_recommendation(
                data.get('Id'), data.get('Limit'))
            self.void_responder(data, result)

        elif method == 'RefreshItem':
            server.jellyfin.refresh_item(data['Id'])

        elif method == 'FavoriteItem':
            server.jellyfin.favorite(data['Id'], data['Favorite'])

        elif method == 'DeleteItem':
            server.jellyfin.delete_item(data['Id'])

        elif method == 'PlayPlaylist':

            server.jellyfin.post_session(
                server.config.data['app.session'], "Playing", {
                    'PlayCommand': "PlayNow",
                    'ItemIds': data['Id'],
                    'StartPositionTicks': 0
                })

        elif method == 'Play':

            items = server.jellyfin.get_items(data['ItemIds'])

            PlaylistWorker(data.get('ServerId'), items,
                           data['PlayCommand'] == 'PlayNow',
                           data.get('StartPositionTicks', 0),
                           data.get('AudioStreamIndex'),
                           data.get('SubtitleStreamIndex')).start()

        elif method in ('ReportProgressRequested', 'Player.OnAVChange'):
            self.player.report_playback(data.get('Report', True))

        elif method == 'Playstate':
            self.playstate(data)

        elif method == 'GeneralCommand':
            self.general_commands(data)

        elif method == 'LoadServer':
            self.server_instance(data['ServerId'])

        elif method == 'AddUser':
            server.jellyfin.session_add_user(server.config.data['app.session'],
                                             data['Id'], data['Add'])
            self.additional_users(server)

        elif method == 'Player.OnPlay':
            on_play(data, server)

        elif method == 'VideoLibrary.OnUpdate':
            on_update(data, server)
Beispiel #30
0
    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)
        '''
        self.download_threads = [
            thread for thread in self.download_threads if not thread.is_done
        ]
        self.writer_threads['updated'] = [
            thread for thread in self.writer_threads['updated']
            if not thread.is_done
        ]
        self.writer_threads['userdata'] = [
            thread for thread in self.writer_threads['userdata']
            if not thread.is_done
        ]
        self.writer_threads['removed'] = [
            thread for thread in self.writer_threads['removed']
            if not thread.is_done
        ]

        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(translate('addon_name'),
                                                 translate(33178))
                    self.progress_updates.update(
                        int((float(self.total_updates - queue_size) /
                             float(self.total_updates)) * 100),
                        message="%s: %s" % (translate(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" % (translate(33178), queue_size))
                else:
                    self.progress_updates.update(int(
                        (float(self.total_updates - queue_size) /
                         float(self.total_updates)) * 100),
                                                 message=translate(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')