def confirm(question, options, default="y"): """Takes a question (string), list of options and a default value (used when user simply hits enter). Asks until valid option is entered. """ # Highlight default option with [ ] options_str = [] for x in options: if x == default: x = "[%s]" % x if x != '': options_str.append(x) options_str = "/".join(options_str) while True: p(question) p("(%s) " % (options_str), end="") try: ans = raw_input().strip() except KeyboardInterrupt as errormsg: p("\n", errormsg) raise UserAbort(errormsg) if ans in options: return ans elif ans == '': return default
def populate_from_tvdb(self, tvdb_instance, force_name=None, series_id=None): # mypy: ignore # type: (tvdb_api.Tvdb, Optional[Any], Optional[Any]) -> None """Queries the tvdb_api.Tvdb instance for episode name and corrected series name. If series cannot be found, it will warn the user. If the episode is not found, it will use the corrected show name and not set an episode name. If the site is unreachable, it will warn the user. If the user aborts it will catch tvdb_api's user abort error and raise tvnamer's """ # FIXME: MOve this into each subclass - too much hasattr/isinstance try: if series_id is None: show = tvdb_instance[force_name or self.seriesname] else: series_id = int(series_id) tvdb_instance._getShowData(series_id, Config['language']) show = tvdb_instance[series_id] except tvdb_api.tvdb_error as errormsg: raise DataRetrievalError("Error with www.thetvdb.com: %s" % errormsg) except tvdb_api.tvdb_shownotfound: # No such series found. raise ShowNotFound("Show %s not found on www.thetvdb.com" % self.seriesname) except tvdb_api.tvdb_userabort as error: raise UserAbort("%s" % error) else: # Series was found, use corrected series name self.seriesname = _replace_output_series_name(show['seriesName']) if isinstance(self, DatedEpisodeInfo): # Date-based episode epnames = [] for cepno in self.episodenumbers: try: sr = show.aired_on(cepno) if len(sr) > 1: # filter out specials if multiple episodes aired on the day sr = [s for s in sr if s['seasonnumber'] != '0'] if len(sr) > 1: raise EpisodeNotFound( "Ambigious air date %s, there were %s episodes on that day" % (cepno, len(sr))) epnames.append(sr[0]['episodeName']) except tvdb_api.tvdb_episodenotfound: raise EpisodeNotFound( "Episode that aired on %s could not be found" % (cepno)) self.episodename = epnames # Optional[List[str]] return if not hasattr(self, "seasonnumber") or self.seasonnumber is None: # Series without concept of seasons have all episodes in season 1 seasonnumber = 1 else: seasonnumber = self.seasonnumber epnames = [] for cepno in self.episodenumbers: try: episodeinfo = show[seasonnumber][cepno] except tvdb_api.tvdb_seasonnotfound: raise SeasonNotFound( "Season %s of show %s could not be found" % (seasonnumber, self.seriesname)) except tvdb_api.tvdb_episodenotfound: # Try to search by absolute number sr = show.search(cepno, "absoluteNumber") if len(sr) > 1: # For multiple results try and make sure there is a direct match unsure = True for e in sr: if int(e['absoluteNumber']) == cepno: epnames.append(e['episodeName']) unsure = False # If unsure error out if unsure: raise EpisodeNotFound( "No episode actually matches %s, found %s results instead" % (cepno, len(sr))) elif len(sr) == 1: epnames.append(sr[0]['episodeName']) else: raise EpisodeNotFound( "Episode %s of show %s, season %s could not be found (also tried searching by absolute episode number)" % (cepno, self.seriesname, seasonnumber)) except tvdb_api.tvdb_attributenotfound: raise EpisodeNameNotFound( "Could not find episode name for %s" % cepno) else: epnames.append(episodeinfo['episodeName']) self.episodename = epnames
def processFile(tvdb_instance, episode): """Gets episode name, prompts user for input """ p("#" * 20) p("# Processing file: %s" % episode.fullfilename) if len(Config['input_filename_replacements']) > 0: replaced = applyCustomInputReplacements(episode.fullfilename) p("# With custom replacements: %s" % (replaced)) # Use force_name option. Done after input_filename_replacements so # it can be used to skip the replacements easily if Config['force_name'] is not None: episode.seriesname = Config['force_name'] p("# Detected series: %s (%s)" % (episode.seriesname, episode.number_string())) try: episode.populateFromTvdb(tvdb_instance, force_name=Config['force_name'], series_id=Config['series_id']) except (DataRetrievalError, ShowNotFound) as errormsg: if Config['always_rename'] and Config['skip_file_on_error'] is True: if Config['skip_behaviour'] == 'exit': warn("Exiting due to error: %s" % errormsg) raise SkipBehaviourAbort() warn("Skipping file due to error: %s" % errormsg) return else: warn(errormsg) except (SeasonNotFound, EpisodeNotFound, EpisodeNameNotFound) as errormsg: # Show was found, so use corrected series name if Config['always_rename'] and Config['skip_file_on_error']: if Config['skip_behaviour'] == 'exit': warn("Exiting due to error: %s" % errormsg) raise SkipBehaviourAbort() warn("Skipping file due to error: %s" % errormsg) return warn(errormsg) cnamer = Renamer(episode.fullpath) shouldRename = False if Config["move_files_only"]: newName = episode.fullfilename shouldRename = True else: newName = episode.generateFilename() if newName == episode.fullfilename: p("#" * 20) p("Existing filename is correct: %s" % episode.fullfilename) p("#" * 20) shouldRename = True else: p("#" * 20) p("Old filename: %s" % episode.fullfilename) if len(Config['output_filename_replacements']) > 0: # Show filename without replacements p("Before custom output replacements: %s" % (episode.generateFilename(preview_orig_filename=False))) p("New filename: %s" % newName) if Config['always_rename']: doRenameFile(cnamer, newName) if Config['move_files_enable']: if Config['move_files_destination_is_filepath']: doMoveFile(cnamer=cnamer, destFilepath=getMoveDestination(episode)) else: doMoveFile(cnamer=cnamer, destDir=getMoveDestination(episode)) return elif Config['dry_run']: p("%s will be renamed to %s" % (episode.fullfilename, newName)) if Config['move_files_enable']: p("%s will be moved to %s" % (newName, getMoveDestination(episode))) return ans = confirm("Rename?", options=['y', 'n', 'a', 'q'], default='y') if ans == "a": p("Always renaming") Config['always_rename'] = True shouldRename = True elif ans == "q": p("Quitting") raise UserAbort("User exited with q") elif ans == "y": p("Renaming") shouldRename = True elif ans == "n": p("Skipping") else: p("Invalid input, skipping") if shouldRename: doRenameFile(cnamer, newName) if shouldRename and Config['move_files_enable']: newPath = getMoveDestination(episode) if Config['dry_run']: p("%s will be moved to %s" % (newName, getMoveDestination(episode))) return if Config['move_files_destination_is_filepath']: doMoveFile(cnamer=cnamer, destFilepath=newPath, getPathPreview=True) else: doMoveFile(cnamer=cnamer, destDir=newPath, getPathPreview=True) if not Config['batch'] and Config['move_files_confirmation']: ans = confirm("Move file?", options=['y', 'n', 'q'], default='y') else: ans = 'y' if ans == 'y': p("Moving file") doMoveFile(cnamer, newPath) elif ans == 'q': p("Quitting") raise UserAbort("user exited with q")
def process_file(tvdb_instance, episode): # type: (tvdb_api.Tvdb, BaseInfo) -> None """Gets episode name, prompts user for input """ print("#" * 20) print("# Processing file: %s" % episode.fullfilename) if len(Config["input_filename_replacements"]) > 0: replaced = _apply_replacements_input(episode.fullfilename) print("# With custom replacements: %s" % (replaced)) # Use force_name option. Done after input_filename_replacements so # it can be used to skip the replacements easily if Config["force_name"] is not None: episode.seriesname = Config["force_name"] print("# Detected series: %s (%s)" % (episode.seriesname, episode.number_string())) try: episode.populate_from_tvdb( tvdb_instance, force_name=Config["force_name"], series_id=Config["series_id"], ) except (DataRetrievalError, ShowNotFound) as errormsg: if Config["always_rename"] and Config["skip_file_on_error"] is True: if Config["skip_behaviour"] == "exit": warn("Exiting due to error: %s" % errormsg) raise SkipBehaviourAbort() warn("Skipping file due to error: %s" % errormsg) return else: warn("%s" % (errormsg)) except (SeasonNotFound, EpisodeNotFound, EpisodeNameNotFound) as errormsg: # Show was found, so use corrected series name if Config["always_rename"] and Config["skip_file_on_error"]: if Config["skip_behaviour"] == "exit": warn("Exiting due to error: %s" % errormsg) raise SkipBehaviourAbort() warn("Skipping file due to error: %s" % errormsg) return warn("%s" % (errormsg)) cnamer = Renamer(episode.fullpath) should_rename = False if Config["move_files_only"]: new_name = episode.fullfilename should_rename = True else: new_name = episode.generate_filename() if new_name == episode.fullfilename: print("#" * 20) print("Existing filename is correct: %s" % episode.fullfilename) print("#" * 20) should_rename = True else: print("#" * 20) print("Old filename: %s" % episode.fullfilename) if len(Config["output_filename_replacements"]) > 0: # Show filename without replacements print("Before custom output replacements: %s" % (episode.generate_filename(preview_orig_filename=False))) print("New filename: %s" % new_name) if Config["dry_run"]: print("%s will be renamed to %s" % (episode.fullfilename, new_name)) if Config["move_files_enable"]: print("%s will be moved to %s" % (new_name, get_move_destination(episode))) return elif Config["always_rename"]: do_rename_file(cnamer, new_name) if Config["move_files_enable"]: if Config["move_files_destination_is_filepath"]: do_move_file( cnamer=cnamer, dest_filepath=get_move_destination(episode)) else: do_move_file(cnamer=cnamer, dest_dir=get_move_destination(episode)) return ans = confirm("Rename?", options=["y", "n", "a", "q"], default="y") if ans == "a": print("Always renaming") Config["always_rename"] = True should_rename = True elif ans == "q": print("Quitting") raise UserAbort("User exited with q") elif ans == "y": print("Renaming") should_rename = True elif ans == "n": print("Skipping") else: print("Invalid input, skipping") if should_rename: do_rename_file(cnamer, new_name) if should_rename and Config["move_files_enable"]: new_path = get_move_destination(episode) if Config["dry_run"]: print("%s will be moved to %s" % (new_name, get_move_destination(episode))) return if Config["move_files_destination_is_filepath"]: do_move_file(cnamer=cnamer, dest_filepath=new_path, get_path_preview=True) else: do_move_file(cnamer=cnamer, dest_dir=new_path, get_path_preview=True) if not Config["batch"] and Config["move_files_confirmation"]: ans = confirm("Move file?", options=["y", "n", "q"], default="y") else: ans = "y" if ans == "y": print("Moving file") do_move_file(cnamer, new_path) elif ans == "q": print("Quitting") raise UserAbort("user exited with q")