Example #1
0
    def testPMS(self,
                host=None,
                username=None,
                password=None,
                plex_server_token=None):
        self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')

        if password is not None and set('*') == set(password):
            password = app.PLEX_SERVER_PASSWORD

        host = config.clean_hosts(host)
        final_result = ''

        cur_result = notifiers.plex_notifier.test_notify_pms(
            host, username, password, plex_server_token)
        if cur_result is None:
            final_result += 'Successful test of Plex Media Server(s) ... {host}<br>\n'.format(
                host=unquote_plus(', '.join(host)))
        elif cur_result is False:
            final_result += 'Test failed, No Plex Media Server host specified<br>\n'
        else:
            final_result += 'Test failed for Plex Media Server(s) ... {host}<br>\n'.format(
                host=unquote_plus(cur_result))

        ui.notifications.message('Tested Plex Media Server(s)', final_result)

        return final_result
Example #2
0
 def testNMJv2(host=None):
     host = config.clean_host(host)
     result = notifiers.nmjv2_notifier.test_notify(unquote_plus(host))
     if result:
         return 'Test notice sent successfully to {host}'.format(
             host=unquote_plus(host))
     else:
         return 'Test notice failed to {host}'.format(
             host=unquote_plus(host))
Example #3
0
 def testEMBY(host=None, emby_apikey=None):
     host = config.clean_host(host)
     result = notifiers.emby_notifier.test_notify(unquote_plus(host),
                                                  emby_apikey)
     if result:
         return 'Test notice sent successfully to {host}'.format(
             host=unquote_plus(host))
     else:
         return 'Test notice failed to {host}'.format(
             host=unquote_plus(host))
Example #4
0
    def testKODI(host=None, username=None, password=None):
        host = config.clean_hosts(host)
        final_result = ''
        for curHost in [x.strip() for x in host if x.strip()]:
            cur_result = notifiers.kodi_notifier.test_notify(
                unquote_plus(curHost), username, password)
            if len(cur_result.split(':')) > 2 and 'OK' in cur_result.split(
                    ':')[2]:
                final_result += 'Test KODI notice sent successfully to {host}<br>\n'.format(
                    host=unquote_plus(curHost))
            else:
                final_result += 'Test KODI notice failed to {host}<br>\n'.format(
                    host=unquote_plus(curHost))

        return final_result
Example #5
0
 def testNMJ(host=None, database=None, mount=None):
     host = config.clean_host(host)
     result = notifiers.nmj_notifier.test_notify(unquote_plus(host),
                                                 database, mount)
     if result:
         return 'Successfully started the scan update'
     else:
         return 'Test failed to start the scan update'
Example #6
0
    def play_episode(self, episode, connection_index=0):
        """Handles playing videos on a KODI host via HTTP JSON-RPC

        Attempts to play an episode on a KODI host.

        Args:
            episode: The episode to play
            connection_index: Index of the selected host to play the episode on

        Returns:
            Returns True or False

        """
        try:
            connection = self.connections[int(connection_index)]
        except IndexError:
            logger.log('Incorrect KODI host passed to play an episode, aborting play', logger.WARNING)
            return False

        logger.log("Trying to play episode on Kodi for host: " + connection.host, logger.DEBUG)

        response = connection.VideoLibrary.GetTVShows(filter={"field": "title", "operator": "is", "value": episode.show.name})

        shows = []
        tvshowid = None
        if response and "result" in response and "tvshows" in response["result"]:
            shows = response["result"]["tvshows"]

        check = (episode.show.name, unquote_plus(episode.show.name))
        for show in shows:
            if ("label" in show and show["label"] in check) or ("title" in show and show["title"] in check):
                tvshowid = show["tvshowid"]

        del shows

        if tvshowid is None:
            logger.log('Could not play the item, could not find the show on Kodi')
            return

        response = connection.VideoLibrary.GetEpisodes(
            filter={"field": "title", "operator": "is", "value": episode.name},
            season=episode.season,
            tvshowid=tvshowid,
            properties=["file"]
        )

        if response and "result" in response and "episodes" in response["result"]:
            episodes = response["result"]["episodes"]

            if len(episodes) > 1:
                logger.log('Could not play the item, too many files were returned as options and we could not choose')

            if episodes:
                connection.Player.Open(item={'file': episodes[0]['file']})
            else:
                logger.log('Could not find the episode on Kodi to play')
Example #7
0
    def testGrowl(host=None, password=None):
        success = 'Registered and Tested growl successfully'
        failure = 'Registration and Testing of growl failed'

        host = config.clean_host(host, default_port=23053)
        result = notifiers.growl_notifier.test_notify(host, password)

        return '{message} {host}{password}'.format(
            message=success if result else failure,
            host=unquote_plus(host),
            password='******'.format(
                pwd=password) if password else '')
Example #8
0
    def testPHT(self, host=None, username=None, password=None):
        self.set_header('Cache-Control', 'max-age=0,no-cache,no-store')

        if None is not password and set('*') == set(password):
            password = app.PLEX_CLIENT_PASSWORD

        host = config.clean_hosts(host)
        final_result = ''
        for curHost in [x.strip() for x in host if x.strip()]:
            cur_result = notifiers.plex_notifier.test_notify_pht(
                unquote_plus(curHost), username, password)
            if len(cur_result.split(':')) > 2 and 'OK' in cur_result.split(
                    ':')[2]:
                final_result += 'Successful test notice sent to Plex Home Theater ... {host}<br>\n'.format(
                    host=unquote_plus(curHost))
            else:
                final_result += 'Test failed for Plex Home Theater ... {host}<br>\n'.format(
                    host=unquote_plus(cur_result))

        ui.notifications.message('Tested Plex Home Theater(s)', final_result)

        return final_result
