示例#1
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)
示例#2
0
    def __init__(self):
        ''' Parse the parameters. Reroute to our service.py
            where user is fully identified already.
        '''
        base_url = ADDON_BASE_URL
        path = QUERY_STRING

        try:
            params = dict(parse_qsl(path[1:]))
        except Exception:
            params = {}

        mode = params.get('mode')
        server = params.get('server')

        if server == 'None':
            server = None

        jellyfin_client = Jellyfin(server).get_client()
        api_client = jellyfin_client.jellyfin

        addon_data = xbmc.translatePath(
            "special://profile/addon_data/plugin.video.jellyfin/data.json")
        try:
            with open(addon_data, 'rb') as infile:
                data = json.load(infile)

            server_data = data['Servers'][0]
            api_client.config.data['auth.server'] = server_data.get('address')
            api_client.config.data['auth.server-name'] = server_data.get(
                'Name')
            api_client.config.data['auth.user_id'] = server_data.get('UserId')
            api_client.config.data['auth.token'] = server_data.get(
                'AccessToken')
        except Exception as e:
            LOG.warning('Addon appears to not be configured yet: {}'.format(e))

        LOG.info("path: %s params: %s", path, JsonDebugPrinter(params))

        if '/extrafanart' in base_url:

            jellyfin_path = path[1:]
            jellyfin_id = params.get('id')
            get_fanart(jellyfin_id, jellyfin_path, server, api_client)

        elif '/Extras' in base_url or '/VideoFiles' in base_url:

            jellyfin_path = path[1:]
            jellyfin_id = params.get('id')
            get_video_extras(jellyfin_id, jellyfin_path, server, api_client)

        elif mode == 'play':

            item = api_client.get_item(params['id'])
            item["resumePlayback"] = sys.argv[3].split(":")[1] == "true"
            Actions(server,
                    api_client).play(item,
                                     params.get('dbid'),
                                     params.get('transcode') == 'true',
                                     playlist=params.get('playlist') == 'true')

        elif mode == 'playlist':
            api_client.post_session(
                api_client.config.data['app.session'], "Playing", {
                    'PlayCommand': "PlayNow",
                    'ItemIds': params['id'],
                    'StartPositionTicks': 0
                })
        elif mode == 'deviceid':
            client.reset_device_id()
        elif mode == 'reset':
            reset()
        elif mode == 'delete':
            delete_item()
        elif mode == 'refreshboxsets':
            event('SyncLibrary', {'Id': "Boxsets:Refresh"})
        elif mode == 'nextepisodes':
            get_next_episodes(params['id'], params['limit'])
        elif mode == 'browse':
            browse(params.get('type'), params.get('id'), params.get('folder'),
                   server, api_client)
        elif mode == 'synclib':
            event('SyncLibrary', {'Id': params.get('id')})
        elif mode == 'updatelib':
            event('SyncLibrary', {'Id': params.get('id'), 'Update': True})
        elif mode == 'repairlib':
            event('RepairLibrary', {'Id': params.get('id')})
        elif mode == 'removelib':
            event('RemoveLibrary', {'Id': params.get('id')})
        elif mode == 'repairlibs':
            event('RepairLibrarySelection')
        elif mode == 'updatelibs':
            event('SyncLibrarySelection')
        elif mode == 'removelibs':
            event('RemoveLibrarySelection')
        elif mode == 'addlibs':
            event('AddLibrarySelection')
        elif mode == 'addserver':
            event('AddServer')
        elif mode == 'login':
            event('ServerConnect', {'Id': server})
        elif mode == 'removeserver':
            event('RemoveServer', {'Id': server})
        elif mode == 'settings':
            xbmc.executebuiltin('Addon.OpenSettings(plugin.video.jellyfin)')
        elif mode == 'adduser':
            add_user(api_client)
        elif mode == 'updatepassword':
            event('UpdatePassword')
        elif mode == 'thememedia':
            get_themes(api_client)
        elif mode == 'managelibs':
            manage_libraries()
        elif mode == 'backup':
            backup()
        elif mode == 'restartservice':
            window('jellyfin.restart.bool', True)
        else:
            listing()
