import argparse import json import logging import os import sys import typing from argparse import ArgumentParser import yaml from knowit import ( __url__, __version__, api, ) from knowit.provider import ProviderError from knowit.serializer import ( get_json_encoder, get_yaml_dumper, ) from knowit.utils import recurse_paths logging.basicConfig(stream=sys.stdout, format='%(message)s') logging.getLogger('CONSOLE').setLevel(logging.INFO) logging.getLogger('knowit').setLevel(logging.ERROR) console = logging.getLogger('CONSOLE') logger = logging.getLogger('knowit') def build_argument_parser() -> ArgumentParser: """Build the argument parser.""" opts = ArgumentParser() opts.add_argument( dest='videopath', help='Path to the video to introspect', nargs='*', type=str, ) provider_opts = opts.add_argument_group('Providers') provider_opts.add_argument( '-p', '--provider', dest='provider', help='The provider to be used: mediainfo, ffmpeg, mkvmerge or enzyme.', type=str, ) output_opts = opts.add_argument_group('Output') output_opts.add_argument( '--debug', action='store_true', dest='debug', help='Print information for debugging knowit and for reporting bugs.' ) output_opts.add_argument( '--report', action='store_true', dest='report', help='Parse media and report all non-detected values' ) output_opts.add_argument( '-y', '--yaml', action='store_true', dest='yaml', help='Display output in yaml format' ) output_opts.add_argument( '-N', '--no-units', action='store_true', dest='no_units', help='Display output without units' ) output_opts.add_argument( '-P', '--profile', dest='profile', help='Display values according to specified profile: code, default, human, technical', type=str, ) conf_opts = opts.add_argument_group('Configuration') conf_opts.add_argument( '--mediainfo', dest='mediainfo', help='The location to search for MediaInfo binaries', type=str, ) conf_opts.add_argument( '--ffmpeg', dest='ffmpeg', help='The location to search for ffprobe (FFmpeg) binaries', type=str, ) conf_opts.add_argument( '--mkvmerge', dest='mkvmerge', help='The location to search for mkvmerge (MKVToolNix) binaries', type=str, ) information_opts = opts.add_argument_group('Information') information_opts.add_argument( '--version', dest='version', action='store_true', help='Display knowit version.' ) return opts def knowit( video_path: typing.Union[str, os.PathLike], options: argparse.Namespace, context: typing.MutableMapping, ) -> typing.Mapping: """Extract video metadata.""" context['path'] = video_path if not options.report: console.info('For: %s', video_path) else: console.info('Parsing: %s', video_path) info = api.know(video_path, context) if not options.report: console.info('Knowit %s found: ', __version__) console.info(dumps(info, options, context)) return info def _as_yaml( info: typing.Mapping[str, typing.Any], context: typing.Mapping, ) -> str: """Convert info to string using YAML format.""" data = {info['path']: info} if 'path' in info else info return yaml.dump( data, Dumper=get_yaml_dumper(context), default_flow_style=False, allow_unicode=True, sort_keys=False, ) def _as_json( info: typing.Mapping[str, typing.Any], context: typing.Mapping, ) -> str: """Convert info to string using JSON format.""" return json.dumps( info, cls=get_json_encoder(context), indent=4, ensure_ascii=False, ) def dumps( info: typing.Mapping[str, typing.Any], options: argparse.Namespace, context: typing.Mapping, ) -> str: """Convert info to string using json or yaml format.""" convert = _as_yaml if options.yaml else _as_json return convert(info, context) def main(args: typing.List[str] = None) -> None: """Execute main function for entry point.""" argument_parser = build_argument_parser() args = args or sys.argv[1:] options = argument_parser.parse_args(args) if options.debug: logger.setLevel(logging.DEBUG) logging.getLogger('enzyme').setLevel(logging.INFO) else: logger.setLevel(logging.WARNING) paths = recurse_paths(options.videopath) if not paths: if options.version: console.info(api.debug_info()) else: argument_parser.print_help() return report: typing.MutableMapping[str, str] = {} for i, video_path in enumerate(paths): try: context = {k: v for k, v in vars(options).items() if v is not None} if options.report: context['report'] = report else: del context['report'] knowit(video_path, options, context) except ProviderError: logger.exception('Error when processing video') except OSError: logger.exception('OS error when processing video') except UnicodeError: logger.exception('Character encoding error when processing video') except api.KnowitException as e: logger.error(e) if options.report and i % 20 == 19 and report: console.info('Unknown values so far:') console.info(dumps(report, options, vars(options))) if options.report: if report: console.info('Knowit %s found unknown values:', __version__) console.info(dumps(report, options, vars(options))) console.info('Please report them at %s', __url__) else: console.info('Knowit %s knows everything. :-)', __version__) if __name__ == '__main__': main(sys.argv[1:])