def search_video_files(rootdir, max_age=7, embedded_subs=False): '''Return a list of video files in rootdir which does not have any subtitle. Parameters ---------- rootdir: str Root folder path from where to start the search. # this has been deprecated age: datetime.timedelta Maximum age of the video files in days to be included in the result. embedded_subs: bool If True will also take into account the subtitles embedded in the video file. Returns ------- list of subliminal.video ''' dirpath = op.abspath(op.expanduser(rootdir)) # data path if not op.exists(dirpath): raise IOError('Folder {} not found.'.format(dirpath)) # configure the cache # subliminal.cache_region.configure('dogpile.cache.dbm', arguments={'filename': data_dir.joinpath('cachefile.dbm')}) # scan for videos in the folder and their subtitles videos = subliminal.scan_videos(dirpath, subtitles=True, embedded_subtitles=embedded_subs) #age=timedelta(days=max_age)) return videos
def subliminal(): parser = subliminal_parser() args = parser.parse_args() # parse paths try: args.paths = [p.decode('utf-8') 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.fromalpha2(l) for l in args.languages} except babelfish.Error: parser.error('argument -l/--languages: codes are not ISO-639-1: %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(**match.groupdict()) # setup verbosity if args.verbose: logging.basicConfig(level=logging.DEBUG) elif not args.quiet: logging.basicConfig(level=logging.WARN) # configure cache cache_region.configure('dogpile.cache.dbm', arguments={'filename': os.path.expanduser(args.cache_file)}) # scan videos videos = scan_videos([p for p in args.paths if os.path.exists(p)], subtitles=not args.force, age=args.age) # guess videos videos.extend([Video.fromguess(os.path.split(p)[1], guessit.guess_file_info(p, 'autodetect')) 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=None, single=args.single, min_score=args.min_score, hearing_impaired=args.hearing_impaired) # output result if not subtitles: if not args.quiet: sys.stderr.write('No subtitles downloaded\n') 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 com_meta_fetch_subtitle_batch(dir_name, sub_lang='eng'): """ # batch fetch subtitles """ # configure the cache subliminal.cache_region.configure('dogpile.cache.dbm', arguments={'filename': '/mediakraken/cache/cachefile.dbm'}) # scan for videos in the folder and their subtitles videos = subliminal.scan_videos(dir_name, subtitles=True, embedded_subtitles=True) # download subliminal.download_best_subtitles(videos, Language(sub_lang))
def scan(paths, config): subliminal.cache_region.configure('dogpile.cache.dbm', arguments={'filename': '/usr/local/subliminal/cache/cachefile.dbm'}) 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=None if config.get('General').as_bool('hearing_impaired'): hearing_impaired=True videos = subliminal.scan_videos(paths, subtitles=True, embedded_subtitles=True, age=timedelta(days=config.get('Task').as_int('age'))) subtitles = subliminal.api.download_best_subtitles(videos, languages=languageset, providers=config['General']['providers'], provider_configs=None, single=single, min_score=config.get('General').as_int('min_score'), hearing_impaired=hearing_impaired) return subtitles
def download_callback(self, menuitem, files): # scan videos videos = [] for f in files: # ignore non-writable locations if not f.can_write(): continue # directories if f.is_directory(): try: scanned_videos = scan_videos(f.get_location().get_path()) except: continue for video in scanned_videos: if check_video(video, languages=self.config.languages, age=self.config.age, undefined=self.config.single): video.subtitle_languages |= set(search_external_subtitles(video.name).values()) refine(video, episode_refiners=self.config.refiners, movie_refiners=self.config.refiners, embedded_subtitles=self.config.embedded_subtitles) videos.append(video) continue # other inputs try: video = scan_video(f.get_location().get_path()) except: continue if check_video(video, languages=self.config.languages, undefined=self.config.single): video.subtitle_languages |= set(search_external_subtitles(video.name).values()) refine(video, episode_refiners=self.config.refiners, movie_refiners=self.config.refiners, embedded_subtitles=self.config.embedded_subtitles) videos.append(video) # download best subtitles downloaded_subtitles = defaultdict(list) with AsyncProviderPool(providers=self.config.providers, provider_configs=self.config.provider_configs) as pool: for v in videos: scores = get_scores(v) subtitles = pool.download_best_subtitles( pool.list_subtitles(v, self.config.languages - v.subtitle_languages), v, self.config.languages, min_score=scores['hash'] * self.config.min_score / 100, hearing_impaired=self.config.hearing_impaired, only_one=self.config.single ) downloaded_subtitles[v] = subtitles # save subtitles for v, subtitles in downloaded_subtitles.items(): save_subtitles(v, subtitles, single=self.config.single)
def downloadsubtitles(self_) : LOGGER.info('*** START DOWNLOADING SUBTITLES ***') if SIMULATE_MODE : return # scan for videos in the folder and their subtitles videos = subliminal.scan_videos(self_.scanPath, subtitles=True, embedded_subtitles=True) # download subs = subliminal.download_best_subtitles(videos, {babelfish.Language('eng')}) # save for video, sub in subs.items(): subliminal.save_subtitles(video, sub)
def run(self): """ The main thread. this will run: - download torrent using utorrent - check utorent for status until finish downloading - move file to new location :return: """ # start downloading and get hash self.state = DownloadTorrentThread.STATE_DOWNLOADING before_list = utorrentUtils.get_all_torrents() if not utorrentUtils.download_file(self.magnet_link): # TODO: run utorrent raise RuntimeError('Utorrent not working!') time.sleep(1) after_list = utorrentUtils.get_all_torrents() self.hash, torrent_data = self._get_new_downloaded_hash(before_list, after_list) if not self.hash: print 'file already existing in utorrent' return # print self.hash # print torrent_data torrent_name = self._wait_to_finish_downloading() # get all video files and move them to the correct location self.state = DownloadTorrentThread.STATE_MOVING files = utorrentUtils.get_torrent_files(self.hash) video_files_data = utorrentUtils.get_data_for_video_files(files, torrent_name=torrent_name) if video_files_data: self.video_files_data = video_files_data self._copy_files() # download subtitles for data in video_files_data: src_file = data['full_file_path'] dst_dir = os.path.join(LocalFilesUtil.get_series_path(data['series_name']), 'Season ' + str(data['season'])) dst_file = os.path.join(dst_dir, os.path.split(src_file)[1]) videos = subliminal.scan_videos([dst_file]) subtitles = subliminal.download_best_subtitles(videos, {Language('heb'), Language('eng')}) subliminal.save_subtitles(subtitles) self.state = DownloadTorrentThread.STATE_FINISHED pass
def updateSeriesDB(dbFilePath_, seriesPath_) : """Update the database for a given path :param String dbFilePath_ :param String seriesPath_ """ conn = sqlite3.connect(dbFilePath_) c = conn.cursor() LOGGER.info('*** START UPDATING DATABASE ***') videos = subliminal.scan_videos(seriesPath_, True, True) for video in videos : subEn = babelfish.Language('eng') in video.subtitle_languages subFr = babelfish.Language('fra') in video.subtitle_languages updateEpisode(c, video, subEn, subFr, True) conn.commit() conn.close()
def downloadSubtitles(path_) : """Download all subtitles in the given path :param String path_ """ LOGGER.info('*** START DOWNLOADING SUBTITLES ***') if SIMULATE_MODE : return # configure the cache if (not os.path.isdir(_cachePath)) : os.makedirs(_cachePath) subliminal.cache_region.configure('dogpile.cache.dbm', arguments={'filename': _cachePath + 'cachefile.dbm'}) # scan for videos in the folder and their subtitles upath = unicode(path_) videos = subliminal.scan_videos(upath, subtitles=True, embedded_subtitles=True) # download subs = subliminal.download_best_subtitles(videos, {babelfish.Language('eng')})
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 main(folder_dir): print(folder_dir) videos = scan_videos(folder_dir) subs = download_best_subtitles(videos, {Language('heb')}) print(videos, subs) save_and_merge(subs)
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)
from babelfish import Language from subliminal import download_best_subtitles, region, save_subtitles, scan_videos import os region.configure('dogpile.cache.dbm', arguments={'filename': 'cachefile.dbm'}) videos = scan_videos(os.getcwd()) subtitles = download_best_subtitles(videos, {Language('eng'), Language('fas')}) for v in videos: print(v) save_subtitles(v, subtitles[v])
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)
def fetch_subtitles(self, media_dict): vids = media_dict.keys() if isinstance(media_dict, dict) else media_dict subs = scan_videos(vids, subtitles=True, embedded_subtitles=False) single = True if len(BABELFISH_LANG) == 1 else False subliminal.download_best_subtitles(subs, BABELFISH_LANG, single=single)
'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } # Internationalization # https://docs.djangoproject.com/en/1.6/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.6/howto/static-files/ STATIC_URL = '/static/' # Subliminal stuff SUBLIMINAL_CACHE = subliminal.cache_region.configure('dogpile.cache.dbm', arguments={'filename': '/tmp/cachefile.dbm'}) print "SUBLIMINAL_CACHE => %s" % SUBLIMINAL_CACHE videos = subliminal.scan_videos(['/Users/crsantos/Movies'], subtitles=True, embedded_subtitles=True, age=timedelta(weeks=1)) print videos
from datetime import timedelta from babelfish import Language from subliminal import download_best_subtitles, region, save_subtitles, scan_videos SEARCH_ROOT = '~/Downloads' # configure the cache region.configure('dogpile.cache.dbm', arguments={'filename': 'cachefile.dbm'}) # scan for videos newer than 2 weeks and their existing subtitles in a folder videos = scan_videos(SEARCH_ROOT, age=timedelta(weeks=2)) # download best subtitles subtitles = download_best_subtitles(videos, {Language('eng'), Language('fra')}) # save them to disk, next to the video for v in videos: print(v) save_subtitles(v, subtitles[v])
def run(self, scan_path, scan_age, languages, encoding, min_score, providers, provider_configs, max_workers, plex_url=None, plex_token=None, *args, **kwargs): if not os.path.isdir(scan_path): raise IOError('Path \'%s\' doesn\'t exist!' % scan_path) if not scan_age >= 1: raise ValueError('\'scan_age\' must by at least 1!') if not len(languages) >= 1: raise ValueError('\'languages\' list can\'t be empty!') if not providers: raise ValueError('\'providers\' argument can\'t be empty!') if not max_workers >= 1: raise ValueError('\'max_workers\' must be at least 1!') if not provider_configs: provider_configs = {} __tree_dict = lambda: defaultdict(__tree_dict) result = __tree_dict() encoding = codecs.lookup(encoding).name age = timedelta(weeks=scan_age) languages = set([Language(l) for l in languages]) plex = None if plex_url and plex_token: plex = PlexServer(plex_url, plex_token) scan_start = datetime.now() videos = [] ignored_videos = [] if not region.is_configured: region.configure('dogpile.cache.dbm', expiration_time=timedelta(days=30), arguments={ 'filename': 'subliminal.dbm', 'lock_factory': MutexLock }) # scan videos scanned_videos = scan_videos(scan_path, age=age) for video in scanned_videos: video.subtitle_languages |= set( search_external_subtitles(video.name).values()) if check_video(video, languages=languages, age=age, undefined=False): refine(video) if languages - video.subtitle_languages: videos.append(video) else: ignored_videos.append(video) else: ignored_videos.append(video) if videos: result['videos']['collected'] = [ os.path.split(v.name)[1] for v in videos ] if ignored_videos: result['videos']['ignored'] = len(ignored_videos) if videos: # download best subtitles downloaded_subtitles = defaultdict(list) with AsyncProviderPool(max_workers=max_workers, providers=providers, provider_configs=provider_configs) as p: for video in videos: scores = get_scores(video) subtitles_to_download = p.list_subtitles( video, languages - video.subtitle_languages) downloaded_subtitles[video] = p.download_best_subtitles( subtitles_to_download, video, languages, min_score=scores['hash'] * min_score / 100) if p.discarded_providers: result['providers']['discarded'] = list( p.discarded_providers) # filter subtitles with TinyDB('subtitle_db.json') as db: table = db.table('downloaded') query = Query() for video, subtitles in downloaded_subtitles.items(): discarded_subtitles = list() discarded_subtitles_info = list() for s in subtitles: subtitle_hash = hashlib.sha256(s.content).hexdigest() subtitle_file = get_subtitle_path( os.path.split(video.name)[1], s.language) dbo = {'hash': subtitle_hash, 'file': subtitle_file} if table.search((query.hash == subtitle_hash) & (query.file == subtitle_file)): discarded_subtitles.append(s) discarded_subtitles_info.append(dbo) else: table.insert(dbo) downloaded_subtitles[video] = [ x for x in subtitles if x not in discarded_subtitles ] if discarded_subtitles_info: result['subtitles'][ 'discarded'] = result['subtitles'].get( 'discarded', []) + discarded_subtitles_info downloaded_subtitles = { k: v for k, v in downloaded_subtitles.items() if v } # save subtitles saved_subtitles = {} for video, subtitles in downloaded_subtitles.items(): saved_subtitles[video] = save_subtitles(video, subtitles, directory=None, encoding=encoding) for key, group in groupby(saved_subtitles[video], lambda x: x.provider_name): subtitle_filenames = [ get_subtitle_path( os.path.split(video.name)[1], s.language) for s in list(group) ] result['subtitles'][key] = result['subtitles'].get( key, []) + subtitle_filenames result['subtitles']['total'] = sum( len(v) for v in saved_subtitles.values()) # refresh plex for video, subtitles in saved_subtitles.items(): if plex and subtitles: item_found = False for section in plex.library.sections(): try: if isinstance(section, MovieSection) and isinstance( video, Movie): results = section.search(title=video.title, year=video.year, libtype='movie', sort='addedAt:desc', maxresults=1) if not results: raise NotFound plex_item = results[0] elif isinstance(section, ShowSection) and isinstance( video, Episode): results = section.search(title=video.series, year=video.year, libtype='show', sort='addedAt:desc', maxresults=1) if not results: raise NotFound plex_item = results[0].episode( season=video.season, episode=video.episode) else: continue except NotFound: continue except BadRequest: continue if plex_item: plex_item.refresh() result['plex']['refreshed'] = result['plex'].get( 'refreshed', []) + [ '%s%s' % (repr(plex_item.section()), repr(video)) ] item_found = True if not item_found: result['plex']['failed'] = result['plex'].get( 'failed', []) + [repr(video)] # convert subtitles for video, subtitles in saved_subtitles.items(): target_format = aeidon.formats.SUBRIP for s in subtitles: subtitle_path = get_subtitle_path(video.name, s.language) source_format = aeidon.util.detect_format( subtitle_path, encoding) source_file = aeidon.files.new( source_format, subtitle_path, aeidon.encodings.detect_bom(subtitle_path) or encoding) if source_format != target_format: format_info = { 'file': get_subtitle_path( os.path.split(video.name)[1], s.language), 'from': source_format.label, 'to': target_format.label } result['subtitles'][ 'converted'] = result['subtitles'].get( 'converted', []) + [format_info] aeidon_subtitles = source_file.read() for f in [ aeidon.formats.SUBRIP, aeidon.formats.MICRODVD, aeidon.formats.MPL2 ]: markup = aeidon.markups.new(f) for s in aeidon_subtitles: s.main_text = markup.decode(s.main_text) markup = aeidon.markups.new(target_format) for s in aeidon_subtitles: s.main_text = markup.encode(s.main_text) target_file = aeidon.files.new(target_format, subtitle_path, encoding) target_file.write(aeidon_subtitles, aeidon.documents.MAIN) scan_end = datetime.now() result['meta']['start'] = scan_start.isoformat() result['meta']['end'] = scan_end.isoformat() result['meta']['duration'] = str(scan_end - scan_start) return result
def download_callback(self, menuitem, files): # scan videos videos = [] for f in files: # ignore non-writable locations if not f.can_write(): continue # directories if f.is_directory(): try: scanned_videos = scan_videos(f.get_location().get_path()) except: continue for video in scanned_videos: if check_video(video, languages=self.config.languages, age=self.config.age, undefined=self.config.single): video.subtitle_languages |= set( search_external_subtitles(video.name).values()) refine( video, episode_refiners=self.config.refiners, movie_refiners=self.config.refiners, embedded_subtitles=self.config.embedded_subtitles) videos.append(video) continue # other inputs try: video = scan_video(f.get_location().get_path()) except: continue if check_video(video, languages=self.config.languages, undefined=self.config.single): video.subtitle_languages |= set( search_external_subtitles(video.name).values()) refine(video, episode_refiners=self.config.refiners, movie_refiners=self.config.refiners, embedded_subtitles=self.config.embedded_subtitles) videos.append(video) # download best subtitles downloaded_subtitles = defaultdict(list) with AsyncProviderPool( providers=self.config.providers, provider_configs=self.config.provider_configs) as pool: for v in videos: scores = get_scores(v) subtitles = pool.download_best_subtitles( pool.list_subtitles( v, self.config.languages - v.subtitle_languages), v, self.config.languages, min_score=scores['hash'] * self.config.min_score / 100, hearing_impaired=self.config.hearing_impaired, only_one=self.config.single) downloaded_subtitles[v] = subtitles # save subtitles for v, subtitles in downloaded_subtitles.items(): save_subtitles(v, subtitles, single=self.config.single)
import glob, os from shutil import copyfile, move from subliminal import download_best_subtitles, save_subtitles, scan_videos from babelfish import Language #Vuse Torrent Default: userProf + "/Documents/Vuze Downloads" #BiglyBT Torrent Default: userProf + "/Documents/BiglyBT Downloads" __author__ = "TooSlepy" __PyVer__ = "3.6" userProf = os.getenv("userprofile") videoSaveLoc = userProf + "/Documents/Vuze Downloads" saveLoc = "D:/Moviex/" lang = "eng" os.chdir(videoSaveLoc) for x in ("*.avi", "*.mkv", "*.mp4", "*.mpg"): for file in glob.glob(x): fileDir = file.rsplit('.', 1)[0] if not os.path.exists(fileDir): os.mkdir(fileDir) move(file, fileDir + "/" + file) # scan for videos in a folder videos = scan_videos(fileDir) # download best subtitles subtitles = download_best_subtitles(videos, {Language(lang)}) # save them to disk, next to the video for v in videos: save_subtitles(v, subtitles[v]) move(fileDir, saveLoc + "/" + fileDir) print(file + " has been moved and subtitled.")
if not config['OPENSUBTITLE']['password']: logger.info('Problem in configuration') # configure providers with config providerOpenSubtitles = OpenSubtitlesProvider( username=config['OPENSUBTITLE']['username'], password=config['OPENSUBTITLE']['password']) # configure the cache region.configure('dogpile.cache.dbm', arguments={'filename': 'cachefile.dbm'}) # configure the path to scan pathToScan = config['DEFAULT']['pathToScan'] # scan for videos newer than 2 weeks and their existing subtitles in a folder videos = scan_videos(pathToScan, age=timedelta(days=30)) logger.info('Analyse video % s ' % (videos)) # Download all shooters shooter_providers = ['shooter'] shooter_subtitles = list_subtitles(videos, {Language('zho')}, providers=shooter_providers) for movie, subtitles in shooter_subtitles.items(): try: download_subtitles(subtitles) for subtitle in subtitles: if subtitle.content is None: logger.error('Skipping subtitle %r: no content' % subtitle) continue
#!/usr/bin/python from __future__ import unicode_literals # python 2 only from babelfish import Language from datetime import timedelta import subliminal # configure the cache subliminal.cache_region.configure('dogpile.cache.dbm', arguments={'filename': '/path/to/cachefile.dbm'}) # scan for videos in the folder and their subtitles videos = subliminal.scan_videos(['/path/to/video/folder'], subtitles=True, embedded_subtitles=True, age=timedelta(weeks=1)) # download best subtitles subtitles = subliminal.download_best_subtitles(videos, {Language('eng'), Language('fra')}, age=timedelta(weeks=1)) # save them to disk, next to the video subliminal.save_subtitles(subtitles)
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_subtitles_subliminal(subs_dirs): subliminal.cache_region.configure('dogpile.cache.dbm', arguments={'filename': 'cachefile.dbm'}) videos = subliminal.scan_videos(subs_dirs, subtitles=True) result = subliminal.download_best_subtitles(videos, {Language('eng')}) return result
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)
from datetime import timedelta from babelfish import Language from subliminal import download_best_subtitles, region, save_subtitles, scan_videos # configure the cache region.configure('dogpile.cache.dbm', arguments={'filename': 'cachefile.dbm'}) # scan for videos newer than 2 weeks and their existing subtitles in a folder videos = scan_videos( 'D:\TV Series\Westworld\[TorrentCouch.com].Westworld.S02.Complete.720p.WEB-DL.x264.[MP4].[5.3GB].[Season.2.Full]' ) # download best subtitles subtitles = download_best_subtitles(videos, {Language('eng')}) # save them to disk, next to the video for v in videos: save_subtitles(v, subtitles[v])