|
| 1 | +import logging |
| 2 | +from argparse import ArgumentParser |
| 3 | +from datetime import datetime |
| 4 | +from pathlib import Path |
| 5 | +from sys import argv |
| 6 | +from tempfile import TemporaryDirectory |
| 7 | +from typing import Optional |
| 8 | + |
| 9 | +from . import _handler |
| 10 | +from .ass.ass_document import AssDocument |
| 11 | +from .collect_fonts import collect_subtitle_fonts |
| 12 | +from .font import ( |
| 13 | + FontCollection, |
| 14 | + FontFile, |
| 15 | + FontLoader, |
| 16 | + FontSelectionStrategyLibass |
| 17 | +) |
| 18 | +from .mkvtoolnix import MKVExtract, MKVFontFile, Mkvpropedit |
| 19 | + |
| 20 | +_logger = logging.getLogger(__name__) |
| 21 | + |
| 22 | + |
| 23 | +def main() -> None: |
| 24 | + start_time = datetime.now().strftime("%Y-%m-%d--%H-%M-%S") |
| 25 | + default_log_path = Path.cwd().joinpath(f"{start_time}_mkvfontvalidator.log") |
| 26 | + |
| 27 | + parser = ArgumentParser( |
| 28 | + description="MKV font validator for Advanced SubStation Alpha file." |
| 29 | + ) |
| 30 | + parser.add_argument( |
| 31 | + "-mkv", |
| 32 | + type=Path, |
| 33 | + help=""" |
| 34 | + The video file to be verified. Must be a Matroska file. |
| 35 | + """, |
| 36 | + ) |
| 37 | + parser.add_argument( |
| 38 | + "-mkvpropedit", |
| 39 | + type=Path, |
| 40 | + help=""" |
| 41 | + Path to mkvpropedit.exe if not in variable environments. |
| 42 | + """, |
| 43 | + ) |
| 44 | + parser.add_argument( |
| 45 | + "--need-draw-fonts", |
| 46 | + action="store_true", |
| 47 | + help=""" |
| 48 | + If specified, FontCollector will report a error if a font used in a draw isn't muxed to the mkv. For more detail when this is usefull, see: https://github.com/libass/libass/issues/617 |
| 49 | + """, |
| 50 | + ) |
| 51 | + parser.add_argument( |
| 52 | + "--delete-fonts-not-used", |
| 53 | + "-d", |
| 54 | + action="store_true", |
| 55 | + help=""" |
| 56 | + If specified, FontCollector will remove the fonts that aren't used by the subtitle(s) of the mkv file. |
| 57 | + """, |
| 58 | + ) |
| 59 | + parser.add_argument( |
| 60 | + "--logging", |
| 61 | + "-log", |
| 62 | + type=Path, |
| 63 | + nargs="?", |
| 64 | + const=default_log_path, |
| 65 | + default=None, |
| 66 | + help=""" |
| 67 | + Destination path of log. If it isn't specified, it will be YYYY-MM-DD--HH-MM-SS_mkvfontvalidator.log. |
| 68 | + """, |
| 69 | + ) |
| 70 | + |
| 71 | + args = parser.parse_args() |
| 72 | + |
| 73 | + mkv_path: Path = args.mkv |
| 74 | + need_draw_fonts: bool = args.need_draw_fonts |
| 75 | + delete_fonts_not_used: bool = args.delete_fonts_not_used |
| 76 | + logging_file_path: Optional[Path] = args.logging |
| 77 | + |
| 78 | + if args.mkvpropedit: |
| 79 | + Mkvpropedit.PROGRAM_PATH = args.mkvpropedit |
| 80 | + |
| 81 | + if logging_file_path: |
| 82 | + file_handler = logging.FileHandler(logging_file_path, mode="a", encoding="utf-8") |
| 83 | + file_handler.setLevel(_handler.level) |
| 84 | + file_handler.setFormatter(_handler.formatter) |
| 85 | + root_logger = logging.getLogger() |
| 86 | + root_logger.addHandler(file_handler) |
| 87 | + _logger.info(f"{Path.cwd()}>{' '.join(argv)}") |
| 88 | + |
| 89 | + try: |
| 90 | + with TemporaryDirectory() as tmp_dir: |
| 91 | + |
| 92 | + mkv_ass_files = MKVExtract.get_mkv_ass_files(mkv_path, Path(tmp_dir)) |
| 93 | + mkv_font_files = MKVExtract.get_mkv_font_files(mkv_path, Path(tmp_dir)) |
| 94 | + |
| 95 | + fonts_file_found: set[FontFile] = set() |
| 96 | + additional_fonts = FontLoader.load_additional_fonts([f.filename for f in mkv_font_files]) |
| 97 | + font_collection = FontCollection(use_system_font=False, additional_fonts=additional_fonts) |
| 98 | + font_strategy = FontSelectionStrategyLibass() |
| 99 | + |
| 100 | + for mkv_ass_file in mkv_ass_files: |
| 101 | + subtitle = AssDocument.from_file(mkv_ass_file.filename) |
| 102 | + _logger.info(f"Loaded successfully the .ass stream at index {mkv_ass_file.mkv_id}") |
| 103 | + |
| 104 | + fonts_file_found.update(collect_subtitle_fonts(subtitle, font_collection, font_strategy, need_draw_fonts, False, None)) |
| 105 | + |
| 106 | + font_not_used: list[MKVFontFile] = [] |
| 107 | + for mkv_font_file in mkv_font_files: |
| 108 | + if not any(mkv_font_file.filename.samefile(font_file.filename) for font_file in fonts_file_found): |
| 109 | + font_not_used.append(mkv_font_file) |
| 110 | + |
| 111 | + if not delete_fonts_not_used: |
| 112 | + _logger.warning(f"You can remove the font at the id {mkv_font_file.mkv_id} named \"{mkv_font_file.mkv_font_filename}\". It is not used by any .ass file.") |
| 113 | + |
| 114 | + if delete_fonts_not_used and font_not_used: |
| 115 | + Mkvpropedit.delete_fonts_of_mkv(mkv_path, [mkv_font.mkv_id for mkv_font in font_not_used]) |
| 116 | + _logger.warning(f'Successfully deleted {", ".join([f"{mkv_font.mkv_id}-{mkv_font.mkv_font_filename}" for mkv_font in font_not_used])} of the mkv "{mkv_path}"') |
| 117 | + |
| 118 | + except Exception as e: |
| 119 | + _logger.error("An unexpected error occured", exc_info=True) |
| 120 | + |
| 121 | + |
| 122 | +if __name__ == "__main__": |
| 123 | + main() |
0 commit comments