Example #9
0
 def settingsNMJv2(host=None, dbloc=None, instance=None):
     host = config.clean_host(host)
     result = notifiers.nmjv2_notifier.notify_settings(
         unquote_plus(host), dbloc, instance)
     if result:
         return json.dumps({
             'message':
             'NMJ Database found at: {host}'.format(host=host),
             'database':
             app.NMJv2_DATABASE,
         })
     else:
         return json.dumps({
             'message':
             'Unable to find NMJ Database at location: {db_loc}. '
             'Is the right location selected and PCH running?'.format(
                 db_loc=dbloc),
             'database':
             ''
         })
Example #10
0
 def settingsNMJ(host=None):
     host = config.clean_host(host)
     result = notifiers.nmj_notifier.notify_settings(unquote_plus(host))
     if result:
         return json.dumps({
             'message':
             'Got settings from {host}'.format(host=host),
             'database':
             app.NMJ_DATABASE,
             'mount':
             app.NMJ_MOUNT,
         })
     else:
         return json.dumps({
             'message':
             'Failed! Make sure your Popcorn is on and NMJ is running. '
             '(see Log & Errors -> Debug for detailed info)',
             'database':
             '',
             'mount':
             '',
         })
Example #11
0
    def _update_library(self, host=None, series_name=None):  # pylint: disable=too-many-return-statements, too-many-branches
        """Handle updating KODI host via HTTP JSON-RPC.

        Attempts to update the KODI video library for a specific tv show if passed,
        otherwise update the whole library if enabled.

        Args:
            host: KODI webserver host:port
            series_name: Name of a TV show to specifically target the library update for

        Returns:
            Returns True or False

        """
        if not host:
            log.warning(u'No KODI host passed, aborting update')
            return False

        log.info(u'Updating KODI library via JSON method for host: {0}', host)

        # if we're doing per-show
        if series_name:
            series_name = unquote_plus(series_name)
            tvshowid = -1
            path = ''

            log.debug(u'Updating library in KODI via JSON method for show {0}',
                      series_name)

            # let's try letting kodi filter the shows
            shows_command = {
                'jsonrpc': '2.0',
                'method': 'VideoLibrary.GetTVShows',
                'params': {
                    'filter': {
                        'field': 'title',
                        'operator': 'is',
                        'value': series_name,
                    },
                    'properties': ['title'],
                },
                'id': 'Medusa',
            }

            # get tvshowid by series_name
            series_response = self._send_to_kodi(shows_command, host)

            if series_response and 'result' in series_response and 'tvshows' in series_response[
                    'result']:
                shows = series_response['result']['tvshows']
            else:
                # fall back to retrieving the entire show list
                shows_command = {
                    'jsonrpc': '2.0',
                    'method': 'VideoLibrary.GetTVShows',
                    'id': 1,
                }
                series_response = self._send_to_kodi(shows_command, host)

                if series_response and 'result' in series_response and 'tvshows' in series_response[
                        'result']:
                    shows = series_response['result']['tvshows']
                else:
                    log.debug(u'KODI: No tvshows in KODI TV show list')
                    return False

            for show in shows:
                if ('label' in show and show['label'] == series_name) or (
                        'title' in show and show['title'] == series_name):
                    tvshowid = show['tvshowid']
                    # set the path is we have it already
                    if 'file' in show:
                        path = show['file']

                    break

            # this can be big, so free some memory
            del shows

            # we didn't find the show (exact match), thus revert to just doing a full update if enabled
            if tvshowid == -1:
                log.debug(u'Exact show name not matched in KODI TV show list')
                return False

            # lookup tv-show path if we don't already know it
            if not path:
                path_command = {
                    'jsonrpc': '2.0',
                    'method': 'VideoLibrary.GetTVShowDetails',
                    'params': {
                        'tvshowid': tvshowid,
                        'properties': ['file'],
                    },
                    'id': 1,
                }
                path_response = self._send_to_kodi(path_command, host)

                path = path_response['result']['tvshowdetails']['file']

            log.debug(u'Received Show: {0} with ID: {1} Path: {2}',
                      series_name, tvshowid, path)

            if not path:
                log.warning(u'No valid path found for {0} with ID: {1} on {2}',
                            series_name, tvshowid, host)
                return False

            log.debug(u'KODI Updating {0} on {1} at {2}', series_name, host,
                      path)
            update_command = {
                'jsonrpc': '2.0',
                'method': 'VideoLibrary.Scan',
                'params': {
                    'directory': path,
                },
                'id': 1,
            }
            request = self._send_to_kodi(update_command, host)
            if not request:
                log.warning(
                    u'Update of show directory failed on {0} on {1} at {2}',
                    series_name, host, path)
                return False

            # catch if there was an error in the returned request
            for r in request:
                if 'error' in r:
                    log.warning(
                        u'Error while attempting to update show directory for {0} on {1} at {2} ',
                        series_name, host, path)
                    return False

        # do a full update if requested
        else:
            log.debug(u'Doing Full Library KODI update on host: {0}', host)
            update_command = {
                'jsonrpc': '2.0',
                'method': 'VideoLibrary.Scan',
                'id': 1,
            }
            request = self._send_to_kodi(update_command, host)

            if not request:
                log.warning(u'KODI Full Library update failed on: {0}', host)
                return False

        return True
