{"record":{"id":"7df52ee4eb869709","repo":"harry0703/MoneyPrinterTurbo","slug":"subtitle-font-must-use-the-ttf-or-ttc-extension","errorCode":null,"errorMessage":"subtitle font must use the .ttf or .ttc extension","messagePattern":"subtitle font must use the \\.ttf or \\.ttc extension","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"cli.py","lineNumber":712,"sourceCode":"                params.bgm_file = bgm_service.resolve_bgm_file(params.bgm_file)\n            except ValueError as exc:\n                supported_extensions = \", \".join(\n                    bgm_service.SUPPORTED_BGM_EXTENSIONS\n                )\n                raise ValueError(\n                    \"background music must be a supported audio file inside \"\n                    f\"storage/bgm or resource/songs ({supported_extensions}): \"\n                    f\"{params.bgm_file}\"\n                ) from exc\n\n    if params.subtitle_enabled and params.font_name and stop_at == \"video\":\n        font_path = _resolve_managed_resource_file(\n            params.font_name,\n            resource_dir=utils.font_dir(),\n            description=\"subtitle font\",\n        )\n        if not font_path.lower().endswith((\".ttf\", \".ttc\")):\n            raise ValueError(\"subtitle font must use the .ttf or .ttc extension\")\n        # 下游根据 resource/fonts 内的文件名拼接路径，因此仍保留纯文件名。\n        params.font_name = os.path.basename(font_path)\n\n    if params.video_source != \"local\" or stop_at not in {\"materials\", \"video\"}:\n        return\n\n    local_videos_dir = utils.storage_dir(\"local_videos\", create=True)\n    resolved_materials: list[tuple[MaterialInfo, str, str]] = []\n    for material in params.video_materials or []:\n        source_path = _resolve_cli_file(\n            material.url,\n            description=\"local material\",\n            fallback_dir=local_videos_dir,\n        )\n        extension = os.path.splitext(source_path)[1].lower()\n        if extension not in local_material_extensions:\n            allowed = \", \".join(sorted(local_material_extensions))\n            raise ValueError(","sourceCodeStart":694,"sourceCodeEnd":730,"githubUrl":"https://github.com/harry0703/MoneyPrinterTurbo/blob/1f9f19c2021a68d04df228f33e9099a0c947f6f8/cli.py#L694-L730","documentation":"Thrown by the CLI when subtitles are enabled (--subtitles/--subtitle-enabled), a --font-name was supplied, and stop_at is 'video', but the resolved font file does not end in .ttf or .ttc. TrueType/OpenType-with-true-extensions is the only supported subtitle font format downstream (FFmpeg/ass subtitles), so .otf, .fon, or extension-less files are rejected. Note the font must also already have passed _resolve_managed_resource_file, meaning it lives inside resource/fonts.","triggerScenarios":"CLI invocation with params.subtitle_enabled=True, params.font_name set, stop_at=='video', and the basename resolved from resource/fonts has an extension other than .ttf/.ttc (checked case-insensitively), e.g. myfont.otf or a file with no extension.","commonSituations":"User drops an .otf font into resource/fonts and selects it; font file downloaded without its extension; misnamed file like font.ttf.bak; expecting OpenType support that the renderer does not have.","solutions":["Use only .ttf or .ttc font files in resource/fonts — convert .otf to .ttf with a tool like fontforge or fonttools if needed","Rename the file to carry the correct, real extension (do not just force-rename an incompatible format)","If you did not intend custom subtitles font, omit --font-name to use the default font"],"exampleFix":"# before\n--subtitle-enabled --font-name MyFont.otf --stop-at video\n\n# after (convert otf -> ttf, place in resource/fonts)\nfonttools ttLib.woff2 compress  # or: pyftsubset / fontforge conversion\ncp MyFont.ttf resource/fonts/MyFont.ttf\n--subtitle-enabled --font-name MyFont.ttf --stop-at video","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef font_file_is_supported(font_name: str) -> bool:\n    return Path(font_name).suffix.lower() in (\".ttf\", \".ttc\")","typeGuard":"def is_subtitle_font(name: str) -> bool:\n    \"\"\"Guard: font resolves inside resource/fonts with .ttf/.ttc extension.\"\"\"\n    return (\n        isinstance(name, str)\n        and Path(name).suffix.lower() in (\".ttf\", \".ttc\")\n    )","tryCatchPattern":null,"preventionTips":["Only place .ttf/.ttc files in resource/fonts","Convert .otf fonts to .ttf before deployment","Validate font names at config load time, not only at video render time"],"tags":["cli","subtitle","font","validation"],"backgroundTag":null,"analyzedSha":"1f9f19c2021a68d04df228f33e9099a0c947f6f8","analyzedAt":"2026-08-14T19:41:05.568Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}