def get_subtitle_list(movie_title, source_language_code, destination_language_code): video = Video.fromname(movie_title) return list_subtitles( [video], { Language.fromalpha2(source_language_code), Language.fromalpha2(destination_language_code) }, FreePool)[video]
def save_on_disk(title, source_subtitle, destination_subtitle): try: os.makedirs('assets/subtitles/' + title) except: pass save_subtitles(Video.fromname(title), [source_subtitle, destination_subtitle], directory='assets/subtitles/' + title)
def find_video(name): """ Input: name of the file (movie or episode) Output: returns the video object corresponding to the name Throws: ValueError """ video = Video.fromname(name) return video
def filter_video_files(files): videos = [] for fil in files: _, ext = os.path.splitext(fil) if ext in [".jpeg", ".jpg", ".nfo", ".srt", ".sub", ".nbz"]: log.debug("Ignoring %s because of extension: %s", fil, ext) continue videos.append(Video.fromname(fil)) return videos
def find_subs(filename, movie_dir): video = Video.fromname(filename) subtitles = download_best_subtitles([video], {Language("eng")}) if len(subtitles[video]) == 0: print("No subs found") return best_sub = subtitles[video][0] save_subtitles(video, [best_sub], directory=movie_dir) return get_sub_path(movie_dir)
def _refine_file(video_file) -> List[SeriesEpisodeInfo]: log.debug("Refining file %s", video_file) try: video = Video.fromname(video_file) except ValueError: log.error("Cannot guess video file type from: %s", video_file) return [] refiner = sorted(refiner_manager.names()) refine(video, episode_refiners=refiner, movie_refiners=refiner) log.debug("refine result: %r", video) if isinstance(video, Episode): log.debug("series: %s", video.series) log.debug("season: %s", video.season) if not video.season: log.error("No season defined !") return [] if isinstance(video.season, list): video.season = video.season[0] log.error("Multi season found, only using first one: %s", video.season) log.debug("episode: %s", video.episode) log.debug("title: %s", video.title) log.debug("series_tvdb_id: %s", video.series_tvdb_id) r = [] # Support for double episode if not isinstance(video.episode, list): video.episode = [video.episode] for video_episode in video.episode: r.append( SeriesEpisodeInfo( series_episode_uid=SeriesEpisodeUid( tv_db_id=video.series_tvdb_id, season_number=video.season, episode_number=video_episode, ), series_title=video.series, episode_title=video.title, quality=None, video_languages=None, subtitles_languages=None, media_filename=video_file, dirty=True, )) return r elif isinstance(video, Movie): log.debug("movie: %s", video.title) return []
def downloadSub(myFile, lang, path, verbose): cli = False print "--- Trying to download..." origWD = os.getcwd() # current working directory os.chdir(path) # change working directory to where the videos are if cli == True: # old subliminal: #if call(["subliminal", "-q", "-l", lang, "--", myFile]): # try to download the subtitles # new subliminal if call(["subliminal", "download", "-l", lang, "--", myFile]): # try to download the subtitles print "*** Could not find %s subtitles" % langName(lang).lower() subDownloads = foundLang("%s - not found" % lang) else: print "--- Downloaded %s subtitles" % langName(lang).lower() subDownloads = foundLang(lang) # sending language code to be added # subName = "%s.%s.%s" % (os.path.splitext(myFile)[0], lang, "srt") else: video = Video.fromname(myFile) if verbose: print "--- Checking subtititles for \n %s" % video # configure the cache #region.configure('dogpile.cache.dbm', arguments={'filename': 'cachefile.dbm'}) my_region = region.configure('dogpile.cache.memory', arguments={'filename': 'cachefile.dbm'}, replace_existing_backend=True) if verbose: print "--- Searching for best subtitle..." #best_subtitles = download_best_subtitles([video], {lang}, providers=['podnapisi']) best_subtitles = download_best_subtitles([video], {lang}, providers=['podnapisi', 'opensubtitles', 'addic7ed']) #best_subtitles = download_best_subtitles([video], {lang}) try: best_subtitle = best_subtitles[video][0] except: print "*** Could not find %s subtitles" % langName(lang).lower() subDownloads = foundLang("%s - not found" % lang) else: print "--- Downloaded %s subtitles" % langName(lang).lower() #if verbose: # print "--- Score for this subtitle is %s" % compute_score(best_subtitle, video) subDownloads = foundLang(lang) # sending language code to be added if verbose: print "--- Saving subtitles..." save_subtitles(video, [best_subtitle]) os.chdir(origWD) # change working directory back return subDownloads
def scanme(paths, config): logging.info('scanme: %s' % paths) # configure cache region.configure('dogpile.cache.dbm', expiration_time=timedelta(days=30), # @UndefinedVariable arguments={'filename': cachefile_file, 'lock_factory': MutexLock}) # scan videos #videos = scan_videos([p for p in paths if os.path.exists(p)],# subtitles=True, # embedded_subtitles=True, age=timedelta(days=config.get('Task').as_int('age'))) videos = scan_videos(paths, age=timedelta(days=config.get('Task').as_int('age'))) logging.info(videos) # guess videos videos.extend([Video.fromname(p) for p in paths if not os.path.exists(p)]) logging.info(videos) # download best subtitles languageset=set(Language(language) for language in config['General']['languages']) single=True if not config.get('General').as_bool('single') or len(languageset) > 1: single=False hearing_impaired=False if config.get('General').as_bool('hearing_impaired'): hearing_impaired=True providers = config['General']['providers'] rv = {} for provider in providers: sec = config.get(provider) if sec != None: rv[provider] = {k: v for k, v in config.get(provider).items()} subtitles = download_best_subtitles(videos, languageset, min_score=config.get('General').as_int('min_score'), providers=providers, provider_configs=rv, hearing_impaired=hearing_impaired, only_one=single) # save them to disk, next to the video for v in videos: save_subtitles(v, subtitles[v]) logging.debug(subtitles) logging.info('Scan Done!') return subtitles
def get_subtitles(video_path, ov_subtitles): """ # get subtitles and convert them to web vtt Args: video_path: absolute path to videos ov_subtitles: boolean (True if input has subtitles, False if not) return: empty string if no subtitles was found. Otherwise return dict of subtitle absolute location with str(Language) as key """ languages_to_retrieve = { 'eng', 'fra', } webvtt_fullpath = {} webvtt_ov_fullpath = '' if ov_subtitles: try: webvtt_ov_fullpath = os.path.splitext(video_path)[0] + '_ov.vtt' extract_subtitle(video_path, webvtt_ov_fullpath) except: webvtt_ov_fullpath = '' try: video = Video.fromname(video_path) except ValueError: #This usually happens when there is not enough data for subliminal to guess webvtt_fullpath['ov'] = webvtt_ov_fullpath for lang in languages_to_retrieve: webvtt_fullpath[lang] = '' return webvtt_fullpath try: webvtt_fullpath = handle_subliminal_download(video, video_path, languages_to_retrieve) except: webvtt_fullpath = {} webvtt_fullpath['ov'] = webvtt_ov_fullpath for lang in languages_to_retrieve: if lang not in webvtt_fullpath: webvtt_fullpath[lang] = '' return (webvtt_fullpath)
def main(): """ Main function """ file_path, download_subs, show_omdb_info = get_args() video_file = Video.fromname(file_path) # Display the short info short_info = short_details(video_file) print(short_info) # If -i flag is set show the info from the omdb api if show_omdb_info is True: spinner = Spinner() spinner.start() omdb_details(video_file.title, video_file.year) spinner.stop() # If -d flag is set download the best matching subtitles if download_subs is True: spinner = Spinner() spinner.start() # Set the cache region.configure('dogpile.cache.dbm', arguments={'filename': 'cachefile.dbm'}) # Download subtitles in serbian and english best_subtitles = download_best_subtitles([video_file], {Language('eng'),\ Language('srp')}, providers=None) spinner.stop() print(best_subtitles[video_file]) best_subtitle_sr = best_subtitles[video_file][0] best_subtitle_en = best_subtitles[video_file][1] # Save the 2 subtitle files save_subtitles(video_file, [best_subtitle_sr]) save_subtitles(video_file, [best_subtitle_en])
def manual_search(path, language, hi, providers, providers_auth, sceneName, media_type): logging.debug('BAZARR Manually searching subtitles for this file: ' + path) subtitles_dict = {} if hi == "True": hi = True else: hi = False language_set = set() for lang in ast.literal_eval(language): lang = alpha3_from_alpha2(lang) if lang == 'pob': language_set.add(Language('por', 'BR')) else: language_set.add(Language(lang)) use_scenename = settings.general.getboolean('use_scenename') use_postprocessing = settings.general.getboolean('use_postprocessing') postprocessing_cmd = settings.general.postprocessing_cmd try: if sceneName == "None" or use_scenename is False: used_sceneName = False video = scan_video(path) else: used_sceneName = True video = Video.fromname(sceneName) except: logging.exception("BAZARR Error trying to get video information for this file: " + path) else: if media_type == "movie": max_score = 120.0 elif media_type == "series": max_score = 360.0 try: with AsyncProviderPool(max_workers=None, providers=providers, provider_configs=providers_auth) as p: subtitles = p.list_subtitles(video, language_set) except Exception as e: logging.exception("BAZARR Error trying to get subtitle list from provider for this file: " + path) else: subtitles_list = [] for s in subtitles: {s: compute_score(s, video, hearing_impaired=hi)} if media_type == "movie": matched = set(s.get_matches(video)) if hi == s.hearing_impaired: matched.add('hearing_impaired') not_matched = set(score.movie_scores.keys()) - matched required = set(['title']) if any(elem in required for elem in not_matched): continue if used_sceneName: not_matched.remove('hash') if type(s) is LegendasTVSubtitle: # The pickle doesn't work very well with RAR (rarfile.RarFile) or ZIP (zipfile.ZipFile) s.archive.content = None elif media_type == "series": matched = set(s.get_matches(video)) if hi == s.hearing_impaired: matched.add('hearing_impaired') not_matched = set(score.episode_scores.keys()) - matched required = set(['series', 'season', 'episode']) if any(elem in required for elem in not_matched): continue if used_sceneName: not_matched.remove('hash') if type(s) is LegendasTVSubtitle: # The pickle doesn't work very well with RAR (rarfile.RarFile) or ZIP (zipfile.ZipFile) s.archive.content = None subtitles_list.append(dict(score=round((compute_score(s, video, hearing_impaired=hi) / max_score * 100), 2), language=alpha2_from_alpha3(s.language.alpha3), hearing_impaired=str(s.hearing_impaired), provider=s.provider_name, subtitle=codecs.encode(pickle.dumps(s), "base64").decode(), url=s.page_link, matches=list(matched), dont_matches=list(not_matched))) subtitles_dict = sorted(subtitles_list, key=lambda x: x['score'], reverse=True) logging.debug('BAZARR ' + str(len(subtitles_dict)) + " subtitles have been found for this file: " + path) logging.debug('BAZARR Ended searching subtitles for this file: ' + path) return(subtitles_dict)
def download(directory, language='eng', country=None, verbose=False): """ Takes in a directory path, walks through the file tree, and downloads subtitles for any video files found. Renames the subtitle file to match the video's name (in order to make it compatible with Roku Media Player.) :param (str) directory: Directory where video files or folders are located. :param (str) language : [optional] Desired language for subtitles, expressed as a 3-letter ISO-639-3 code. Visit https://bit.ly/29fjNpm for a list of language codes. :param (str) country : [optional] Country code (needed in addition to language code for certain languages). :param (bool) verbose : [optional] Prints more output to the console. Examples: 1. download_subtitles('/Users/Laura/Movies') 2. download_subtitles(directory='./Users/Tim/TV Shows', language='spa', verbose=True) """ successful = 0 videos = [] incompatible = [] # Walk the file path, identifying video files that do not have a matching .srt (subtitle) file in the same folder. # Then collect the file and directory names within list 'videos' that we will iterate over later. directory = os.path.abspath(directory) if not os.path.isdir(directory): print( f'>>> Error: "{directory}" is not a valid directory. Please try again.' ) return False print('>>> Walking the file tree...') for subdir, dirs, files in os.walk(directory): if verbose: print(subdir) for file in files: if file.endswith((".mp4", ".avi", ".mkv", ".part")) and not file.startswith('.'): movie_title = re.split('.mp4|.avi|.mkv', file)[0] if file.endswith('.avi'): incompatible.append(movie_title) if os.path.isfile( str(os.path.join(subdir, movie_title) + '.srt')): continue else: videos.append([movie_title, file, subdir]) # check to see if video files found if not videos: print('No movie files without subtitles found in folder.') return False # iterate over 'videos' and download subtitles with click.progressbar(videos, label='Searching for subtitles') as bar: for movie_title, file, subdir in bar: try: video = Video.fromname(file) except ValueError: if verbose: print(f'Could not find a match for file: {file}') continue try: if country is not None: lang = Language(language, country) else: lang = Language(language) os.chdir(subdir) best_subtitles = download_best_subtitles( [video], {Language(language)}, providers=['opensubtitles', 'thesubdb', 'tvsubtitles']) best_subtitle = best_subtitles[video][0] save_subtitles(video, [best_subtitle]) if verbose: print(f'Successfully downloaded subtitle for: {file}') except IndexError: if verbose: print(f'Subtitles not found online for: {file}') continue successful += 1 if language == 'eng': try: old_name = str(os.path.join(subdir, movie_title)) + '.en.srt' new_name = str(os.path.join(subdir, movie_title)) + '.srt' if verbose: print(f'Old name: {old_name}') print(f'New name: {new_name}') print( f'Path : {str(os.path.join(subdir, movie_title))}' ) os.rename(old_name, new_name) except FileNotFoundError: if verbose: print( f"Couldn't rename subtitle file for: {movie_title}." ) continue print() print(f'>>> Finished!') print( f'>>> Fetched {successful} / {len(videos)} subtitle files successfully.' ) if verbose: print('Movies incompatible with Roku:') pprint(incompatible) return
def manual_download_subtitle(path, language, hi, subtitle, provider, providers_auth, sceneName, media_type): logging.debug('BAZARR Manually downloading subtitles for this file: ' + path) if hi == "True": hi = True else: hi = False subtitle = pickle.loads(codecs.decode(subtitle.encode(), "base64")) if media_type == 'series': type_of_score = 360 elif media_type == 'movie': type_of_score = 120 use_scenename = settings.general.getboolean('use_scenename') use_postprocessing = settings.general.getboolean('use_postprocessing') postprocessing_cmd = settings.general.postprocessing_cmd language = alpha3_from_alpha2(language) if language == 'pob': lang_obj = Language('por', 'BR') else: lang_obj = Language(language) try: if sceneName is None or use_scenename is False: used_sceneName = False video = scan_video(path) else: used_sceneName = True video = Video.fromname(sceneName) except Exception as e: logging.exception("BAZARR Error trying to get video information for this file: " + path) pass else: try: download_subtitles([subtitle], providers=provider, provider_configs=providers_auth) logging.debug('BAZARR Subtitles file downloaded for this file:' + path) except Exception as e: logging.exception('BAZARR Error downloading subtitles for this file ' + path) return None else: single = settings.general.getboolean('single_language') try: score = round(float(compute_score(subtitle, video, hearing_impaired=hi)) / type_of_score * 100, 2) if used_sceneName == True: video = scan_video(path) if single is True: result = save_subtitles(video, [subtitle], single=True, encoding='utf-8') else: result = save_subtitles(video, [subtitle], encoding='utf-8') except Exception as e: logging.exception('BAZARR Error saving subtitles file to disk for this file:' + path) return None else: if len(result) > 0: downloaded_provider = result[0].provider_name downloaded_language = language_from_alpha3(result[0].language.alpha3) downloaded_language_code2 = alpha2_from_alpha3(result[0].language.alpha3) downloaded_language_code3 = result[0].language.alpha3 downloaded_path = get_subtitle_path(path, downloaded_language_code2) logging.debug('BAZARR Subtitles file saved to disk: ' + downloaded_path) message = downloaded_language + " subtitles downloaded from " + downloaded_provider + " with a score of " + unicode(score) + "% using manual search." if use_postprocessing is True: command = pp_replace(postprocessing_cmd, path, downloaded_path, downloaded_language, downloaded_language_code2, downloaded_language_code3) try: if os.name == 'nt': codepage = subprocess.Popen("chcp", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # wait for the process to terminate out_codepage, err_codepage = codepage.communicate() encoding = out_codepage.split(':')[-1].strip() process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # wait for the process to terminate out, err = process.communicate() if os.name == 'nt': out = out.decode(encoding) except: if out == "": logging.error('BAZARR Post-processing result for file ' + path + ' : Nothing returned from command execution') else: logging.error('BAZARR Post-processing result for file ' + path + ' : ' + out) else: if out == "": logging.info('BAZARR Post-processing result for file ' + path + ' : Nothing returned from command execution') else: logging.info('BAZARR Post-processing result for file ' + path + ' : ' + out) return message else: logging.error("BAZARR Tried to manually download a subtitles for file: " + path + " but we weren't able to do (probably throttled by " + str(subtitle.provider_name) + ". Please retry later or select a subtitles from another provider.") return None logging.debug('BAZARR Ended manually downloading subtitles for file: ' + path)
def download( obj, provider, language, age, directory, encoding, single, force, hearing_impaired, min_score, max_workers, verbose, path, ): """Download best subtitles. PATH can be an directory containing videos, a video file path or a video file name. It can be used multiple times. If an existing subtitle is detected (external or embedded) in the correct language, the download is skipped for the associated video. """ # process parameters language = set(language) # scan videos videos = [] ignored_videos = [] errored_paths = [] with click.progressbar(path, label="Collecting videos", item_show_func=lambda p: p or "") as bar: for p in bar: logger.debug("Collecting path %s", p) # non-existing if not os.path.exists(p): try: video = Video.fromname(p) except: logger.exception("Unexpected error while collecting non-existing path %s", p) errored_paths.append(p) continue videos.append(video) continue # directories if os.path.isdir(p): try: scanned_videos = scan_videos( p, subtitles=not force, embedded_subtitles=not force, subtitles_dir=directory, age=age ) except: logger.exception("Unexpected error while collecting directory path %s", p) errored_paths.append(p) continue for video in scanned_videos: if check_video(video, languages=language, age=age, undefined=single): videos.append(video) else: ignored_videos.append(video) continue # other inputs try: video = scan_video(p, subtitles=not force, embedded_subtitles=not force, subtitles_dir=directory) except: logger.exception("Unexpected error while collecting path %s", p) errored_paths.append(p) continue if check_video(video, languages=language, age=age, undefined=single): videos.append(video) else: ignored_videos.append(video) # output errored paths if verbose > 0: for p in errored_paths: click.secho("%s errored" % p, fg="red") # output ignored videos if verbose > 1: for video in ignored_videos: click.secho( "%s ignored - subtitles: %s / age: %d day%s" % ( os.path.split(video.name)[1], ", ".join(str(s) for s in video.subtitle_languages) or "none", video.age.days, "s" if video.age.days > 1 else "", ), fg="yellow", ) # report collected videos click.echo( "%s video%s collected / %s video%s ignored / %s error%s" % ( click.style(str(len(videos)), bold=True, fg="green" if videos else None), "s" if len(videos) > 1 else "", click.style(str(len(ignored_videos)), bold=True, fg="yellow" if ignored_videos else None), "s" if len(ignored_videos) > 1 else "", click.style(str(len(errored_paths)), bold=True, fg="red" if errored_paths else None), "s" if len(errored_paths) > 1 else "", ) ) # exit if no video collected if not videos: return # download best subtitles downloaded_subtitles = defaultdict(list) with AsyncProviderPool(max_workers=max_workers, providers=provider, provider_configs=obj["provider_configs"]) as p: with click.progressbar( videos, label="Downloading subtitles", item_show_func=lambda v: os.path.split(v.name)[1] if v is not None else "", ) as bar: for v in bar: scores = get_scores(v) subtitles = p.download_best_subtitles( p.list_subtitles(v, language - v.subtitle_languages), v, language, min_score=scores["hash"] * min_score / 100, hearing_impaired=hearing_impaired, only_one=single, ) downloaded_subtitles[v] = subtitles # TODO: warn about discarded providers # save subtitles total_subtitles = 0 for v, subtitles in downloaded_subtitles.items(): saved_subtitles = save_subtitles(v, subtitles, single=single, directory=directory, encoding=encoding) total_subtitles += len(saved_subtitles) if verbose > 0: click.echo( "%s subtitle%s downloaded for %s" % ( click.style(str(len(saved_subtitles)), bold=True), "s" if len(saved_subtitles) > 1 else "", os.path.split(v.name)[1], ) ) if verbose > 1: for s in saved_subtitles: matches = s.get_matches(v) score = compute_score(s, v) # score color score_color = None scores = get_scores(v) if isinstance(v, Movie): if score < scores["title"]: score_color = "red" elif score < scores["title"] + scores["year"] + scores["release_group"]: score_color = "yellow" else: score_color = "green" elif isinstance(v, Episode): if score < scores["series"] + scores["season"] + scores["episode"]: score_color = "red" elif score < scores["series"] + scores["season"] + scores["episode"] + scores["release_group"]: score_color = "yellow" else: score_color = "green" # scale score from 0 to 100 taking out preferences scaled_score = score if s.hearing_impaired == hearing_impaired: scaled_score -= scores["hearing_impaired"] scaled_score *= 100 / scores["hash"] # echo some nice colored output click.echo( " - [{score}] {language} subtitle from {provider_name} (match on {matches})".format( score=click.style("{:5.1f}".format(scaled_score), fg=score_color, bold=score >= scores["hash"]), language=s.language.name if s.language.country is None else "%s (%s)" % (s.language.name, s.language.country.name), provider_name=s.provider_name, matches=", ".join(sorted(matches, key=scores.get, reverse=True)), ) ) if verbose == 0: click.echo( "Downloaded %s subtitle%s" % (click.style(str(total_subtitles), bold=True), "s" if total_subtitles > 1 else "") )
def download(obj, provider, refiner, language, age, directory, encoding, single, force, hearing_impaired, min_score, max_workers, archives, verbose, path): """Download best subtitles. PATH can be an directory containing videos, a video file path or a video file name. It can be used multiple times. If an existing subtitle is detected (external or embedded) in the correct language, the download is skipped for the associated video. """ # process parameters language = set(language) # scan videos videos = [] ignored_videos = [] errored_paths = [] with click.progressbar(path, label='Collecting videos', item_show_func=lambda p: p or '') as bar: for p in bar: logger.debug('Collecting path %s', p) # non-existing if not os.path.exists(p): try: video = Video.fromname(p) except: logger.exception( 'Unexpected error while collecting non-existing path %s', p) errored_paths.append(p) continue if not force: video.subtitle_languages |= set( search_external_subtitles( video.name, directory=directory).values()) refine(video, episode_refiners=refiner, movie_refiners=refiner, embedded_subtitles=not force) videos.append(video) continue # directories if os.path.isdir(p): try: scanned_videos = scan_videos(p, age=age, archives=archives) except: logger.exception( 'Unexpected error while collecting directory path %s', p) errored_paths.append(p) continue for video in scanned_videos: if check_video(video, languages=language, age=age, undefined=single): if not force: video.subtitle_languages |= set( search_external_subtitles( video.name, directory=directory).values()) refine(video, episode_refiners=refiner, movie_refiners=refiner, embedded_subtitles=not force) videos.append(video) else: ignored_videos.append(video) continue # other inputs try: video = scan_video(p) except: logger.exception('Unexpected error while collecting path %s', p) errored_paths.append(p) continue if check_video(video, languages=language, age=age, undefined=single): if not force: video.subtitle_languages |= set( search_external_subtitles( video.name, directory=directory).values()) refine(video, episode_refiners=refiner, movie_refiners=refiner, embedded_subtitles=not force) videos.append(video) else: ignored_videos.append(video) # output errored paths if verbose > 0: for p in errored_paths: click.secho('%s errored' % p, fg='red') # output ignored videos if verbose > 1: for video in ignored_videos: click.secho( '%s ignored - subtitles: %s / age: %d day%s' % (os.path.split(video.name)[1], ', '.join(str(s) for s in video.subtitle_languages) or 'none', video.age.days, 's' if video.age.days > 1 else ''), fg='yellow') # report collected videos click.echo('%s video%s collected / %s video%s ignored / %s error%s' % ( click.style( str(len(videos)), bold=True, fg='green' if videos else None), 's' if len(videos) > 1 else '', click.style(str(len(ignored_videos)), bold=True, fg='yellow' if ignored_videos else None), 's' if len(ignored_videos) > 1 else '', click.style(str(len(errored_paths)), bold=True, fg='red' if errored_paths else None), 's' if len(errored_paths) > 1 else '', )) # exit if no video collected if not videos: return # download best subtitles downloaded_subtitles = defaultdict(list) with AsyncProviderPool(max_workers=max_workers, providers=provider, provider_configs=obj['provider_configs']) as p: with click.progressbar( videos, label='Downloading subtitles', item_show_func=lambda v: os.path.split(v.name)[1] if v is not None else '') as bar: for v in bar: scores = get_scores(v) subtitles = p.download_best_subtitles( p.list_subtitles(v, language - v.subtitle_languages), v, language, min_score=scores['hash'] * min_score / 100, hearing_impaired=hearing_impaired, only_one=single) downloaded_subtitles[v] = subtitles if p.discarded_providers: click.secho( 'Some providers have been discarded due to unexpected errors: %s' % ', '.join(p.discarded_providers), fg='yellow') # save subtitles total_subtitles = 0 for v, subtitles in downloaded_subtitles.items(): saved_subtitles = save_subtitles(v, subtitles, single=single, directory=directory, encoding=encoding) total_subtitles += len(saved_subtitles) if verbose > 0: click.echo( '%s subtitle%s downloaded for %s' % (click.style(str(len(saved_subtitles)), bold=True), 's' if len(saved_subtitles) > 1 else '', os.path.split(v.name)[1])) if verbose > 1: for s in saved_subtitles: matches = s.get_matches(v) score = compute_score(s, v) # score color score_color = None scores = get_scores(v) if isinstance(v, Movie): if score < scores['title']: score_color = 'red' elif score < scores['title'] + scores['year'] + scores[ 'release_group']: score_color = 'yellow' else: score_color = 'green' elif isinstance(v, Episode): if score < scores['series'] + scores['season'] + scores[ 'episode']: score_color = 'red' elif score < scores['series'] + scores['season'] + scores[ 'episode'] + scores['release_group']: score_color = 'yellow' else: score_color = 'green' # scale score from 0 to 100 taking out preferences scaled_score = score if s.hearing_impaired == hearing_impaired: scaled_score -= scores['hearing_impaired'] scaled_score *= 100 / scores['hash'] # echo some nice colored output click.echo( ' - [{score}] {language} subtitle from {provider_name} (match on {matches})' .format(score=click.style('{:5.1f}'.format(scaled_score), fg=score_color, bold=score >= scores['hash']), language=s.language.name if s.language.country is None else '%s (%s)' % (s.language.name, s.language.country.name), provider_name=s.provider_name, matches=', '.join( sorted(matches, key=scores.get, reverse=True)))) if verbose == 0: click.echo('Downloaded %s subtitle%s' % (click.style(str(total_subtitles), bold=True), 's' if total_subtitles > 1 else ''))
def download(obj, provider, language, age, directory, encoding, single, force, hearing_impaired, min_score, verbose, path): """Download best subtitles. PATH can be an directory containing videos, a video file path or a video file name. It can be used multiple times. If an existing subtitle is detected (external or embedded) in the correct language, the download is skipped for the associated video. """ # process parameters language = set(language) # scan videos videos = [] ignored_videos = [] errored_paths = [] with click.progressbar(path, label='Collecting videos', item_show_func=lambda p: p or '') as bar: for p in bar: logger.debug('Collecting path %s', p) # non-existing if not os.path.exists(p): try: video = Video.fromname(p) except: logger.exception('Unexpected error while collecting non-existing path %s', p) errored_paths.append(p) continue videos.append(video) continue # directories if os.path.isdir(p): try: scanned_videos = scan_videos(p, subtitles=not force, embedded_subtitles=not force, subtitles_dir=directory) except: logger.exception('Unexpected error while collecting directory path %s', p) errored_paths.append(p) continue for video in scanned_videos: if check_video(video, languages=language, age=age, undefined=single): videos.append(video) else: ignored_videos.append(video) continue # other inputs try: video = scan_video(p, subtitles=not force, embedded_subtitles=not force, subtitles_dir=directory) except: logger.exception('Unexpected error while collecting path %s', p) errored_paths.append(p) continue if check_video(video, languages=language, age=age, undefined=single): videos.append(video) else: ignored_videos.append(video) # output errored paths if verbose > 0: for p in errored_paths: click.secho('%s errored' % p, fg='red') # output ignored videos if verbose > 1: for video in ignored_videos: click.secho('%s ignored - subtitles: %s / age: %d day%s' % ( os.path.split(video.name)[1], ', '.join(str(s) for s in video.subtitle_languages) or 'none', video.age.days, 's' if video.age.days > 1 else '' ), fg='yellow') # report collected videos click.echo('%s video%s collected / %s video%s ignored / %s error%s' % ( click.style(str(len(videos)), bold=True, fg='green' if videos else None), 's' if len(videos) > 1 else '', click.style(str(len(ignored_videos)), bold=True, fg='yellow' if ignored_videos else None), 's' if len(ignored_videos) > 1 else '', click.style(str(len(errored_paths)), bold=True, fg='red' if errored_paths else None), 's' if len(errored_paths) > 1 else '', )) # exit if no video collected if not videos: return # download best subtitles downloaded_subtitles = defaultdict(list) with ProviderPool(providers=provider, provider_configs=obj['provider_configs']) as pool: with click.progressbar(videos, label='Downloading subtitles', item_show_func=lambda v: os.path.split(v.name)[1] if v is not None else '') as bar: for v in bar: subtitles = pool.download_best_subtitles(pool.list_subtitles(v, language - v.subtitle_languages), v, language, min_score=v.scores['hash'] * min_score / 100, hearing_impaired=hearing_impaired, only_one=single) downloaded_subtitles[v] = subtitles # save subtitles total_subtitles = 0 for v, subtitles in downloaded_subtitles.items(): saved_subtitles = save_subtitles(v, subtitles, single=single, directory=directory, encoding=encoding) total_subtitles += len(saved_subtitles) if verbose > 0: click.echo('%s subtitle%s downloaded for %s' % (click.style(str(len(saved_subtitles)), bold=True), 's' if len(saved_subtitles) > 1 else '', os.path.split(v.name)[1])) if verbose > 1: for s in saved_subtitles: matches = s.get_matches(v, hearing_impaired=hearing_impaired) score = compute_score(matches, v) # score color score_color = None if isinstance(v, Movie): if score < v.scores['title']: score_color = 'red' elif score < v.scores['title'] + v.scores['year'] + v.scores['release_group']: score_color = 'yellow' else: score_color = 'green' elif isinstance(v, Episode): if score < v.scores['series'] + v.scores['season'] + v.scores['episode']: score_color = 'red' elif score < (v.scores['series'] + v.scores['season'] + v.scores['episode'] + v.scores['release_group']): score_color = 'yellow' else: score_color = 'green' # scale score from 0 to 100 taking out preferences scaled_score = score if s.hearing_impaired == hearing_impaired: scaled_score -= v.scores['hearing_impaired'] scaled_score *= 100 / v.scores['hash'] # echo some nice colored output click.echo(' - [{score}] {language} subtitle from {provider_name} (match on {matches})'.format( score=click.style('{:5.1f}'.format(scaled_score), fg=score_color, bold=score >= v.scores['hash']), language=s.language.name if s.language.country is None else '%s (%s)' % (s.language.name, s.language.country.name), provider_name=s.provider_name, matches=', '.join(sorted(matches, key=v.scores.get, reverse=True)) )) if verbose == 0: click.echo('Downloaded %s subtitle%s' % (click.style(str(total_subtitles), bold=True), 's' if total_subtitles > 1 else ''))
def download_subtitle(path, language, hi, providers, providers_auth, sceneName, media_type): logging.debug('BAZARR Searching subtitles for this file: ' + path) if hi == "True": hi = True else: hi = False language_set = set() if language == 'pob': language_set.add(Language('por', 'BR')) else: language_set.add(Language(language)) use_scenename = settings.general.getboolean('use_scenename') minimum_score = settings.general.minimum_score minimum_score_movie = settings.general.minimum_score_movie use_postprocessing = settings.general.getboolean('use_postprocessing') postprocessing_cmd = settings.general.postprocessing_cmd try: if sceneName == "None" or use_scenename is False: used_sceneName = False video = scan_video(path) else: used_sceneName = True video = Video.fromname(sceneName) except Exception as e: logging.exception("BAZARR Error trying to get video information for this file: " + path) pass else: if media_type == "movie": max_score = 120.0 elif media_type == "series": max_score = 360.0 try: with AsyncProviderPool(max_workers=None, providers=providers, provider_configs=providers_auth) as p: subtitles = p.list_subtitles(video, language_set) except Exception as e: logging.exception("BAZARR Error trying to get subtitle list from provider for this file: " + path) else: subtitles_list = [] try: sorted_subtitles = sorted([(s, compute_score(s, video, hearing_impaired=hi)) for s in subtitles], key=operator.itemgetter(1), reverse=True) except Exception as e: logging.exception('BAZARR Exception raised while trying to compute score for this file: ' + path) return None else: for s, preliminary_score in sorted_subtitles: if media_type == "movie": if (preliminary_score / max_score * 100) < int(minimum_score_movie): continue matched = set(s.get_matches(video)) if hi == s.hearing_impaired: matched.add('hearing_impaired') not_matched = set(score.movie_scores.keys()) - matched required = set(['title']) if any(elem in required for elem in not_matched): continue elif media_type == "series": if (preliminary_score / max_score * 100) < int(minimum_score): continue matched = set(s.get_matches(video)) if hi == s.hearing_impaired: matched.add('hearing_impaired') not_matched = set(score.episode_scores.keys()) - matched required = set(['series', 'season', 'episode']) if any(elem in required for elem in not_matched): continue subtitles_list.append(s) logging.debug('BAZARR ' + str(len(subtitles_list)) + " subtitles have been found for this file: " + path) if len(subtitles_list) > 0: try: download_result = False for subtitle in subtitles_list: download_result = p.download_subtitle(subtitle) if download_result: logging.debug('BAZARR Subtitles file downloaded from ' + str(subtitle.provider_name) + ' for this file: ' + path) break else: logging.warning('BAZARR Subtitles file skipped from ' + str(subtitle.provider_name) + ' for this file: ' + path + ' because no content was returned by the provider (probably throttled).') continue if not download_result: logging.error('BAZARR Tried to download a subtitles for file: ' + path + " but we weren't able to do it this time (probably being throttled). Going to retry on next search.") return None except Exception as e: logging.exception('BAZARR Error downloading subtitles for this file ' + path) return None else: try: calculated_score = round(float(compute_score(subtitle, video, hearing_impaired=hi)) / max_score * 100, 2) if used_sceneName: video = scan_video(path) single = settings.general.getboolean('single_language') if single is True: result = save_subtitles(video, [subtitle], single=True, encoding='utf-8') else: result = save_subtitles(video, [subtitle], encoding='utf-8') except Exception as e: logging.exception('BAZARR Error saving subtitles file to disk for this file:' + path) pass else: downloaded_provider = result[0].provider_name downloaded_language = language_from_alpha3(result[0].language.alpha3) downloaded_language_code2 = alpha2_from_alpha3(result[0].language.alpha3) downloaded_language_code3 = result[0].language.alpha3 downloaded_path = get_subtitle_path(path, downloaded_language_code2) logging.debug('BAZARR Subtitles file saved to disk: ' + downloaded_path) if used_sceneName: message = downloaded_language + " subtitles downloaded from " + downloaded_provider + " with a score of " + unicode(calculated_score) + "% using this scene name: " + sceneName else: message = downloaded_language + " subtitles downloaded from " + downloaded_provider + " with a score of " + unicode(calculated_score) + "% using filename guessing." if use_postprocessing is True: command = pp_replace(postprocessing_cmd, path, downloaded_path, downloaded_language, downloaded_language_code2, downloaded_language_code3) try: if os.name == 'nt': codepage = subprocess.Popen("chcp", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # wait for the process to terminate out_codepage, err_codepage = codepage.communicate() encoding = out_codepage.split(':')[-1].strip() process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # wait for the process to terminate out, err = process.communicate() if os.name == 'nt': out = out.decode(encoding) except: if out == "": logging.error('BAZARR Post-processing result for file ' + path + ' : Nothing returned from command execution') else: logging.error('BAZARR Post-processing result for file ' + path + ' : ' + out) else: if out == "": logging.info('BAZARR Post-processing result for file ' + path + ' : Nothing returned from command execution') else: logging.info('BAZARR Post-processing result for file ' + path + ' : ' + out) return message else: logging.debug('BAZARR No subtitles were found for this file: ' + path) return None logging.debug('BAZARR Ended searching subtitles for file: ' + path)
def subliminal(): parser = argparse.ArgumentParser( prog='subliminal', description='Subtitles, faster than your thoughts', epilog='Suggestions and bug reports are greatly appreciated: ' 'https://github.com/Diaoul/subliminal/issues', add_help=False) # required arguments required_arguments_group = parser.add_argument_group('required arguments') required_arguments_group.add_argument('paths', nargs='+', metavar='PATH', help='path to video file or folder') required_arguments_group.add_argument( '-l', '--languages', nargs='+', required=True, metavar='LANGUAGE', help='wanted languages as IETF codes e.g. fr, pt-BR, sr-Cyrl ') # configuration configuration_group = parser.add_argument_group('configuration') configuration_group.add_argument( '-s', '--single', action='store_true', help= 'download without language code in subtitle\'s filename i.e. .srt only' ) configuration_group.add_argument('-c', '--cache-file', default=DEFAULT_CACHE_FILE, help='cache file (default: %(default)s)') # filtering filtering_group = parser.add_argument_group('filtering') filtering_group.add_argument( '-p', '--providers', nargs='+', metavar='PROVIDER', help='providers to use (%s)' % ', '.join(provider_manager.available_providers)) filtering_group.add_argument( '-m', '--min-score', type=int, default=0, help='minimum score for subtitles (0-%d for episodes, 0-%d for movies)' % (Episode.scores['hash'], Movie.scores['hash'])) filtering_group.add_argument( '-a', '--age', help='download subtitles for videos newer than AGE e.g. 12h, 1w2d') filtering_group.add_argument('-h', '--hearing-impaired', action='store_true', help='download hearing impaired subtitles') filtering_group.add_argument( '-f', '--force', action='store_true', help='force subtitle download for videos with existing subtitles') # addic7ed addic7ed_group = parser.add_argument_group('addic7ed') addic7ed_group.add_argument('--addic7ed-username', metavar='USERNAME', help='username for addic7ed provider') addic7ed_group.add_argument('--addic7ed-password', metavar='PASSWORD', help='password for addic7ed provider') # output output_group = parser.add_argument_group('output') output_group.add_argument( '-d', '--directory', help= 'save subtitles in the given directory rather than next to the video') output_group.add_argument( '-e', '--encoding', default=None, help='encoding to convert the subtitle to (default: no conversion)') output_exclusive_group = output_group.add_mutually_exclusive_group() output_exclusive_group.add_argument('-q', '--quiet', action='store_true', help='disable output') output_exclusive_group.add_argument('-v', '--verbose', action='store_true', help='verbose output') output_group.add_argument('--log-file', help='log into a file instead of stdout') output_group.add_argument( '--color', action='store_true', help='add color to console output (requires colorlog)') # troubleshooting troubleshooting_group = parser.add_argument_group('troubleshooting') troubleshooting_group.add_argument('--debug', action='store_true', help='debug output') troubleshooting_group.add_argument('--version', action='version', version=__version__) troubleshooting_group.add_argument('--help', action='help', help='show this help message and exit') # parse args args = parser.parse_args() # parse paths try: args.paths = [ os.path.abspath( os.path.expanduser( p.decode('utf-8') if isinstance(p, bytes) else p)) for p in args.paths ] except UnicodeDecodeError: parser.error('argument paths: encodings is not utf-8: %r' % args.paths) # parse languages try: args.languages = { babelfish.Language.fromietf(l) for l in args.languages } except babelfish.Error: parser.error('argument -l/--languages: codes are not IETF: %r' % args.languages) # parse age if args.age is not None: match = re.match( r'^(?:(?P<weeks>\d+?)w)?(?:(?P<days>\d+?)d)?(?:(?P<hours>\d+?)h)?$', args.age) if not match: parser.error('argument -a/--age: invalid age: %r' % args.age) args.age = datetime.timedelta( **{k: int(v) for k, v in match.groupdict(0).items()}) # parse cache-file args.cache_file = os.path.abspath(os.path.expanduser(args.cache_file)) if not os.path.exists(os.path.split(args.cache_file)[0]): parser.error( 'argument -c/--cache-file: directory %r for cache file does not exist' % os.path.split(args.cache_file)[0]) # parse provider configs provider_configs = {} if (args.addic7ed_username is not None and args.addic7ed_password is None or args.addic7ed_username is None and args.addic7ed_password is not None): parser.error( 'argument --addic7ed-username/--addic7ed-password: both arguments are required or none' ) if args.addic7ed_username is not None and args.addic7ed_password is not None: provider_configs['addic7ed'] = { 'username': args.addic7ed_username, 'password': args.addic7ed_password } # parse color if args.color and colorlog is None: parser.error('argument --color: colorlog required') # setup output if args.log_file is None: handler = logging.StreamHandler() else: handler = logging.FileHandler(args.log_file, encoding='utf-8') if args.debug: if args.color: if args.log_file is None: log_format = '%(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s-%(funcName)s:%(lineno)d%(reset)s] %(message)s' else: log_format = '%(purple)s%(asctime)s%(reset)s %(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s-%(funcName)s:%(lineno)d%(reset)s] %(message)s' handler.setFormatter( colorlog.ColoredFormatter( log_format, log_colors=dict(colorlog.default_log_colors.items() + [('DEBUG', 'cyan')]))) else: if args.log_file is None: log_format = '%(levelname)-8s [%(name)s-%(funcName)s:%(lineno)d] %(message)s' else: log_format = '%(asctime)s %(levelname)-8s [%(name)s-%(funcName)s:%(lineno)d] %(message)s' handler.setFormatter(logging.Formatter(log_format)) logging.getLogger().addHandler(handler) logging.getLogger().setLevel(logging.DEBUG) elif args.verbose: if args.color: if args.log_file is None: log_format = '%(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s%(reset)s] %(message)s' else: log_format = '%(purple)s%(asctime)s%(reset)s %(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s%(reset)s] %(message)s' handler.setFormatter(colorlog.ColoredFormatter(log_format)) else: log_format = '%(levelname)-8s [%(name)s] %(message)s' if args.log_file is not None: log_format = '%(asctime)s ' + log_format handler.setFormatter(logging.Formatter(log_format)) logging.getLogger('subliminal').addHandler(handler) logging.getLogger('subliminal').setLevel(logging.INFO) elif not args.quiet: if args.color: if args.log_file is None: log_format = '[%(log_color)s%(levelname)s%(reset)s] %(message)s' else: log_format = '%(purple)s%(asctime)s%(reset)s [%(log_color)s%(levelname)s%(reset)s] %(message)s' handler.setFormatter(colorlog.ColoredFormatter(log_format)) else: if args.log_file is None: log_format = '%(levelname)s: %(message)s' else: log_format = '%(asctime)s %(levelname)s: %(message)s' handler.setFormatter(logging.Formatter(log_format)) logging.getLogger('subliminal.api').addHandler(handler) logging.getLogger('subliminal.api').setLevel(logging.INFO) # configure cache cache_region.configure( 'dogpile.cache.dbm', expiration_time=datetime.timedelta(days=30), # @UndefinedVariable arguments={ 'filename': args.cache_file, 'lock_factory': MutexLock }) # scan videos videos = scan_videos([p for p in args.paths if os.path.exists(p)], subtitles=not args.force, embedded_subtitles=not args.force, age=args.age) # guess videos videos.extend( [Video.fromname(p) for p in args.paths if not os.path.exists(p)]) # download best subtitles subtitles = download_best_subtitles(videos, args.languages, providers=args.providers, provider_configs=provider_configs, min_score=args.min_score, hearing_impaired=args.hearing_impaired, single=args.single) # save subtitles save_subtitles(subtitles, single=args.single, directory=args.directory, encoding=args.encoding) # result output if not subtitles: if not args.quiet: print('No subtitles downloaded', file=sys.stderr) exit(1) if not args.quiet: subtitles_count = sum([len(s) for s in subtitles.values()]) if subtitles_count == 1: print('%d subtitle downloaded' % subtitles_count) else: print('%d subtitles downloaded' % subtitles_count)
def manual_search(path, language, hi, providers, providers_auth, sceneName, media_type): if hi == "True": hi = True else: hi = False language_set = set() for lang in ast.literal_eval(language): lang = alpha3_from_alpha2(lang) if lang == 'pob': language_set.add(Language('por', 'BR')) else: language_set.add(Language(lang)) use_scenename = get_general_settings()[9] use_postprocessing = get_general_settings()[10] postprocessing_cmd = get_general_settings()[11] try: if sceneName == "None" or use_scenename is False: used_sceneName = False video = scan_video(path) else: used_sceneName = True video = Video.fromname(sceneName) except: logging.error("Error trying to get video information.") else: if media_type == "movie": max_score = 120.0 elif media_type == "series": max_score = 360.0 try: with AsyncProviderPool(max_workers=None, providers=providers, provider_configs=providers_auth) as p: subtitles = p.list_subtitles(video, language_set) except Exception as e: logging.exception("Error trying to get subtitle list from provider") else: subtitles_list = [] for s in subtitles: {s: compute_score(s, video, hearing_impaired=hi)} if media_type == "movie": matched = set(s.get_matches(video)) if hi == s.hearing_impaired: matched.add('hearing_impaired') not_matched = set(score.movie_scores.keys()) - matched required = set(['title']) if any(elem in required for elem in not_matched): continue if used_sceneName: not_matched.remove('hash') elif media_type == "series": matched = set(s.get_matches(video)) if hi == s.hearing_impaired: matched.add('hearing_impaired') not_matched = set(score.episode_scores.keys()) - matched required = set(['series', 'season', 'episode']) if any(elem in required for elem in not_matched): continue if used_sceneName: not_matched.remove('hash') subtitles_list.append(dict(score=round((compute_score(s, video, hearing_impaired=hi) / max_score * 100), 2), language=alpha2_from_alpha3(s.language.alpha3), hearing_impaired=str(s.hearing_impaired), provider=s.provider_name, subtitle=codecs.encode(pickle.dumps(s), "base64").decode(), url=s.page_link, matches=list(matched), dont_matches=list(not_matched))) subtitles_dict = {} subtitles_dict = sorted(subtitles_list, key=lambda x: x['score'], reverse=True) return(subtitles_dict)
def manual_download_subtitle(path, language, hi, subtitle, provider, providers_auth, sceneName, media_type): if hi == "True": hi = True else: hi = False subtitle = pickle.loads(codecs.decode(subtitle.encode(), "base64")) if media_type == 'series': type_of_score = 360 elif media_type == 'movie': type_of_score = 120 use_scenename = get_general_settings()[9] use_postprocessing = get_general_settings()[10] postprocessing_cmd = get_general_settings()[11] language = alpha3_from_alpha2(language) if language == 'pob': lang_obj = Language('por', 'BR') else: lang_obj = Language(language) try: if sceneName is None or use_scenename is False: used_sceneName = False video = scan_video(path) else: used_sceneName = True video = Video.fromname(sceneName) except Exception as e: logging.exception('Error trying to extract information from this filename: ' + path) return None else: try: best_subtitle = subtitle download_subtitles([best_subtitle], providers=provider, provider_configs=providers_auth) except Exception as e: logging.exception('Error downloading subtitles for ' + path) return None else: single = get_general_settings()[7] try: score = round(float(compute_score(best_subtitle, video, hearing_impaired=hi)) / type_of_score * 100, 2) if used_sceneName == True: video = scan_video(path) if single is True: result = save_subtitles(video, [best_subtitle], single=True, encoding='utf-8') else: result = save_subtitles(video, [best_subtitle], encoding='utf-8') except Exception as e: logging.exception('Error saving subtitles file to disk.') return None else: if len(result) > 0: downloaded_provider = result[0].provider_name downloaded_language = language_from_alpha3(result[0].language.alpha3) downloaded_language_code2 = alpha2_from_alpha3(result[0].language.alpha3) downloaded_language_code3 = result[0].language.alpha3 downloaded_path = get_subtitle_path(path, language=lang_obj) message = downloaded_language + " subtitles downloaded from " + downloaded_provider + " with a score of " + unicode(score) + "% using manual search." if use_postprocessing is True: command = pp_replace(postprocessing_cmd, path, downloaded_path, downloaded_language, downloaded_language_code2, downloaded_language_code3) try: if os.name == 'nt': codepage = subprocess.Popen("chcp", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # wait for the process to terminate out_codepage, err_codepage = codepage.communicate() encoding = out_codepage.split(':')[-1].strip() process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # wait for the process to terminate out, err = process.communicate() if os.name == 'nt': out = out.decode(encoding) except: if out == "": logging.error('Post-processing result for file ' + path + ' : Nothing returned from command execution') else: logging.error('Post-processing result for file ' + path + ' : ' + out) else: if out == "": logging.info('Post-processing result for file ' + path + ' : Nothing returned from command execution') else: logging.info('Post-processing result for file ' + path + ' : ' + out) return message else: return None
def download_subtitle(path, language, hi, providers, providers_auth, sceneName, media_type): if hi == "True": hi = True else: hi = False language_set = set() if language == 'pob': language_set.add(Language('por', 'BR')) else: language_set.add(Language(language)) use_scenename = get_general_settings()[9] minimum_score = get_general_settings()[8] minimum_score_movie = get_general_settings()[22] use_postprocessing = get_general_settings()[10] postprocessing_cmd = get_general_settings()[11] try: if sceneName == "None" or use_scenename is False: used_sceneName = False video = scan_video(path) else: used_sceneName = True video = Video.fromname(sceneName) except Exception as e: logging.exception("Error trying to get video information for this file: " + path) else: if media_type == "movie": max_score = 120.0 elif media_type == "series": max_score = 360.0 try: with AsyncProviderPool(max_workers=None, providers=providers, provider_configs=providers_auth) as p: subtitles = p.list_subtitles(video, language_set) except Exception as e: logging.exception("Error trying to get subtitle list from provider") else: subtitles_list = [] sorted_subtitles = sorted([(s, compute_score(s, video, hearing_impaired=hi)) for s in subtitles], key=operator.itemgetter(1), reverse=True) for s, preliminary_score in sorted_subtitles: if media_type == "movie": if (preliminary_score / max_score * 100) < int(minimum_score_movie): continue matched = set(s.get_matches(video)) if hi == s.hearing_impaired: matched.add('hearing_impaired') not_matched = set(score.movie_scores.keys()) - matched required = set(['title']) if any(elem in required for elem in not_matched): continue elif media_type == "series": if (preliminary_score / max_score * 100) < int(minimum_score): continue matched = set(s.get_matches(video)) if hi == s.hearing_impaired: matched.add('hearing_impaired') not_matched = set(score.episode_scores.keys()) - matched required = set(['series', 'season', 'episode']) if any(elem in required for elem in not_matched): continue subtitles_list.append(s) if len(subtitles_list) > 0: best_subtitle = subtitles_list[0] download_subtitles([best_subtitle], providers=providers, provider_configs=providers_auth) try: calculated_score = round(float(compute_score(best_subtitle, video, hearing_impaired=hi)) / max_score * 100, 2) if used_sceneName == True: video = scan_video(path) single = get_general_settings()[7] if single is True: result = save_subtitles(video, [best_subtitle], single=True, encoding='utf-8') else: result = save_subtitles(video, [best_subtitle], encoding='utf-8') except Exception as e: logging.exception('Error saving subtitles file to disk.') return None else: if len(result) > 0: downloaded_provider = result[0].provider_name downloaded_language = language_from_alpha3(result[0].language.alpha3) downloaded_language_code2 = alpha2_from_alpha3(result[0].language.alpha3) downloaded_language_code3 = result[0].language.alpha3 downloaded_path = get_subtitle_path(path, language=language_set) if used_sceneName == True: message = downloaded_language + " subtitles downloaded from " + downloaded_provider + " with a score of " + unicode(calculated_score) + "% using this scene name: " + sceneName else: message = downloaded_language + " subtitles downloaded from " + downloaded_provider + " with a score of " + unicode(calculated_score) + "% using filename guessing." if use_postprocessing is True: command = pp_replace(postprocessing_cmd, path, downloaded_path, downloaded_language, downloaded_language_code2, downloaded_language_code3) try: if os.name == 'nt': codepage = subprocess.Popen("chcp", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # wait for the process to terminate out_codepage, err_codepage = codepage.communicate() encoding = out_codepage.split(':')[-1].strip() process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # wait for the process to terminate out, err = process.communicate() if os.name == 'nt': out = out.decode(encoding) except: if out == "": logging.error('Post-processing result for file ' + path + ' : Nothing returned from command execution') else: logging.error('Post-processing result for file ' + path + ' : ' + out) else: if out == "": logging.info('Post-processing result for file ' + path + ' : Nothing returned from command execution') else: logging.info('Post-processing result for file ' + path + ' : ' + out) return message else: return None else: return None
def subliminal(): parser = argparse.ArgumentParser(prog='subliminal', description='Subtitles, faster than your thoughts', epilog='Suggestions and bug reports are greatly appreciated: ' 'https://github.com/Diaoul/subliminal/issues', add_help=False) # required arguments required_arguments_group = parser.add_argument_group('required arguments') required_arguments_group.add_argument('paths', nargs='+', metavar='PATH', help='path to video file or folder') required_arguments_group.add_argument('-l', '--languages', nargs='+', required=True, metavar='LANGUAGE', help='wanted languages as IETF codes e.g. fr, pt-BR, sr-Cyrl ') # configuration configuration_group = parser.add_argument_group('configuration') configuration_group.add_argument('-s', '--single', action='store_true', help='download without language code in subtitle\'s filename i.e. .srt only') configuration_group.add_argument('-c', '--cache-file', default=DEFAULT_CACHE_FILE, help='cache file (default: %(default)s)') # filtering filtering_group = parser.add_argument_group('filtering') filtering_group.add_argument('-p', '--providers', nargs='+', metavar='PROVIDER', help='providers to use (%s)' % ', '.join(provider_manager.available_providers)) filtering_group.add_argument('-m', '--min-score', type=int, default=0, help='minimum score for subtitles (0-%d for episodes, 0-%d for movies)' % (Episode.scores['hash'], Movie.scores['hash'])) filtering_group.add_argument('-a', '--age', help='download subtitles for videos newer than AGE e.g. 12h, 1w2d') filtering_group.add_argument('-h', '--hearing-impaired', action='store_true', help='download hearing impaired subtitles') filtering_group.add_argument('-f', '--force', action='store_true', help='force subtitle download for videos with existing subtitles') # addic7ed addic7ed_group = parser.add_argument_group('addic7ed') addic7ed_group.add_argument('--addic7ed-username', metavar='USERNAME', help='username for addic7ed provider') addic7ed_group.add_argument('--addic7ed-password', metavar='PASSWORD', help='password for addic7ed provider') # output output_group = parser.add_argument_group('output') output_group.add_argument('-d', '--directory', help='save subtitles in the given directory rather than next to the video') output_group.add_argument('-e', '--encoding', default='utf-8', help='subtitles encoding (default: %(default)s)') output_exclusive_group = output_group.add_mutually_exclusive_group() output_exclusive_group.add_argument('-q', '--quiet', action='store_true', help='disable output') output_exclusive_group.add_argument('-v', '--verbose', action='store_true', help='verbose output') output_group.add_argument('--log-file', help='log into a file instead of stdout') output_group.add_argument('--color', action='store_true', help='add color to console output (requires colorlog)') # troubleshooting troubleshooting_group = parser.add_argument_group('troubleshooting') troubleshooting_group.add_argument('--debug', action='store_true', help='debug output') troubleshooting_group.add_argument('--version', action='version', version=__version__) troubleshooting_group.add_argument('--help', action='help', help='show this help message and exit') # parse args args = parser.parse_args() # parse paths try: args.paths = [os.path.abspath(os.path.expanduser(p.decode('utf-8') if isinstance(p, bytes) else p)) for p in args.paths] except UnicodeDecodeError: parser.error('argument paths: encodings is not utf-8: %r' % args.paths) # parse languages try: args.languages = {babelfish.Language.fromietf(l) for l in args.languages} except babelfish.Error: parser.error('argument -l/--languages: codes are not IETF: %r' % args.languages) # parse age if args.age is not None: match = re.match(r'^(?:(?P<weeks>\d+?)w)?(?:(?P<days>\d+?)d)?(?:(?P<hours>\d+?)h)?$', args.age) if not match: parser.error('argument -a/--age: invalid age: %r' % args.age) args.age = datetime.timedelta(**{k: int(v) for k, v in match.groupdict(0).items()}) # parse cache-file args.cache_file = os.path.abspath(os.path.expanduser(args.cache_file)) if not os.path.exists(os.path.split(args.cache_file)[0]): parser.error('argument -c/--cache-file: directory %r for cache file does not exist' % os.path.split(args.cache_file)[0]) # parse provider configs provider_configs = {} if (args.addic7ed_username is not None and args.addic7ed_password is None or args.addic7ed_username is None and args.addic7ed_password is not None): parser.error('argument --addic7ed-username/--addic7ed-password: both arguments are required or none') if args.addic7ed_username is not None and args.addic7ed_password is not None: provider_configs['addic7ed'] = {'username': args.addic7ed_username, 'password': args.addic7ed_password} # parse color if args.color and colorlog is None: parser.error('argument --color: colorlog required') # setup output if args.log_file is None: handler = logging.StreamHandler() else: handler = logging.FileHandler(args.log_file, encoding='utf-8') if args.debug: if args.color: if args.log_file is None: log_format = '%(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s-%(funcName)s:%(lineno)d%(reset)s] %(message)s' else: log_format = '%(purple)s%(asctime)s%(reset)s %(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s-%(funcName)s:%(lineno)d%(reset)s] %(message)s' handler.setFormatter(colorlog.ColoredFormatter(log_format, log_colors=dict(colorlog.default_log_colors.items() + [('DEBUG', 'cyan')]))) else: if args.log_file is None: log_format = '%(levelname)-8s [%(name)s-%(funcName)s:%(lineno)d] %(message)s' else: log_format = '%(asctime)s %(levelname)-8s [%(name)s-%(funcName)s:%(lineno)d] %(message)s' handler.setFormatter(logging.Formatter(log_format)) logging.getLogger().addHandler(handler) logging.getLogger().setLevel(logging.DEBUG) elif args.verbose: if args.color: if args.log_file is None: log_format = '%(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s%(reset)s] %(message)s' else: log_format = '%(purple)s%(asctime)s%(reset)s %(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s%(reset)s] %(message)s' handler.setFormatter(colorlog.ColoredFormatter(log_format)) else: log_format = '%(levelname)-8s [%(name)s] %(message)s' if args.log_file is not None: log_format = '%(asctime)s ' + log_format handler.setFormatter(logging.Formatter(log_format)) logging.getLogger('subliminal').addHandler(handler) logging.getLogger('subliminal').setLevel(logging.INFO) elif not args.quiet: if args.color: if args.log_file is None: log_format = '[%(log_color)s%(levelname)s%(reset)s] %(message)s' else: log_format = '%(purple)s%(asctime)s%(reset)s [%(log_color)s%(levelname)s%(reset)s] %(message)s' handler.setFormatter(colorlog.ColoredFormatter(log_format)) else: if args.log_file is None: log_format = '%(levelname)s: %(message)s' else: log_format = '%(asctime)s %(levelname)s: %(message)s' handler.setFormatter(logging.Formatter(log_format)) logging.getLogger('subliminal.api').addHandler(handler) logging.getLogger('subliminal.api').setLevel(logging.INFO) # configure cache cache_region.configure('dogpile.cache.dbm', expiration_time=datetime.timedelta(days=30), # @UndefinedVariable arguments={'filename': args.cache_file, 'lock_factory': MutexLock}) # scan videos videos = scan_videos([p for p in args.paths if os.path.exists(p)], subtitles=not args.force, embedded_subtitles=not args.force, age=args.age) # guess videos videos.extend([Video.fromname(p) for p in args.paths if not os.path.exists(p)]) # download best subtitles subtitles = download_best_subtitles(videos, args.languages, providers=args.providers, provider_configs=provider_configs, min_score=args.min_score, hearing_impaired=args.hearing_impaired, single=args.single) # save subtitles save_subtitles(subtitles, single=args.single, directory=args.directory, encoding=args.encoding) # result output if not subtitles: if not args.quiet: print('No subtitles downloaded', file=sys.stderr) exit(1) if not args.quiet: subtitles_count = sum([len(s) for s in subtitles.values()]) if subtitles_count == 1: print('%d subtitle downloaded' % subtitles_count) else: print('%d subtitles downloaded' % subtitles_count)
def subliminal(): parser = argparse.ArgumentParser( prog="subliminal", description="Subtitles, faster than your thoughts", epilog="Suggestions and bug reports are greatly appreciated: " "https://github.com/Diaoul/subliminal/issues", add_help=False, ) # required arguments required_arguments_group = parser.add_argument_group("required arguments") required_arguments_group.add_argument("paths", nargs="+", metavar="PATH", help="path to video file or folder") required_arguments_group.add_argument( "-l", "--languages", action="append", required=True, metavar="LANGUAGE", help="wanted languages as IETF codes e.g. fr, pt-BR, sr-Cyrl ", ) # configuration configuration_group = parser.add_argument_group("configuration") configuration_group.add_argument( "-s", "--single", action="store_true", help="download without language code in subtitle's filename i.e. .srt only", ) configuration_group.add_argument( "-c", "--cache-file", default=DEFAULT_CACHE_FILE, help="cache file (default: %(default)s)" ) configuration_group.add_argument("--all", action="store_true", help="download all subtitles") # filtering filtering_group = parser.add_argument_group("filtering") filtering_group.add_argument( "-p", "--providers", action="append", metavar="PROVIDER", help="providers to use (%s)" % ", ".join(provider_manager.available_providers), ) filtering_group.add_argument( "-m", "--min-score", type=int, default=0, help="minimum score for subtitles (0-%d for episodes, 0-%d for movies)" % (Episode.scores["hash"], Movie.scores["hash"]), ) filtering_group.add_argument("-a", "--age", help="download subtitles for videos newer than AGE e.g. 12h, 1w2d") filtering_group.add_argument( "-h", "--hearing-impaired", action="store_true", help="download hearing impaired subtitles" ) filtering_group.add_argument( "-f", "--force", action="store_true", help="force subtitle download for videos with existing subtitles" ) # addic7ed addic7ed_group = parser.add_argument_group("addic7ed") addic7ed_group.add_argument("--addic7ed-username", metavar="USERNAME", help="username for addic7ed provider") addic7ed_group.add_argument("--addic7ed-password", metavar="PASSWORD", help="password for addic7ed provider") # output output_group = parser.add_argument_group("output") output_group.add_argument( "-d", "--directory", help="save subtitles in the given directory rather than next to the video" ) output_group.add_argument( "-e", "--encoding", default=None, help="encoding to convert the subtitle to (default: no conversion)" ) output_exclusive_group = output_group.add_mutually_exclusive_group() output_exclusive_group.add_argument("-q", "--quiet", action="store_true", help="disable output") output_exclusive_group.add_argument("-v", "--verbose", action="store_true", help="verbose output") output_group.add_argument("--log-file", help="log into a file instead of stdout") output_group.add_argument("--color", action="store_true", help="add color to console output (requires colorlog)") # troubleshooting troubleshooting_group = parser.add_argument_group("troubleshooting") troubleshooting_group.add_argument("--debug", action="store_true", help="debug output") troubleshooting_group.add_argument("--version", action="version", version=__version__) troubleshooting_group.add_argument("--help", action="help", help="show this help message and exit") # parse args args = parser.parse_args() # parse paths try: args.paths = [ os.path.abspath(os.path.expanduser(p.decode("utf-8") if isinstance(p, bytes) else p)) for p in args.paths ] except UnicodeDecodeError: parser.error("argument paths: encodings is not utf-8: %r" % args.paths) # parse languages try: args.languages = {babelfish.Language.fromietf(l) for l in args.languages} except babelfish.Error: parser.error("argument -l/--languages: codes are not IETF: %r" % args.languages) # parse age if args.age is not None: match = re.match(r"^(?:(?P<weeks>\d+?)w)?(?:(?P<days>\d+?)d)?(?:(?P<hours>\d+?)h)?$", args.age) if not match: parser.error("argument -a/--age: invalid age: %r" % args.age) args.age = datetime.timedelta(**{k: int(v) for k, v in match.groupdict(0).items()}) # parse cache-file args.cache_file = os.path.abspath(os.path.expanduser(args.cache_file)) if not os.path.exists(os.path.split(args.cache_file)[0]): parser.error( "argument -c/--cache-file: directory %r for cache file does not exist" % os.path.split(args.cache_file)[0] ) # parse provider configs provider_configs = {} if ( args.addic7ed_username is not None and args.addic7ed_password is None or args.addic7ed_username is None and args.addic7ed_password is not None ): parser.error("argument --addic7ed-username/--addic7ed-password: both arguments are required or none") if args.addic7ed_username is not None and args.addic7ed_password is not None: provider_configs["addic7ed"] = {"username": args.addic7ed_username, "password": args.addic7ed_password} # parse color if args.color and colorlog is None: parser.error("argument --color: colorlog required") # setup output if args.log_file is None: handler = logging.StreamHandler() else: handler = logging.FileHandler(args.log_file, encoding="utf-8") if args.debug: if args.color: if args.log_file is None: log_format = "%(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s-%(funcName)s:%(lineno)d%(reset)s] %(message)s" else: log_format = "%(purple)s%(asctime)s%(reset)s %(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s-%(funcName)s:%(lineno)d%(reset)s] %(message)s" handler.setFormatter( colorlog.ColoredFormatter( log_format, log_colors=dict(colorlog.default_log_colors.items() + [("DEBUG", "cyan")]) ) ) else: if args.log_file is None: log_format = "%(levelname)-8s [%(name)s-%(funcName)s:%(lineno)d] %(message)s" else: log_format = "%(asctime)s %(levelname)-8s [%(name)s-%(funcName)s:%(lineno)d] %(message)s" handler.setFormatter(logging.Formatter(log_format)) logging.getLogger().addHandler(handler) logging.getLogger().setLevel(logging.DEBUG) elif args.verbose: if args.color: if args.log_file is None: log_format = "%(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s%(reset)s] %(message)s" else: log_format = "%(purple)s%(asctime)s%(reset)s %(log_color)s%(levelname)-8s%(reset)s [%(blue)s%(name)s%(reset)s] %(message)s" handler.setFormatter(colorlog.ColoredFormatter(log_format)) else: log_format = "%(levelname)-8s [%(name)s] %(message)s" if args.log_file is not None: log_format = "%(asctime)s " + log_format handler.setFormatter(logging.Formatter(log_format)) logging.getLogger("subliminal").addHandler(handler) logging.getLogger("subliminal").setLevel(logging.INFO) elif not args.quiet: if args.color: if args.log_file is None: log_format = "[%(log_color)s%(levelname)s%(reset)s] %(message)s" else: log_format = "%(purple)s%(asctime)s%(reset)s [%(log_color)s%(levelname)s%(reset)s] %(message)s" handler.setFormatter(colorlog.ColoredFormatter(log_format)) else: if args.log_file is None: log_format = "%(levelname)s: %(message)s" else: log_format = "%(asctime)s %(levelname)s: %(message)s" handler.setFormatter(logging.Formatter(log_format)) logging.getLogger("subliminal.api").addHandler(handler) logging.getLogger("subliminal.api").setLevel(logging.INFO) # configure cache cache_region.configure( "dogpile.cache.dbm", expiration_time=datetime.timedelta(days=30), # @UndefinedVariable arguments={"filename": args.cache_file, "lock_factory": MutexLock}, ) # scan videos videos = scan_videos( [p for p in args.paths if os.path.exists(p)], subtitles=not args.force, embedded_subtitles=not args.force, age=args.age, ) # guess videos videos.extend([Video.fromname(p) for p in args.paths if not os.path.exists(p)]) if args.all: subtitles = collections.defaultdict(list) with ProviderPool(args.providers, provider_configs) as pp: for video in videos: for subtitle in pp.list_subtitles(video, args.languages): if pp.download_subtitle(subtitle): subtitles[video].append(subtitle) else: # download best subtitles subtitles = download_best_subtitles( videos, args.languages, providers=args.providers, provider_configs=provider_configs, min_score=args.min_score, hearing_impaired=args.hearing_impaired, single=args.single, ) # save subtitles save_subtitles( subtitles, single=args.single, directory=args.directory, encoding=args.encoding, save_multiple=args.all ) # result output if not subtitles: if not args.quiet: print("No subtitles downloaded", file=sys.stderr) exit(1) if not args.quiet: subtitles_count = sum([len(s) for s in subtitles.values()]) if subtitles_count == 1: print("%d subtitle downloaded" % subtitles_count) else: print("%d subtitles downloaded" % subtitles_count)