Example #12
0
    def massAddTable(self, rootDir=None):
        t = PageTemplate(rh=self, filename='home_massAddTable.mako')

        if not rootDir:
            return 'No folders selected.'
        elif not isinstance(rootDir, list):
            root_dirs = [rootDir]
        else:
            root_dirs = rootDir

        root_dirs = [unquote_plus(x) for x in root_dirs]

        if sickbeard.ROOT_DIRS:
            default_index = int(sickbeard.ROOT_DIRS.split('|')[0])
        else:
            default_index = 0

        if len(root_dirs) > default_index:
            tmp = root_dirs[default_index]
            if tmp in root_dirs:
                root_dirs.remove(tmp)
                root_dirs = [tmp] + root_dirs

        dir_list = []

        main_db_con = db.DBConnection()
        for root_dir in root_dirs:
            try:
                file_list = ek(os.listdir, root_dir)
            except Exception:
                continue

            for cur_file in file_list:

                try:
                    cur_path = ek(os.path.normpath, ek(os.path.join, root_dir, cur_file))
                    if not ek(os.path.isdir, cur_path):
                        continue
                except Exception:
                    continue

                cur_dir = {
                    'dir': cur_path,
                    'display_dir': '<b>{dir}{sep}</b>{base}'.format(
                        dir=ek(os.path.dirname, cur_path), sep=os.sep, base=ek(os.path.basename, cur_path)),
                }

                # see if the folder is in KODI already
                dir_results = main_db_con.select(
                    b'SELECT indexer_id '
                    b'FROM tv_shows '
                    b'WHERE location = ? LIMIT 1', 
                    [cur_path]
                )

                if dir_results:
                    cur_dir['added_already'] = True
                else:
                    cur_dir['added_already'] = False

                dir_list.append(cur_dir)

                indexer_id = show_name = indexer = None
                for cur_provider in sickbeard.metadata_provider_dict.values():
                    if not (indexer_id and show_name):
                        (indexer_id, show_name, indexer) = cur_provider.retrieveShowMetadata(cur_path)

                        # default to TVDB if indexer was not detected
                        if show_name and not (indexer or indexer_id):
                            (_, idxr, i) = helpers.searchIndexerForShowID(show_name, indexer, indexer_id)

                            # set indexer and indexer_id from found info
                            if not indexer and idxr:
                                indexer = idxr

                            if not indexer_id and i:
                                indexer_id = i

                cur_dir['existing_info'] = (indexer_id, show_name, indexer)

                if indexer_id and Show.find(sickbeard.showList, indexer_id):
                    cur_dir['added_already'] = True
        return t.render(dirList=dir_list)
Example #13
0
    def addExistingShows(self, shows_to_add=None, promptForSettings=None):
        """
        Receives a dir list and add them. Adds the ones with given TVDB IDs first, then forwards
        along to the newShow page.
        """
        prompt_for_settings = promptForSettings

        # grab a list of other shows to add, if provided
        if not shows_to_add:
            shows_to_add = []
        elif not isinstance(shows_to_add, list):
            shows_to_add = [shows_to_add]

        shows_to_add = [unquote_plus(x) for x in shows_to_add]

        prompt_for_settings = config.checkbox_to_value(prompt_for_settings)

        indexer_id_given = []
        dirs_only = []
        # separate all the ones with Indexer IDs
        for cur_dir in shows_to_add:
            if '|' in cur_dir:
                split_vals = cur_dir.split('|')
                if len(split_vals) < 3:
                    dirs_only.append(cur_dir)
            if '|' not in cur_dir:
                dirs_only.append(cur_dir)
            else:
                indexer, show_dir, indexer_id, show_name = self.split_extra_show(cur_dir)

                if not show_dir or not indexer_id or not show_name:
                    continue

                indexer_id_given.append((int(indexer), show_dir, int(indexer_id), show_name))

        # if they want me to prompt for settings then I will just carry on to the newShow page
        if prompt_for_settings and shows_to_add:
            return self.newShow(shows_to_add[0], shows_to_add[1:])

        # if they don't want me to prompt for settings then I can just add all the nfo shows now
        num_added = 0
        for cur_show in indexer_id_given:
            indexer, show_dir, indexer_id, show_name = cur_show

            if indexer is not None and indexer_id is not None:
                # add the show
                sickbeard.showQueueScheduler.action.addShow(
                    indexer, indexer_id, show_dir,
                    default_status=sickbeard.STATUS_DEFAULT,
                    quality=sickbeard.QUALITY_DEFAULT,
                    flatten_folders=sickbeard.FLATTEN_FOLDERS_DEFAULT,
                    subtitles=sickbeard.SUBTITLES_DEFAULT,
                    anime=sickbeard.ANIME_DEFAULT,
                    scene=sickbeard.SCENE_DEFAULT,
                    default_status_after=sickbeard.STATUS_DEFAULT_AFTER
                )
                num_added += 1

        if num_added:
            ui.notifications.message('Shows Added',
                                     'Automatically added {quantity} from their existing metadata files'.format(quantity=num_added))

        # if we're done then go home
        if not dirs_only:
            return self.redirect('/home/')

        # for the remaining shows we need to prompt for each one, so forward this on to the newShow page
        return self.newShow(dirs_only[0], dirs_only[1:])
