def get_best_subtitle(self): # Get a subtitle for a given video file self.status_bar.pop(self.context_id) self.status_bar.push(self.context_id, "Downloading Subtitle") self.timeout = GObject.timeout_add( 100, self.progress_pulse ) self.subtitle = download_best_subtitles( [self.video], { Language( self.language_combo.get_active_text() ) }, providers=[self.provider_combo.get_active_text()] ) try: self.subtitle = self.subtitle[self.video][0] self.status_bar.pop(self.context_id) self.status_bar.push(self.context_id, "Subtitle Downloaded Successfully") except IndexError: self.status_bar.pop(self.context_id) self.status_bar.push(self.context_id, "No Subtitle Found") GObject.source_remove(self.timeout) self.progress_bar.set_fraction(0) return False save_subtitles(self.video, [self.subtitle]) GObject.source_remove(self.timeout) self.progress_bar.set_fraction(1)
def test_save_subtitles(movies, tmpdir, monkeypatch): monkeypatch.chdir(str(tmpdir)) tmpdir.ensure(movies['man_of_steel'].name) subtitle_no_content = Subtitle(Language('eng')) subtitle = Subtitle(Language('fra')) subtitle.content = b'Some content' subtitle_other = Subtitle(Language('fra')) subtitle_other.content = b'Some other content' subtitle_pt_br = Subtitle(Language('por', 'BR')) subtitle_pt_br.content = b'Some brazilian content' subtitles = [subtitle_no_content, subtitle, subtitle_other, subtitle_pt_br] save_subtitles(movies['man_of_steel'], subtitles) # subtitle without content is skipped path = os.path.join(str(tmpdir), os.path.splitext(movies['man_of_steel'].name)[0] + '.en.srt') assert not os.path.exists(path) # first subtitle with language is saved path = os.path.join(str(tmpdir), os.path.splitext(movies['man_of_steel'].name)[0] + '.fr.srt') assert os.path.exists(path) assert io.open(path, 'rb').read() == b'Some content' # ietf language in path path = os.path.join(str(tmpdir), os.path.splitext(movies['man_of_steel'].name)[0] + '.pt-BR.srt') assert os.path.exists(path) assert io.open(path, 'rb').read() == b'Some brazilian content'
def test_save_subtitles(self): videos = [EPISODES[0], EPISODES[1]] for video in videos: video.name = os.path.join(TEST_DIR, os.path.split(video.name)[1]) languages = {Language('eng'), Language('fra')} subtitles = list_subtitles(videos, languages) # make a list of subtitles to download (one per language per video) subtitles_to_download = [] for video, video_subtitles in subtitles.items(): video_subtitle_languages = set() for video_subtitle in video_subtitles: if video_subtitle.language in video_subtitle_languages: continue subtitles_to_download.append(video_subtitle) video_subtitle_languages.add(video_subtitle.language) if video_subtitle_languages == languages: break self.assertEqual(len(subtitles_to_download), 4) # download download_subtitles(subtitles_to_download) save_subtitles(subtitles) for video in videos: self.assertTrue(os.path.exists(os.path.splitext(video.name)[0] + '.en.srt')) self.assertTrue(os.path.exists(os.path.splitext(video.name)[0] + '.fr.srt'))
def download_subs(videos, languages): """ Will scan for videos newer than one week and try to download subtitles in English and Spanish for them. Parameters ---------- videos: list of languages: list of babelfish.language """ nu_vids = [] for vid in videos: if len(vid.subtitle_languages) == 0: nu_vids.append(vid) # download try: subs = subliminal.download_best_subtitles(nu_vids, languages) except: raise log.info('Subs found:') log.info(subs) # save log.debug('Saving subtitle files.') subliminal.save_subtitles(subs, single=False)
def subtitle(self, episodes): # Parse babelfish languages bb_lang = {Language.fromietf(l) for l in self.languages} # Create subliminal episode set sub_episodes = set() for episode in episodes: ep_path = os.path.join(episode['dir'], episode['filename']) sub_episode = Episode.fromguess(ep_path, episode) # Look for external subtitles (not done automatically, apparently) sub_episode.subtitle_languages |= set(search_external_subtitles(sub_episode.name).values()) sub_episodes.add(sub_episode) # download subtitles in the specified language subl_subtitles = download_best_subtitles(sub_episodes, bb_lang, providers=self.providers) for video, subtitles in subl_subtitles.items(): save_subtitles(video, subtitles) # save subtitle languages in episode dict
def download_sub(self, language): l = Language(language) v = scan_video(self.path) sub_path = get_subtitle_path(v.name, l) if not os.path.isfile(sub_path): sub = download_best_subtitles((v,), {l}) # TODO Save in tmp folder if regular is not available save_subtitles(sub) return sub_path
def downloadSubtitles(subtitles_info): existing_subtitles = subtitles_info['subtitles'] # First of all, check if we need subtitles languages = getNeededLanguages(existing_subtitles) if not languages: logger.log(u'%s: No missing subtitles for S%02dE%02d' % (subtitles_info['show.indexerid'], subtitles_info['season'], subtitles_info['episode']), logger.DEBUG) return (existing_subtitles, None) subtitles_path = getSubtitlesPath(subtitles_info['location']).encode(sickbeard.SYS_ENCODING) video_path = subtitles_info['location'].encode(sickbeard.SYS_ENCODING) providers = getEnabledServiceList() try: video = subliminal.scan_video(video_path, subtitles=False, embedded_subtitles=False) except Exception: logger.log(u'%s: Exception caught in subliminal.scan_video for S%02dE%02d' % (subtitles_info['show.indexerid'], subtitles_info['season'], subtitles_info['episode']), logger.DEBUG) return (existing_subtitles, None) try: # TODO: Add gui option for hearing_impaired parameter ? found_subtitles = subliminal.download_best_subtitles([video], languages=languages, hearing_impaired=False, only_one=not sickbeard.SUBTITLES_MULTI, providers=providers) if not found_subtitles: logger.log(u'%s: No subtitles found for S%02dE%02d on any provider' % (subtitles_info['show.indexerid'], subtitles_info['season'], subtitles_info['episode']), logger.DEBUG) return (existing_subtitles, None) for index, subtitle in enumerate(found_subtitles[video]): encoding = subliminal.subtitle.Subtitle.guess_encoding(subtitle) found_subtitles[video][index].encoding = encoding subliminal.save_subtitles(video, found_subtitles[video], directory=subtitles_path, single=not sickbeard.SUBTITLES_MULTI) for video, subtitles in found_subtitles.iteritems(): for subtitle in subtitles: new_video_path = subtitles_path + "/" + video.name.rsplit("/", 1)[-1] new_subtitles_path = subliminal.subtitle.get_subtitle_path(new_video_path, subtitle.language if sickbeard.SUBTITLES_MULTI else None) sickbeard.helpers.chmodAsParent(new_subtitles_path) sickbeard.helpers.fixSetGroupID(new_subtitles_path) if not sickbeard.EMBEDDED_SUBTITLES_ALL and sickbeard.SUBTITLES_EXTRA_SCRIPTS and video_path.endswith(('.mkv','.mp4')): run_subs_extra_scripts(subtitles_info, found_subtitles) current_subtitles = subtitlesLanguages(video_path)[0] new_subtitles = frozenset(current_subtitles).difference(existing_subtitles) except Exception as e: logger.log("Error occurred when downloading subtitles for: %s" % video_path) logger.log(traceback.format_exc(), logger.ERROR) return (existing_subtitles, None) if sickbeard.SUBTITLES_HISTORY: for video, subtitles in found_subtitles.iteritems(): for subtitle in subtitles: logger.log(u'history.logSubtitle %s, %s' % (subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG) history.logSubtitle(subtitles_info['show.indexerid'], subtitles_info['season'], subtitles_info['episode'], subtitles_info['status'], subtitle) return (current_subtitles, new_subtitles)
def test_save_subtitles_single(self): videos = [EPISODES[0], EPISODES[1]] for video in videos: video.name = os.path.join(TEST_DIR, os.path.split(video.name)[1]) languages = {Language('eng'), Language('fra')} subtitles = download_best_subtitles(videos, languages) save_subtitles(subtitles, single=True) for video in videos: self.assertIn(video, subtitles) self.assertEqual(len(subtitles[video]), 2) self.assertTrue(os.path.exists(os.path.splitext(video.name)[0] + '.srt'))
def test_save_subtitles_single_directory_encoding(movies, tmpdir): subtitle = Subtitle(Language('jpn')) subtitle.content = u'ハローワールド'.encode('shift-jis') subtitle_pt_br = Subtitle(Language('por', 'BR')) subtitle_pt_br.content = b'Some brazilian content' subtitles = [subtitle, subtitle_pt_br] save_subtitles(movies['man_of_steel'], subtitles, single=True, directory=str(tmpdir), encoding='utf-8') # first subtitle only and correctly encoded path = os.path.join(str(tmpdir), os.path.splitext(os.path.split(movies['man_of_steel'].name)[1])[0] + '.srt') assert os.path.exists(path) assert io.open(path, encoding='utf-8').read() == u'ハローワールド'
def _download_subtitle(): # download the subtitle with ProviderPool(providers=self.config.providers, provider_configs=self.config.provider_configs) as pool: pool.download_subtitle(subtitle) # save the subtitle save_subtitles(self.video, [subtitle], single=self.config.single) # mark the subtitle as downloaded model.set_value(iter, 6, True) # stop the spinner self.spinner.stop()
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 download_subs(file): print(" Analyzing video file...") try: video = scan_video(file['full_path']) except ValueError as ex: print(" Failed to analyze video. ", ex) return None print(" Choosing subtitle from online providers...") best_subtitles = download_best_subtitles({video}, {Language('eng')}, only_one=True) if best_subtitles[video]: sub = best_subtitles[video][0] print(" Choosen subtitle: {f}".format(f=sub)) print(" Downloading...") save_subtitles(video, [sub], single=True) else: print(" ERROR: No subtitles found online.")
def save(self): """ Save the subtitle without further handling """ log.info("Saving subtitle") # Check download_item if 'video' in self._keys and 'subtitles' in self._keys and 'single' in self._keys: # Save the subtitle video = self._download_item['video'] subliminal.save_subtitles(video, self._download_item['subtitles'][video], self._download_item['single']) return True else: log.error("Download item is not complete, skipping") return False
def import_subs(filename): if not core.GETSUBS: return try: subliminal.cache_region.configure('dogpile.cache.memory') except: pass languages = set() for item in core.SLANGUAGES: try: languages.add(Language(item)) except: pass if not languages: return logger.debug("Attempting to download subtitles for %s" %(filename), 'SUBTITLES') try: # subliminal.logger = subliminal.logging.getLogger('subliminal') # subliminal.logger.setLevel(subliminal.logging.DEBUG) # ch = subliminal.logging.StreamHandler() # ch.setLevel(subliminal.logging.DEBUG) # formatter = subliminal.logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # ch.setFormatter(formatter) # subliminal.logger.addHandler(ch) video = subliminal.scan_video(filename, subtitles=True, embedded_subtitles=True) subtitles = subliminal.download_best_subtitles([video], languages, hearing_impaired=False) saved_subtitles = subliminal.save_subtitles(video, subtitles[video]) logger.debug("Saved subtitles:%s" %(saved_subtitles), 'SUBTITLES') except Exception as e: logger.error("Failed to download subtitles for %s due to: %s" %(filename, e), 'SUBTITLES')
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 save(self): """ Save the subtitle """ log.debug('Saving subtitle') # Check download_item (check for not None on boolean variables!) if self._download_item.video and self._download_item.subtitles and self._download_item.single is not None: # Save the subtitle video = self._download_item.video encoding = 'utf-8' if autosubliminal.SUBTITLEUTF8ENCODING else None subliminal.save_subtitles(video, self._download_item.subtitles, single=self._download_item.single, encoding=encoding) return True else: log.error('Download item is not complete, skipping') return False
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 on_subtitles_treeview_row_activated(self, treeview, path, view_column): model = treeview.get_model() iter = model.get_iter(path) # return if already downloaded if model.get_value(iter, 6): return # get the subtitle object subtitle = self.subtitles[model.get_value(iter, 3).lower() + '-' + model.get_value(iter, 0)] # download the subtitle with ProviderPool(providers=self.config.providers, provider_configs=self.config.provider_configs) as pool: pool.download_subtitle(subtitle) # save the subtitle save_subtitles(self.video, [subtitle], single=self.config.single) # mark the subtitle as downloaded model.set_value(iter, 6, True)
def run(self): """ Save the subtitle with further handling """ log.info("Running sub downloader") # Check download_item if 'video' in self._keys and 'subtitles' in self._keys and 'single' in self._keys: # Save the subtitle video = self._download_item['video'] subliminal.save_subtitles(video, self._download_item['subtitles'][video], self._download_item['single']) # Add download_item to last downloads self._download_item['timestamp'] = time.strftime('%Y-%m-%d %H:%M:%S') LastDownloads().set_last_downloads(self._download_item) # Notify if autosubliminal.NOTIFY: Notifier(self._download_item).notify_download() # Post processing if autosubliminal.POSTPROCESS: PostProcessor(self._download_item).run() # Show success message language = self._download_item['downlang'] name = utils.display_name(self._download_item) provider = self._download_item['provider'] utils.add_notification_message( "Downloaded '" + language + "' subtitle for '" + name + "' from '" + provider + "'", 'success') return True else: log.error("Download item is not complete, skipping") return False
def import_subs(filename): if not nzbtomedia.GETSUBS: return try: subliminal.cache_region.configure('dogpile.cache.memory') except: pass languages = set() for item in nzbtomedia.SLANGUAGES: try: languages.add(Language(item)) except: pass if not languages: return logger.debug("Attempting to download subtitles for %s" %(filename), 'SUBTITLES') try: video = subliminal.scan_video(filename, subtitles=True, embedded_subtitles=True) subtitles = subliminal.download_best_subtitles([video], languages, hearing_impaired=False) subliminal.save_subtitles(subtitles) except: logger.error("Failed to download subtitles for %s" %(filename), 'SUBTITLES')
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 import_subs(filename): if not core.GETSUBS: return try: subliminal.region.configure('dogpile.cache.dbm', arguments={'filename': 'cachefile.dbm'}) except: pass languages = set() for item in core.SLANGUAGES: try: languages.add(Language(item)) except: pass if not languages: return logger.info("Attempting to download subtitles for {0}".format(filename), 'SUBTITLES') try: video = subliminal.scan_video(filename) subtitles = subliminal.download_best_subtitles({video}, languages) subliminal.save_subtitles(video, subtitles[video]) except Exception as e: logger.error("Failed to download subtitles for {0} due to: {1}".format(filename, e), 'SUBTITLES')
def save_subs(tv_episode, video, found_subtitles, video_path=None): """Save subtitles. :param tv_episode: the episode to download subtitles :type tv_episode: sickbeard.tv.Episode :param video: :type video: subliminal.Video :param found_subtitles: :type found_subtitles: list of subliminal.Subtitle :param video_path: the video path. If none, the episode location will be used :type video_path: str :return: a sorted list of the opensubtitles codes for the downloaded subtitles :rtype: list of str """ video_path = video_path or tv_episode.location show_name = tv_episode.series.name season = tv_episode.season episode = tv_episode.episode episode_name = tv_episode.name show_indexerid = tv_episode.series.indexerid subtitles_dir = get_subtitles_dir(video_path) saved_subtitles = save_subtitles(video, found_subtitles, directory=subtitles_dir, single=not app.SUBTITLES_MULTI, encoding='utf-8') for subtitle in saved_subtitles: logger.info(u'Found subtitle for %s in %s provider with language %s', os.path.basename(video_path), subtitle.provider_name, subtitle.language.opensubtitles) subtitle_path = compute_subtitle_path(subtitle, video_path, subtitles_dir) helpers.chmod_as_parent(subtitle_path) helpers.fix_set_group_id(subtitle_path) if app.SUBTITLES_EXTRA_SCRIPTS and is_media_file(video_path): subtitle_path = compute_subtitle_path(subtitle, video_path, subtitles_dir) run_subs_extra_scripts(video_path=video_path, subtitle_path=subtitle_path, subtitle_language=subtitle.language, show_name=show_name, season=season, episode=episode, episode_name=episode_name, show_indexerid=show_indexerid) if app.SUBTITLES_HISTORY: logger.debug(u'Logging to history downloaded subtitle from provider %s and language %s', subtitle.provider_name, subtitle.language.opensubtitles) history.log_subtitle(tv_episode, subtitle) # Refresh the subtitles property if tv_episode.location: tv_episode.refresh_subtitles() return sorted({subtitle.language.opensubtitles for subtitle in saved_subtitles})
def generateOptions(self, inputfile, original=None): #Get path information from the input file input_dir, filename, input_extension = self.parseFile(inputfile) info = Converter(self.FFMPEG_PATH, self.FFPROBE_PATH).probe(inputfile) #Video stream print "Video codec detected: " + info.video.codec vcodec = 'copy' if info.video.codec in self.video_codec else self.video_codec[0] #Audio streams audio_settings = {} l = 0 for a in info.audio: print "Audio stream detected: " + a.codec + " " + a.language + " [Stream " + str(a.index) + "]" # Set undefined language to default language if specified if self.adl is not None and a.language == 'und': print "Undefined language detected, defaulting to " + self.adl a.language = self.adl # Proceed if no whitelist is set, or if the language is in the whitelist if self.awl is None or a.language in self.awl: # Create iOS friendly audio stream if the default audio stream has too many channels (iOS only likes AAC stereo) if self.iOS: if a.audio_channels > 2: print "Creating dual audio channels for iOS compatability for this stream" audio_settings.update({l: { 'map': a.index, 'codec': self.iOS, 'channels': 2, 'bitrate': 256, 'language': a.language, }}) l += 1 # If the iOS audio option is enabled and the source audio channel is only stereo, the additional iOS channel will be skipped and a single AAC 2.0 channel will be made regardless of codec preference to avoid multiple stereo channels if self.iOS and a.audio_channels == 2: acodec = 'copy' if a.codec == 'aac' else self.iOS else: # If desired codec is the same as the source codec, copy to avoid quality loss acodec = 'copy' if a.codec in self.audio_codec else self.audio_codec[0] # Bitrate calculations/overrides if self.audio_bitrate is None or self.audio_bitrate > (a.audio_channels * 256): abitrate = 256 * a.audio_channels else: abitrate = self.audio_bitrate audio_settings.update({l: { 'map': a.index, 'codec': acodec, 'channels': a.audio_channels, 'bitrate': abitrate, 'language': a.language, }}) l = l + 1 # Subtitle streams subtitle_settings = {} l = 0 for s in info.subtitle: print "Subtitle stream detected: " + s.codec + " " + s.language + " [Stream " + str(s.index) + "]" # Set undefined language to default language if specified if self.sdl is not None and s.language == 'und': s.language = self.sdl # Make sure its not an image based codec if s.codec not in bad_subtitle_codecs and self.embedsubs: # Proceed if no whitelist is set, or if the language is in the whitelist if self.swl is None or s.language in self.swl: subtitle_settings.update({l: { 'map': s.index, 'codec': 'mov_text', 'language': s.language #'forced': s.sub_forced, #'default': s.sub_default }}) l = l + 1 else: if self.swl is None or s.language in self.swl: ripsub = {1: { 'map': s.index, 'codec': 'srt', 'language': s.language }} options = { 'format': 'srt', 'subtitle': ripsub, } input_dir, filename, input_extension = self.parseFile(inputfile) output_dir = input_dir if self.output_dir is None else self.output_dir outputfile = os.path.join(output_dir, filename + "." + s.language + ".srt") i = 2 while os.path.isfile(outputfile): outputfile = os.path.join(output_dir, filename + "." + s.language + "." + str(i) + ".srt") i += i print "Ripping " + s.language + " subtitle from file" conv = Converter(self.FFMPEG_PATH, self.FFPROBE_PATH).convert(inputfile, outputfile, options, timeout=None) for timecode in conv: pass try: print outputfile + " created" except: print "File created" # External subtitle import # Attempt to download subtitles if they are missing using subliminal languages = set() if self.swl: for alpha3 in self.swl: languages.add(Language(alpha3)) elif self.sdl: languages.add(Language(self.sdl)) else: self.downloadsubs = False if self.downloadsubs: import subliminal print "Attempting to download subtitles, please wait" try: subliminal.cache_region.configure('dogpile.cache.memory') except: pass try: video = subliminal.scan_video(os.path.abspath(inputfile), subtitles=True, embedded_subtitles=True, original=original) subtitles = subliminal.download_best_subtitles([video], languages, hearing_impaired=False, providers=self.subproviders) subliminal.save_subtitles(subtitles) except Exception as e: print e print "Unable to download subtitle" src = 1 # FFMPEG input source number for dirName, subdirList, fileList in os.walk(input_dir): for fname in fileList: subname, subextension = os.path.splitext(fname) # Watch for appropriate file extension if subextension[1:] in valid_subtitle_extensions: x, lang = os.path.splitext(subname) lang = lang[1:] # Using bablefish to convert a 2 language code to a 3 language code if len(lang) is 2: try: babel = Language.fromalpha2(lang) lang = babel.alpha3 except: pass # If subtitle file name and input video name are the same, proceed if x == filename and self.embedsubs: print "External subtitle file detected, language " + lang if self.swl is None or lang in self.swl: print "Importing %s subtitle stream" % (fname) subtitle_settings.update({l: { 'path': os.path.join(dirName, fname), 'source': src, 'map': 0, 'codec': 'mov_text', 'language': lang, }}) l = l + 1 src = src + 1 self.deletesubs.add(os.path.join(dirName, fname)) else: print "Ignoring %s external subtitle stream due to language: %s" % (fname, lang) # Collect all options options = { 'format': self.output_format, 'video': { 'codec': vcodec, 'map': info.video.index, 'bitrate': info.format.bitrate }, 'audio': audio_settings, 'subtitle': subtitle_settings, } self.options = options return options
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 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_subtitles(filepath): video = scan_video(filepath) subtitles = download_best_subtitles([video], {Language('eng')}) save_subtitles(video, subtitles[video], single=True)
def generateOptions(self, inputfile, original=None): # Get path information from the input file input_dir, filename, input_extension = self.parseFile(inputfile) info = Converter(self.FFMPEG_PATH, self.FFPROBE_PATH).probe(inputfile) # Video stream self.log.info("Reading video stream.") self.log.info("Video codec detected: %s." % info.video.codec) try: vbr = self.estimateVideoBitrate(info) except: vbr = info.format.bitrate / 1000 if info.video.codec.lower() in self.video_codec: vcodec = 'copy' else: vcodec = self.video_codec[0] vbitrate = self.video_bitrate if self.video_bitrate else vbr self.log.info("Pix Fmt: %s." % info.video.pix_fmt) if self.pix_fmt and info.video.pix_fmt.lower() not in self.pix_fmt: vcodec = self.video_codec[0] if self.video_bitrate is not None and vbr > self.video_bitrate: self.log.debug("Overriding video bitrate. Codec cannot be copied because video bitrate is too high.") vcodec = self.video_codec[0] vbitrate = self.video_bitrate if self.video_width is not None and self.video_width < info.video.video_width: self.log.debug("Video width is over the max width, it will be downsampled. Video stream can no longer be copied.") vcodec = self.video_codec[0] vwidth = self.video_width else: vwidth = None if '264' in info.video.codec.lower() and self.h264_level and info.video.video_level and (info.video.video_level / 10 > self.h264_level): self.log.info("Video level %0.1f." % (info.video.video_level / 10)) vcodec = self.video_codec[0] self.log.debug("Video codec: %s." % vcodec) self.log.debug("Video bitrate: %s." % vbitrate) # Audio streams self.log.info("Reading audio streams.") overrideLang = True for a in info.audio: try: if a.metadata['language'].strip() == "" or a.metadata['language'] is None: a.metadata['language'] = 'und' except KeyError: a.metadata['language'] = 'und' if (a.metadata['language'] == 'und' and self.adl) or (self.awl and a.metadata['language'].lower() in self.awl): overrideLang = False break if overrideLang: self.awl = None self.log.info("No audio streams detected in any appropriate language, relaxing restrictions so there will be some audio stream present.") audio_settings = {} l = 0 for a in info.audio: try: if a.metadata['language'].strip() == "" or a.metadata['language'] is None: a.metadata['language'] = 'und' except KeyError: a.metadata['language'] = 'und' self.log.info("Audio detected for stream #%s: %s [%s]." % (a.index, a.codec, a.metadata['language'])) # Set undefined language to default language if specified if self.adl is not None and a.metadata['language'] == 'und': self.log.debug("Undefined language detected, defaulting to [%s]." % self.adl) a.metadata['language'] = self.adl # Proceed if no whitelist is set, or if the language is in the whitelist iosdata = None if self.awl is None or a.metadata['language'].lower() in self.awl: # Create iOS friendly audio stream if the default audio stream has too many channels (iOS only likes AAC stereo) if self.iOS and a.audio_channels > 2: iOSbitrate = 256 if (self.audio_bitrate * 2) > 256 else (self.audio_bitrate * 2) self.log.info("Creating audio stream %s from source audio stream %s [iOS-audio]." % (str(l), a.index)) self.log.debug("Audio codec: %s." % self.iOS[0]) self.log.debug("Channels: 2.") self.log.debug("Filter: %s." % self.iOS_filter) self.log.debug("Bitrate: %s." % iOSbitrate) self.log.debug("Language: %s." % a.metadata['language']) if l == 0: disposition = 'default' self.log.info("Audio track is number %s setting disposition to %s" % (str(l), disposition)) else: disposition = 'none' self.log.info("Audio track is number %s setting disposition to %s" % (str(l), disposition)) iosdata = { 'map': a.index, 'codec': self.iOS[0], 'channels': 2, 'bitrate': iOSbitrate, 'filter': self.iOS_filter, 'language': a.metadata['language'], 'disposition': disposition, } if not self.iOSLast: audio_settings.update({l: iosdata}) l += 1 # If the iOS audio option is enabled and the source audio channel is only stereo, the additional iOS channel will be skipped and a single AAC 2.0 channel will be made regardless of codec preference to avoid multiple stereo channels self.log.info("Creating audio stream %s from source stream %s." % (str(l), a.index)) if self.iOS and a.audio_channels <= 2: self.log.debug("Overriding default channel settings because iOS audio is enabled but the source is stereo [iOS-audio].") acodec = 'copy' if a.codec in self.iOS else self.iOS[0] audio_channels = a.audio_channels afilter = self.iOS_filter abitrate = a.audio_channels * 128 if (a.audio_channels * self.audio_bitrate) > (a.audio_channels * 128) else (a.audio_channels * self.audio_bitrate) else: # If desired codec is the same as the source codec, copy to avoid quality loss acodec = 'copy' if a.codec.lower() in self.audio_codec else self.audio_codec[0] # Audio channel adjustments if self.maxchannels and a.audio_channels > self.maxchannels: audio_channels = self.maxchannels if acodec == 'copy': acodec = self.audio_codec[0] abitrate = self.maxchannels * self.audio_bitrate else: audio_channels = a.audio_channels abitrate = a.audio_channels * self.audio_bitrate # Bitrate calculations/overrides if self.audio_bitrate is 0: self.log.debug("Attempting to set bitrate based on source stream bitrate.") try: abitrate = a.bitrate / 1000 except: self.log.warning("Unable to determine audio bitrate from source stream %s, defaulting to 256 per channel." % a.index) abitrate = a.audio_channels * 256 afilter = self.audio_filter self.log.debug("Audio codec: %s." % acodec) self.log.debug("Channels: %s." % audio_channels) self.log.debug("Bitrate: %s." % abitrate) self.log.debug("Language: %s" % a.metadata['language']) self.log.debug("Filter: %s" % afilter) # If the iOSFirst option is enabled, disable the iOS option after the first audio stream is processed if self.iOS and self.iOSFirst: self.log.debug("Not creating any additional iOS audio streams.") self.iOS = False # Set first track as default disposition if l == 0: disposition = 'default' self.log.info("Audio Track is number %s setting disposition to %s" % (a.index, disposition)) else: disposition = 'none' self.log.info("Audio Track is number %s setting disposition to %s" % (a.index, disposition)) audio_settings.update({l: { 'map': a.index, 'codec': acodec, 'channels': audio_channels, 'bitrate': abitrate, 'filter': afilter, 'language': a.metadata['language'], 'disposition': disposition, }}) if acodec == 'copy' and a.codec == 'aac' and self.aac_adtstoasc: audio_settings[l]['bsf'] = 'aac_adtstoasc' l += 1 #Add the iOS track last instead if self.iOSLast and iosdata: iosdata['disposition'] = 'none' audio_settings.update({l: iosdata}) l += 1 if self.audio_copyoriginal and acodec != 'copy': self.log.info("Adding copy of original audio track in format %s" % a.codec) audio_settings.update({l: { 'map': a.index, 'codec': 'copy', 'language': a.metadata['language'], 'disposition': 'none', }}) # Subtitle streams subtitle_settings = {} l = 0 self.log.info("Reading subtitle streams.") for s in info.subtitle: try: if s.metadata['language'].strip() == "" or s.metadata['language'] is None: s.metadata['language'] = 'und' except KeyError: s.metadata['language'] = 'und' self.log.info("Subtitle detected for stream #%s: %s [%s]." % (s.index, s.codec, s.metadata['language'])) # Set undefined language to default language if specified if self.sdl is not None and s.metadata['language'] == 'und': self.log.debug("Undefined language detected, defaulting to [%s]." % self.sdl) s.metadata['language'] = self.sdl # Make sure its not an image based codec if s.codec.lower() not in bad_subtitle_codecs and self.embedsubs: # Proceed if no whitelist is set, or if the language is in the whitelist if self.swl is None or s.metadata['language'].lower() in self.swl: subtitle_settings.update({l: { 'map': s.index, 'codec': self.scodec[0], 'language': s.metadata['language'], 'encoding': self.subencoding, # 'forced': s.sub_forced, # 'default': s.sub_default }}) self.log.info("Creating subtitle stream %s from source stream %s." % (l, s.index)) l = l + 1 elif s.codec.lower() not in bad_subtitle_codecs and not self.embedsubs: if self.swl is None or s.metadata['language'].lower() in self.swl: for codec in self.scodec: ripsub = {0: { 'map': s.index, 'codec': codec, 'language': s.metadata['language'] }} options = { 'format': codec, 'subtitle': ripsub, } try: extension = subtitle_codec_extensions[codec] except: self.log.info("Wasn't able to determine subtitle file extension, defaulting to '.srt'.") extension = 'srt' forced = ".forced" if s.sub_forced else "" input_dir, filename, input_extension = self.parseFile(inputfile) output_dir = input_dir if self.output_dir is None else self.output_dir outputfile = os.path.join(output_dir, filename + "." + s.metadata['language'] + forced + "." + extension) i = 2 while os.path.isfile(outputfile): self.log.debug("%s exists, appending %s to filename." % (outputfile, i)) outputfile = os.path.join(output_dir, filename + "." + s.metadata['language'] + forced + "." + str(i) + "." + extension) i += 1 try: self.log.info("Ripping %s subtitle from source stream %s into external file." % (s.metadata['language'], s.index)) conv = Converter(self.FFMPEG_PATH, self.FFPROBE_PATH).convert(inputfile, outputfile, options, timeout=None) for timecode in conv: pass self.log.info("%s created." % outputfile) except: self.log.exception("Unabled to create external subtitle file for stream %s." % (s.index)) # Attempt to download subtitles if they are missing using subliminal languages = set() try: if self.swl: for alpha3 in self.swl: languages.add(Language(alpha3)) elif self.sdl: languages.add(Language(self.sdl)) else: self.downloadsubs = False self.log.error("No valid subtitle language specified, cannot download subtitles.") except: self.log.exception("Unable to verify subtitle languages for download.") self.downloadsubs = False if self.downloadsubs: import subliminal self.log.info("Attempting to download subtitles.") # Attempt to set the dogpile cache try: subliminal.region.configure('dogpile.cache.memory') except: pass try: video = subliminal.scan_video(os.path.abspath(inputfile), subtitles=True, embedded_subtitles=True) subtitles = subliminal.download_best_subtitles([video], languages, hearing_impaired=False, providers=self.subproviders) try: subliminal.save_subtitles(video, subtitles[video]) except: # Support for older versions of subliminal subliminal.save_subtitles(subtitles) self.log.info("Please update to the latest version of subliminal.") except Exception as e: self.log.info("Unable to download subtitles.", exc_info=True) self.log.debug("Unable to download subtitles.", exc_info=True) # External subtitle import if self.embedsubs and not self.embedonlyinternalsubs: # Don't bother if we're not embeddeding subtitles and external subtitles src = 1 # FFMPEG input source number for dirName, subdirList, fileList in os.walk(input_dir): for fname in fileList: subname, subextension = os.path.splitext(fname) # Watch for appropriate file extension if subextension[1:] in valid_subtitle_extensions: x, lang = os.path.splitext(subname) lang = lang[1:] # Using bablefish to convert a 2 language code to a 3 language code if len(lang) is 2: try: babel = Language.fromalpha2(lang) lang = babel.alpha3 except: pass # If subtitle file name and input video name are the same, proceed if x == filename: self.log.info("External %s subtitle file detected." % lang) if self.swl is None or lang in self.swl: self.log.info("Creating subtitle stream %s by importing %s." % (l, fname)) subtitle_settings.update({l: { 'path': os.path.join(dirName, fname), 'source': src, 'map': 0, 'codec': 'mov_text', 'language': lang}}) self.log.debug("Path: %s." % os.path.join(dirName, fname)) self.log.debug("Source: %s." % src) self.log.debug("Codec: mov_text.") self.log.debug("Langauge: %s." % lang) l = l + 1 src = src + 1 self.deletesubs.add(os.path.join(dirName, fname)) else: self.log.info("Ignoring %s external subtitle stream due to language %s." % (fname, lang)) # Collect all options options = { 'format': self.output_format, 'video': { 'codec': vcodec, 'map': info.video.index, 'bitrate': vbitrate, 'level': self.h264_level }, 'audio': audio_settings, 'subtitle': subtitle_settings, 'preopts': [], 'postopts': ['-threads', self.threads] } # If a CRF option is set, override the determine bitrate if self.vcrf: del options['video']['bitrate'] options['video']['crf'] = self.vcrf if len(options['subtitle']) > 0: options['preopts'].append('-fix_sub_duration') if self.preopts: options['preopts'].extend(self.preopts) if self.postopts: options['postopts'].extend(self.postopts) # If using h264qsv, add the codec in front of the input for decoding if vcodec == "h264qsv" and info.video.codec.lower() == "h264" and self.qsv_decoder and (info.video.video_level / 10) < 5: options['preopts'].extend(['-vcodec', 'h264_qsv']) if self.dxva2_decoder: # DXVA2 will fallback to CPU decoding when it hits a file that it cannot handle, so we don't need to check if the file is supported. options['preopts'].extend(['-hwaccel', 'dxva2' ]) # Add width option if vwidth: options['video']['width'] = vwidth # Add pix_fmt if self.pix_fmt: options['video']['pix_fmt'] = self.pix_fmt[0] self.options = options return options
def on_task_output(self, task, config): """ Configuration:: subliminal: languages: List of languages (as IETF codes) in order of preference. At least one is required. alternatives: List of second-choice languages; subs will be downloaded but entries rejected. exact_match: Use file hash only to search for subs, otherwise Subliminal will try to guess by filename. providers: List of providers from where to download subtitles. single: Download subtitles in single mode (no language code added to subtitle filename). """ if not task.accepted: log.debug('nothing accepted, aborting') return from babelfish import Language from dogpile.cache.exception import RegionAlreadyConfigured import subliminal from subliminal.cli import MutexLock try: subliminal.region.configure('dogpile.cache.dbm', arguments={'filename': os.path.join(tempfile.gettempdir(), 'cachefile.dbm'), 'lock_factory': MutexLock}) except RegionAlreadyConfigured: pass logging.getLogger("subliminal").setLevel(logging.CRITICAL) logging.getLogger("enzyme").setLevel(logging.WARNING) langs = set([Language.fromietf(s) for s in config.get('languages', [])]) alts = set([Language.fromietf(s) for s in config.get('alternatives', [])]) # keep all downloaded subtitles and save to disk when done (no need to write every time) downloaded_subtitles = collections.defaultdict(list) providers_list = config.get('providers', None) # test if only one language was provided, if so we will download in single mode # (aka no language code added to subtitle filename) # unless we are forced not to by configuration # if we pass 'yes' for single in configuration but choose more than one language # we ignore the configuration and add the language code to the # potentially downloaded files single_mode = config.get('single', '') and len(langs | alts) <= 1 for entry in task.accepted: if 'location' not in entry: log.warning('Cannot act on entries that do not represent a local file.') elif not os.path.exists(entry['location']): entry.fail('file not found: %s' % entry['location']) elif '$RECYCLE.BIN' not in entry['location']: # ignore deleted files in Windows shares try: entry_langs = entry.get('subtitle_languages', []) if not entry_langs: entry_langs = langs video = subliminal.scan_video(entry['location']) if isinstance(video, subliminal.Episode): title = video.series else: title = video.title log.info('Name computed for %s was %s' % (entry['location'], title)) msc = video.scores['hash'] if config['exact_match'] else 0 if entry_langs & video.subtitle_languages: entry['subtitles_missing'] = set() continue # subs for preferred lang(s) already exists else: subtitle = subliminal.download_best_subtitles([video], entry_langs, providers=providers_list, min_score=msc) if subtitle and any(subtitle.values()): downloaded_subtitles.update(subtitle) log.info('Subtitles found for %s' % entry['location']) else: # TODO check performance hit -- this explicit check may be better on slower devices # but subliminal already handles it for us, but it loops over all providers before stopping remaining_alts = alts - video.subtitle_languages if remaining_alts: # only try to download for alternatives that aren't alread downloaded subtitle = subliminal.download_best_subtitles([video], remaining_alts, providers=providers_list, min_score=msc) # this potentially just checks an already checked assignment bleh if subtitle and any(subtitle.values()): downloaded_subtitles.update(subtitle) entry.fail('subtitles found for a second-choice language.') else: entry.fail('cannot find any subtitles for now.') downloaded_languages = set([Language.fromietf(unicode(l.language)) for l in subtitle[video]]) if entry_langs: entry['subtitles_missing'] = entry_langs - downloaded_languages except Exception as err: # don't want to abort the entire task for errors in a # single video file or for occasional network timeouts if err.args: msg = unicode(err.args[0]) else: # Subliminal errors don't always have a message, just use the name msg = 'subliminal error: %s' % err.__class__.__name__ log.debug(msg) entry.fail(msg) if downloaded_subtitles: # save subtitles to disk for k, v in downloaded_subtitles.iteritems(): if v: subliminal.save_subtitles(k, v, single=single_mode)
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 # 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_subtitles(subtitles_info): # pylint: disable=too-many-locals, too-many-branches, too-many-statements existing_subtitles = subtitles_info['subtitles'] if not needs_subtitles(existing_subtitles): logger.log( u'Episode already has all needed subtitles, skipping {} {}'.format( subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG) return existing_subtitles, None # Check if we really need subtitles languages = get_needed_languages(existing_subtitles) if not languages: logger.log( u'No subtitles needed for {} {}'.format( subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG) return existing_subtitles, None subtitles_path = get_subtitles_path(subtitles_info['location']) video_path = subtitles_info['location'] # Perfect match = hash score - hearing impaired score - resolution score (subtitle for 720p its the same for 1080p) # Perfect match = 215 -1 -1 = 213 # No-perfect match = series + year + season + episode # No-perfect match = 108 + 54 + 18 + 18 = 198 # From latest subliminal code: # episode_scores = {'hash': 215, 'series': 108, 'year': 54, 'season': 18, 'episode': 18, 'release_group': 9, # 'format': 4, 'audio_codec': 2, 'resolution': 1, 'hearing_impaired': 1, 'video_codec': 1} user_score = 213 if sickbeard.SUBTITLES_PERFECT_MATCH else 198 video = get_video(video_path, subtitles_path=subtitles_path) if not video: logger.log( u'Exception caught in subliminal.scan_video for {} {}'.format( subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG) return existing_subtitles, None providers = enabled_service_list() provider_configs = { 'addic7ed': { 'username': sickbeard.ADDIC7ED_USER, 'password': sickbeard.ADDIC7ED_PASS }, 'legendastv': { 'username': sickbeard.LEGENDASTV_USER, 'password': sickbeard.LEGENDASTV_PASS }, 'opensubtitles': { 'username': sickbeard.OPENSUBTITLES_USER, 'password': sickbeard.OPENSUBTITLES_PASS } } pool = ProviderPool(providers=providers, provider_configs=provider_configs) try: subtitles_list = pool.list_subtitles(video, languages) for provider in providers: if provider in pool.discarded_providers: logger.log( u'Could not search in {} provider. Discarding for now'. format(provider), logger.DEBUG) if not subtitles_list: logger.log( u'No subtitles found for {} {}'.format( subtitles_info['show_name'], episode_num(subtitles_info['season'], subtitles_info['episode']) or episode_num(subtitles_info['season'], subtitles_info['episode'], numbering='absolute')), logger.DEBUG) return existing_subtitles, None for subtitle in subtitles_list: score = subliminal.score.compute_score( subtitle, video, hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED) logger.log( u'[{}] Subtitle score for {} is: {} (min={})'.format( subtitle.provider_name, subtitle.id, score, user_score), logger.DEBUG) found_subtitles = pool.download_best_subtitles( subtitles_list, video, languages=languages, hearing_impaired=sickbeard.SUBTITLES_HEARING_IMPAIRED, min_score=user_score, only_one=not sickbeard.SUBTITLES_MULTI) subliminal.save_subtitles(video, found_subtitles, directory=subtitles_path, single=not sickbeard.SUBTITLES_MULTI) except IOError as error: if 'No space left on device' in ex(error): logger.log(u'Not enough space on the drive to save subtitles', logger.WARNING) else: logger.log(traceback.format_exc(), logger.WARNING) except Exception: logger.log(u'Error occurred when downloading subtitles for: {}'.format( video_path)) logger.log(traceback.format_exc(), logger.ERROR) return existing_subtitles, None for subtitle in found_subtitles: subtitle_path = subliminal.subtitle.get_subtitle_path( video.name, None if not sickbeard.SUBTITLES_MULTI else subtitle.language) if subtitles_path is not None: subtitle_path = os.path.join(subtitles_path, os.path.split(subtitle_path)[1]) sickbeard.helpers.chmodAsParent(subtitle_path) sickbeard.helpers.fixSetGroupID(subtitle_path) if sickbeard.SUBTITLES_HISTORY: logger.log( u'history.logSubtitle {}, {}'.format( subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG) history.logSubtitle(subtitles_info['show_indexerid'], subtitles_info['season'], subtitles_info['episode'], subtitles_info['status'], subtitle) if sickbeard.SUBTITLES_EXTRA_SCRIPTS and isMediaFile( video_path) and not sickbeard.EMBEDDED_SUBTITLES_ALL: run_subs_extra_scripts(subtitles_info, subtitle, video, single=not sickbeard.SUBTITLES_MULTI) new_subtitles = sorted( {subtitle.language.opensubtitles for subtitle in found_subtitles}) current_subtitles = sorted( {subtitle for subtitle in new_subtitles + existing_subtitles}) if existing_subtitles else new_subtitles if not sickbeard.SUBTITLES_MULTI and len(found_subtitles) == 1: new_code = found_subtitles[0].language.opensubtitles if new_code not in existing_subtitles: current_subtitles.remove(new_code) current_subtitles.append('und') return current_subtitles, new_subtitles
def subtitles_download_in_pp(): # pylint: disable=too-many-locals, too-many-branches logger.log(u'Checking for needed subtitles in Post-Process folder', logger.INFO) providers = enabled_service_list() provider_configs = { 'addic7ed': { 'username': sickbeard.ADDIC7ED_USER, 'password': sickbeard.ADDIC7ED_PASS }, 'legendastv': { 'username': sickbeard.LEGENDASTV_USER, 'password': sickbeard.LEGENDASTV_PASS }, 'opensubtitles': { 'username': sickbeard.OPENSUBTITLES_USER, 'password': sickbeard.OPENSUBTITLES_PASS } } pool = ProviderPool(providers=providers, provider_configs=provider_configs) # Search for all wanted languages languages = {from_code(language) for language in wanted_languages()} if not languages: return run_post_process = False # Check if PP folder is set if sickbeard.TV_DOWNLOAD_DIR and os.path.isdir( sickbeard.TV_DOWNLOAD_DIR): for root, _, files in os.walk(sickbeard.TV_DOWNLOAD_DIR, topdown=False): rar_files = [x for x in files if isRarFile(x)] if rar_files and sickbeard.UNPACK: video_files = [x for x in files if isMediaFile(x)] if u'_UNPACK' not in root and ( not video_files or root == sickbeard.TV_DOWNLOAD_DIR): logger.log( u'Found rar files in post-process folder: {}'. format(rar_files), logger.DEBUG) result = processTV.ProcessResult() processTV.unRAR(root, rar_files, False, result) elif rar_files and not sickbeard.UNPACK: logger.log( u'Unpack is disabled. Skipping: {}'.format(rar_files), logger.WARNING) for root, _, files in os.walk(sickbeard.TV_DOWNLOAD_DIR, topdown=False): for video_filename in sorted(files): try: # Remove non release groups from video file. Needed to match subtitles new_video_filename = remove_non_release_groups( video_filename) if new_video_filename != video_filename: os.rename(video_filename, new_video_filename) video_filename = new_video_filename except Exception as error: logger.log( u'Couldn\'t remove non release groups from video file. Error: {}' .format(ex(error)), logger.DEBUG) if isMediaFile(video_filename): try: video = subliminal.scan_video( os.path.join(root, video_filename), subtitles=False, embedded_subtitles=False) subtitles_list = pool.list_subtitles( video, languages) for provider in providers: if provider in pool.discarded_providers: logger.log( u'Could not search in {} provider. Discarding for now' .format(provider), logger.DEBUG) if not subtitles_list: logger.log( u'No subtitles found for {}'.format( os.path.join(root, video_filename)), logger.DEBUG) continue logger.log( u'Found subtitle(s) canditate(s) for {}'. format(video_filename), logger.INFO) hearing_impaired = sickbeard.SUBTITLES_HEARING_IMPAIRED user_score = 213 if sickbeard.SUBTITLES_PERFECT_MATCH else 198 found_subtitles = pool.download_best_subtitles( subtitles_list, video, languages=languages, hearing_impaired=hearing_impaired, min_score=user_score, only_one=not sickbeard.SUBTITLES_MULTI) for subtitle in subtitles_list: score = subliminal.score.compute_score( subtitle, video, hearing_impaired=sickbeard. SUBTITLES_HEARING_IMPAIRED) logger.log( u'[{}] Subtitle score for {} is: {} (min={})' .format(subtitle.provider_name, subtitle.id, score, user_score), logger.DEBUG) downloaded_languages = set() for subtitle in found_subtitles: logger.log( u'Found subtitle for {} in {} provider with language {}' .format(os.path.join(root, video_filename), subtitle.provider_name, subtitle.language.opensubtitles), logger.DEBUG) subliminal.save_subtitles( video, found_subtitles, directory=root, single=not sickbeard.SUBTITLES_MULTI) subtitles_multi = not sickbeard.SUBTITLES_MULTI subtitle_path = subliminal.subtitle.get_subtitle_path( video.name, None if subtitles_multi else subtitle.language) if root is not None: subtitle_path = os.path.join( root, os.path.split(subtitle_path)[1]) sickbeard.helpers.chmodAsParent(subtitle_path) sickbeard.helpers.fixSetGroupID(subtitle_path) downloaded_languages.add( subtitle.language.opensubtitles) # Don't run post processor unless at least one file has all of the needed subtitles if not needs_subtitles(downloaded_languages): run_post_process = True except Exception as error: logger.log( u'Error occurred when downloading subtitles for: {}. Error: {}' .format(os.path.join(root, video_filename), ex(error))) if run_post_process: logger.log( u'Starting post-process with default settings now that we found subtitles' ) processTV.processDir(sickbeard.TV_DOWNLOAD_DIR)
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 = 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( "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 == True: 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 download_result == False: 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 == True: video = scan_video(path) single = get_general_settings()[7] 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 == 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( '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 download_subtitles(episode, force_lang=None): existing_subtitles = episode.subtitles if not needs_subtitles(existing_subtitles, force_lang): logger.debug( "Episode already has all needed subtitles, skipping {0} {1}".format( episode.show.name, episode_num(episode.season, episode.episode) or episode_num(episode.season, episode.episode, numbering="absolute") ) ) return existing_subtitles, None if not force_lang: languages = get_needed_languages(existing_subtitles) else: languages = {from_code(force_lang)} if not languages: logger.debug( "No subtitles needed for {0} {1}".format( episode.show.name, episode_num(episode.season, episode.episode) or episode_num(episode.season, episode.episode, numbering="absolute") ) ) return existing_subtitles, None subtitles_path = get_subtitles_path(episode.location) video_path = episode.location # Perfect match = hash score - hearing impaired score - resolution score # (subtitle for 720p is the same as for 1080p) # Perfect match = 215 - 1 - 1 = 213 # Non-perfect match = series + year + season + episode # Non-perfect match = 108 + 54 + 18 + 18 = 198 # From latest subliminal code: # episode_scores = {'hash': 215, 'series': 108, 'year': 54, 'season': 18, 'episode': 18, 'release_group': 9, # 'source': 4, 'audio_codec': 2, 'resolution': 1, 'hearing_impaired': 1, 'video_codec': 1} user_score = 213 if settings.SUBTITLES_PERFECT_MATCH else 198 video = get_video(video_path, subtitles_path=subtitles_path, episode=episode) if not video: logger.debug( "Exception caught in subliminal.scan_video for {0} {1}".format( episode.show.name, episode_num(episode.season, episode.episode) or episode_num(episode.season, episode.episode, numbering="absolute") ) ) return existing_subtitles, None providers = enabled_service_list() pool = SubtitleProviderPool() try: subtitles_list = pool.list_subtitles(video, languages) for provider in providers: if provider in pool.discarded_providers: logger.debug("Could not search in {0} provider. Discarding for now".format(provider)) if not subtitles_list: logger.debug( "No subtitles found for {0} {1}".format( episode.show.name, episode_num(episode.season, episode.episode) or episode_num(episode.season, episode.episode, numbering="absolute") ) ) return existing_subtitles, None for subtitle in subtitles_list: score = subliminal.score.compute_score(subtitle, video, hearing_impaired=settings.SUBTITLES_HEARING_IMPAIRED) logger.debug("[{0}] Subtitle score for {1} is: {2} (min={3})".format(subtitle.provider_name, subtitle.id, score, user_score)) found_subtitles = pool.download_best_subtitles( subtitles_list, video, languages=languages, hearing_impaired=settings.SUBTITLES_HEARING_IMPAIRED, min_score=user_score, only_one=not settings.SUBTITLES_MULTI, ) subliminal.save_subtitles(video, found_subtitles, directory=subtitles_path, single=not settings.SUBTITLES_MULTI, encoding="utf8") except IOError as error: if "No space left on device" in str(error): logger.warning("Not enough space on the drive to save subtitles") else: logger.warning(traceback.format_exc()) return existing_subtitles, None except Exception: logger.info("Error occurred when downloading subtitles for: {0}".format(video_path)) logger.exception(traceback.format_exc()) return existing_subtitles, None for subtitle in found_subtitles: subtitle_path = subliminal.subtitle.get_subtitle_path(video.name, None if not settings.SUBTITLES_MULTI else subtitle.language) if subtitles_path is not None: subtitle_path = os.path.join(subtitles_path, os.path.split(subtitle_path)[1]) sickchill.oldbeard.helpers.chmodAsParent(subtitle_path) sickchill.oldbeard.helpers.fixSetGroupID(subtitle_path) if settings.SUBTITLES_HISTORY: logger.debug("history.logSubtitle {0}, {1}".format(subtitle.provider_name, subtitle.language.opensubtitles)) history.logSubtitle(episode.show.indexerid, episode.season, episode.episode, episode.status, subtitle) if settings.SUBTITLES_EXTRA_SCRIPTS and is_media_file(video_path) and not settings.EMBEDDED_SUBTITLES_ALL: run_subs_extra_scripts(episode, subtitle, video, single=not settings.SUBTITLES_MULTI) new_subtitles = sorted({subtitle.language.opensubtitles for subtitle in found_subtitles}) current_subtitles = sorted({subtitle for subtitle in new_subtitles + existing_subtitles}) if existing_subtitles else new_subtitles if not settings.SUBTITLES_MULTI and len(found_subtitles) == 1: new_code = found_subtitles[0].language.opensubtitles if new_code not in existing_subtitles: current_subtitles.remove(new_code) current_subtitles.append("und") return current_subtitles, new_subtitles
def on_task_output(self, task, config): """ Configuration:: subliminal: languages: List of languages (as IETF codes) in order of preference. At least one is required. alternatives: List of second-choice languages; subs will be downloaded but entries rejected. exact_match: Use file hash only to search for subs, otherwise Subliminal will try to guess by filename. providers: List of providers from where to download subtitles. single: Download subtitles in single mode (no language code added to subtitle filename). """ if not task.accepted: log.debug('nothing accepted, aborting') return from babelfish import Language from dogpile.cache.exception import RegionAlreadyConfigured import subliminal from subliminal.cli import MutexLock from subliminal.score import episode_scores, movie_scores try: subliminal.region.configure('dogpile.cache.dbm', arguments={ 'filename': os.path.join(tempfile.gettempdir(), 'cachefile.dbm'), 'lock_factory': MutexLock, }) except RegionAlreadyConfigured: pass logging.getLogger("subliminal").setLevel(logging.CRITICAL) logging.getLogger("enzyme").setLevel(logging.WARNING) languages = set([Language.fromietf(s) for s in config.get('languages', [])]) alternative_languages = set([Language.fromietf(s) for s in config.get('alternatives', [])]) # keep all downloaded subtitles and save to disk when done (no need to write every time) downloaded_subtitles = collections.defaultdict(list) providers_list = config.get('providers', None) # test if only one language was provided, if so we will download in single mode # (aka no language code added to subtitle filename) # unless we are forced not to by configuration # if we pass 'yes' for single in configuration but choose more than one language # we ignore the configuration and add the language code to the # potentially downloaded files single_mode = config.get('single', '') and len(languages | alternative_languages) <= 1 for entry in task.accepted: if 'location' not in entry: log.warning('Cannot act on entries that do not represent a local file.') elif not os.path.exists(entry['location']): entry.fail('file not found: %s' % entry['location']) elif '$RECYCLE.BIN' not in entry['location']: # ignore deleted files in Windows shares try: entry_languages = entry.get('subtitle_languages') or languages video = subliminal.scan_video(entry['location']) existing_subtitles = set(subliminal.core.search_external_subtitles(entry['location']).values()) video.subtitle_languages = existing_subtitles if isinstance(video, subliminal.Episode): title = video.series hash_scores = episode_scores['hash'] else: title = video.title hash_scores = movie_scores['hash'] log.info('Name computed for %s was %s', entry['location'], title) msc = hash_scores if config['exact_match'] else 0 if entry_languages & existing_subtitles: log.debug('All preferred languages already exist for "%s"', entry['title']) entry['subtitles_missing'] = set() continue # subs for preferred lang(s) already exists else: subtitle = subliminal.download_best_subtitles([video], entry_languages, providers=providers_list, min_score=msc) if subtitle and any(subtitle.values()): downloaded_subtitles.update(subtitle) log.info('Subtitles found for %s', entry['location']) else: # only try to download for alternatives that aren't alread downloaded subtitle = subliminal.download_best_subtitles([video], alternative_languages, providers=providers_list, min_score=msc) if subtitle and any(subtitle.values()): downloaded_subtitles.update(subtitle) entry.fail('subtitles found for a second-choice language.') else: entry.fail('cannot find any subtitles for now.') downloaded_languages = set([Language.fromietf(str(l.language)) for l in subtitle[video]]) if entry_languages: entry['subtitles_missing'] = entry_languages - downloaded_languages except ValueError as e: log.error('subliminal error: %s', e) entry.fail() if downloaded_subtitles: # save subtitles to disk for video, subtitle in downloaded_subtitles.items(): if subtitle: subliminal.save_subtitles(video, subtitle, single=single_mode)
def on_task_output(self, task, config): """ Configuration:: subliminal: languages: List of languages (as IETF codes) in order of preference. At least one is required. alternatives: List of second-choice languages; subs will be downloaded but entries rejected. exact_match: Use file hash only to search for subs, otherwise Subliminal will try to guess by filename. providers: List of providers from where to download subtitles. single: Download subtitles in single mode (no language code added to subtitle filename). directory: Path to directory where to save the subtitles, default is next to the video. hearing_impaired: Prefer subtitles for the hearing impaired when available authentication: > Dictionary of configuration options for different providers. Keys correspond to provider names, and values are dictionaries, usually specifying `username` and `password`. """ if not task.accepted: log.debug('nothing accepted, aborting') return from babelfish import Language from dogpile.cache.exception import RegionAlreadyConfigured import subliminal from subliminal import scan_video, save_subtitles from subliminal.cli import MutexLock from subliminal.core import ARCHIVE_EXTENSIONS, scan_archive, refine, search_external_subtitles from subliminal.score import episode_scores, movie_scores from subliminal.video import VIDEO_EXTENSIONS try: subliminal.region.configure('dogpile.cache.dbm', arguments={ 'filename': os.path.join(tempfile.gettempdir(), 'cachefile.dbm'), 'lock_factory': MutexLock, }) except RegionAlreadyConfigured: pass # Let subliminal be more verbose if our logger is set to DEBUG if log.isEnabledFor(logging.DEBUG): logging.getLogger("subliminal").setLevel(logging.INFO) else: logging.getLogger("subliminal").setLevel(logging.CRITICAL) logging.getLogger("dogpile").setLevel(logging.CRITICAL) logging.getLogger("enzyme").setLevel(logging.WARNING) try: languages = set([Language.fromietf(s) for s in config.get('languages', [])]) alternative_languages = set([Language.fromietf(s) for s in config.get('alternatives', [])]) except ValueError as e: raise plugin.PluginError(e) # keep all downloaded subtitles and save to disk when done (no need to write every time) downloaded_subtitles = collections.defaultdict(list) providers_list = config.get('providers', None) provider_configs = config.get('authentication', None) # test if only one language was provided, if so we will download in single mode # (aka no language code added to subtitle filename) # unless we are forced not to by configuration # if we pass 'yes' for single in configuration but choose more than one language # we ignore the configuration and add the language code to the # potentially downloaded files single_mode = config.get('single', '') and len(languages | alternative_languages) <= 1 hearing_impaired = config.get('hearing_impaired', False) with subliminal.core.ProviderPool(providers=providers_list, provider_configs=provider_configs) as provider_pool: for entry in task.accepted: if 'location' not in entry: log.warning('Cannot act on entries that do not represent a local file.') continue if not os.path.exists(entry['location']): entry.fail('file not found: %s' % entry['location']) continue if '$RECYCLE.BIN' in entry['location']: # ignore deleted files in Windows shares continue try: entry_languages = set(entry.get('subtitle_languages', [])) or languages if entry['location'].endswith(VIDEO_EXTENSIONS): video = scan_video(entry['location']) elif entry['location'].endswith(ARCHIVE_EXTENSIONS): video = scan_archive(entry['location']) else: entry.reject('File extension is not a supported video or archive extension') continue # use metadata refiner to get mkv metadata refiner = ('metadata',) refine(video, episode_refiners=refiner, movie_refiners=refiner) existing_subtitles = set(search_external_subtitles(entry['location']).values()) video.subtitle_languages |= existing_subtitles if isinstance(video, subliminal.Episode): title = video.series hash_scores = episode_scores['hash'] else: title = video.title hash_scores = movie_scores['hash'] log.info('Name computed for %s was %s', entry['location'], title) msc = hash_scores if config['exact_match'] else 0 if entry_languages.issubset(video.subtitle_languages): log.debug('All preferred languages already exist for "%s"', entry['title']) entry['subtitles_missing'] = set() continue # subs for preferred lang(s) already exists else: # Gather the subtitles for the alternative languages too, to avoid needing to search the sites # again. They'll just be ignored if the main languages are found. all_subtitles = provider_pool.list_subtitles(video, entry_languages | alternative_languages) try: subtitles = provider_pool.download_best_subtitles(all_subtitles, video, entry_languages, min_score=msc, hearing_impaired=hearing_impaired) except TypeError as e: log.error('Downloading subtitles failed due to a bug in subliminal. Please see' 'https://github.com/Diaoul/subliminal/issues/921. Error: %s', e) subtitles = [] if subtitles: downloaded_subtitles[video].extend(subtitles) log.info('Subtitles found for %s', entry['location']) else: # only try to download for alternatives that aren't already downloaded subtitles = provider_pool.download_best_subtitles(all_subtitles, video, alternative_languages, min_score=msc, hearing_impaired=hearing_impaired) if subtitles: downloaded_subtitles[video].extend(subtitles) entry.reject('subtitles found for a second-choice language.') else: entry.reject('cannot find any subtitles for now.') downloaded_languages = set([Language.fromietf(str(l.language)) for l in subtitles]) if entry_languages: entry['subtitles_missing'] = entry_languages - downloaded_languages if len(entry['subtitles_missing']) > 0: entry.reject('Subtitles for all primary languages not found') except ValueError as e: log.error('subliminal error: %s', e) entry.fail() if downloaded_subtitles: if task.options.test: log.verbose('Test mode. Found subtitles:') # save subtitles to disk for video, subtitle in downloaded_subtitles.items(): if subtitle: _directory = config.get('directory') if _directory: _directory = os.path.expanduser(_directory) if task.options.test: log.verbose(' FOUND LANGUAGES %s for %s', [str(l.language) for l in subtitle], video.name) continue save_subtitles(video, subtitle, single=single_mode, directory=_directory)
def generateOptions(self, inputfile, original=None): # Get path information from the input file input_dir, filename, input_extension = self.parseFile(inputfile) info = Converter(self.FFMPEG_PATH, self.FFPROBE_PATH).probe(inputfile) # Video stream self.log.info("Reading video stream.") self.log.info("Video codec detected: %s." % info.video.codec) try: vbr = self.estimateVideoBitrate(info) except: vbr = info.format.bitrate / 1000 if info.video.codec.lower() in self.video_codec: vcodec = "copy" else: vcodec = self.video_codec[0] vbitrate = self.video_bitrate if self.video_bitrate else vbr self.log.info("Pix Fmt: %s." % info.video.pix_fmt) if self.pix_fmt and info.video.pix_fmt.lower() not in self.pix_fmt: vcodec = self.video_codec[0] if self.video_bitrate is not None and vbr > self.video_bitrate: self.log.debug("Overriding video bitrate. Codec cannot be copied because video bitrate is too high.") vcodec = self.video_codec[0] vbitrate = self.video_bitrate if self.video_width is not None and self.video_width < info.video.video_width: self.log.debug( "Video width is over the max width, it will be downsampled. Video stream can no longer be copied." ) vcodec = self.video_codec[0] vwidth = self.video_width else: vwidth = None if self.h264_level and info.video.video_level and (info.video.video_level / 10 > self.h264_level): self.log.info("Video level %0.1f." % (info.video.video_level / 10)) vcodec = self.video_codec[0] self.log.debug("Video codec: %s." % vcodec) self.log.debug("Video bitrate: %s." % vbitrate) # Audio streams self.log.info("Reading audio streams.") overrideLang = True for a in info.audio: try: if a.metadata["language"].strip() == "" or a.metadata["language"] is None: a.metadata["language"] = "und" except KeyError: a.metadata["language"] = "und" if (a.metadata["language"] == "und" and self.adl) or ( self.awl and a.metadata["language"].lower() in self.awl ): overrideLang = False break if overrideLang: self.awl = None self.log.info( "No audio streams detected in any appropriate language, relaxing restrictions so there will be some audio stream present." ) audio_settings = {} l = 0 for a in info.audio: try: if a.metadata["language"].strip() == "" or a.metadata["language"] is None: a.metadata["language"] = "und" except KeyError: a.metadata["language"] = "und" self.log.info("Audio detected for stream #%s: %s [%s]." % (a.index, a.codec, a.metadata["language"])) # Set undefined language to default language if specified if self.adl is not None and a.metadata["language"] == "und": self.log.debug("Undefined language detected, defaulting to [%s]." % self.adl) a.metadata["language"] = self.adl # Proceed if no whitelist is set, or if the language is in the whitelist if self.awl is None or a.metadata["language"].lower() in self.awl: # Create iOS friendly audio stream if the default audio stream has too many channels (iOS only likes AAC stereo) if self.iOS: if a.audio_channels > 2: iOSbitrate = 256 if (self.audio_bitrate * 2) > 256 else (self.audio_bitrate * 2) self.log.info( "Creating audio stream %s from source audio stream %s [iOS-audio]." % (str(l), a.index) ) self.log.debug("Audio codec: %s." % self.iOS) self.log.debug("Channels: 2.") self.log.debug("Bitrate: %s." % iOSbitrate) self.log.debug("Language: %s." % a.metadata["language"]) audio_settings.update( { l: { "map": a.index, "codec": self.iOS, "channels": 2, "bitrate": iOSbitrate, "language": a.metadata["language"], } } ) l += 1 # If the iOS audio option is enabled and the source audio channel is only stereo, the additional iOS channel will be skipped and a single AAC 2.0 channel will be made regardless of codec preference to avoid multiple stereo channels self.log.info("Creating audio stream %s from source stream %s." % (str(l), a.index)) if self.iOS and a.audio_channels <= 2: self.log.debug( "Overriding default channel settings because iOS audio is enabled but the source is stereo [iOS-audio]." ) acodec = "copy" if a.codec == self.iOS else self.iOS audio_channels = a.audio_channels abitrate = ( a.audio_channels * 128 if (a.audio_channels * self.audio_bitrate) > (a.audio_channels * 128) else (a.audio_channels * self.audio_bitrate) ) else: # If desired codec is the same as the source codec, copy to avoid quality loss acodec = "copy" if a.codec.lower() in self.audio_codec else self.audio_codec[0] # Audio channel adjustments if self.maxchannels and a.audio_channels > self.maxchannels: audio_channels = self.maxchannels if acodec == "copy": acodec = self.audio_codec[0] abitrate = self.maxchannels * self.audio_bitrate else: audio_channels = a.audio_channels abitrate = a.audio_channels * self.audio_bitrate # Bitrate calculations/overrides if self.audio_bitrate is 0: self.log.debug("Attempting to set bitrate based on source stream bitrate.") try: abitrate = a.bitrate / 1000 except: self.log.warning( "Unable to determine audio bitrate from source stream %s, defaulting to 256 per channel." % a.index ) abitrate = a.audio_channels * 256 self.log.debug("Audio codec: %s." % acodec) self.log.debug("Channels: %s." % audio_channels) self.log.debug("Bitrate: %s." % abitrate) self.log.debug("Language: %s" % a.metadata["language"]) # If the iOSFirst option is enabled, disable the iOS option after the first audio stream is processed if self.iOS and self.iOSFirst: self.log.debug("Not creating any additional iOS audio streams.") self.iOS = False audio_settings.update( { l: { "map": a.index, "codec": acodec, "channels": audio_channels, "bitrate": abitrate, "language": a.metadata["language"], } } ) if acodec == "copy" and a.codec == "aac": audio_settings[l]["bsf"] = "aac_adtstoasc" l = l + 1 # Subtitle streams subtitle_settings = {} l = 0 self.log.info("Reading subtitle streams.") for s in info.subtitle: try: if s.metadata["language"].strip() == "" or s.metadata["language"] is None: s.metadata["language"] = "und" except KeyError: s.metadata["language"] = "und" self.log.info("Subtitle detected for stream #%s: %s [%s]." % (s.index, s.codec, s.metadata["language"])) # Set undefined language to default language if specified if self.sdl is not None and s.metadata["language"] == "und": self.log.debug("Undefined language detected, defaulting to [%s]." % self.sdl) s.metadata["language"] = self.sdl # Make sure its not an image based codec if s.codec.lower() not in bad_subtitle_codecs and self.embedsubs: # Proceed if no whitelist is set, or if the language is in the whitelist if self.swl is None or s.metadata["language"].lower() in self.swl: subtitle_settings.update( { l: { "map": s.index, "codec": self.scodec[0], "language": s.metadata["language"], "encoding": self.subencoding, # 'forced': s.sub_forced, # 'default': s.sub_default } } ) self.log.info("Creating subtitle stream %s from source stream %s." % (l, s.index)) l = l + 1 elif s.codec.lower() not in bad_subtitle_codecs and not self.embedsubs: if self.swl is None or s.metadata["language"].lower() in self.swl: for codec in self.scodec: ripsub = {0: {"map": s.index, "codec": codec, "language": s.metadata["language"]}} options = {"format": codec, "subtitle": ripsub} try: extension = subtitle_codec_extensions[codec] except: self.log.info("Wasn't able to determine subtitle file extension, defaulting to '.srt'.") extension = "srt" forced = ".forced" if s.sub_forced else "" input_dir, filename, input_extension = self.parseFile(inputfile) output_dir = input_dir if self.output_dir is None else self.output_dir outputfile = os.path.join( output_dir, filename + "." + s.metadata["language"] + forced + "." + extension ) i = 2 while os.path.isfile(outputfile): self.log.debug("%s exists, appending %s to filename." % (outputfile, i)) outputfile = os.path.join( output_dir, filename + "." + s.metadata["language"] + forced + "." + str(i) + "." + extension, ) i += 1 try: self.log.info( "Ripping %s subtitle from source stream %s into external file." % (s.metadata["language"], s.index) ) conv = Converter(self.FFMPEG_PATH, self.FFPROBE_PATH).convert( inputfile, outputfile, options, timeout=None ) for timecode in conv: pass self.log.info("%s created." % outputfile) except: self.log.exception("Unabled to create external subtitle file for stream %s." % (s.index)) # Attempt to download subtitles if they are missing using subliminal languages = set() try: if self.swl: for alpha3 in self.swl: languages.add(Language(alpha3)) elif self.sdl: languages.add(Language(self.sdl)) else: self.downloadsubs = False self.log.error("No valid subtitle language specified, cannot download subtitles.") except: self.log.exception("Unable to verify subtitle languages for download.") self.downloadsubs = False if self.downloadsubs: import subliminal self.log.info("Attempting to download subtitles.") # Attempt to set the dogpile cache try: subliminal.region.configure("dogpile.cache.memory") except: pass try: video = subliminal.scan_video(os.path.abspath(inputfile), subtitles=True, embedded_subtitles=True) subtitles = subliminal.download_best_subtitles( [video], languages, hearing_impaired=False, providers=self.subproviders ) try: subliminal.save_subtitles(video, subtitles[video]) except: # Support for older versions of subliminal subliminal.save_subtitles(subtitles) self.log.info("Please update to the latest version of subliminal.") except Exception as e: self.log.info("Unable to download subtitles.", exc_info=True) self.log.debug("Unable to download subtitles.", exc_info=True) # External subtitle import if self.embedsubs: # Don't bother if we're not embeddeding any subtitles src = 1 # FFMPEG input source number for dirName, subdirList, fileList in os.walk(input_dir): for fname in fileList: subname, subextension = os.path.splitext(fname) # Watch for appropriate file extension if subextension[1:] in valid_subtitle_extensions: x, lang = os.path.splitext(subname) lang = lang[1:] # Using bablefish to convert a 2 language code to a 3 language code if len(lang) is 2: try: babel = Language.fromalpha2(lang) lang = babel.alpha3 except: pass # If subtitle file name and input video name are the same, proceed if x == filename: self.log.info("External %s subtitle file detected." % lang) if self.swl is None or lang in self.swl: self.log.info("Creating subtitle stream %s by importing %s." % (l, fname)) subtitle_settings.update( { l: { "path": os.path.join(dirName, fname), "source": src, "map": 0, "codec": "mov_text", "language": lang, } } ) self.log.debug("Path: %s." % os.path.join(dirName, fname)) self.log.debug("Source: %s." % src) self.log.debug("Codec: mov_text.") self.log.debug("Langauge: %s." % lang) l = l + 1 src = src + 1 self.deletesubs.add(os.path.join(dirName, fname)) else: self.log.info( "Ignoring %s external subtitle stream due to language %s." % (fname, lang) ) # Collect all options options = { "format": self.output_format, "video": {"codec": vcodec, "map": info.video.index, "bitrate": vbitrate, "level": self.h264_level}, "audio": audio_settings, "subtitle": subtitle_settings, "preopts": ["-fix_sub_duration"], "postopts": ["-threads", self.threads], } # If using h264qsv, add the codec in front of the input for decoding if ( vcodec == "h264qsv" and info.video.codec.lower() == "h264" and self.qsv_decoder and (info.video.video_level / 10) < 5 ): options["preopts"].extend(["-vcodec", "h264_qsv"]) # Add width option if vwidth: options["video"]["width"] = vwidth # Add pix_fmt if self.pix_fmt: options["video"]["pix_fmt"] = self.pix_fmt[0] self.options = options return options
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 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 = 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( "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 = get_general_settings()[7] 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 subtitles_download_in_pp(): # pylint: disable=too-many-locals, too-many-branches, too-many-statements logger.log(u'Checking for needed subtitles in Post-Process folder', logger.INFO) providers = enabled_service_list() pool = SubtitleProviderPool() # Search for all wanted languages languages = {from_code(language) for language in wanted_languages()} if not languages: return # Dict of language exceptions to use with subliminal language_exceptions = {'pt-br': 'pob'} run_post_process = False # Check if PP folder is set if sickbeard.TV_DOWNLOAD_DIR and os.path.isdir( sickbeard.TV_DOWNLOAD_DIR): for root, _, files in os.walk(sickbeard.TV_DOWNLOAD_DIR, topdown=False): rar_files = [ rar_file for rar_file in files if isRarFile(rar_file) ] if rar_files and sickbeard.UNPACK: video_files = [ video_file for video_file in files if isMediaFile(video_file) ] if u'_UNPACK' not in root and ( not video_files or root == sickbeard.TV_DOWNLOAD_DIR): logger.log( u'Found rar files in post-process folder: {0}'. format(rar_files), logger.DEBUG) result = processTV.ProcessResult() processTV.unRAR(root, rar_files, False, result) elif rar_files and not sickbeard.UNPACK: logger.log( u'Unpack is disabled. Skipping: {0}'.format(rar_files), logger.WARNING) for root, _, files in os.walk(sickbeard.TV_DOWNLOAD_DIR, topdown=False): for filename in sorted(files): try: # Remove non release groups from video file. Needed to match subtitles new_filename = remove_non_release_groups(filename) if new_filename != filename: os.rename(filename, new_filename) filename = new_filename except Exception as error: logger.log( u"Couldn't remove non release groups from video file. Error: {0}" .format(ex(error)), logger.DEBUG) # Delete unwanted subtitles before downloading new ones if sickbeard.SUBTITLES_MULTI and sickbeard.SUBTITLES_KEEP_ONLY_WANTED and filename.rpartition( '.')[2] in subtitle_extensions: subtitle_language = filename.rsplit('.', 2)[1].lower() if len( subtitle_language ) == 2 and subtitle_language in language_converters[ 'opensubtitles'].codes: subtitle_language = Language.fromcode( subtitle_language, 'alpha2').opensubtitles elif subtitle_language in language_exceptions: subtitle_language = language_exceptions.get( subtitle_language, subtitle_language) elif subtitle_language not in language_converters[ 'opensubtitles'].codes: subtitle_language = 'unknown' if subtitle_language not in sickbeard.SUBTITLES_LANGUAGES: try: os.remove(os.path.join(root, filename)) logger.log( u"Deleted '{0}' because we don't want subtitle language '{1}'. We only want '{2}' language(s)" .format( filename, subtitle_language, ','.join( sickbeard.SUBTITLES_LANGUAGES)), logger.DEBUG) except Exception as error: logger.log( u"Couldn't delete subtitle: {0}. Error: {1}" .format(filename, ex(error)), logger.DEBUG) if isMediaFile(filename) and processTV.subtitles_enabled( filename): try: video = get_video(os.path.join(root, filename), subtitles=False, embedded_subtitles=False) subtitles_list = pool.list_subtitles( video, languages) for provider in providers: if provider in pool.discarded_providers: logger.log( u'Could not search in {0} provider. Discarding for now' .format(provider), logger.DEBUG) if not subtitles_list: logger.log( u'No subtitles found for {0}'.format( os.path.join(root, filename)), logger.DEBUG) continue logger.log( u'Found subtitle(s) canditate(s) for {0}'. format(filename), logger.INFO) hearing_impaired = sickbeard.SUBTITLES_HEARING_IMPAIRED user_score = 213 if sickbeard.SUBTITLES_PERFECT_MATCH else 198 found_subtitles = pool.download_best_subtitles( subtitles_list, video, languages=languages, hearing_impaired=hearing_impaired, min_score=user_score, only_one=not sickbeard.SUBTITLES_MULTI) for subtitle in subtitles_list: score = subliminal.score.compute_score( subtitle, video, hearing_impaired=sickbeard. SUBTITLES_HEARING_IMPAIRED) logger.log( u'[{0}] Subtitle score for {1} is: {2} (min={3})' .format(subtitle.provider_name, subtitle.id, score, user_score), logger.DEBUG) downloaded_languages = set() for subtitle in found_subtitles: logger.log( u'Found subtitle for {0} in {1} provider with language {2}' .format(os.path.join(root, filename), subtitle.provider_name, subtitle.language.opensubtitles), logger.INFO) subliminal.save_subtitles( video, found_subtitles, directory=root, single=not sickbeard.SUBTITLES_MULTI) subtitles_multi = not sickbeard.SUBTITLES_MULTI subtitle_path = subliminal.subtitle.get_subtitle_path( video.name, None if subtitles_multi else subtitle.language) if root is not None: subtitle_path = os.path.join( root, os.path.split(subtitle_path)[1]) sickbeard.helpers.chmodAsParent(subtitle_path) sickbeard.helpers.fixSetGroupID(subtitle_path) downloaded_languages.add( subtitle.language.opensubtitles) # Don't run post processor unless at least one file has all of the needed subtitles if not needs_subtitles(downloaded_languages): run_post_process = True except Exception as error: logger.log( u'Error occurred when downloading subtitles for: {0}. Error: {1}' .format(os.path.join(root, filename), ex(error))) if run_post_process: logger.log( u'Starting post-process with default settings now that we found subtitles' ) processTV.processDir(sickbeard.TV_DOWNLOAD_DIR)