添加 id3 标签 html5 文件系统 api

adding id3 tags html5 filesystem api

我有一个场景,我正在构建一个允许收听和存储 .mp3 播客文件的播客 Web 应用程序。

我正在尝试实现一个基本的 Web 界面,其中有人可以从客户端添加 整个 id3 标签(文件将存储在客户端本地:此客户端不像每个人的客户,而只是那些最好没有任何 id3 标签的原始播客文件的人)。然后他在本地托管这个页面并添加正确的 id3 标签,然后将这些 .mp3 复制到一个 WebDav 文件夹中。

我知道编辑需要在服务器上完成,但如果这一切都可以在浏览器本地完成,那将非常有帮助。

当然没有现成的库来编辑文件,所以我决定使用 HTML5 文件系统 api,即将文件放入虚拟文件系统,编辑它然后复制它回到本地系统。 (对于复制,有一个现成的库 FileSaver.js)。

我已经能够做到以下几点: 1) 使用 webkitGetAsEntry

将放置在放置区的 mp3 文件关联到文件系统 api

2) 将此文件复制到文件系统 api.

部分代码如下所示:

function onDrop(e) 
{
    e.preventDefault();
    e.stopPropagation();

    var items = e.dataTransfer.items;
    var files = e.dataTransfer.files;

    for (var i = 0, item; item = items[i]; ++i) 
    {
        // Skip this one if we didn't get a file.
        if (item.kind != 'file') {
            continue;
        }

        var entry = item.webkitGetAsEntry();

        if (entry.isFile) 
        {
            // Copy the dropped entry into local filesystem.
            entry.copyTo(cwd, null, function(copiedEntry) {
            //setLoadingTxt({txt: DONE_MSG});
            renderMp3Writer(entry);

我的困惑是如何添加 entire id3 标签? .我现在迷路了,因为我不确定:

1) 我们可以通过 fileWriter 方法将整个 id3 标签添加到文件中吗? 2) 如果是,这将是二进制编辑还是如何编辑? .

任何帮助都会很有用。尝试了以下但我猜我错了。

var blob1 = new Blob(['ID3hTIT2ga'], {type: 'audio/mp3'});
fileWriter.write(blob1);

您需要构建一个 ID3 缓冲区,然后创建一个足够大的缓冲区来容纳 ID3 和 MP3 文件,插入 ID3 并追加 MP3 数据。

为此,您需要 ID3 specification 并使用带有 DataView 的类型化数组来构建您的数组。

ID3整体结构定义如下(见上文link):

 +-----------------------------+
 |      Header (10 bytes)      |
 +-----------------------------+
 |       Extended Header       |
 | (variable length, OPTIONAL) |
 +-----------------------------+
 |   Frames (variable length)  |
 +-----------------------------+
 |           Padding           |
 | (variable length, OPTIONAL) |
 +-----------------------------+
 | Footer (10 bytes, OPTIONAL) |
 +-----------------------------+

此时缓冲区长度未知,因此您需要分步进行。有几种方法可以做到这一点,您可以为每个字段构建小的缓冲区段,然后将它们汇总到一个缓冲区中。或者你可以制作一个更大的缓冲区,你知道它可以容纳你想要包含的所有字段,并将字段的总和从该缓冲区复制到最后一个缓冲区。

后者往往更简单,因为我们处理的是非常小的尺寸,这可能是最好的方法(考虑到第一种方法中的每个片段都有其开销)。

所以你需要做的第一件事就是定义header。 header 是这样定义的:

ID3v2/file identifier      "ID3"
ID3v2 version               00
ID3v2 flags                %abcd0000  (note: bit-representation)
ID3v2 size             4 * %0xxxxxxx  (note: bit-representation/mask)

ID3和version是固定值(当然还有其他版本,但还是按现行的吧)。

您可以忽略大部分标志,即使不是全部,也可以将它们设置为 0。但是请检查您的 use-case 的文档,例如,如果您想使用扩展的 headers .

尺寸定义:

The ID3v2 tag size is stored as a 32 bit synchsafe integer (section 6.2), making a total of 28 effective bits (representing up to 256MB).

The ID3v2 tag size is the sum of the byte length of the extended
header, the padding and the frames after unsynchronisation. If a
footer is present this equals to ('total size' - 20) bytes, otherwise ('total size' - 10) bytes.

如何构建缓冲区的示例。首先定义一个足够大的缓冲区来容纳所有数据以及一个DataView:

var id3buffer = new ArrayBuffer(1024),    // 1kb "space"
    view = new DataView(id3buffer);

DataView默认为big-endian就完美了,所以我们现在要做的就是把数据填到应该填的地方。我们可以制作一些辅助方法来帮助我们在编写的同时移动位置。 DataView 的位置是 byte-bound:

 var pos = 0;    // global start position

function setU8(value) {
    view.setUint8(pos++, value)
}

function setU16(value) {
    view.setUint16(pos, value);
    pos += 2;
}

function setU32(value) {
    view.setUint32(pos, value);
    pos += 4;
}

等您可以让助手编写文本 unicode 字符串(例如,参见 TextEncoder)等等。

要定义header,我们可以在"magic"中写入ID3字。您可以转换一个字符串,或者因为它只有 3 个字节,所以也只需将其写为 straight-forward。 ID3 = 0x494433 十六进制所以:

setU8(0x49);     // at pos 0
setU8(0x44);     // at pos 1
setU8(0x33);     // at pos 2

因为我们制作了一个包装器,所以我们不需要担心缓冲区位置。

然后写入版本(根据规范 v.2.4.0 使用 0x0400 不使用主要版本 (2)):

setU16(0x0400);  // default is big-endian so this works

现在您可以继续使用标志和尺寸(参见规格)。

当 ID3 header 填满后,pos 现在将保留总长度。所以为ID3标签和MP3缓冲区创建一个新的缓冲区:

var mp3 = new ArrayBuffer(pos + mp3Buffer.byteLength),
    view8 = new Uint8Array(mp3);

view8 视图将使我们能够简单地复制到目的地:

// create a segment from the tag buffer that will fit target:
var segment = new Uint8Array(view.buffer, 0, n); // replace n with actual length
view8.set(segment, 0);
view8.set(mp3buffer, pos);

如果一切顺利,您现在拥有带有 ID3 标签的 MP3(记得检查现有的 ID3 - 您需要扫描到结束)。

您现在可以将 ArrayBuffer 发送到服务器,或者转换为 IndexedDB 的 Blob,或者转换为 Object-URL 如果您想要呈现 link 以供下载(此处显示 none因为答案变成 out-of-scope).

这应该足以让您入门 - 如前所述,您需要研究规范。如果您不熟悉 typed array,也请检查一下。

另请参阅 other resources(框架等)的站点。

Sync-safe 值

"MP3" 文件使用以 11 位开始的帧,全部设置为 1。如果 header 的大小字段恰好包含设置为 1 的 11 位,解码器可能会错误地解释它作为声音数据。为避免这种情况,使用 sync-safe 整数的概念确保每个字节的 MSB(最高有效位,第 7 位)始终设置为 0。该位向左移动,下一个字节移动一位,对于 ID3 标签 4 次(因此 4x %01111111)。

以下是如何使用 JavaScript(来自 Wikipedia C/C++ source)对 sync-safe 整数进行编码和解码:

// test values
var value = 0xfffffff,
    sync = intToSyncsafe(value);
document.write("<pre>Original size: 0x" + value.toString(16) + "<br>");
document.write("Synch-safe   : 0x" + sync.toString(16) + "<br>");
document.write("Decoded value: 0x" + syncsafeToInt(sync).toString(16) + "</pre>");


function intToSyncsafe(value) {
    var out, mask = 0x7f;
    while(mask ^ 0x7fffffff) {
        out = value & ~mask;
        out <<= 1;
        out |= value & mask;
        mask = ((mask + 1) << 8) - 1;
        value = out;
    }
    return out
}

function syncsafeToInt(value) {
    var out = 0, mask = 0x7F000000;
    while (mask) {
        out >>= 1;
        out |= value & mask;
        mask >>= 8;
    }
    return out;
}

sync-safe 值将显示如下位:&b01111111011111110111111101111111 对于上面演示中使用的示例值。