Example #14
0
    def _update_library_json(self, host=None, showName=None):  # pylint: disable=too-many-return-statements, too-many-branches
        """Handles updating KODI host via HTTP JSON-RPC

        Attempts to update the KODI video library for a specific tv show if passed,
        otherwise update the whole library if enabled.

        Args:
            host: KODI webserver host:port
            showName: Name of a TV show to specifically target the library update for

        Returns:
            Returns True or False

        """

        if not host:
            logger.log(u'No KODI host passed, aborting update', logger.WARNING)
            return False

        logger.log(u"Updating KODI library via JSON method for host: " + host, logger.DEBUG)

        # if we're doing per-show
        if showName:
            showName = unquote_plus(showName)
            tvshowid = -1
            path = ''

            logger.log(u"Updating library in KODI via JSON method for show " + showName, logger.DEBUG)

            # let's try letting kodi filter the shows
            showsCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","params":{"filter":{"field":"title","operator":"is","value":"%s"},"properties":["title"]},"id":"Medusa"}'

            # get tvshowid by showName
            showsResponse = self._send_to_kodi_json(showsCommand % showName, host)

            if showsResponse and "result" in showsResponse and "tvshows" in showsResponse["result"]:
                shows = showsResponse["result"]["tvshows"]
            else:
                # fall back to retrieving the entire show list
                showsCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","id":1}'
                showsResponse = self._send_to_kodi_json(showsCommand, host)

                if showsResponse and "result" in showsResponse and "tvshows" in showsResponse["result"]:
                    shows = showsResponse["result"]["tvshows"]
                else:
                    logger.log(u"KODI: No tvshows in KODI TV show list", logger.DEBUG)
                    return False

            for show in shows:
                if ("label" in show and show["label"] == showName) or ("title" in show and show["title"] == showName):
                    tvshowid = show["tvshowid"]
                    # set the path is we have it already
                    if "file" in show:
                        path = show["file"]

                    break

            # this can be big, so free some memory
            del shows

            # we didn't find the show (exact match), thus revert to just doing a full update if enabled
            if tvshowid == -1:
                logger.log(u'Exact show name not matched in KODI TV show list', logger.DEBUG)
                return False

            # lookup tv-show path if we don't already know it
            if not path:
                pathCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShowDetails","params":{"tvshowid":%d, "properties": ["file"]},"id":1}' % tvshowid
                pathResponse = self._send_to_kodi_json(pathCommand, host)

                path = pathResponse["result"]["tvshowdetails"]["file"]

            logger.log(u"Received Show: " + showName + " with ID: " + str(tvshowid) + " Path: " + path, logger.DEBUG)

            if not path:
                logger.log(u"No valid path found for " + showName + " with ID: " + str(tvshowid) + " on " + host, logger.WARNING)
                return False

            logger.log(u"KODI Updating " + showName + " on " + host + " at " + path, logger.DEBUG)
            updateCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.Scan","params":{"directory":%s},"id":1}' % (json.dumps(path))
            request = self._send_to_kodi_json(updateCommand, host)
            if not request:
                logger.log(u"Update of show directory failed on " + showName + " on " + host + " at " + path, logger.WARNING)
                return False

            # catch if there was an error in the returned request
            for r in request:
                if 'error' in r:
                    logger.log(u"Error while attempting to update show directory for " + showName + " on " + host + " at " + path, logger.WARNING)
                    return False

        # do a full update if requested
        else:
            logger.log(u"Doing Full Library KODI update on host: " + host, logger.DEBUG)
            updateCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.Scan","id":1}'
            request = self._send_to_kodi_json(updateCommand, host)

            if not request:
                logger.log(u"KODI Full Library update failed on: " + host, logger.WARNING)
                return False

        return True
Example #15
0
    def massAddTable(self, rootDir=None):
        t = PageTemplate(rh=self, filename='home_massAddTable.mako')

        if not rootDir:
            return 'No folders selected.'
        elif not isinstance(rootDir, list):
            root_dirs = [rootDir]
        else:
            root_dirs = rootDir

        root_dirs = [unquote_plus(x) for x in root_dirs]

        if app.ROOT_DIRS:
            default_index = int(app.ROOT_DIRS.split('|')[0])
        else:
            default_index = 0

        if len(root_dirs) > default_index:
            tmp = root_dirs[default_index]
            if tmp in root_dirs:
                root_dirs.remove(tmp)
                root_dirs = [tmp] + root_dirs

        dir_list = []

        main_db_con = db.DBConnection()
        for root_dir in root_dirs:
            try:
                file_list = ek(os.listdir, root_dir)
            except Exception:
                continue

            for cur_file in file_list:

                try:
                    cur_path = ek(os.path.normpath, ek(os.path.join, root_dir, cur_file))
                    if not ek(os.path.isdir, cur_path):
                        continue
                except Exception:
                    continue

                cur_dir = {
                    'dir': cur_path,
                    'display_dir': '<b>{dir}{sep}</b>{base}'.format(
                        dir=ek(os.path.dirname, cur_path), sep=os.sep, base=ek(os.path.basename, cur_path)),
                }

                # see if the folder is in KODI already
                dir_results = main_db_con.select(
                    b'SELECT indexer_id '
                    b'FROM tv_shows '
                    b'WHERE location = ? LIMIT 1',
                    [cur_path]
                )

                cur_dir['added_already'] = bool(dir_results)

                dir_list.append(cur_dir)

                indexer_id = show_name = indexer = None
                for cur_provider in app.metadata_provider_dict.values():
                    if not (indexer_id and show_name):
                        (indexer_id, show_name, indexer) = cur_provider.retrieveShowMetadata(cur_path)

                        # default to TVDB if indexer was not detected
                        if show_name and not (indexer or indexer_id):
                            (_, idxr, i) = helpers.searchIndexerForShowID(show_name, indexer, indexer_id)

                            # set indexer and indexer_id from found info
                            if not indexer and idxr:
                                indexer = idxr

                            if not indexer_id and i:
                                indexer_id = i

                cur_dir['existing_info'] = (indexer_id, show_name, indexer)

                if indexer_id and Show.find(app.showList, indexer_id):
                    cur_dir['added_already'] = True
        return t.render(dirList=dir_list)
