是否可以使用诱变剂将 ID3 标签添加到 m4a 文件?

Is it possible to add ID3 tags to m4a files using mutagen?

我 运行 在编写脚本以使用诱变剂标记我的音乐库时遇到问题。 以下代码在处理 mp3 文件时工作正常,但会损坏 m4a 文件。

def set_video_tags(video, filepath):
    try:
        tags = ID3(filepath, v2_version=3)
    except ID3NoHeaderError:
        tags = ID3()

    tags.add(TXXX(3, desc='desc:custom_tag',text= video.custom_text))
    tags.save(filepath, v2_version=3)

处理 m4a 文件后,标签可以使用 mutagen 读回,但不会被任何其他播放器检测到,音频也不会播放。

我尝试在调用此函数之前从文件中删除 MP4 标签,但没有帮助。

我做错了什么?

正如我在评论中所说,MP4 和 MP3 是不同的容器,它们使用完全不同的结构来存储元数据。虽然从技术上讲,您可以在 MP4 元数据中编码 ID3 字段,但这种方法的使用在很大程度上取决于您的 tagger/player,因为有很多方法可以做到这一点,而且每个人都认为他们有最好的方法,因此可以推动他们自己的结构。

什么是 MP3 容器的 ID3,XMP 是 MP4/M4A 容器(以及许多其他容器,如 JPEG、PDF 甚至 MP3,如果您愿意的话)——除了 XMP 是一个真正的标准,而 ID3 是事后才想到的(它实际上只是在 MP3 文件末尾添加的尾随结构),每个人都同意,它的功能主要由当时非常流行的播放器决定,如 WinAmp、foobar2000 等。相比之下,XMP 本质上是一种类似原子馈送的结构(这就是为什么您会听到 mp4 标签被称为 'atoms',即使它们指的不是同一事物),它连同其元数据也携带信息元数据所呈现的内容,因此即使是第一次遇到它的玩家在理论上也可以从不相关的元数据中辨别出相关的内容。

当然,在实践中这也变成了 player/tagger war 所以他们中的很多人使用他们自己的自定义 'extensions' 来标记 - 目前 iTunes 拥有巨大的影响力应该使用哪些字段以及如何使用,以及其他玩家的打法 - mutagen 也是如此。 iTunes 不是单一的 XMP 结构,而是在 MP4 结构本身中将标签作为 non-video/audio 帧(实际的 'atoms')传播,并且一些 'tags' 必须被奇怪地命名(二进制名称等)所以他们不会干扰格式本身。虽然这种方法有一些优势(通过流式传输更改元数据,对现场活动非常有用),但它使标记变得不必要地复杂,并且再次成为非标准的。

无论如何,您的问题出现是因为您试图将 ID3 结构写入 MP4 容器 - 写入 ID3 标签时 mutagen 不会解析整个文件以辨别底层文件是否支持 ID3 和where/how 来写它,而不是假设它被输入一个普通的 MP3 文件并在错误的地方以错误的格式写下来,充其量只是在你的文件末尾添加一些垃圾(早期的,非-流式 ID3 版本),或者最坏的情况是破坏您的 M4A 容器。当你读回文件时也是如此——它可以在一个可预测的地方读取它之前写的 ID3 结构,它不关心其余的数据是什么。

所以,正如我所说的那样,正如文档中所示,在处理 MP4/M4A 容器时使用 mutagen.mp4.MP4(或者更确切地说是底层 mutagen.mp4.MP4Tags)创建用于 MP4 容器。例如,要在尝试时更改 desc 标签:

from mutagen.mp4 import MP4

def get_description(filename):
    return MP4(filename).tags.get("desc", [None])[-1]

def set_description(filename, description):
    tags = MP4(filename).tags
    tags["desc"] = description
    tags.save(filename)

注意:我只使用 get_description() 函数中的最后一个 desc 条目,因为支持每个 'tag' 的多个条目,因此现有标签 return 作为列表。您显然不会在生产环境中使用上述内容。

您可以通过以下方式进行测试:

print("Current description: {}".format(get_description("test.m4a")))
# Current description: None

set_description("test.m4a", "Test description")  # let's add some description
print("Current description: {}".format(get_description("test.m4a")))
# Current description: Test description

# You can also modify the description once set:
set_description("test.m4a", get_description("test.m4a") + " update")
print("Current description: {}".format(get_description("test.m4a")))
# Current description: Test description update

# etc.

对于全套 'supported'(或者更确切地说,iTunes,嗯,鼓励)密钥,您可以查看 mutagen.mp4.MP4Tag 文档。当然,您可以使用自由形式结构(即 ----:foo:bar)发明自己的密钥,但不要指望任何其他玩家都能识别它们。