示例#3
0
    def request(self, data, session=None):
        ''' Give a chance to retry the connection. Jellyfin sometimes can be slow to answer back
            data dictionary can contain:
            type: GET, POST, etc.
            url: (optional)
            handler: not considered when url is provided (optional)
            params: request parameters (optional)
            json: request body (optional)
            headers: (optional),
            verify: ssl certificate, True (verify using device built-in library) or False
        '''
        if not data:
            raise AttributeError("Request cannot be empty")

        data = self._request(data)
        LOG.debug("--->[ http ] %s", JsonDebugPrinter(data))
        retry = data.pop('retry', 5)

        while True:

            try:
                r = self._requests(session or self.session or requests,
                                   data.pop('type', "GET"), **data)
                r.content  # release the connection

                if not self.keep_alive and self.session is not None:
                    self.stop_session()

                r.raise_for_status()

            except requests.exceptions.ConnectionError as error:
                if retry:

                    retry -= 1
                    time.sleep(1)

                    continue

                LOG.error(error)
                self.client.callback(
                    "ServerUnreachable",
                    {'ServerId': self.config.data['auth.server-id']})

                raise HTTPException("ServerUnreachable", error)

            except requests.exceptions.ReadTimeout as error:
                if retry:

                    retry -= 1
                    time.sleep(1)

                    continue

                LOG.error(error)

                raise HTTPException("ReadTimeout", error)

            except requests.exceptions.HTTPError as error:
                LOG.error(error)

                if r.status_code == 401:

                    if 'X-Application-Error-Code' in r.headers:
                        self.client.callback(
                            "AccessRestricted",
                            {'ServerId': self.config.data['auth.server-id']})

                        raise HTTPException("AccessRestricted", error)
                    else:
                        self.client.callback(
                            "Unauthorized",
                            {'ServerId': self.config.data['auth.server-id']})
                        self.client.auth.revoke_token()

                        raise HTTPException("Unauthorized", error)

                elif r.status_code == 400:
                    LOG.warning(error)
                    LOG.warning(data)
                    try:
                        LOG.warning(r.json())
                    except Exception:
                        LOG.warning(r.text)

                elif r.status_code == 500:  # log and ignore.
                    LOG.error("--[ 500 response ] %s", error)

                    return

                elif r.status_code == 502:
                    if retry:

                        retry -= 1
                        time.sleep(1)

                        continue

                raise HTTPException(r.status_code, error)

            except requests.exceptions.MissingSchema as error:
                LOG.error("Request missing Schema. " + str(error))
                raise HTTPException(
                    "MissingSchema",
                    {'Id': self.config.data.get('auth.server', "None")})

            else:
                try:
                    self.config.data['server-time'] = r.headers['Date']
                    elapsed = int(r.elapsed.total_seconds() * 1000)
                    response = r.json()
                    LOG.debug("---<[ http ][%s ms]", elapsed)
                    LOG.debug(JsonDebugPrinter(response))

                    return clean_none_dict_values(response)
                except ValueError:
                    return
                except TypeError:
                    # Empty json
                    return
示例#4
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',
                              'UserConfigurationUpdated', 'ServerRestarting',
                              'RemoveServer', 'UpdatePassword',
                              '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, JsonDebugPrinter(data))

        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 == 'UpdatePassword':
            self.connect.setup_login_manual()
        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' and data.get(
                'ServerId') is None:
            Views().get_views()
示例#5
0
    def __init__(self):
        ''' Parse the parameters. Reroute to our service.py
            where user is fully identified already.
        '''
        base_url = sys.argv[0]
        path = sys.argv[2]

        try:
            params = dict(parse_qsl(path[1:]))
        except Exception:
            params = {}

        mode = params.get('mode')
        server = params.get('server')

        if server == 'None':
            server = None

        LOG.info("path: %s params: %s", path, JsonDebugPrinter(params))

        if '/extrafanart' in base_url:

            jellyfin_path = path[1:]
            jellyfin_id = params.get('id')
            get_fanart(jellyfin_id, jellyfin_path, server)

        elif '/Extras' in base_url or '/VideoFiles' in base_url:

            jellyfin_path = path[1:]
            jellyfin_id = params.get('id')
            get_video_extras(jellyfin_id, jellyfin_path, server)

        elif mode == 'play':

            item = TheVoid('GetItem', {
                'Id': params['id'],
                'ServerId': server
            }).get()
            Actions(server).play(item,
                                 params.get('dbid'),
                                 params.get('transcode') == 'true',
                                 playlist=params.get('playlist') == 'true')

        elif mode == 'playlist':
            event('PlayPlaylist', {'Id': params['id'], 'ServerId': server})
        elif mode == 'deviceid':
            client.reset_device_id()
        elif mode == 'reset':
            reset()
        elif mode == 'delete':
            delete_item()
        elif mode == 'refreshboxsets':
            event('SyncLibrary', {'Id': "Boxsets:Refresh"})
        elif mode == 'nextepisodes':
            get_next_episodes(params['id'], params['limit'])
        elif mode == 'browse':
            browse(params.get('type'), params.get('id'), params.get('folder'),
                   server)
        elif mode == 'synclib':
            event('SyncLibrary', {'Id': params.get('id')})
        elif mode == 'updatelib':
            event('SyncLibrary', {'Id': params.get('id'), 'Update': True})
        elif mode == 'repairlib':
            event('RepairLibrary', {'Id': params.get('id')})
        elif mode == 'removelib':
            event('RemoveLibrary', {'Id': params.get('id')})
        elif mode == 'repairlibs':
            event('RepairLibrarySelection')
        elif mode == 'updatelibs':
            event('SyncLibrarySelection')
        elif mode == 'removelibs':
            event('RemoveLibrarySelection')
        elif mode == 'addlibs':
            event('AddLibrarySelection')
        elif mode == 'addserver':
            event('AddServer')
        elif mode == 'login':
            event('ServerConnect', {'Id': server})
        elif mode == 'removeserver':
            event('RemoveServer', {'Id': server})
        elif mode == 'settings':
            xbmc.executebuiltin('Addon.OpenSettings(plugin.video.jellyfin)')
        elif mode == 'adduser':
            add_user()
        elif mode == 'updateserver':
            event('UpdateServer')
        elif mode == 'thememedia':
            get_themes()
        elif mode == 'managelibs':
            manage_libraries()
        elif mode == 'backup':
            backup()
        elif mode == 'restartservice':
            window('jellyfin.restart.bool', True)
        else:
            listing()