def saveAddShowDefaults(defaultStatus, anyQualities, bestQualities, defaultSeasonFolders, subtitles=False, anime=False, scene=False, defaultStatusAfter=WANTED): if anyQualities: anyQualities = anyQualities.split(',') else: anyQualities = [] if bestQualities: bestQualities = bestQualities.split(',') else: bestQualities = [] newQuality = Quality.combineQualities( [int(quality) for quality in anyQualities], [int(quality) for quality in bestQualities]) settings.STATUS_DEFAULT = int(defaultStatus) settings.STATUS_DEFAULT_AFTER = int(defaultStatusAfter) settings.QUALITY_DEFAULT = int(newQuality) settings.SEASON_FOLDERS_DEFAULT = config.checkbox_to_value( defaultSeasonFolders) settings.SUBTITLES_DEFAULT = config.checkbox_to_value(subtitles) settings.ANIME_DEFAULT = config.checkbox_to_value(anime) settings.SCENE_DEFAULT = config.checkbox_to_value(scene) sickchill.start.save_config() ui.notifications.message( _('Saved Defaults'), _('Your "add show" defaults have been set to your current selections.' ))
def get_quality(self, item, anime=False): (title, url_) = self._get_title_and_url(item) quality = Quality.scene_quality(title, anime) return quality
def addNewShow( self, whichSeries=None, indexerLang=None, rootDir=None, defaultStatus=None, quality_preset=None, anyQualities=None, bestQualities=None, season_folders=None, subtitles=None, subtitles_sr_metadata=None, fullShowPath=None, other_shows=None, skipShow=None, providedIndexer=None, anime=None, scene=None, blacklist=None, whitelist=None, defaultStatusAfter=None, ): """ Receive tvdb id, dir, and other options and create a show from them. If extra show dirs are provided then it forwards back to newShow, if not it goes to /home. """ if not indexerLang: indexerLang = settings.INDEXER_DEFAULT_LANGUAGE # grab our list of other dirs if given if not other_shows: other_shows = [] elif not isinstance(other_shows, list): other_shows = [other_shows] def finishAddShow(): # if there are no extra shows then go home if not other_shows: return self.redirect("/home/") # peel off the next one next_show_dir = other_shows[0] rest_of_show_dirs = other_shows[1:] # go to add the next show return self.newShow(next_show_dir, rest_of_show_dirs) # if we're skipping then behave accordingly if skipShow: return finishAddShow() # sanity check on our inputs if (not rootDir and not fullShowPath) or not whichSeries: return _( "Missing params, no Indexer ID or folder: {show_to_add} and {root_dir}/{show_path}" ).format(show_to_add=whichSeries, root_dir=rootDir, show_path=fullShowPath) # figure out what show we're adding and where series_pieces = whichSeries.split("|") if (whichSeries and rootDir) or (whichSeries and fullShowPath and len(series_pieces) > 1): if len(series_pieces) < 6: logger.error( "Unable to add show due to show selection. Not enough arguments: {0}" .format((repr(series_pieces)))) ui.notifications.error( _("Unknown error. Unable to add show due to problem with show selection." )) return self.redirect("/addShows/existingShows/") indexer = int(series_pieces[1]) indexer_id = int(series_pieces[3]) # Show name was sent in UTF-8 in the form show_name = xhtml_unescape(series_pieces[4]) else: # if no indexer was provided use the default indexer set in General settings if not providedIndexer: providedIndexer = settings.INDEXER_DEFAULT indexer = int(providedIndexer) indexer_id = int(whichSeries) show_name = os.path.basename( os.path.normpath(xhtml_unescape(fullShowPath))) # use the whole path if it's given, or else append the show name to the root dir to get the full show path if fullShowPath: show_dir = os.path.normpath(xhtml_unescape(fullShowPath)) extra_check_dir = show_dir else: folder_name = show_name s = sickchill.indexer.series_by_id(indexerid=indexer_id, indexer=indexer, language=indexerLang) if settings.ADD_SHOWS_WITH_YEAR and s.firstAired: try: year = "({0})".format( dateutil.parser.parse(s.firstAired).year) if year not in folder_name: folder_name = "{0} {1}".format(s.seriesName, year) except (TypeError, ValueError): logger.info( _("Could not append the show year folder for the show: {0}" ).format(folder_name)) show_dir = os.path.join( rootDir, sanitize_filename(xhtml_unescape(folder_name))) extra_check_dir = os.path.join( rootDir, sanitize_filename(xhtml_unescape(show_name))) # blanket policy - if the dir exists you should have used "add existing show" numbnuts if (os.path.isdir(show_dir) or os.path.isdir(extra_check_dir)) and not fullShowPath: ui.notifications.error( _("Unable to add show"), _("Folder {show_dir} exists already").format( show_dir=show_dir)) return self.redirect("/addShows/existingShows/") # don't create show dir if config says not to if settings.ADD_SHOWS_WO_DIR: logger.info("Skipping initial creation of " + show_dir + " due to config.ini setting") else: dir_exists = helpers.makeDir(show_dir) if not dir_exists: logger.exception("Unable to create the folder " + show_dir + ", can't add the show") ui.notifications.error( _("Unable to add show"), _("Unable to create the folder {show_dir}, can't add the show" ).format(show_dir=show_dir)) # Don't redirect to default page because user wants to see the new show return self.redirect("/home/") else: helpers.chmodAsParent(show_dir) # prepare the inputs for passing along scene = config.checkbox_to_value(scene) anime = config.checkbox_to_value(anime) season_folders = config.checkbox_to_value(season_folders) subtitles = config.checkbox_to_value(subtitles) subtitles_sr_metadata = config.checkbox_to_value(subtitles_sr_metadata) if whitelist: whitelist = short_group_names(whitelist) if blacklist: blacklist = short_group_names(blacklist) if not anyQualities: anyQualities = [] if not bestQualities or try_int(quality_preset, None): bestQualities = [] if not isinstance(anyQualities, list): anyQualities = [anyQualities] if not isinstance(bestQualities, list): bestQualities = [bestQualities] newQuality = Quality.combineQualities([int(q) for q in anyQualities], [int(q) for q in bestQualities]) # add the show settings.showQueueScheduler.action.add_show( indexer, indexer_id, showDir=show_dir, default_status=int(defaultStatus), quality=newQuality, season_folders=season_folders, lang=indexerLang, subtitles=subtitles, subtitles_sr_metadata=subtitles_sr_metadata, anime=anime, scene=scene, paused=None, blacklist=blacklist, whitelist=whitelist, default_status_after=int(defaultStatusAfter), root_dir=rootDir, ) ui.notifications.message( _("Show added"), _("Adding the specified show into {show_dir}").format( show_dir=show_dir)) return finishAddShow()
def addShowByID( self, indexer_id, show_name, indexer="TVDB", which_series=None, indexer_lang=None, root_dir=None, default_status=None, quality_preset=None, any_qualities=None, best_qualities=None, season_folders=None, subtitles=None, full_show_path=None, other_shows=None, skip_show=None, provided_indexer=None, anime=None, scene=None, blacklist=None, whitelist=None, default_status_after=None, default_season_folders=None, configure_show_options=None, ): if indexer != "TVDB": indexer_id = helpers.tvdbid_from_remote_id(indexer_id, indexer.upper()) if not indexer_id: logger.info( "Unable to to find tvdb ID to add {0}".format(show_name)) ui.notifications.error( "Unable to add {0}".format(show_name), "Could not add {0}. We were unable to locate the tvdb id at this time." .format(show_name)) return indexer_id = try_int(indexer_id) if indexer_id <= 0 or Show.find(settings.showList, indexer_id): return # Sanitize the parameter anyQualities and bestQualities. As these would normally be passed as lists any_qualities = any_qualities.split(",") if any_qualities else [] best_qualities = best_qualities.split(",") if best_qualities else [] # If configure_show_options is enabled let's use the provided settings if config.checkbox_to_value(configure_show_options): # prepare the inputs for passing along scene = config.checkbox_to_value(scene) anime = config.checkbox_to_value(anime) season_folders = config.checkbox_to_value(season_folders) subtitles = config.checkbox_to_value(subtitles) if whitelist: whitelist = short_group_names(whitelist) if blacklist: blacklist = short_group_names(blacklist) if not any_qualities: any_qualities = [] if not best_qualities or try_int(quality_preset, None): best_qualities = [] if not isinstance(any_qualities, list): any_qualities = [any_qualities] if not isinstance(best_qualities, list): best_qualities = [best_qualities] quality = Quality.combineQualities( [int(q) for q in any_qualities], [int(q) for q in best_qualities]) location = root_dir else: default_status = settings.STATUS_DEFAULT quality = settings.QUALITY_DEFAULT season_folders = settings.SEASON_FOLDERS_DEFAULT subtitles = settings.SUBTITLES_DEFAULT anime = settings.ANIME_DEFAULT scene = settings.SCENE_DEFAULT default_status_after = settings.STATUS_DEFAULT_AFTER if settings.ROOT_DIRS: root_dirs = settings.ROOT_DIRS.split("|") location = root_dirs[int(root_dirs[0]) + 1] else: location = None if not location: logger.info( "There was an error creating the show, no root directory setting found" ) return _("No root directories setup, please go back and add one.") show_name = sickchill.indexer[1].get_series_by_id( indexer_id, indexer_lang).seriesName show_dir = None if not show_name: ui.notifications.error(_("Unable to add show")) return self.redirect("/home/") # add the show settings.showQueueScheduler.action.add_show( indexer=1, indexer_id=indexer_id, showDir=show_dir, default_status=default_status, quality=quality, season_folders=season_folders, lang=indexer_lang, subtitles=subtitles, subtitles_sr_metadata=None, anime=anime, scene=scene, paused=None, blacklist=blacklist, whitelist=whitelist, default_status_after=default_status_after, root_dir=location, ) ui.notifications.message( _("Show added"), _("Adding the specified show {show_name}").format( show_name=show_name)) # done adding show return self.redirect("/home/")
def search(self, search_params, age=0, ep_obj=None): results = [] if not self.login(): return results self.categories = "cat=" + str(self.cat) for mode in search_params: items = [] logger.debug(_(f"Search Mode: {mode}")) for search_string in search_params[mode]: if mode == 'RSS': self.page = 2 last_page = 0 y = int(self.page) if search_string == '': continue search_string = str(search_string).replace('.', ' ') for x in range(0, y): z = x * 20 if last_page: break if mode != 'RSS': search_url = (self.urls['search_page'] + '&filter={2}').format( z, self.categories, search_string) else: search_url = self.urls['search_page'].format( z, self.categories) if mode != 'RSS': logger.debug(_(f"Search String: {search_string}")) data = self.get_url(search_url, returns='text') if not data: logger.debug("No data returned from provider") continue try: with BS4Parser(data, 'html5lib') as html: torrent_table = html.find('table', class_='copyright') torrent_rows = torrent_table( 'tr') if torrent_table else [] # Continue only if one Release is found if len(torrent_rows) < 3: logger.debug( "Data returned from provider does not contain any torrents" ) last_page = 1 continue if len(torrent_rows) < 42: last_page = 1 for result in torrent_table('tr')[2:]: try: link = result.find('td').find('a') title = link.string download_url = self.urls[ 'download'] % result('td')[8].find( 'a')['href'][-8:] leechers = result('td')[3]('td')[0].text leechers = int(leechers.strip('[]')) seeders = result('td')[3]('td')[1].text seeders = int(seeders.strip('[]')) torrent_size = result('td')[3]( 'td')[3].text.strip('[]') + " GB" size = convert_size(torrent_size) or -1 except (AttributeError, TypeError): continue filename_qt = self._reverseQuality( self._episodeQuality(result)) for text in self.hdtext: title1 = title title = title.replace(text, filename_qt) if title != title1: break if Quality.nameQuality( title) == Quality.UNKNOWN: title += filename_qt if not self._is_italian( result) and not self.subtitle: logger.debug( "Torrent is subtitled, skipping: {0} ". format(title)) continue if self.engrelease and not self._is_english( result): logger.debug( "Torrent isnt english audio/subtitled , skipping: {0} " .format(title)) continue search_show = re.split(r'([Ss][\d{1,2}]+)', search_string)[0] show_title = search_show rindex = re.search(r'([Ss][\d{1,2}]+)', title) if rindex: show_title = title[:rindex.start()] ep_params = title[rindex.start():] if show_title.lower() != search_show.lower( ) and search_show.lower() in show_title.lower( ): new_title = search_show + ep_params title = new_title if not all([title, download_url]): continue if self._is_season_pack(title): title = re.sub(r'([Ee][\d{1,2}\-?]+)', '', title) # Filter unseeded torrent if seeders < self.minseed or leechers < self.minleech: if mode != 'RSS': logger.debug( "Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})" .format(title, seeders, leechers)) continue item = { 'title': title, 'link': download_url, 'size': size, 'seeders': seeders, 'leechers': leechers, 'hash': '' } if mode != 'RSS': logger.debug( "Found result: {0} with {1} seeders and {2} leechers" .format(title, seeders, leechers)) items.append(item) except Exception: logger.exception( "Failed parsing provider. Traceback: {0}".format( traceback.format_exc())) # For each search mode sort all the items by seeders if available if available items.sort(key=lambda d: try_int(d.get('seeders', 0)), reverse=True) results += items return results
def search(self, search_params, age=0, ep_obj=None): results = [] for mode in search_params: items = [] logger.debug(_(f'Search Mode: {mode}')) for search_string in search_params[mode]: if search_string == '': continue search_string = str(search_string).replace('.', ' ') logger.debug(_(f'Search String: {search_string}')) last_page = False for page in range(0, self.max_pages): if last_page: break logger.debug('Processing page {0} of results'.format(page)) search_url = self.urls['search'].format( search_string, page) data = self.get_url(search_url, returns='text') if not data: logger.debug(_('No data returned from provider')) continue try: with BS4Parser(data, 'html5lib') as html: table_header = html.find('tr', class_='bordo') torrent_table = table_header.find_parent( 'table') if table_header else None if not torrent_table: logger.exception( 'Could not find table of torrents') continue torrent_rows = torrent_table('tr') # Continue only if one Release is found if len(torrent_rows) < 6 or len( torrent_rows[2]('td')) == 1: logger.debug( 'Data returned from provider does not contain any torrents' ) last_page = True continue if len(torrent_rows) < 45: last_page = True for result in torrent_rows[2:-3]: result_cols = result('td') if len(result_cols) == 1: # Ignore empty rows in the middle of the table continue try: title = result('td')[1].get_text( strip=True) torrent_size = result('td')[2].get_text( strip=True) info_hash = result('td')[3].find( 'input', class_='downarrow')['value'].upper() download_url = self._magnet_from_result( info_hash, title) seeders = try_int( result('td')[5].get_text(strip=True)) leechers = try_int( result('td')[6].get_text(strip=True)) size = convert_size(torrent_size) or -1 except (AttributeError, IndexError, TypeError): continue filename_qt = self._reverseQuality( self._episodeQuality(result)) for text in self.hdtext: title1 = title title = title.replace(text, filename_qt) if title != title1: break if Quality.nameQuality( title) == Quality.UNKNOWN: title += filename_qt if not self._is_italian( title) and not self.subtitle: logger.debug( 'Torrent is subtitled, skipping: {0}'. format(title)) continue if self.engrelease and not self._is_english( title): logger.debug( 'Torrent isn\'t english audio/subtitled, skipping: {0}' .format(title)) continue search_show = re.split(r'([Ss][\d{1,2}]+)', search_string)[0] show_title = search_show ep_params = '' rindex = re.search(r'([Ss][\d{1,2}]+)', title) if rindex: show_title = title[:rindex.start()] ep_params = title[rindex.start():] if show_title.lower() != search_show.lower( ) and search_show.lower() in show_title.lower( ): new_title = search_show + ep_params title = new_title if not all([title, download_url]): continue if self._is_season_pack(title): title = re.sub(r'([Ee][\d{1,2}\-?]+)', '', title) # Filter unseeded torrent if seeders < self.minseed or leechers < self.minleech: logger.debug( 'Discarding torrent because it doesn\'t meet the minimum seeders or leechers: {0} (S:{1} L:{2})' .format(title, seeders, leechers)) continue item = { 'title': title, 'link': download_url, 'size': size, 'seeders': seeders, 'leechers': leechers, 'hash': info_hash } if mode != 'RSS': logger.debug( _(f'Found result: {title} with {seeders} seeders and {leechers} leechers' )) items.append(item) except Exception as error: logger.exception( 'Failed parsing provider. Error: {0}'.format( error)) # For each search mode sort all the items by seeders if available items.sort(key=lambda d: try_int(d.get('seeders', 0)), reverse=True) results += items return results
def search(self, search_params, age=0, ep_obj=None): results = [] for mode in search_params: items = [] logger.debug(_("Search Mode: {mode}".format(mode=mode))) for search_string in search_params[mode]: if search_string == "": continue search_string = str(search_string).replace(".", " ") logger.debug( _("Search String: {search_string}".format( search_string=search_string))) last_page = False for page in range(0, self.max_pages): if last_page: break logger.debug("Processing page {0} of results".format(page)) search_url = self.urls["search"].format( search_string, page) data = self.get_url(search_url, returns="text") if not data: logger.debug(_("No data returned from provider")) continue try: with BS4Parser(data, "html5lib") as html: table_header = html.find("tr", class_="bordo") torrent_table = table_header.find_parent( "table") if table_header else None if not torrent_table: logger.exception( "Could not find table of torrents") continue torrent_rows = torrent_table("tr") # Continue only if one Release is found if len(torrent_rows) < 6 or len( torrent_rows[2]("td")) == 1: logger.debug( "Data returned from provider does not contain any torrents" ) last_page = True continue if len(torrent_rows) < 45: last_page = True for result in torrent_rows[2:-3]: result_cols = result("td") if len(result_cols) == 1: # Ignore empty rows in the middle of the table continue try: title = result("td")[1].get_text( strip=True) torrent_size = result("td")[2].get_text( strip=True) info_hash = result("td")[3].find( "input", class_="downarrow")["value"].upper() download_url = self._magnet_from_result( info_hash, title) seeders = try_int( result("td")[5].get_text(strip=True)) leechers = try_int( result("td")[6].get_text(strip=True)) size = convert_size(torrent_size) or -1 except (AttributeError, IndexError, TypeError): continue filename_qt = self._reverseQuality( self._episodeQuality(result)) for text in self.hdtext: title1 = title title = title.replace(text, filename_qt) if title != title1: break if Quality.nameQuality( title) == Quality.UNKNOWN: title += filename_qt if not self._is_italian( title) and not self.subtitle: logger.debug( "Torrent is subtitled, skipping: {0}". format(title)) continue if self.engrelease and not self._is_english( title): logger.debug( "Torrent isn't english audio/subtitled, skipping: {0}" .format(title)) continue search_show = re.split(r"([Ss][\d{1,2}]+)", search_string)[0] show_title = search_show ep_params = "" rindex = re.search(r"([Ss][\d{1,2}]+)", title) if rindex: show_title = title[:rindex.start()] ep_params = title[rindex.start():] if show_title.lower() != search_show.lower( ) and search_show.lower() in show_title.lower( ): new_title = search_show + ep_params title = new_title if not all([title, download_url]): continue if self._is_season_pack(title): title = re.sub(r"([Ee][\d{1,2}\-?]+)", "", title) # Filter unseeded torrent if seeders < self.minseed or leechers < self.minleech: logger.debug( "Discarding torrent because it doesn't meet the minimum seeders or leechers: {0} (S:{1} L:{2})" .format(title, seeders, leechers)) continue item = { "title": title, "link": download_url, "size": size, "seeders": seeders, "leechers": leechers, "hash": info_hash } if mode != "RSS": logger.debug( _("Found result: {title} with {seeders} seeders and {leechers} leechers" .format(title=title, seeders=seeders, leechers=leechers))) items.append(item) except Exception as error: logger.exception( "Failed parsing provider. Error: {0}".format( error)) # For each search mode sort all the items by seeders if available items.sort(key=lambda d: try_int(d.get("seeders", 0)), reverse=True) results += items return results
def massEditSubmit( self, paused=None, default_ep_status=None, anime=None, sports=None, scene=None, season_folders=None, quality_preset=None, subtitles=None, air_by_date=None, anyQualities=None, bestQualities=None, toEdit=None, *args, **kwargs, ): dir_map = {} for cur_arg in [x for x in kwargs if x.startswith("orig_root_dir_")]: dir_map[kwargs[cur_arg]] = kwargs[cur_arg.replace( "orig_root_dir_", "new_root_dir_")] showIDs = toEdit.split("|") errors = [] for curShow in showIDs: curErrors = [] show_obj = Show.find(settings.showList, int(curShow or 0)) if not show_obj: continue cur_root_dir = self.__gooey_path(show_obj._location, "dirname") cur_show_dir = self.__gooey_path(show_obj._location, "basename") if cur_root_dir and dir_map.get( cur_root_dir ) and cur_root_dir != dir_map.get(cur_root_dir): new_show_dir = os.path.join(dir_map[cur_root_dir], cur_show_dir) logger.info("For show " + show_obj.name + " changing dir from " + show_obj._location + " to " + new_show_dir) else: new_show_dir = show_obj._location new_paused = ("off", "on")[(paused == "enable", show_obj.paused)[paused == "keep"]] new_default_ep_status = ( default_ep_status, show_obj.default_ep_status)[default_ep_status == "keep"] new_anime = ("off", "on")[(anime == "enable", show_obj.anime)[anime == "keep"]] new_sports = ("off", "on")[(sports == "enable", show_obj.sports)[sports == "keep"]] new_scene = ("off", "on")[(scene == "enable", show_obj.scene)[scene == "keep"]] new_air_by_date = ( "off", "on")[(air_by_date == "enable", show_obj.air_by_date)[air_by_date == "keep"]] new_season_folders = ("off", "on")[( season_folders == "enable", show_obj.season_folders)[season_folders == "keep"]] new_subtitles = ("off", "on")[(subtitles == "enable", show_obj.subtitles)[subtitles == "keep"]] if quality_preset == "keep": anyQualities, bestQualities = Quality.splitQuality( show_obj.quality) elif try_int(quality_preset, None): bestQualities = [] exceptions_list = [] curErrors += self.editShow( curShow, new_show_dir, anyQualities, bestQualities, exceptions_list, defaultEpStatus=new_default_ep_status, season_folders=new_season_folders, paused=new_paused, sports=new_sports, subtitles=new_subtitles, anime=new_anime, scene=new_scene, air_by_date=new_air_by_date, directCall=True, ) if curErrors: logger.exception("Errors: " + str(curErrors)) errors.append( "<b>{0}:</b>\n<ul>".format(show_obj.name) + " ".join( ["<li>{0}</li>".format(error) for error in curErrors]) + "</ul>") if errors: ui.notifications.error( _("{num_errors:d} error{plural} while saving changes:").format( num_errors=len(errors), plural="" if len(errors) == 1 else "s"), " ".join(errors)) return self.redirect("/manage/")
def showQualSnatched(show): return Quality.splitQuality(show.quality)[1]