Example #16
0
    def _update_library_json(self, host=None, showName=None):  # pylint: disable=too-many-return-statements, too-many-branches
        """Handles updating KODI host via HTTP JSON-RPC

        Attempts to update the KODI video library for a specific tv show if passed,
        otherwise update the whole library if enabled.

        Args:
            host: KODI webserver host:port
            showName: Name of a TV show to specifically target the library update for

        Returns:
            Returns True or False

        """

        if not host:
            logger.log(u'No KODI host passed, aborting update', logger.WARNING)
            return False

        logger.log(u"Updating KODI library via JSON method for host: " + host, logger.INFO)

        # if we're doing per-show
        if showName:
            showName = unquote_plus(showName)
            tvshowid = -1
            path = ''

            logger.log(u"Updating library in KODI via JSON method for show " + showName, logger.DEBUG)

            # let's try letting kodi filter the shows
            showsCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","params":{"filter":{"field":"title","operator":"is","value":"%s"},"properties":["title"]},"id":"Medusa"}'

            # get tvshowid by showName
            showsResponse = self._send_to_kodi_json(showsCommand % showName, host)

            if showsResponse and "result" in showsResponse and "tvshows" in showsResponse["result"]:
                shows = showsResponse["result"]["tvshows"]
            else:
                # fall back to retrieving the entire show list
                showsCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","id":1}'
                showsResponse = self._send_to_kodi_json(showsCommand, host)

                if showsResponse and "result" in showsResponse and "tvshows" in showsResponse["result"]:
                    shows = showsResponse["result"]["tvshows"]
                else:
                    logger.log(u"KODI: No tvshows in KODI TV show list", logger.DEBUG)
                    return False

            for show in shows:
                if ("label" in show and show["label"] == showName) or ("title" in show and show["title"] == showName):
                    tvshowid = show["tvshowid"]
                    # set the path is we have it already
                    if "file" in show:
                        path = show["file"]

                    break

            # this can be big, so free some memory
            del shows

            # we didn't find the show (exact match), thus revert to just doing a full update if enabled
            if tvshowid == -1:
                logger.log(u'Exact show name not matched in KODI TV show list', logger.DEBUG)
                return False

            # lookup tv-show path if we don't already know it
            if not path:
                pathCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.GetTVShowDetails","params":{"tvshowid":%d, "properties": ["file"]},"id":1}' % tvshowid
                pathResponse = self._send_to_kodi_json(pathCommand, host)

                path = pathResponse["result"]["tvshowdetails"]["file"]

            logger.log(u"Received Show: " + showName + " with ID: " + str(tvshowid) + " Path: " + path, logger.DEBUG)

            if not path:
                logger.log(u"No valid path found for " + showName + " with ID: " + str(tvshowid) + " on " + host, logger.WARNING)
                return False

            logger.log(u"KODI Updating " + showName + " on " + host + " at " + path, logger.DEBUG)
            updateCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.Scan","params":{"directory":%s},"id":1}' % (json.dumps(path))
            request = self._send_to_kodi_json(updateCommand, host)
            if not request:
                logger.log(u"Update of show directory failed on " + showName + " on " + host + " at " + path, logger.WARNING)
                return False

            # catch if there was an error in the returned request
            for r in request:
                if 'error' in r:
                    logger.log(u"Error while attempting to update show directory for " + showName + " on " + host + " at " + path, logger.WARNING)
                    return False

        # do a full update if requested
        else:
            logger.log(u"Doing Full Library KODI update on host: " + host, logger.DEBUG)
            updateCommand = '{"jsonrpc":"2.0","method":"VideoLibrary.Scan","id":1}'
            request = self._send_to_kodi_json(updateCommand, host)

            if not request:
                logger.log(u"KODI Full Library update failed on: " + host, logger.WARNING)
                return False

        return True
Example #17
0
    def massAddTable(self, rootDir=None):
        t = PageTemplate(rh=self, filename='home_massAddTable.mako')

        if not rootDir:
            return 'No folders selected.'
        elif not isinstance(rootDir, list):
            root_dirs = [rootDir]
        else:
            root_dirs = rootDir

        root_dirs = [unquote_plus(x) for x in root_dirs]

        if app.ROOT_DIRS:
            default_index = int(app.ROOT_DIRS[0])
        else:
            default_index = 0

        if len(root_dirs) > default_index:
            tmp = root_dirs[default_index]
            if tmp in root_dirs:
                root_dirs.remove(tmp)
                root_dirs = [tmp] + root_dirs

        dir_list = []

        main_db_con = db.DBConnection()
        for root_dir in root_dirs:
            try:
                file_list = os.listdir(root_dir)
            except Exception as error:
                logger.log('Unable to listdir {path}: {e!r}'.format(path=root_dir, e=error))
                continue

            for cur_file in file_list:

                try:
                    cur_path = os.path.normpath(os.path.join(root_dir, cur_file))
                    if not os.path.isdir(cur_path):
                        continue
                except Exception as error:
                    logger.log('Unable to get current path {path} and {file}: {e!r}'.format(path=root_dir, file=cur_file, e=error))
                    continue

                cur_dir = {
                    'dir': cur_path,
                    'display_dir': '<b>{dir}{sep}</b>{base}'.format(
                        dir=os.path.dirname(cur_path), sep=os.sep, base=os.path.basename(cur_path)),
                }

                # see if the folder is in KODI already
                dir_results = main_db_con.select(
                    b'SELECT indexer_id '
                    b'FROM tv_shows '
                    b'WHERE location = ? LIMIT 1',
                    [cur_path]
                )

                cur_dir['added_already'] = bool(dir_results)

                dir_list.append(cur_dir)

                indexer_id = show_name = indexer = None
                # You may only call .values() on metadata_provider_dict! As on values() call the indexer_api attribute
                # is reset. This will prevent errors, when using multiple indexers and caching.
                for cur_provider in app.metadata_provider_dict.values():
                    if not (indexer_id and show_name):
                        (indexer_id, show_name, indexer) = cur_provider.retrieveShowMetadata(cur_path)

                cur_dir['existing_info'] = (indexer_id, show_name, indexer)

                if indexer_id and Show.find(app.showList, indexer_id):
                    cur_dir['added_already'] = True
        return t.render(dirList=dir_list)
