1
0
mirror of https://github.com/hanxi/xiaomusic.git synced 2026-05-04 23:56:02 +08:00

feat: 增强OGG封面解析,支持FLAC二进制结构的metadata_block_picture (#851)

* feat: 支持自定义temp_path路径,临时文件不再依赖music_path挂载

以前临时文件需挂载在music_path下才能访问,现在temp/前缀的文件直接从
config.temp_path提供访问,支持自定义任意临时目录路径。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat: 增强OGG封面解析,支持FLAC二进制结构的metadata_block_picture

解析metadata_block_picture标签时,先尝试JSON格式再尝试FLAC二进制结构格式,
兼容更多工具生成的OGG文件封面数据。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: mwjdot888 <mwjdot888@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
mwjdot888
2026-05-03 21:52:41 +08:00
committed by GitHub
parent fa0511f4e1
commit e6f3c07eea

View File

@@ -11,6 +11,7 @@ import mimetypes
import os
import re
import shutil
import struct
import subprocess
import tempfile
from dataclasses import (
@@ -412,6 +413,48 @@ def _get_alltag_value(tags, k: str) -> str:
return ""
def _parse_metadata_block_picture(tag_value: str) -> bytes | None:
"""解析 OGG Vorbis 的 metadata_block_picture 标签
先尝试 JSON 格式(部分工具使用),再尝试 FLAC 二进制结构格式。
FLAC METADATA_BLOCK_PICTURE 二进制结构:
4B picture type | 4B MIME length + MIME | 4B desc length + desc
| 4B width | 4B height | 4B depth | 4B colors | 4B data length + data
"""
raw = base64.b64decode(tag_value)
# 尝试 JSON 格式
try:
picture = json.loads(raw)
if isinstance(picture, dict) and "data" in picture:
return base64.b64decode(picture["data"])
except (json.JSONDecodeError, UnicodeDecodeError):
pass
# 尝试 FLAC 二进制结构格式
try:
offset = 0
# picture type (4B)
offset += 4
# MIME type
mime_len = struct.unpack_from(">I", raw, offset)[0]
offset += 4 + mime_len
# description
desc_len = struct.unpack_from(">I", raw, offset)[0]
offset += 4 + desc_len
# width, height, depth, colors (各4B)
offset += 16
# picture data
data_len = struct.unpack_from(">I", raw, offset)[0]
offset += 4
return raw[offset : offset + data_len]
except (struct.error, IndexError):
pass
log.warning("Failed to parse metadata_block_picture")
return None
def _save_picture(picture_data: bytes, save_root: str, file_path: str) -> str:
"""保存图片"""
# 计算文件名的哈希值
@@ -542,10 +585,13 @@ def extract_audio_metadata(file_path: str, save_root: str) -> dict:
metadata.year = _get_tag_value(tags, "DATE")
metadata.genre = _get_tag_value(tags, "GENRE")
if "metadata_block_picture" in tags:
picture = json.loads(base64.b64decode(tags["metadata_block_picture"][0]))
metadata.picture = _save_picture(
base64.b64decode(picture["data"]), save_root, file_path
picture_data = _parse_metadata_block_picture(
tags["metadata_block_picture"][0]
)
if picture_data:
metadata.picture = _save_picture(
picture_data, save_root, file_path
)
elif isinstance(audio, ASF):
metadata.title = _get_tag_value(tags, "Title")