def get_video(video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None): if not subtitles_path: subtitles_path = get_subtitles_path(video_path) try: # Encode paths to UTF-8 to ensure subliminal support. video_path = video_path.encode('utf-8') subtitles_path = subtitles_path.encode('utf-8') except UnicodeEncodeError: # Fallback to system encoding. This should never happen. video_path = video_path.encode(sickbeard.SYS_ENCODING) subtitles_path = subtitles_path.encode(sickbeard.SYS_ENCODING) try: video = subliminal.scan_video(video_path) # external subtitles if subtitles: video.subtitle_languages |= \ set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values()) if embedded_subtitles is None: embedded_subtitles = bool(not sickbeard.EMBEDDED_SUBTITLES_ALL and video_path.endswith('.mkv')) subliminal.refine(video, embedded_subtitles=embedded_subtitles) except Exception as error: logger.log(u'Exception: {0}'.format(error), logger.DEBUG) return None return video
def get_video(video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None, episode=None): if not subtitles_path: subtitles_path = get_subtitles_path(video_path) try: video = subliminal.scan_video(video_path) # external subtitles if subtitles: video.subtitle_languages |= \ set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values()) if embedded_subtitles is None: embedded_subtitles = bool( not sickrage.app.config.embedded_subtitles_all and video_path.endswith('.mkv')) # Let sickrage add more information to video file, based on the metadata. if episode: refine_video(video, episode) subliminal.refine(video, embedded_subtitles=embedded_subtitles) except Exception as error: sickrage.app.log.debug('Exception: {}'.format(error)) return None # remove format metadata video.format = "" return video
def get_video(video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None, episode=None): if not subtitles_path: subtitles_path = get_subtitles_path(video_path) try: video = subliminal.scan_video(video_path) except Exception as error: sickrage.app.log.debug('Exception: {}'.format(error)) else: if video.size > 10485760: video.hashes['itasa'] = hash_itasa(video_path) # external subtitles if subtitles: video.subtitle_languages |= \ set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values()) if embedded_subtitles is None: embedded_subtitles = bool( not sickrage.app.config.embedded_subtitles_all and video_path.endswith('.mkv')) subliminal.refine(video, episode_refiners=episode_refiners, embedded_subtitles=embedded_subtitles, release_name=episode.name, tv_episode=episode) video.alternative_series = list(get_scene_exceptions(episode.show.indexerid)) # remove format metadata video.format = "" return video
def get_video(self, video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None, episode_object=None): if not subtitles_path: subtitles_path = self.get_subtitles_path(video_path) try: video = subliminal.scan_video(video_path) except Exception as error: sickrage.app.log.debug('Exception: {}'.format(error)) else: if video.size > 10485760: video.hashes['itasa'] = hash_itasa(video_path) # external subtitles if subtitles: video.subtitle_languages |= set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values()) if embedded_subtitles is None: embedded_subtitles = bool( not sickrage.app.config.embedded_subtitles_all and video_path.endswith('.mkv')) subliminal.refine(video, episode_refiners=self.episode_refiners, embedded_subtitles=embedded_subtitles, release_name=episode_object.name, tv_episode=episode_object) from sickrage.core.scene_exceptions import get_scene_exceptions video.alternative_series = list(get_scene_exceptions(episode_object.show.indexer_id)) # remove format metadata video.format = "" return video
def get_video(video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None, episode=None): if not subtitles_path: subtitles_path = get_subtitles_path(video_path) try: video = subliminal.scan_video(video_path) # external subtitles if subtitles: video.subtitle_languages |= set( subliminal.core.search_external_subtitles( video_path, directory=subtitles_path).values()) if embedded_subtitles is None: embedded_subtitles = bool(not settings.EMBEDDED_SUBTITLES_ALL and video_path.endswith(".mkv")) # Let sickchill add more information to video file, based on the metadata. if episode: refine_video(video, episode) subliminal.refine(video, embedded_subtitles=embedded_subtitles) except Exception as error: logger.info(traceback.format_exc()) logger.debug("Exception: {0}".format(error)) return None return video
def get_video(video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None, episode=None): if not subtitles_path: subtitles_path = get_subtitles_path(video_path) try: # Encode paths to UTF-8 to ensure subliminal support. video_path = video_path.encode('utf-8') subtitles_path = subtitles_path.encode('utf-8') except UnicodeEncodeError: # Fallback to system encoding. This should never happen. video_path = video_path.encode(sickrage.srCore.SYS_ENCODING) subtitles_path = subtitles_path.encode(sickrage.srCore.SYS_ENCODING) try: video = subliminal.scan_video(video_path) # external subtitles if subtitles: video.subtitle_languages |= \ set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values()) if embedded_subtitles is None: embedded_subtitles = bool( not sickrage.srCore.srConfig.EMBEDDED_SUBTITLES_ALL and video_path.endswith('.mkv')) # Let sickrage add more information to video file, based on the metadata. if episode: refine_video(video, episode) subliminal.refine(video, embedded_subtitles=embedded_subtitles) except Exception as error: sickrage.srCore.srLogger.debug('Exception: {}'.format(error)) return None return video
def choose_callback(self, menuitem, files): # scan the video video = scan_video(files[0].get_location().get_path()) refine(video, episode_refiners=self.config.refiners, movie_refiners=self.config.refiners, embedded_subtitles=False) # load the interface builder = Gtk.Builder() builder.set_translation_domain('subliminal') builder.add_from_file( os.path.join(os.path.dirname(__file__), 'subliminal', 'ui', 'choose.glade')) # set the video filename video_filename = builder.get_object('video_filename_label') video_filename.set_text(files[0].get_name()) # start the spinner spinner = builder.get_object('spinner') spinner.start() def _list_subtitles(): # list subtitles with AsyncProviderPool( providers=self.config.providers, provider_configs=self.config.provider_configs) as pool: subtitles = pool.list_subtitles(video, self.config.languages) # fill the subtitle liststore subtitle_liststore = builder.get_object('subtitle_liststore') for s in subtitles: scaled_score = compute_score(s, video) scores = get_scores(video) if s.hearing_impaired == self.config.hearing_impaired: scaled_score -= scores['hearing_impaired'] scaled_score *= 100 / scores['hash'] subtitle_liststore.append([ s.id, nice_language(s.language), scaled_score, s.provider_name.capitalize(), s.hearing_impaired, s.page_link, False ]) subtitle_liststore.set_sort_column_id(2, Gtk.SortType.DESCENDING) # stop the spinner spinner.stop() # connect signals builder.connect_signals( ChooseHandler(self.config, video, subtitles, spinner)) threading.Thread(target=_list_subtitles).start() # display window window = builder.get_object('subtitle_window') window.show_all() Gtk.main()
def get_video(tv_episode, video_path, subtitles_dir=None, subtitles=True, embedded_subtitles=None, release_name=None): """Return the subliminal video for the given path. The video_path is used as a key to cache the video to avoid scanning and parsing the video metadata all the time :param tv_episode: :type tv_episode: medusa.tv.Episode :param video_path: the video path :type video_path: str :param subtitles_dir: the subtitles directory :type subtitles_dir: str or None :param subtitles: True if existing external subtitles should be taken into account :type subtitles: bool or None :param embedded_subtitles: True if embedded subtitles should be taken into account :type embedded_subtitles: bool or None :param release_name: the release name :type release_name: str or None :return: video :rtype: subliminal.video.Video """ key = video_key.format(video_path=video_path) payload = {'subtitles_dir': subtitles_dir, 'subtitles': subtitles, 'embedded_subtitles': embedded_subtitles, 'release_name': release_name} cached_payload = memory_cache.get(key, expiration_time=VIDEO_EXPIRATION_TIME) if cached_payload != NO_VALUE and {k: v for k, v in iteritems(cached_payload) if k != 'video'} == payload: logger.debug(u'Found cached video information under key %s', key) return cached_payload['video'] video_is_mkv = video_path.endswith('.mkv') subtitles_dir = subtitles_dir or get_subtitles_dir(video_path) logger.debug(u'Scanning video %s...', video_path) try: video = scan_video(video_path) except ValueError as error: logger.warning(u'Unable to scan video: %s. Error: %r', video_path, error) else: if subtitles: video.subtitle_languages |= set(search_external_subtitles(video_path, directory=subtitles_dir).values()) if embedded_subtitles is None: embedded_subtitles = bool(not app.IGNORE_EMBEDDED_SUBS and video_is_mkv) refine(video, episode_refiners=episode_refiners, embedded_subtitles=embedded_subtitles, release_name=release_name, tv_episode=tv_episode) video.alternative_series = [alias.title for alias in tv_episode.series.aliases] payload['video'] = video memory_cache.set(key, payload) logger.debug(u'Video information cached under key %s', key) return video
def get_embedded_subtitles(video_path): """Return all embedded subtitles for the given video path. :param video_path: video filename to be checked :type video_path: str :return: :rtype: set of Language """ subliminal_video = scan_video(video_path) refine(subliminal_video, episode_refiners=('metadata', )) return subliminal_video.subtitle_languages
def get_embedded_subtitles(video_path): """Return all embedded subtitles for the given video path. :param video_path: video filename to be checked :type video_path: str :return: :rtype: set of Language """ subliminal_video = scan_video(video_path) refine(subliminal_video, episode_refiners=('metadata',)) return subliminal_video.subtitle_languages
def get_video(tv_episode, video_path, subtitles_dir=None, subtitles=True, embedded_subtitles=None, release_name=None): """Return the subliminal video for the given path. The video_path is used as a key to cache the video to avoid scanning and parsing the video metadata all the time :param tv_episode: :type tv_episode: sickbeard.tv.TVEpisode :param video_path: the video path :type video_path: str :param subtitles_dir: the subtitles directory :type subtitles_dir: str or None :param subtitles: True if existing external subtitles should be taken into account :type subtitles: bool or None :param embedded_subtitles: True if embedded subtitles should be taken into account :type embedded_subtitles: bool or None :param release_name: the release name :type release_name: str or None :return: video :rtype: subliminal.video.Video """ key = video_key.format(video_path=video_path) payload = {'subtitles_dir': subtitles_dir, 'subtitles': subtitles, 'embedded_subtitles': embedded_subtitles, 'release_name': release_name} cached_payload = region.get(key, expiration_time=VIDEO_EXPIRATION_TIME) if cached_payload != NO_VALUE and {k: v for k, v in iteritems(cached_payload) if k != 'video'} == payload: logger.debug(u'Found cached video information under key %s', key) return cached_payload['video'] try: video_path = _encode(video_path) subtitles_dir = _encode(subtitles_dir or get_subtitles_dir(video_path)) logger.debug(u'Scanning video %s...', video_path) video = scan_video(video_path) # external subtitles if subtitles: video.subtitle_languages |= set(search_external_subtitles(video_path, directory=subtitles_dir).values()) if embedded_subtitles is None: embedded_subtitles = bool(not sickbeard.EMBEDDED_SUBTITLES_ALL and video_path.endswith('.mkv')) refine(video, episode_refiners=episode_refiners, embedded_subtitles=embedded_subtitles, release_name=release_name, tv_episode=tv_episode) payload['video'] = video region.set(key, payload) logger.debug(u'Video information cached under key %s', key) return video except Exception as error: logger.info(u'Exception: %s', error)
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 choose_callback(self, menuitem, files): # scan the video video = scan_video(files[0].get_location().get_path()) refine(video, episode_refiners=self.config.refiners, movie_refiners=self.config.refiners, embedded_subtitles=False) # load the interface builder = Gtk.Builder() builder.set_translation_domain('subliminal') builder.add_from_file(os.path.join(os.path.dirname(__file__), 'subliminal', 'ui', 'choose.glade')) # set the video filename video_filename = builder.get_object('video_filename_label') video_filename.set_text(files[0].get_name()) # start the spinner spinner = builder.get_object('spinner') spinner.start() def _list_subtitles(): # list subtitles with AsyncProviderPool(providers=self.config.providers, provider_configs=self.config.provider_configs) as pool: subtitles = pool.list_subtitles(video, self.config.languages) # fill the subtitle liststore subtitle_liststore = builder.get_object('subtitle_liststore') for s in subtitles: scaled_score = compute_score(s, video) scores = get_scores(video) if s.hearing_impaired == self.config.hearing_impaired: scaled_score -= scores['hearing_impaired'] scaled_score *= 100 / scores['hash'] subtitle_liststore.append([s.id, nice_language(s.language), scaled_score, s.provider_name.capitalize(), s.hearing_impaired, s.page_link, False]) subtitle_liststore.set_sort_column_id(2, Gtk.SortType.DESCENDING) # stop the spinner spinner.stop() # connect signals builder.connect_signals(ChooseHandler(self.config, video, subtitles, spinner)) threading.Thread(target=_list_subtitles).start() # display window window = builder.get_object('subtitle_window') window.show_all() Gtk.main()
def _refine_file(video_file) -> List[SeriesEpisodeInfo]: log.debug("Refining file %s", video_file) try: video = Video.fromname(video_file) except ValueError: log.error("Cannot guess video file type from: %s", video_file) return [] refiner = sorted(refiner_manager.names()) refine(video, episode_refiners=refiner, movie_refiners=refiner) log.debug("refine result: %r", video) if isinstance(video, Episode): log.debug("series: %s", video.series) log.debug("season: %s", video.season) if not video.season: log.error("No season defined !") return [] if isinstance(video.season, list): video.season = video.season[0] log.error("Multi season found, only using first one: %s", video.season) log.debug("episode: %s", video.episode) log.debug("title: %s", video.title) log.debug("series_tvdb_id: %s", video.series_tvdb_id) r = [] # Support for double episode if not isinstance(video.episode, list): video.episode = [video.episode] for video_episode in video.episode: r.append( SeriesEpisodeInfo( series_episode_uid=SeriesEpisodeUid( tv_db_id=video.series_tvdb_id, season_number=video.season, episode_number=video_episode, ), series_title=video.series, episode_title=video.title, quality=None, video_languages=None, subtitles_languages=None, media_filename=video_file, dirty=True, )) return r elif isinstance(video, Movie): log.debug("movie: %s", video.title) return []
def run(self, scan_path, scan_age, languages, *args, **kwargs): if not os.path.isdir(scan_path): raise IOError('Path \'%s\' doesn\'t exist!' % scan_path) if not scan_age >= 1: raise ValueError('\'scan_age\' must by at least 1!') if not len(languages) >= 1: raise ValueError('\'languages\' list can\'t be empty!') __tree_dict = lambda: defaultdict(__tree_dict) result = __tree_dict() age = timedelta(weeks=scan_age) languages = set([Language(l) for l in languages]) scan_start = datetime.now() videos = [] ignored_videos = [] if not region.is_configured: region.configure('dogpile.cache.dbm', expiration_time=timedelta(days=30), arguments={'filename': 'subliminal.dbm', 'lock_factory': MutexLock}) # scan videos scanned_videos = scan_videos(scan_path, age=age) for video in scanned_videos: video.subtitle_languages |= set(search_external_subtitles(video.name).values()) if check_video(video, languages=languages, age=age, undefined=False): refine(video) if languages - video.subtitle_languages: videos.append(video) else: ignored_videos.append(video) else: ignored_videos.append(video) if videos: result['videos']['collected'] = [os.path.split(v.name)[1] for v in videos] if ignored_videos: result['videos']['ignored'] = [os.path.split(v.name)[1] for v in ignored_videos] scan_end = datetime.now() result['meta']['start'] = scan_start.isoformat() result['meta']['end'] = scan_end.isoformat() result['meta']['duration'] = str(scan_end - scan_start) return result
def _scan_wanted_item_for_video(wanted_item, is_manual=False): video_path = wanted_item.videopath log.info('Scanning video') try: # Scan the video video = subliminal.scan_video(video_path) # Use our manual refiner only for manual search if enabled # Always keep this first because you may completely override the video with this! if is_manual and autosubliminal.MANUALREFINEVIDEO: refiners = ('manual',) # don't remove the , -> needs to be a tuple subliminal.refine(video, episode_refiners=refiners, movie_refiners=refiners, wanted_item=wanted_item) # Use build-in refiners if autosubliminal.REFINEVIDEO: subliminal.refine(video) # Use our namemapping refiner (always enabled to enable our name mappings) # This should always be at the end since we want to enrich the result after the build-in refiners refiners = ('namemapping',) # don't remove the , -> needs to be a tuple subliminal.refine(video, episode_refiners=refiners, movie_refiners=refiners) except Exception: log.exception('Error while scanning video, skipping %s', video_path) return # Add video to wanted item wanted_item.video = video return video
def get_video(video_path, subtitles_path=None, subtitles=True, embedded_subtitles=None, episode=None): if not subtitles_path: subtitles_path = get_subtitles_path(video_path) try: # Encode paths to UTF-8 to ensure subliminal support. video_path = video_path.encode('utf-8') subtitles_path = subtitles_path.encode('utf-8') except UnicodeEncodeError: # Fallback to system encoding. This should never happen. video_path = video_path.encode(sickrage.app.sys_encoding) subtitles_path = subtitles_path.encode(sickrage.app.sys_encoding) try: video = subliminal.scan_video(video_path) # external subtitles if subtitles: video.subtitle_languages |= \ set(subliminal.core.search_external_subtitles(video_path, directory=subtitles_path).values()) if embedded_subtitles is None: embedded_subtitles = bool( not sickrage.app.config.embedded_subtitles_all and video_path.endswith('.mkv')) # Let sickrage add more information to video file, based on the metadata. if episode: refine_video(video, episode) subliminal.refine(video, embedded_subtitles=embedded_subtitles) except Exception as error: sickrage.app.log.debug('Exception: {}'.format(error)) return None # remove format metadata video.format = "" return video
def run(self, scan_path, scan_age, languages, encoding, min_score, providers, provider_configs, max_workers, plex_url=None, plex_token=None, *args, **kwargs): if not os.path.isdir(scan_path): raise IOError('Path \'%s\' doesn\'t exist!' % scan_path) if not scan_age >= 1: raise ValueError('\'scan_age\' must by at least 1!') if not len(languages) >= 1: raise ValueError('\'languages\' list can\'t be empty!') if not providers: raise ValueError('\'providers\' argument can\'t be empty!') if not max_workers >= 1: raise ValueError('\'max_workers\' must be at least 1!') if not provider_configs: provider_configs = {} __tree_dict = lambda: defaultdict(__tree_dict) result = __tree_dict() encoding = codecs.lookup(encoding).name age = timedelta(weeks=scan_age) languages = set([Language(l) for l in languages]) plex = None if plex_url and plex_token: plex = PlexServer(plex_url, plex_token) scan_start = datetime.now() videos = [] ignored_videos = [] if not region.is_configured: region.configure('dogpile.cache.dbm', expiration_time=timedelta(days=30), arguments={ 'filename': 'subliminal.dbm', 'lock_factory': MutexLock }) # scan videos scanned_videos = scan_videos(scan_path, age=age) for video in scanned_videos: video.subtitle_languages |= set( search_external_subtitles(video.name).values()) if check_video(video, languages=languages, age=age, undefined=False): refine(video) if languages - video.subtitle_languages: videos.append(video) else: ignored_videos.append(video) else: ignored_videos.append(video) if videos: result['videos']['collected'] = [ os.path.split(v.name)[1] for v in videos ] if ignored_videos: result['videos']['ignored'] = len(ignored_videos) if videos: # download best subtitles downloaded_subtitles = defaultdict(list) with AsyncProviderPool(max_workers=max_workers, providers=providers, provider_configs=provider_configs) as p: for video in videos: scores = get_scores(video) subtitles_to_download = p.list_subtitles( video, languages - video.subtitle_languages) downloaded_subtitles[video] = p.download_best_subtitles( subtitles_to_download, video, languages, min_score=scores['hash'] * min_score / 100) if p.discarded_providers: result['providers']['discarded'] = list( p.discarded_providers) # filter subtitles with TinyDB('subtitle_db.json') as db: table = db.table('downloaded') query = Query() for video, subtitles in downloaded_subtitles.items(): discarded_subtitles = list() discarded_subtitles_info = list() for s in subtitles: subtitle_hash = hashlib.sha256(s.content).hexdigest() subtitle_file = get_subtitle_path( os.path.split(video.name)[1], s.language) dbo = {'hash': subtitle_hash, 'file': subtitle_file} if table.search((query.hash == subtitle_hash) & (query.file == subtitle_file)): discarded_subtitles.append(s) discarded_subtitles_info.append(dbo) else: table.insert(dbo) downloaded_subtitles[video] = [ x for x in subtitles if x not in discarded_subtitles ] if discarded_subtitles_info: result['subtitles'][ 'discarded'] = result['subtitles'].get( 'discarded', []) + discarded_subtitles_info downloaded_subtitles = { k: v for k, v in downloaded_subtitles.items() if v } # save subtitles saved_subtitles = {} for video, subtitles in downloaded_subtitles.items(): saved_subtitles[video] = save_subtitles(video, subtitles, directory=None, encoding=encoding) for key, group in groupby(saved_subtitles[video], lambda x: x.provider_name): subtitle_filenames = [ get_subtitle_path( os.path.split(video.name)[1], s.language) for s in list(group) ] result['subtitles'][key] = result['subtitles'].get( key, []) + subtitle_filenames result['subtitles']['total'] = sum( len(v) for v in saved_subtitles.values()) # refresh plex for video, subtitles in saved_subtitles.items(): if plex and subtitles: item_found = False for section in plex.library.sections(): try: if isinstance(section, MovieSection) and isinstance( video, Movie): results = section.search(title=video.title, year=video.year, libtype='movie', sort='addedAt:desc', maxresults=1) if not results: raise NotFound plex_item = results[0] elif isinstance(section, ShowSection) and isinstance( video, Episode): results = section.search(title=video.series, year=video.year, libtype='show', sort='addedAt:desc', maxresults=1) if not results: raise NotFound plex_item = results[0].episode( season=video.season, episode=video.episode) else: continue except NotFound: continue except BadRequest: continue if plex_item: plex_item.refresh() result['plex']['refreshed'] = result['plex'].get( 'refreshed', []) + [ '%s%s' % (repr(plex_item.section()), repr(video)) ] item_found = True if not item_found: result['plex']['failed'] = result['plex'].get( 'failed', []) + [repr(video)] # convert subtitles for video, subtitles in saved_subtitles.items(): target_format = aeidon.formats.SUBRIP for s in subtitles: subtitle_path = get_subtitle_path(video.name, s.language) source_format = aeidon.util.detect_format( subtitle_path, encoding) source_file = aeidon.files.new( source_format, subtitle_path, aeidon.encodings.detect_bom(subtitle_path) or encoding) if source_format != target_format: format_info = { 'file': get_subtitle_path( os.path.split(video.name)[1], s.language), 'from': source_format.label, 'to': target_format.label } result['subtitles'][ 'converted'] = result['subtitles'].get( 'converted', []) + [format_info] aeidon_subtitles = source_file.read() for f in [ aeidon.formats.SUBRIP, aeidon.formats.MICRODVD, aeidon.formats.MPL2 ]: markup = aeidon.markups.new(f) for s in aeidon_subtitles: s.main_text = markup.decode(s.main_text) markup = aeidon.markups.new(target_format) for s in aeidon_subtitles: s.main_text = markup.encode(s.main_text) target_file = aeidon.files.new(target_format, subtitle_path, encoding) target_file.write(aeidon_subtitles, aeidon.documents.MAIN) scan_end = datetime.now() result['meta']['start'] = scan_start.isoformat() result['meta']['end'] = scan_end.isoformat() result['meta']['duration'] = str(scan_end - scan_start) return result
def get_video(tv_episode, video_path, subtitles_dir=None, subtitles=True, embedded_subtitles=None, release_name=None): """Return the subliminal video for the given path. The video_path is used as a key to cache the video to avoid scanning and parsing the video metadata all the time :param tv_episode: :type tv_episode: medusa.tv.TVEpisode :param video_path: the video path :type video_path: str :param subtitles_dir: the subtitles directory :type subtitles_dir: str or None :param subtitles: True if existing external subtitles should be taken into account :type subtitles: bool or None :param embedded_subtitles: True if embedded subtitles should be taken into account :type embedded_subtitles: bool or None :param release_name: the release name :type release_name: str or None :return: video :rtype: subliminal.video.Video """ key = video_key.format(video_path=video_path) payload = { 'subtitles_dir': subtitles_dir, 'subtitles': subtitles, 'embedded_subtitles': embedded_subtitles, 'release_name': release_name } cached_payload = region.get(key, expiration_time=VIDEO_EXPIRATION_TIME) if cached_payload != NO_VALUE and { k: v for k, v in iteritems(cached_payload) if k != 'video' } == payload: logger.debug(u'Found cached video information under key %s', key) return cached_payload['video'] try: video_path = _encode(video_path) subtitles_dir = _encode(subtitles_dir or get_subtitles_dir(video_path)) logger.debug(u'Scanning video %s...', video_path) video = scan_video(video_path) # external subtitles if subtitles: video.subtitle_languages |= set( search_external_subtitles(video_path, directory=subtitles_dir).values()) if embedded_subtitles is None: embedded_subtitles = bool(not app.EMBEDDED_SUBTITLES_ALL and video_path.endswith('.mkv')) refine(video, episode_refiners=episode_refiners, embedded_subtitles=embedded_subtitles, release_name=release_name, tv_episode=tv_episode) payload['video'] = video region.set(key, payload) logger.debug(u'Video information cached under key %s', key) return video except Exception as error: logger.info(u'Exception: %s', error)
def get_video(self, path): video = scan_video(path) refine(video, episode_refiners=episode_refiners, movie_refiners=movie_refiners, embedded_subtitles=False) return video
def download(obj, provider, language, age, directory, encoding, single, force, hearing_impaired, min_score, max_workers, archives, verbose, path): """Download best subtitles. PATH can be an directory containing videos, a video file path or a video file name. It can be used multiple times. If an existing subtitle is detected (external or embedded) in the correct language, the download is skipped for the associated video. """ # process parameters language = set(language) # scan videos videos = [] ignored_videos = [] errored_paths = [] with click.progressbar(path, label='Collecting videos', item_show_func=lambda p: p or '') as bar: for p in bar: logger.debug('Collecting path %s', p) # non-existing if not os.path.exists(p): try: video = Video.fromname(p) except: logger.exception('Unexpected error while collecting non-existing path %s', p) errored_paths.append(p) continue if not force: video.subtitle_languages |= set(search_external_subtitles(video.name, directory=directory).values()) refine(video, embedded_subtitles=not force) videos.append(video) continue # directories if os.path.isdir(p): try: scanned_videos = scan_videos(p, age=age, archives=archives, subtitles=not force, subtitles_dir=directory) except: logger.exception('Unexpected error while collecting directory path %s', p) errored_paths.append(p) continue for video in scanned_videos: if check_video(video, languages=language, age=age, undefined=single): if not force: video.subtitle_languages |= set(search_external_subtitles(video.name, directory=directory).values()) refine(video, embedded_subtitles=not force) videos.append(video) else: ignored_videos.append(video) continue # other inputs try: video = scan_video(p) except: logger.exception('Unexpected error while collecting path %s', p) errored_paths.append(p) continue if check_video(video, languages=language, age=age, undefined=single): if not force: video.subtitle_languages |= set(search_external_subtitles(video.name, directory=directory).values()) refine(video, embedded_subtitles=not force) videos.append(video) else: ignored_videos.append(video) # output errored paths if verbose > 0: for p in errored_paths: click.secho('%s errored' % p, fg='red') # output ignored videos if verbose > 1: for video in ignored_videos: click.secho('%s ignored - subtitles: %s / age: %d day%s' % ( os.path.split(video.name)[1], ', '.join(str(s) for s in video.subtitle_languages) or 'none', video.age.days, 's' if video.age.days > 1 else '' ), fg='yellow') # report collected videos click.echo('%s video%s collected / %s video%s ignored / %s error%s' % ( click.style(str(len(videos)), bold=True, fg='green' if videos else None), 's' if len(videos) > 1 else '', click.style(str(len(ignored_videos)), bold=True, fg='yellow' if ignored_videos else None), 's' if len(ignored_videos) > 1 else '', click.style(str(len(errored_paths)), bold=True, fg='red' if errored_paths else None), 's' if len(errored_paths) > 1 else '', )) # exit if no video collected if not videos: return # download best subtitles downloaded_subtitles = defaultdict(list) with AsyncProviderPool(max_workers=max_workers, providers=provider, provider_configs=obj['provider_configs']) as p: with click.progressbar(videos, label='Downloading subtitles', item_show_func=lambda v: os.path.split(v.name)[1] if v is not None else '') as bar: for v in bar: scores = get_scores(v) subtitles = p.download_best_subtitles(p.list_subtitles(v, language - v.subtitle_languages), v, language, min_score=scores['hash'] * min_score / 100, hearing_impaired=hearing_impaired, only_one=single) downloaded_subtitles[v] = subtitles # TODO: warn about discarded providers # save subtitles total_subtitles = 0 for v, subtitles in downloaded_subtitles.items(): saved_subtitles = save_subtitles(v, subtitles, single=single, directory=directory, encoding=encoding) total_subtitles += len(saved_subtitles) if verbose > 0: click.echo('%s subtitle%s downloaded for %s' % (click.style(str(len(saved_subtitles)), bold=True), 's' if len(saved_subtitles) > 1 else '', os.path.split(v.name)[1])) if verbose > 1: for s in saved_subtitles: matches = s.get_matches(v) score = compute_score(s, v) # score color score_color = None scores = get_scores(v) if isinstance(v, Movie): if score < scores['title']: score_color = 'red' elif score < scores['title'] + scores['year'] + scores['release_group']: score_color = 'yellow' else: score_color = 'green' elif isinstance(v, Episode): if score < scores['series'] + scores['season'] + scores['episode']: score_color = 'red' elif score < scores['series'] + scores['season'] + scores['episode'] + scores['release_group']: score_color = 'yellow' else: score_color = 'green' # scale score from 0 to 100 taking out preferences scaled_score = score if s.hearing_impaired == hearing_impaired: scaled_score -= scores['hearing_impaired'] scaled_score *= 100 / scores['hash'] # echo some nice colored output click.echo(' - [{score}] {language} subtitle from {provider_name} (match on {matches})'.format( score=click.style('{:5.1f}'.format(scaled_score), fg=score_color, bold=score >= scores['hash']), language=s.language.name if s.language.country is None else '%s (%s)' % (s.language.name, s.language.country.name), provider_name=s.provider_name, matches=', '.join(sorted(matches, key=scores.get, reverse=True)) )) if verbose == 0: click.echo('Downloaded %s subtitle%s' % (click.style(str(total_subtitles), bold=True), 's' if total_subtitles > 1 else ''))
def download(obj, provider, refiner, language, age, directory, encoding, single, force, hearing_impaired, min_score, max_workers, archives, verbose, path): """Download best subtitles. PATH can be an directory containing videos, a video file path or a video file name. It can be used multiple times. If an existing subtitle is detected (external or embedded) in the correct language, the download is skipped for the associated video. """ # process parameters language = set(language) # scan videos videos = [] ignored_videos = [] errored_paths = [] with click.progressbar(path, label='Collecting videos', item_show_func=lambda p: p or '') as bar: for p in bar: logger.debug('Collecting path %s', p) # non-existing if not os.path.exists(p): try: video = Video.fromname(p) except: logger.exception( 'Unexpected error while collecting non-existing path %s', p) errored_paths.append(p) continue if not force: video.subtitle_languages |= set( search_external_subtitles( video.name, directory=directory).values()) refine(video, episode_refiners=refiner, movie_refiners=refiner, embedded_subtitles=not force) videos.append(video) continue # directories if os.path.isdir(p): try: scanned_videos = scan_videos(p, age=age, archives=archives) except: logger.exception( 'Unexpected error while collecting directory path %s', p) errored_paths.append(p) continue for video in scanned_videos: if check_video(video, languages=language, age=age, undefined=single): if not force: video.subtitle_languages |= set( search_external_subtitles( video.name, directory=directory).values()) refine(video, episode_refiners=refiner, movie_refiners=refiner, embedded_subtitles=not force) videos.append(video) else: ignored_videos.append(video) continue # other inputs try: video = scan_video(p) except: logger.exception('Unexpected error while collecting path %s', p) errored_paths.append(p) continue if check_video(video, languages=language, age=age, undefined=single): if not force: video.subtitle_languages |= set( search_external_subtitles( video.name, directory=directory).values()) refine(video, episode_refiners=refiner, movie_refiners=refiner, embedded_subtitles=not force) videos.append(video) else: ignored_videos.append(video) # output errored paths if verbose > 0: for p in errored_paths: click.secho('%s errored' % p, fg='red') # output ignored videos if verbose > 1: for video in ignored_videos: click.secho( '%s ignored - subtitles: %s / age: %d day%s' % (os.path.split(video.name)[1], ', '.join(str(s) for s in video.subtitle_languages) or 'none', video.age.days, 's' if video.age.days > 1 else ''), fg='yellow') # report collected videos click.echo('%s video%s collected / %s video%s ignored / %s error%s' % ( click.style( str(len(videos)), bold=True, fg='green' if videos else None), 's' if len(videos) > 1 else '', click.style(str(len(ignored_videos)), bold=True, fg='yellow' if ignored_videos else None), 's' if len(ignored_videos) > 1 else '', click.style(str(len(errored_paths)), bold=True, fg='red' if errored_paths else None), 's' if len(errored_paths) > 1 else '', )) # exit if no video collected if not videos: return # download best subtitles downloaded_subtitles = defaultdict(list) with AsyncProviderPool(max_workers=max_workers, providers=provider, provider_configs=obj['provider_configs']) as p: with click.progressbar( videos, label='Downloading subtitles', item_show_func=lambda v: os.path.split(v.name)[1] if v is not None else '') as bar: for v in bar: scores = get_scores(v) subtitles = p.download_best_subtitles( p.list_subtitles(v, language - v.subtitle_languages), v, language, min_score=scores['hash'] * min_score / 100, hearing_impaired=hearing_impaired, only_one=single) downloaded_subtitles[v] = subtitles if p.discarded_providers: click.secho( 'Some providers have been discarded due to unexpected errors: %s' % ', '.join(p.discarded_providers), fg='yellow') # save subtitles total_subtitles = 0 for v, subtitles in downloaded_subtitles.items(): saved_subtitles = save_subtitles(v, subtitles, single=single, directory=directory, encoding=encoding) total_subtitles += len(saved_subtitles) if verbose > 0: click.echo( '%s subtitle%s downloaded for %s' % (click.style(str(len(saved_subtitles)), bold=True), 's' if len(saved_subtitles) > 1 else '', os.path.split(v.name)[1])) if verbose > 1: for s in saved_subtitles: matches = s.get_matches(v) score = compute_score(s, v) # score color score_color = None scores = get_scores(v) if isinstance(v, Movie): if score < scores['title']: score_color = 'red' elif score < scores['title'] + scores['year'] + scores[ 'release_group']: score_color = 'yellow' else: score_color = 'green' elif isinstance(v, Episode): if score < scores['series'] + scores['season'] + scores[ 'episode']: score_color = 'red' elif score < scores['series'] + scores['season'] + scores[ 'episode'] + scores['release_group']: score_color = 'yellow' else: score_color = 'green' # scale score from 0 to 100 taking out preferences scaled_score = score if s.hearing_impaired == hearing_impaired: scaled_score -= scores['hearing_impaired'] scaled_score *= 100 / scores['hash'] # echo some nice colored output click.echo( ' - [{score}] {language} subtitle from {provider_name} (match on {matches})' .format(score=click.style('{:5.1f}'.format(scaled_score), fg=score_color, bold=score >= scores['hash']), language=s.language.name if s.language.country is None else '%s (%s)' % (s.language.name, s.language.country.name), provider_name=s.provider_name, matches=', '.join( sorted(matches, key=scores.get, reverse=True)))) if verbose == 0: click.echo('Downloaded %s subtitle%s' % (click.style(str(total_subtitles), bold=True), 's' if total_subtitles > 1 else ''))
def get_video(tv_episode, video_path, subtitles_dir=None, subtitles=True, embedded_subtitles=None, release_name=None): """Return the subliminal video for the given path. The video_path is used as a key to cache the video to avoid scanning and parsing the video metadata all the time :param tv_episode: :type tv_episode: medusa.tv.Episode :param video_path: the video path :type video_path: str :param subtitles_dir: the subtitles directory :type subtitles_dir: str or None :param subtitles: True if existing external subtitles should be taken into account :type subtitles: bool or None :param embedded_subtitles: True if embedded subtitles should be taken into account :type embedded_subtitles: bool or None :param release_name: the release name :type release_name: str or None :return: video :rtype: subliminal.video.Video """ key = video_key.format(video_path=video_path) payload = {'subtitles_dir': subtitles_dir, 'subtitles': subtitles, 'embedded_subtitles': embedded_subtitles, 'release_name': release_name} cached_payload = memory_cache.get(key, expiration_time=VIDEO_EXPIRATION_TIME) if cached_payload != NO_VALUE and {k: v for k, v in iteritems(cached_payload) if k != 'video'} == payload: logger.debug(u'Found cached video information under key %s', key) return cached_payload['video'] video_is_mkv = video_path.endswith('.mkv') subtitles_dir = subtitles_dir or get_subtitles_dir(video_path) logger.debug(u'Scanning video %s...', video_path) try: video = scan_video(video_path) except ValueError as error: logger.warning(u'Unable to scan video: %s. Error: %r', video_path, error) else: # Add hash of our custom provider Itasa video.size = os.path.getsize(video_path) if video.size > 10485760: video.hashes['itasa'] = hash_itasa(video_path) # external subtitles if subtitles: video.subtitle_languages |= set(search_external_subtitles(video_path, directory=subtitles_dir).values()) if embedded_subtitles is None: embedded_subtitles = bool(not app.IGNORE_EMBEDDED_SUBS and video_is_mkv) refine(video, episode_refiners=episode_refiners, embedded_subtitles=embedded_subtitles, release_name=release_name, tv_episode=tv_episode) video.alternative_series = list(tv_episode.series.aliases) payload['video'] = video memory_cache.set(key, payload) logger.debug(u'Video information cached under key %s', key) return video
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)