Example #18
0
def get_headers_for_request(url,
                            region,
                            service,
                            access_key,
                            secret_key,
                            session_token=None,
                            payload='',
                            headers={},
                            method='GET',
                            t=None):
    # Create a date for headers and the credential string
    if not t:
        t = datetime.datetime.utcnow()
    amzdate = t.strftime('%Y%m%dT%H%M%SZ')
    datestamp = t.strftime('%Y%m%d')  # Date w/o time, used in credential scope

    # ************* TASK 1: CREATE A CANONICAL REQUEST *************
    # http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html

    # Step 1 is to define the verb (GET, POST, etc.)--already done.

    # Step 2: Create canonical URI--the part of the URI from domain to query
    # string (use '/' if no path)
    parsed = url_parse(url)
    host = parsed.netloc
    canonical_uri = quote(unquote_plus(parsed.path),
                          safe="!#$%&'()*+,/:;=?@[]~")

    # Step 3: Create the canonical query string. In this example (a GET request),
    # request parameters are in the query string. Query string values must
    # be URL-encoded (space=%20). The parameters must be sorted by name.
    # For this example, the query string is pre-formatted in the request_parameters variable.
    params = {}
    if parsed.query:
        pq = parse_qs(parsed.query).items()
        pq = [(p[0], sorted(p[1], key=lambda s: s.lower())) for p in pq]
        params = OrderedDict(sorted(pq, key=lambda s: s[0].lower()))

    if is_py2:
        # patch quote_plus aws sigv4 does not like escaped spaces with +
        qp_orig = deepcopy(urllib.quote_plus)
        urllib.quote_plus = quote
        canonical_querystring = urlencode(params, doseq=True)
        urllib.quote_plus = qp_orig
    else:
        canonical_querystring = urlencode(params, doseq=True, quote_via=quote)
    # bit hacky, but quote takes these out as urllib doesn't consider them safe
    canonical_querystring = canonical_querystring.replace('%7E', '~')

    # Step 4: Create the canonical headers and signed headers. Header names
    # and value must be trimmed and lowercase, and sorted in ASCII order.
    # Note that there is a trailing \n.
    canonical_headers = ''
    headers_to_sign = []
    _headers = {'host': host, 'x-amz-date': amzdate}
    _headers.update(deepcopy(headers))
    _headers = OrderedDict(sorted(_headers.items(),
                                  key=lambda s: s[0].lower()))
    for c in _headers:
        cv = c.strip().lower()
        val = ' '.join(_headers[c].split()).strip()
        if cv == 'x-amz-date':
            val = _headers[c]
        if c not in headers_to_sign:
            headers_to_sign.append(cv)
            canonical_headers = '{}{}:{}\n'.format(canonical_headers, cv, val)

    # Step 5: Create the list of signed headers. This lists the headers
    # in the canonical_headers list, delimited with ";" and in alpha order.
    # Note: The request can include any headers; canonical_headers and
    # signed_headers lists those that you want to be included in the
    # hash of the request. "Host" and "x-amz-date" are always required.
    signed_headers = ';'.join(headers_to_sign)

    # Step 6: Create payload hash (hash of the request body content). For GET
    # requests, the payload is an empty string ("").

    if payload is None:
        payload = ''
    # handle differences between library requests 2.11.0 and previous
    if type(payload) is bytes:
        payload_hash = hashlib.sha256(payload).hexdigest()
    else:
        payload_hash = hashlib.sha256(payload.encode("utf-8")).hexdigest()

    # Step 7: Combine elements to create create canonical request
    canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + \
        '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash

    # ************* TASK 2: CREATE THE STRING TO SIGN*************
    # Match the algorithm to the hashing algorithm you use, either SHA-1 or
    # SHA-256 (recommended)
    algorithm = 'AWS4-HMAC-SHA256'
    credential_scope = datestamp + '/' + region + \
        '/' + service + '/' + 'aws4_request'
    string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + \
        '\n' + hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()

    # ************* TASK 3: CALCULATE THE SIGNATURE *************
    # Create the signing key using the function defined above.
    signing_key = getSignatureKey(secret_key, datestamp, region, service)

    # Sign the string_to_sign using the signing_key
    signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'),
                         hashlib.sha256).hexdigest()

    # ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
    # The signing information can be either in a query string value or in
    # a header named Authorization. This code shows how to use a header.
    # Create authorization header and add to request headers
    authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + \
        credential_scope + ', ' + 'SignedHeaders=' + \
        signed_headers + ', ' + 'Signature=' + signature

    # The request can include any headers, but MUST include "host", "x-amz-date",
    # and (for this scenario) "Authorization". "host" and "x-amz-date" must
    # be included in the canonical_headers and signed_headers, as noted
    # earlier. Order here is not significant.
    # Python note: The 'host' header is added automatically by the Python 'requests' library.
    headers_to_add = {
        'x-amz-date': amzdate,
        'Authorization': authorization_header
    }

    if session_token:
        headers_to_add['X-Amz-Security-Token'] = session_token

    headers.update(headers_to_add)
    return headers
