{"record":{"id":"c85d6a06a20bea65","repo":"harry0703/MoneyPrinterTurbo","slug":"unsupported-background-music-format-supported-for","errorCode":null,"errorMessage":"unsupported background music format; supported formats: {supported_formats}","messagePattern":"unsupported background music format; supported formats: (.+?)","errorType":"validation","errorClass":"BgmUploadError","httpStatus":400,"severity":"error","filePath":"app/services/bgm.py","lineNumber":114,"sourceCode":"        or len(safe_name) > 255\n        or any(ord(character) < 32 for character in safe_name)\n        or any(character in _WINDOWS_INVALID_FILENAME_CHARS for character in safe_name)\n        or safe_name.lower().startswith(_INTERNAL_UPLOAD_PREFIX)\n    ):\n        raise BgmUploadError(\"invalid background music filename\")\n\n    # Windows 会把扩展名前的首段识别为设备名，例如 CON.mp3、LPT1.wav 都\n    # 不能作为普通文件创建。即使服务端最终使用 UUID，提前拒绝这类名称也能\n    # 保证 API 在不同平台上的输入行为一致。\n    windows_basename = safe_name.split(\".\", 1)[0].rstrip(\" .\").upper()\n    if windows_basename in _WINDOWS_RESERVED_FILENAMES:\n        raise BgmUploadError(\"invalid background music filename\")\n    if Path(safe_name).suffix.lower() not in SUPPORTED_BGM_EXTENSIONS:\n        supported_formats = \", \".join(\n            extension.removeprefix(\".\").upper()\n            for extension in SUPPORTED_BGM_EXTENSIONS\n        )\n        raise BgmUploadError(\n            f\"unsupported background music format; supported formats: {supported_formats}\"\n        )\n    return safe_name\n\n\ndef _validate_audio(file_path: str, timeout_seconds: int = 30) -> None:\n    \"\"\"\n    仅使用项目当前配置的 FFmpeg 验证文件包含可完整解码的音频流。\n\n    项目允许 imageio-ffmpeg 提供便携 FFmpeg，该安装方式不保证同时存在\n    FFprobe，因此不能新增独立二进制依赖。`-map 0:a:0` 会在没有音频流时失败，\n    `-xerror` 会把解码错误提升为失败；完整解码还能拦截加密文件或随机数据偶然\n    命中音频帧头的误判。文件可以包含专辑封面等附加流，但只校验第一条音频流。\n    \"\"\"\n    try:\n        decoded = subprocess.run(\n            [\n                utils.get_ffmpeg_binary(),","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/harry0703/MoneyPrinterTurbo/blob/1f9f19c2021a68d04df228f33e9099a0c947f6f8/app/services/bgm.py#L96-L132","documentation":"Raised by sanitize_upload_filename when Path(safe_name).suffix.lower() is not in SUPPORTED_BGM_EXTENSIONS. The message lists the accepted formats (uppercased, dot-stripped) so the caller can self-correct. This is a cheap extension-level gate before the more expensive FFmpeg decode validation; content correctness is checked later by _validate_audio.","triggerScenarios":"Uploading .wma, .ogg (if unsupported), .flac, or a mismatched double extension like 'song.mp3.txt' whose actual suffix is .txt; a file with no extension at all.","commonSituations":"Users with music in formats outside the supported set; clients preserving original store formats; renaming that leaves the wrong final extension.","solutions":["Re-encode to one of the formats named in the error message (e.g. MP3/M4A/WAV) using ffmpeg or another transcoder.","Verify the final suffix client-side: Path(name).suffix.lower() must be in the supported set — fix double extensions before upload."],"exampleFix":"# before: wrong final extension\nfiles = {\"file\": (\"song.mp3.txt\", open(\"song.mp3\", \"rb\"))}\n\n# after\nfiles = {\"file\": (\"song.mp3\", open(\"song.mp3\", \"rb\"))}","handlingStrategy":"validation","validationCode":"from pathlib import Path\nSUPPORTED = {\".mp3\", \".m4a\", \".wav\"}  # mirror SUPPORTED_BGM_EXTENSIONS\nif Path(filename).suffix.lower() not in SUPPORTED:\n    raise ValueError(f\"unsupported format; use one of {sorted(SUPPORTED)}\")","typeGuard":"def has_supported_audio_suffix(name: str, supported: frozenset[str]) -> bool:\n    return Path((name or \"\").strip()).suffix.lower() in supported","tryCatchPattern":null,"preventionTips":["Filter file dialogs to the supported extension list.","Re-encode exotic formats to MP3/M4A/WAV client-side before upload.","Beware double extensions and extension-less files."],"tags":["audio-format","upload","bgm","file-extension"],"backgroundTag":null,"analyzedSha":"1f9f19c2021a68d04df228f33e9099a0c947f6f8","analyzedAt":"2026-08-14T19:41:05.568Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}