def is_valid_mkv(path): with open(path, 'rb') as f: try: enzyme.MKV(f) return True except enzyme.exceptions.MalformedMKVError: return False
def check_mkv(path): mkv = None if not os.path.isfile(path): return None try: with open(path, "rb") as f: mkv = enzyme.MKV(f) except (IOError, OverflowError, enzyme.exceptions.MalformedMKVError, AttributeError) as e: print os.path.basename(path), str(e) if mkv is None: return None newname = fix_makemkv_name(os.path.basename(path)) audio_info = [] for track in mkv.audio_tracks: audio_info.append('%s %s' % (track.language, track.name)) subtitle_info = [] for track in mkv.subtitle_tracks: subtitle_info.append('%s %s%s%s' % (track.language, "E" if track.enabled else "", "D" if track.default else "", "F" if track.forced else "")) track = mkv.video_tracks[0] print "%s - %s - %ux%u%s %s - %s - %s" % ( newname, mkv.info.duration, track.width, track.height, 'i' if track.interlaced else '', track.codec_id, ', '.join(audio_info), ", ".join(subtitle_info)) return mkv
def list_languages(self, file): subtitles_list = [] if self.ffprobe: parser = VideoFileParser(ffprobe=self.ffprobe, includeMissing=True, rawMode=False) data = parser.parseFfprobe(file) for detected_language in data['subtitles']: subtitles_list.append([ detected_language['language'], detected_language['forced'], detected_language["codec"] ]) else: if os.path.splitext(file)[1] == '.mkv': with open(file, 'rb') as f: try: mkv = enzyme.MKV(f) except MalformedMKVError: logging.error( 'BAZARR cannot analyze this MKV with our built-in MKV parser, you should install ffmpeg: ' + file) else: for subtitle_track in mkv.subtitle_tracks: subtitles_list.append([ subtitle_track.language, subtitle_track.forced, subtitle_track.codec_id ]) return subtitles_list
def list_languages(self, file): subtitles_list = [] if os.path.splitext(file)[1] == '.mkv': with open(file, 'rb') as f: mkv = enzyme.MKV(f) for subtitle_track in mkv.subtitle_tracks: subtitles_list.append( [subtitle_track.language, subtitle_track.forced]) else: if self.ffprobe: detected_languages = [] try: detected_languages = subprocess.check_output( [ self.ffprobe, "-loglevel", "error", "-select_streams", "s", "-show_entries", "stream_tags=language", "-of", "csv=p=0", file.encode(locale.getpreferredencoding()) ], universal_newlines=True, stderr=subprocess.STDOUT).strip().split("\n") except subprocess.CalledProcessError as e: raise FFprobeError(e.output) else: for detected_language in detected_languages: subtitles_list.append([detected_language, False]) # I can't get the forced flag from ffprobe so I always assume it isn't forced return subtitles_list
def describe(self, video_path, context): """Return video metadata.""" try: with open(video_path, 'rb') as f: data = defaultdict(dict) ff = todict(enzyme.MKV(f)) data.update(ff) if 'info' in data and data['info'] is None: return {} data['info']['complete_name'] = video_path data['info']['file_size'] = os.path.getsize(video_path) except enzyme.MalformedMKVError: # pragma: no cover logger.warning('Invalid file %r', video_path) if context.get('fail_on_error'): raise MalformedFileError return {} if context.get('raw'): return data result = self._describe_tracks(data.get('info'), data.get('video_tracks'), data.get('audio_tracks'), data.get('subtitle_tracks'), context) result['provider'] = 'Enzyme {0}'.format(enzyme.__version__) return result
def list_languages(self, file): subtitles_list = [] if self.ffprobe: api.initialize({'provider': 'ffmpeg', 'ffmpeg': self.ffprobe}) data = api.know(file) if 'subtitle' in data: for detected_language in data['subtitle']: if 'language' in detected_language: language = detected_language['language'].alpha3 forced = detected_language[ 'forced'] if 'forced' in detected_language else None codec = detected_language[ 'format'] if 'format' in detected_language else None subtitles_list.append([language, forced, codec]) else: continue else: if os.path.splitext(file)[1] == '.mkv': with open(file, 'rb') as f: try: mkv = enzyme.MKV(f) except MalformedMKVError: logging.error( 'BAZARR cannot analyze this MKV with our built-in MKV parser, you should install ffmpeg: ' + file) else: for subtitle_track in mkv.subtitle_tracks: subtitles_list.append([ subtitle_track.language, subtitle_track.forced, subtitle_track.codec_id ]) return subtitles_list
def list_languages(self, file): subtitles_list = [] if self.ffprobe: parser = VideoFileParser(ffprobe=self.ffprobe, includeMissing=True, rawMode=False) data = parser.parseFfprobe(file) for detected_language in data['subtitles']: subtitles_list.append([ detected_language['language'], detected_language['forced'], detected_language["codec"] ]) else: if os.path.splitext(file)[1] == '.mkv': with open(file, 'rb') as f: mkv = enzyme.MKV(f) for subtitle_track in mkv.subtitle_tracks: subtitles_list.append([ subtitle_track.language, subtitle_track.forced, subtitle_track.codec_id ]) return subtitles_list
def __is_uhd_mkv(target): with open(target, 'rb') as mkv_f: import enzyme mkv = enzyme.MKV(mkv_f) if len(mkv.video_tracks) > 0: uhd_track = next((v for v in mkv.video_tracks if v.display_width > 1920), None) return uhd_track is not None return False
def mkv_for_filenames(**kwargs): for fname in cli_filenames(**kwargs): with open(fname, 'rb') as f: try: mkv = enzyme.MKV(f) except enzyme.exceptions.MalformedMKVError as ex: print('Error parsing %s: %s' % (fname, ex)) continue yield (fname, mkv)
def rename_files(movie_dir, kind="movie"): print("Renaming files in: {}".format(movie_dir)) files = sorted( [x for x in os.listdir(movie_dir) if os.path.isfile(os.path.join(movie_dir, x)) and x != "MANUALLY_NAMED"], key=functools.cmp_to_key(logical_sort)) prefix, imdb_id = create_file_name_prefix(os.path.basename(movie_dir), kind, files) runtime = int(get_imdb_runtime(imdb_id)) main_file_found = 0 filename_maps = [] for old_file in files: old_name, extension = os.path.splitext(old_file) new_name = prefix is_main_file = False movie = None if extension == ".mkv": if len(files) == 1: new_name += " - Main file" else: with open(os.path.join(movie_dir, old_file), "rb") as fi: try: movie = enzyme.MKV(fi) except: movie = None if movie and (movie.info.duration.seconds - (movie.info.duration.seconds % 60)) / 60 in [runtime - 1, runtime, runtime + 1]: if not main_file_found: new_name += " - Main file" else: new_name += " - Main file ({})".format(main_file_found) print("\tFound main file") main_file_found += 1 is_main_file = True part = get_file_part_name(old_name) if part and not is_main_file: new_name += " - pt{}".format(part[-1]) new_name += extension filename_maps.append((old_file, new_name, is_main_file, get_aspect_ratio(movie, new_name))) aspect_ratios_found = list(set([x[3] for x in filename_maps if x[2]])) for name_map in filename_maps: old_file = name_map[0] new_name = name_map[1] is_main_file = name_map[2] if is_main_file and main_file_found == 2 and len(aspect_ratios_found) == 2: name, extension = os.path.splitext(new_name) new_name = name.replace(" (1)", "") + " - " + name_map[3] + extension if not is_main_file and main_file_found: if not os.path.isdir(os.path.join(movie_dir, "Other")): os.mkdir(os.path.join(movie_dir, "Other")) os.rename(os.path.join(movie_dir, old_file), os.path.join(movie_dir, "Other", new_name)) elif old_file != new_name: try: os.rename(os.path.join(movie_dir, old_file), os.path.join(movie_dir, new_name)) except: pdb.set_trace()
def getMatroska(vfile): """ Get metadata and track information from Matroska containers """ try: with open(vfile) as f: mkv = enzyme.MKV(f) except enzyme.MalformedMKVError as e: logexc(e, "Not a Matroska container or segment is corrupt") return False return mkv.to_dict()
def store_subtitles(file): languages = [] actual_subtitles = [] if os.path.exists(file): if os.path.splitext(file)[1] == '.mkv': try: with open(file, 'rb') as f: mkv = enzyme.MKV(f) for subtitle_track in mkv.subtitle_tracks: try: if alpha2_from_alpha3(subtitle_track.language) != None: actual_subtitles.append([str(alpha2_from_alpha3(subtitle_track.language)),None]) except: pass except: pass brazilian_portuguese = [".pt-br", ".pob", "pb"] try: subtitles = core.search_external_subtitles(file) except: pass else: for subtitle, language in subtitles.iteritems(): if str(os.path.splitext(subtitle)[0]).lower().endswith(tuple(brazilian_portuguese)) is True: actual_subtitles.append([str("pb"), path_replace_reverse(os.path.join(os.path.dirname(file), subtitle))]) elif str(language) != 'und': actual_subtitles.append([str(language), path_replace_reverse(os.path.join(os.path.dirname(file), subtitle))]) else: with open(path_replace(os.path.join(os.path.dirname(file), subtitle)), 'r') as f: text = list(islice(f, 100)) text = ' '.join(text) encoding = UnicodeDammit(text) try: text = text.decode(encoding.original_encoding) except Exception as e: logging.exception('Error trying to detect character encoding for this subtitles file: ' + path_replace(os.path.join(os.path.dirname(file), subtitle)) + ' You should try to delete this subtitles file manually and ask Bazarr to download it again.') else: detected_language = langdetect.detect(text) if len(detected_language) > 0: actual_subtitles.append([str(detected_language), path_replace_reverse(os.path.join(os.path.dirname(file), subtitle))]) conn_db = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'data/db/bazarr.db'), timeout=30) c_db = conn_db.cursor() c_db.execute("UPDATE table_episodes SET subtitles = ? WHERE path = ?", (str(actual_subtitles), path_replace_reverse(file))) conn_db.commit() c_db.close() return actual_subtitles
def main(args): parser = argparse.ArgumentParser() parser.add_argument("--select", "-s", action="store_true", help="Use zenity to select witch tracks to export") parser.add_argument( "--notify", action="store_true", help="Use notify-send to notify when each jobe is done") parser.add_argument("--dry-run", "-n", action="store_true", help="Don't run the mkvextract command") parser.add_argument("--progress", action="store_true", help="Use zenity to draw a progress bar") parser.add_argument("filename", nargs="+") args = parser.parse_args() for filename in args.filename: with open(filename, 'rb') as f: mkv = enzyme.MKV(f) subtitle_tracks = mkv.subtitle_tracks if args.select: cmd = construct_zenity_cmd(subtitle_tracks) print(cmd) selected_numbers = [ int(x) for x in subprocess.check_output( cmd, shell=True).decode().strip().split("|") ] print(selected_numbers) subtitle_tracks = subtitle_track_by_number(subtitle_tracks, selected_numbers) if not subtitle_tracks: print(f"No subtitle tracks found in {filename}", file=sys.stderr) continue print( f"Will run mkvextract with the following tracks:\n{subtitle_tracks}\n" ) cmd = construct_mkvextract_cmd(filename, subtitle_tracks) if args.progress: cmd += " | " + ZENITY_PROGRESS_CMD.format(filename=filename) print("Command to be executed:\n" + cmd) if not args.dry_run: result = subprocess.call(cmd, shell=True) result = "Done" if result == 0 else "Failed" if args.notify: subprocess.call( f"notify-send 'Subtitle Extraction {result}' '{result} with {filename}'", shell=True) return 0
def read_mkv_file(source_file): """Retrieve MKV file information""" import sys import enzyme from enzyme.exceptions import MalformedMKVError with open(source_file, "rb") as mkv_source: try: mkv_file = enzyme.MKV(mkv_source) except MalformedMKVError: return SABResult(False, error="[ERROR] {0}".format(sys.exc_info()[0])) return SABResult(True, data=mkv_file)
def get_vid_duraiton(file_name): """ use enzyme library to extract video duration object param: file_name """ try: with open(file_name, "rb") as f: meta = enzyme.MKV(f) t_delta = meta.info.duration except OSError: f.close() return None f.close() return t_delta
def read_mkv_file(source_file): """Retrieve MKV file information""" import sys import collections import enzyme from enzyme.exceptions import MalformedMKVError result = collections.namedtuple('Result', 'mkv_file error') with open(source_file, 'rb') as mkv_source: try: mkv_file = enzyme.MKV(mkv_source) except MalformedMKVError: return result(None, '[ERROR] %s' % sys.exc_info()[0]) return result(mkv_file, None)
def mkv_metadata(file): """retrieve info from MKV's metadata file""" with open(file, 'rb') as f: mkv = enzyme.MKV(f) resolution = str(mkv.video_tracks[0].width) + 'x' + str( mkv.video_tracks[0].height) audio_channels = mkv.audio_tracks[0].channels runtime = str(mkv.info.duration).split('.', 1)[0] return { 'resolution': resolution, 'audio_channels': audio_channels, 'runtime': runtime }
def store_subtitles_movie(file): languages = [] actual_subtitles = [] if os.path.exists(file): if os.path.splitext(file)[1] == '.mkv': try: with open(file, 'rb') as f: mkv = enzyme.MKV(f) for subtitle_track in mkv.subtitle_tracks: try: actual_subtitles.append([str(pycountry.languages.lookup(subtitle_track.language).alpha_2), None]) except: pass except: pass subtitles = core.search_external_subtitles(file) for subtitle, language in subtitles.iteritems(): if str(language) != 'und': actual_subtitles.append([str(language), path_replace_reverse_movie(os.path.join(os.path.dirname(file), subtitle))]) else: if os.path.splitext(subtitle)[1] != ".sub": with open(path_replace_movie(os.path.join(os.path.dirname(file), subtitle)), 'r') as f: text = list(islice(f, 100)) text = ' '.join(text) encoding = UnicodeDammit(text) try: text = text.decode(encoding.original_encoding) except Exception as e: logging.exception('Error trying to detect character encoding for this subtitles file: ' + path_replace_movie(os.path.join(os.path.dirname(file), subtitle)) + ' You should try to delete this subtitles file manually and ask Bazarr to download it again.') else: detected_language = langdetect.detect(text) if len(detected_language) > 0: actual_subtitles.append([str(detected_language), path_replace_reverse_movie(os.path.join(os.path.dirname(file), subtitle))]) conn_db = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'data/db/bazarr.db'), timeout=30) c_db = conn_db.cursor() c_db.execute("UPDATE table_movies SET subtitles = ? WHERE path = ?", (str(actual_subtitles), path_replace_reverse_movie(file))) conn_db.commit() c_db.close() return actual_subtitles
def prepare_subtitles(self, s, offset=None, keep_on_screen=False): if s.is_internal and not keep_on_screen: full_path = os.path.join(s.directory, s.file_name) with open(full_path, 'rb') as fd: mkv = enzyme.MKV(fd) # TODO: check which track number instead of iterate for sub_track in mkv.subtitle_tracks: sub_track = mkv.subtitle_tracks[0] if sub_track.codec_id == 'S_TEXT/UTF8': new_file = 'subtitle_{id}.srt'.format(id=s.id) cmd = [ 'mkvextract', 'tracks', full_path, '{n}:{e}'.format(n=sub_track.number - 1, e=new_file) ] subprocess.check_output(cmd) subtitle_path = new_file break if not s.is_internal: subtitle_path = os.path.join(s.directory, s.file_name) if keep_on_screen: subtitle_path = '{id}_keep.srt'.format(id=s.id) call_command('export_subtitles', s.id, subtitle_path, True) res = subtitle_path output = subprocess.check_output(['file', subtitle_path]) cmd = None extension = s.extension if s.extension != 'mkv' else 'srt' new_name = '{id}.utf8.{ext}'.format(id=s.id, ext=extension) if 'ISO-8859' in output or 'Non-ISO extended-ASCII' in output: cmd = 'iconv --from-code=ISO-8859-1 --to-code=UTF-8 "{s}" > "{n}"'\ .format(s=subtitle_path, n=new_name) elif 'ASCII' in output: cmd = 'iconv --from-code=ASCII --to-code=UTF-8 "{s}" > "{n}"'\ .format(s=subtitle_path, n=new_name) if cmd: os.system(cmd) res = new_name if offset: split = new_name.split('.') n = '.'.join(split[:-1]) new_name = '{n}.ss.{ext}'.format(n=n, ext=split[-1]) cmd = 'ffmpeg -i "{res}" -ss {offset} -f {ext} -y "{nn}"'.format( res=res, offset=offset, ext=split[-1], nn=new_name) os.system(cmd) res = new_name return res
def list_languages(self, file): from utils import get_binary self.ffprobe = get_binary("ffprobe") subtitles_list = [] if self.ffprobe: api.initialize({'provider': 'ffmpeg', 'ffmpeg': self.ffprobe}) data = api.know(file) traditional_chinese = ["cht", "tc", "traditional", "zht", "hant", "big5", u"繁", u"雙語"] brazilian_portuguese = ["pt-br", "pob", "pb", "brazilian", "brasil", "brazil"] if 'subtitle' in data: for detected_language in data['subtitle']: if 'language' in detected_language: language = detected_language['language'].alpha3 if language == 'zho' and 'name' in detected_language: if any (ext in (detected_language['name'].lower()) for ext in traditional_chinese): language = 'zht' if language == 'por' and 'name' in detected_language: if any (ext in (detected_language['name'].lower()) for ext in brazilian_portuguese): language = 'pob' forced = detected_language['forced'] if 'forced' in detected_language else False hearing_impaired = detected_language['hearing_impaired'] if 'hearing_impaired' in \ detected_language else False codec = detected_language['format'] if 'format' in detected_language else None subtitles_list.append([language, forced, hearing_impaired, codec]) else: continue else: if os.path.splitext(file)[1] == '.mkv': with open(file, 'rb') as f: try: mkv = enzyme.MKV(f) except MalformedMKVError: logging.error('BAZARR cannot analyze this MKV with our built-in MKV parser, you should install ffmpeg: ' + file) else: for subtitle_track in mkv.subtitle_tracks: hearing_impaired = False if subtitle_track.name: if 'sdh' in subtitle_track.name.lower(): hearing_impaired = True subtitles_list.append([subtitle_track.language, subtitle_track.forced, hearing_impaired, subtitle_track.codec_id]) return subtitles_list
def parse_video(file, guid): with open(file, 'rb') as f: mkv = enzyme.MKV(f) length = mkv.info.duration bitrate = mkv.audio_tracks[0].sampling_frequency channels = mkv.audio_tracks[0].channels f_format = get_extension(file) width = mkv.video_tracks[0].width height = mkv.video_tracks[0].height with connection.cursor() as cursor: cursor.execute( """ INSERT INTO video_metadata ( file_guid, file_format, length, bit_rate, mono_or_stereo, width, height ) VALUES ( %s, %s, %s, %s, %s, %s, %s ) """, [guid, f_format, length, bitrate, channels, width, height]) return True
def get_mkv_ttid_map(self): mkv_ttid_map = dict() for path in Path(self.download_dir).rglob('*.mkv'): try: with open(path, 'rb') as f: mkv = enzyme.MKV(f) if mkv.tags: for x in mkv.tags: for y in x.simpletags: if y.name == 'TTID': mkv_ttid_map[y.string] = str(path) raise GetOutOfLoop except GetOutOfLoop: pass except enzyme.MalformedMKVError as ex: self.logger.warning("Exception while parsing file {}".format(str(path))) self.logger.warning("You may want to delete and re-download this file.") self.logger.warning("Exception: {}".format(ex)) pass return mkv_ttid_map
def weebify_mkv(mkv_path, noop=False): print('Opening {}'.format(mkv_path), file=sys.stderr) with open(mkv_path, 'rb') as fp: mkv = enzyme.MKV(fp) jpn_audio_track = find_jpn_audio_track(mkv) def_audio_track = find_default_audio_track(mkv) set_default_sub = should_avoid_default_sub(mkv) set_default_audio = jpn_audio_track and jpn_audio_track != def_audio_track track_changes = {track.number: [] for track in mkv.audio_tracks + mkv.subtitle_tracks} if set_default_audio: if noop: print('Would flag audio track {} as default'.format(jpn_audio_track.number), file=sys.stderr) track_changes[jpn_audio_track.number].append('flag-default=1') for track in mkv.audio_tracks: if track != jpn_audio_track and track.default: if noop: print('Would unflag audio track {} as default'.format(track.number), file=sys.stderr) track_changes[track.number].append('flag-default=0') if set_default_sub: primary_eng_sub = find_eng_sub(mkv) if noop: print('Would flag subtitle track {} as default'.format(primary_eng_sub.number), file=sys.stderr) track_changes[primary_eng_sub.number].append('flag-default=1') for track in mkv.subtitle_tracks: if track != primary_eng_sub and track.default: if noop: print('Would unflag subtitle track {} as default'.format(track.number), file=sys.stderr) track_changes[track.number].append('flag-default=0') args = build_args(track_changes) if args: if noop: return 1 else: subprocess.check_call(['mkvpropedit', '-v', mkv_path, '-v'] + args) return 0 else: print('Nothing to do', file=sys.stderr) return 0
def has_subtitle(self, language): """ Returns true if the video has already a subtitle for a specific language. """ has_subtitle = False # Look for embedded subtitle in mkv video if Video.is_mkv(self.signature): with open(self.filename, 'rb') as file_handler: mkv_video = enzyme.MKV(file_handler) for sub in mkv_video.subtitle_tracks: try: if sub.language and \ Language.fromalpha3b(sub.language) == language: has_subtitle = True break elif sub.name and \ Language.fromname(sub.name) == language: has_subtitle = True break except BabelfishError: LOG.error("Embedded subtitle track" "language {} is not a valid language".format( sub.language)) # Look for external subtitle dir_name = os.path.dirname(self.filename) base_name, _ = os.path.splitext(os.path.basename(self.filename)) search_subtitle = os.path.join( dir_name, "{}.{}.*".format(base_name, language.alpha2)) existing_subtitles = [ sub_file for sub_file in glob.glob(search_subtitle) if os.path.splitext(sub_file)[1] in Subtitle.EXTENSIONS ] if existing_subtitles: has_subtitle = True return has_subtitle
def list_languages(self, file): if self.ffprobe: try: if not settings.general.getboolean('ignore_pgs_subs'): subtitle_languages = subprocess.check_output([self.ffprobe, "-loglevel", "error", "-select_streams", "s", "-show_entries", "stream_tags=language", "-of", "csv=p=0", file.encode(locale.getpreferredencoding())], universal_newlines=True, stderr=subprocess.STDOUT).strip() if not subtitle_languages: return [] return subtitle_languages.split('\n') subtitle_tracks = subprocess.check_output([self.ffprobe, "-loglevel", "error", "-select_streams", "s", "-show_entries", "stream=codec_name:stream_tags=language", "-of", "csv=p=0", file.encode(locale.getpreferredencoding())], universal_newlines=True, stderr=subprocess.STDOUT).strip() if not subtitle_tracks: return [] subtitle_tracks = subtitle_tracks.split('\n') return [lang for (sub_type, lang) in map(lambda subtitle_track: subtitle_track.split(','), subtitle_tracks) if sub_type != 'hdmv_pgs_subtitle'] except subprocess.CalledProcessError as e: raise FFprobeError(e.output) if os.path.splitext(file)[1] != '.mkv': raise NotMKVAndNoFFprobe() with open(file, 'rb') as f: mkv = enzyme.MKV(f) if not settings.general.getboolean('ignore_pgs_subs'): return [subtitle_track.language for subtitle_track in mkv.subtitle_tracks] return [subtitle_track.language for subtitle_track in mkv.subtitle_tracks if subtitle_track.codec_id != "S_HDMV/PGS"]
def collect_mkv(self, directory, file_name): full_path = os.path.join(directory, file_name) with open(full_path, 'rb') as fd: try: mkv = enzyme.MKV(fd) except Exception as e: print(full_path) print(e) return if mkv.subtitle_tracks: # TODO: collect all subtitles s = mkv.subtitle_tracks[0] # TODO: collect S_TEXT/ASS too if s.codec_id == 'S_TEXT/UTF8': temp_file = 'temp_subtitle.srt' cmd = [ 'mkvextract', 'tracks', full_path, '{n}:{e}'.format(n=s.number - 1, e=temp_file) ] subprocess.check_output(cmd) # TODO: guess the language lang = 'simple' rel_mediafile = MediaFile.objects.filter( directory=directory, file_name=file_name).first() subtitle_file = SubtitlesFile.objects.create( file_name=file_name, directory=directory, extension='mkv', mediafile=rel_mediafile, language=lang) self.collect_subtitles_lines(subtitle_file, temp_file) os.remove(temp_file) return subtitle_file else: print 'Found other codec id {id} in {f}'.format( id=s.codec_id, f=full_path)
def scan_video(path, subtitles=True, embedded_subtitles=True, original=None): """Scan a video and its subtitle languages from a video `path` :param string path: absolute path to the video :param bool subtitles: scan for subtitles with the same name :param bool embedded_subtitles: scan for embedded subtitles :return: the scanned video :rtype: :class:`Video` :raise: ValueError if cannot guess enough information from the path """ if not original: original = path dirpath, filename = os.path.split(path) logger.info('Scanning video %r in %r', filename, dirpath) video = Video.fromguess(path, guessit.guess_file_info(original, 'autodetect')) video.size = os.path.getsize(path) if video.size > 10485760: logger.debug('Size is %d', video.size) video.hashes['opensubtitles'] = hash_opensubtitles(path) video.hashes['thesubdb'] = hash_thesubdb(path) logger.debug('Computed hashes %r', video.hashes) else: logger.warning('Size is lower than 10MB: hashes not computed') if subtitles: video.subtitle_languages |= scan_subtitle_languages(path) # enzyme try: if filename.endswith('.mkv'): with open(path, 'rb') as f: mkv = enzyme.MKV(f) if mkv.video_tracks: video_track = mkv.video_tracks[0] # resolution if video_track.height in (480, 720, 1080): if video_track.interlaced: video.resolution = '%di' % video_track.height logger.debug('Found resolution %s with enzyme', video.resolution) else: video.resolution = '%dp' % video_track.height logger.debug('Found resolution %s with enzyme', video.resolution) # video codec if video_track.codec_id == 'V_MPEG4/ISO/AVC': video.video_codec = 'h264' logger.debug('Found video_codec %s with enzyme', video.video_codec) elif video_track.codec_id == 'V_MPEG4/ISO/SP': video.video_codec = 'DivX' logger.debug('Found video_codec %s with enzyme', video.video_codec) elif video_track.codec_id == 'V_MPEG4/ISO/ASP': video.video_codec = 'XviD' logger.debug('Found video_codec %s with enzyme', video.video_codec) else: logger.warning('MKV has no video track') if mkv.audio_tracks: audio_track = mkv.audio_tracks[0] # audio codec if audio_track.codec_id == 'A_AC3': video.audio_codec = 'AC3' logger.debug('Found audio_codec %s with enzyme', video.audio_codec) elif audio_track.codec_id == 'A_DTS': video.audio_codec = 'DTS' logger.debug('Found audio_codec %s with enzyme', video.audio_codec) elif audio_track.codec_id == 'A_AAC': video.audio_codec = 'AAC' logger.debug('Found audio_codec %s with enzyme', video.audio_codec) else: logger.warning('MKV has no audio track') if mkv.subtitle_tracks: # embedded subtitles if embedded_subtitles: embedded_subtitle_languages = set() for st in mkv.subtitle_tracks: if st.language: try: embedded_subtitle_languages.add( babelfish.Language.fromalpha3b( st.language)) except babelfish.Error: logger.error( 'Embedded subtitle track language %r is not a valid language', st.language) embedded_subtitle_languages.add( babelfish.Language('und')) elif st.name: try: embedded_subtitle_languages.add( babelfish.Language.fromname(st.name)) except babelfish.Error: logger.debug( 'Embedded subtitle track name %r is not a valid language', st.name) embedded_subtitle_languages.add( babelfish.Language('und')) else: embedded_subtitle_languages.add( babelfish.Language('und')) logger.debug('Found embedded subtitle %r with enzyme', embedded_subtitle_languages) video.subtitle_languages |= embedded_subtitle_languages else: logger.debug('MKV has no subtitle track') except enzyme.Error: logger.exception('Parsing video metadata with enzyme failed') return video
def guess_video_metadata(filename): """Gets the video metadata properties out of a given file. The file needs to exist on the filesystem to be able to be analyzed. An empty guess is returned otherwise. You need to have the Enzyme python package installed for this to work.""" result = Guess() def found(prop, value): result[prop] = value log.debug('Found with enzyme %s: %s' % (prop, value)) # first get the size of the file, in bytes try: size = os.stat(filename).st_size found('fileSize', size) except Exception as e: log.error('Cannot get video file size: %s' % e) # file probably does not exist, we might as well return now return result # then get additional metadata from the file using enzyme, if available try: import enzyme with open(filename) as f: mkv = enzyme.MKV(f) found('duration', mkv.info.duration.total_seconds()) if mkv.video_tracks: video_track = mkv.video_tracks[0] # resolution if video_track.height in (480, 720, 1080): if video_track.interlaced: found('screenSize', '%di' % video_track.height) else: found('screenSize', '%dp' % video_track.height) else: # TODO: do we want this? #found('screenSize', '%dx%d' % (video_track.width, video_track.height)) pass # video codec if video_track.codec_id == 'V_MPEG4/ISO/AVC': found('videoCodec', 'h264') elif video_track.codec_id == 'V_MPEG4/ISO/SP': found('videoCodec', 'DivX') elif video_track.codec_id == 'V_MPEG4/ISO/ASP': found('videoCodec', 'XviD') else: log.warning('MKV has no video track') if mkv.audio_tracks: audio_track = mkv.audio_tracks[0] # audio codec if audio_track.codec_id == 'A_AC3': found('audioCodec', 'AC3') elif audio_track.codec_id == 'A_DTS': found('audioCodec', 'DTS') elif audio_track.codec_id == 'A_AAC': found('audioCodec', 'AAC') else: log.warning('MKV has no audio track') if mkv.subtitle_tracks: embedded_subtitle_languages = set() for st in mkv.subtitle_tracks: try: if st.language: lang = babelfish.Language.fromalpha3b(st.language) elif st.name: lang = babelfish.Language.fromname(st.name) else: lang = babelfish.Language('und') except babelfish.Error: lang = babelfish.Language('und') embedded_subtitle_languages.add(lang) found('subtitleLanguage', embedded_subtitle_languages) else: log.debug('MKV has no subtitle track') return result except ImportError: log.error('Cannot get video file metadata, missing dependency: enzyme') log.error( 'Please install it from PyPI, by doing eg: pip install enzyme') return result except IOError as e: log.error('Could not open file: %s' % filename) log.error( 'Make sure it exists and is available for reading on the filesystem' ) log.error('Error: %s' % e) return result except enzyme.Error as e: log.error('Cannot guess video file metadata') log.error('enzyme.Error while reading file: %s' % filename) log.error('Error: %s' % e) return result
def parse_video_metadata(file, file_size, episode_file_id=None, movie_file_id=None): # Define default data keys value data = { 'ffprobe': {}, 'enzyme': {}, 'file_id': episode_file_id if episode_file_id else movie_file_id, 'file_size': file_size } # Get the actual cache value form database if episode_file_id: cache_key = database.execute( 'SELECT ffprobe_cache FROM table_episodes WHERE episode_file_id=? AND file_size=?', (episode_file_id, file_size), only_one=True) elif movie_file_id: cache_key = database.execute( 'SELECT ffprobe_cache FROM table_movies WHERE movie_file_id=? AND file_size=?', (movie_file_id, file_size), only_one=True) else: cache_key = None # check if we have a value for that cache key if not isinstance(cache_key, dict): return data else: try: # Unpickle ffprobe cache cached_value = pickle.loads(cache_key['ffprobe_cache']) except: pass else: # Check if file size and file id matches and if so, we return the cached value if cached_value['file_size'] == file_size and cached_value[ 'file_id'] in [episode_file_id, movie_file_id]: return cached_value # if not, we retrieve the metadata from the file from utils import get_binary ffprobe_path = get_binary("ffprobe") # if we have ffprobe available if ffprobe_path: api.initialize({'provider': 'ffmpeg', 'ffmpeg': ffprobe_path}) data['ffprobe'] = api.know(file) # if nto, we use enzyme for mkv files else: if os.path.splitext(file)[1] == '.mkv': with open(file, 'rb') as f: try: mkv = enzyme.MKV(f) except MalformedMKVError: logging.error( 'BAZARR cannot analyze this MKV with our built-in MKV parser, you should install ' 'ffmpeg/ffprobe: ' + file) else: data['enzyme'] = mkv # we write to db the result and return the newly cached ffprobe dict if episode_file_id: database.execute( 'UPDATE table_episodes SET ffprobe_cache=? WHERE episode_file_id=?', (pickle.dumps(data, pickle.HIGHEST_PROTOCOL), episode_file_id)) elif movie_file_id: database.execute( 'UPDATE table_movies SET ffprobe_cache=? WHERE movie_file_id=?', (pickle.dumps(data, pickle.HIGHEST_PROTOCOL), movie_file_id)) return data
def extract_info(cls, video_path): """Extract info from the video.""" with open(video_path, 'rb') as f: return to_dict(enzyme.MKV(f))