Example #19
0
    def update_library(self, show_name=None):
        if not (sickbeard.USE_KODI and sickbeard.KODI_UPDATE_LIBRARY):
            return False

        if not sickbeard.KODI_HOST:
            logger.log("No KODI hosts specified, check your settings",
                       logger.DEBUG)
            return False

        result = 0
        for connection in self.connections:
            try:
                logger.log("Updating KODI library on host: " + connection.host,
                           logger.DEBUG)

                if show_name:
                    tvshowid = -1
                    path = ''

                    logger.log(
                        "Updating library in KODI for show " + show_name,
                        logger.DEBUG)

                    # let's try letting kodi filter the shows
                    response = connection.VideoLibrary.GetTVShows(
                        filter={
                            "field": "title",
                            "operator": "is",
                            "value": show_name
                        },
                        properties=["file"])
                    if response and "result" in response and "tvshows" in response[
                            "result"]:
                        shows = response["result"]["tvshows"]
                    else:
                        # fall back to retrieving the entire show list
                        response = connection.VideoLibrary.GetTVShows(
                            properties=["file"])
                        if response and "result" in response and "tvshows" in response[
                                "result"]:
                            shows = response["result"]["tvshows"]
                        else:
                            logger.log("KODI: No tvshows in KODI TV show list",
                                       logger.DEBUG)
                            continue

                    check = (show_name, unquote_plus(show_name))
                    for show in shows:
                        if ("label" in show and show["label"] in check) or (
                                "title" in show and show["title"] in check):
                            tvshowid = show["tvshowid"]
                            path = show["file"]
                            break

                    del shows

                    # we didn't find the show (exact match), thus revert to just doing a full update if enabled
                    if tvshowid == -1:
                        logger.log(
                            'Exact show name not matched in KODI TV show list',
                            logger.DEBUG)

                    if not path:
                        logger.log(
                            "No valid path found for " + show_name +
                            " with ID: " + str(tvshowid) + " on " +
                            connection.host, logger.WARNING)

                    if path and tvshowid != -1:
                        logger.log(
                            "KODI Updating " + show_name + " with ID: " +
                            str(tvshowid) + " at " + path + " on " +
                            connection.host, logger.DEBUG)
                        response = connection.VideoLibrary.Scan(directory=path)
                        if not self.success(response):
                            logger.log(
                                "Update of show directory failed on " +
                                show_name + " on " + connection.host + " at " +
                                path, logger.WARNING)
                        else:
                            if sickbeard.KODI_UPDATE_ONLYFIRST:
                                logger.log(
                                    "Successfully updated '" +
                                    connection.host +
                                    "', stopped sending update library commands.",
                                    logger.DEBUG)
                                return True

                            result += 1
                            continue

                if show_name and not sickbeard.KODI_UPDATE_FULL:
                    continue

                logger.log(
                    "Doing Full Library KODI update on host: " +
                    connection.host, logger.DEBUG)
                response = connection.VideoLibrary.Scan()
                if not response:
                    logger.log(
                        "KODI Full Library update failed on: " +
                        connection.host, logger.WARNING)
                elif self.success(response):
                    if sickbeard.KODI_UPDATE_ONLYFIRST:
                        logger.log(
                            "Successfully updated '" + connection.host +
                            "', stopped sending update library commands.",
                            logger.DEBUG)
                        return True

                    result += 1
            except (URLError, RequestTimeout):
                pass

        return result > 0
Example #20
0
    def massAddTable(self, rootDir=None):
        t = PageTemplate(rh=self, filename="home_massAddTable.mako")

        if not rootDir:
            return _("No folders selected.")
        elif not isinstance(rootDir, list):
            root_dirs = [rootDir]
        else:
            root_dirs = rootDir

        root_dirs = [unquote_plus(xhtml_unescape(x)) for x in root_dirs]

        if sickbeard.ROOT_DIRS:
            default_index = int(sickbeard.ROOT_DIRS.split('|')[0])
        else:
            default_index = 0

        if len(root_dirs) > default_index:
            tmp = root_dirs[default_index]
            if tmp in root_dirs:
                root_dirs.remove(tmp)
                root_dirs.insert(0, tmp)

        dir_list = []

        main_db_con = db.DBConnection()
        for root_dir in root_dirs:
            # noinspection PyBroadException
            try:
                file_list = ek(os.listdir, root_dir)
            except Exception:
                continue

            for cur_file in file_list:
                # noinspection PyBroadException
                try:
                    cur_path = ek(os.path.normpath,
                                  ek(os.path.join, root_dir, cur_file))
                    if not ek(os.path.isdir, cur_path):
                        continue
                    # ignore Synology folders
                    if cur_file.lower() in ['#recycle', '@eadir']:
                        continue
                except Exception:
                    continue

                cur_dir = {
                    'dir':
                    cur_path,
                    'existing_info': (None, None, None),
                    'display_dir':
                    '<b>' + ek(os.path.dirname, cur_path) + os.sep + '</b>' +
                    ek(os.path.basename, cur_path),
                }

                # see if the folder is in KODI already
                dirResults = main_db_con.select(
                    "SELECT indexer_id FROM tv_shows WHERE location = ? LIMIT 1",
                    [cur_path])

                if dirResults:
                    cur_dir['added_already'] = True
                else:
                    cur_dir['added_already'] = False

                dir_list.append(cur_dir)

                indexer_id = show_name = indexer = None
                for cur_provider in sickbeard.metadata_provider_dict.values():
                    if not (indexer_id and show_name):
                        (indexer_id, show_name,
                         indexer) = cur_provider.retrieveShowMetadata(cur_path)
                        if all((indexer_id, show_name, indexer)):
                            break

                if all((indexer_id, show_name, indexer)):
                    cur_dir['existing_info'] = (indexer_id, show_name, indexer)

                if indexer_id and Show.find(sickbeard.showList, indexer_id):
                    cur_dir['added_already'] = True
        return t.render(dirList=dir_list)
