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
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))
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))
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
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'
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')
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 '')
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
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': '' })
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': '', })
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
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)
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:])
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
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)
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
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)
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
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
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)
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:])
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