Example #21
0
    def addExistingShows(self, shows_to_add=None, promptForSettings=None):
        """
        Receives a dir list and add them. Adds the ones with given TVDB IDs first, then forwards
        along to the newShow page.
        """
        prompt_for_settings = promptForSettings

        # grab a list of other shows to add, if provided
        if not shows_to_add:
            shows_to_add = []
        elif not isinstance(shows_to_add, list):
            shows_to_add = [shows_to_add]

        shows_to_add = [unquote_plus(x) for x in shows_to_add]

        prompt_for_settings = config.checkbox_to_value(prompt_for_settings)

        indexer_id_given = []
        dirs_only = []
        # separate all the ones with Indexer IDs
        for cur_dir in shows_to_add:
            if '|' in cur_dir:
                split_vals = cur_dir.split('|')
                if len(split_vals) < 3:
                    dirs_only.append(cur_dir)
            if '|' not in cur_dir:
                dirs_only.append(cur_dir)
            else:
                indexer, show_dir, indexer_id, show_name = self.split_extra_show(cur_dir)
                if indexer and show_dir and not indexer_id:
                    dirs_only.append(cur_dir)

                if not show_dir or not indexer_id or not show_name:
                    continue

                indexer_id_given.append((int(indexer), show_dir, int(indexer_id), show_name))

        # if they want me to prompt for settings then I will just carry on to the newShow page
        if prompt_for_settings and shows_to_add:
            return self.newShow(shows_to_add[0], shows_to_add[1:])

        # if they don't want me to prompt for settings then I can just add all the nfo shows now
        num_added = 0
        for cur_show in indexer_id_given:
            indexer, show_dir, indexer_id, show_name = cur_show

            if indexer is not None and indexer_id is not None:
                # add the show
                app.show_queue_scheduler.action.addShow(
                    indexer, indexer_id, show_dir,
                    default_status=app.STATUS_DEFAULT,
                    quality=app.QUALITY_DEFAULT,
                    flatten_folders=app.FLATTEN_FOLDERS_DEFAULT,
                    subtitles=app.SUBTITLES_DEFAULT,
                    anime=app.ANIME_DEFAULT,
                    scene=app.SCENE_DEFAULT,
                    default_status_after=app.STATUS_DEFAULT_AFTER
                )
                num_added += 1

        if num_added:
            ui.notifications.message('Shows Added',
                                     'Automatically added {quantity} from their existing metadata files'.format(quantity=num_added))

        # if we're done then go home
        if not dirs_only:
            return self.redirect('/home/')

        # for the remaining shows we need to prompt for each one, so forward this on to the newShow page
        return self.newShow(dirs_only[0], dirs_only[1:])
Example #22
0
    def _update_library(self, host=None, series_name=None):
        """Handle updating KODI host via HTTP JSON-RPC.

        Attempts to update the KODI video library for a specific tv show if passed,
        otherwise update the whole library if enabled.

        Args:
            host: KODI webserver host:port
            series_name: Name of a TV show to specifically target the library update for

        Returns:
            Returns True or False

        """
        if not host:
            log.warning(u'No KODI host passed, aborting update')
            return False

        log.info(u'Updating KODI library via JSON method for host: {0}', host)

        # if we're doing per-show
        if series_name:
            series_name = unquote_plus(series_name)
            tvshowid = -1
            path = ''

            log.debug(u'Updating library in KODI via JSON method for show {0}', series_name)

            # let's try letting kodi filter the shows
            shows_command = {
                'jsonrpc': '2.0',
                'method': 'VideoLibrary.GetTVShows',
                'params': {
                    'filter': {
                        'field': 'title',
                        'operator': 'is',
                        'value': series_name,
                    },
                    'properties': ['title'],
                },
                'id': 'Medusa',
            }

            # get tvshowid by series_name
            series_response = self._send_to_kodi(shows_command, host)

            if series_response and 'result' in series_response and 'tvshows' in series_response['result']:
                shows = series_response['result']['tvshows']
            else:
                # fall back to retrieving the entire show list
                shows_command = {
                    'jsonrpc': '2.0',
                    'method': 'VideoLibrary.GetTVShows',
                    'id': 1,
                }
                series_response = self._send_to_kodi(shows_command, host)

                if series_response and 'result' in series_response and 'tvshows' in series_response['result']:
                    shows = series_response['result']['tvshows']
                else:
                    log.debug(u'KODI: No tvshows in KODI TV show list')
                    return False

            for show in shows:
                if ('label' in show and show['label'] == series_name) or ('title' in show and show['title'] == series_name):
                    tvshowid = show['tvshowid']
                    # set the path is we have it already
                    if 'file' in show:
                        path = show['file']

                    break

            # this can be big, so free some memory
            del shows

            # we didn't find the show (exact match), thus revert to just doing a full update if enabled
            if tvshowid == -1:
                log.debug(u'Exact show name not matched in KODI TV show list')
                return False

            # lookup tv-show path if we don't already know it
            if not path:
                path_command = {
                    'jsonrpc': '2.0',
                    'method': 'VideoLibrary.GetTVShowDetails',
                    'params': {
                        'tvshowid': tvshowid,
                        'properties': ['file'],
                    },
                    'id': 1,
                }
                path_response = self._send_to_kodi(path_command, host)

                path = path_response['result']['tvshowdetails']['file']

            log.debug(u'Received Show: {0} with ID: {1} Path: {2}', series_name, tvshowid, path)

            if not path:
                log.warning(u'No valid path found for {0} with ID: {1} on {2}', series_name, tvshowid, host)
                return False

            log.debug(u'KODI Updating {0} on {1} at {2}', series_name, host, path)
            update_command = {
                'jsonrpc': '2.0',
                'method': 'VideoLibrary.Scan',
                'params': {
                    'directory': path,
                },
                'id': 1,
            }
            request = self._send_to_kodi(update_command, host)
            if not request:
                log.warning(u'Update of show directory failed on {0} on {1} at {2}', series_name, host, path)
                return False

            # catch if there was an error in the returned request
            for r in request:
                if 'error' in r:
                    log.warning(u'Error while attempting to update show directory for {0} on {1} at {2} ',
                                series_name, host, path)
                    return False

        # do a full update if requested
        else:
            log.debug(u'Doing Full Library KODI update on host: {0}', host)
            update_command = {
                'jsonrpc': '2.0',
                'method': 'VideoLibrary.Scan',
                'id': 1,
            }
            request = self._send_to_kodi(update_command, host)

            if not request:
                log.warning(u'KODI Full Library update failed on: {0}', host)
                return False

        return True