добавление тегов id3 в файловую систему html5 API

У меня есть сценарий, в котором я создаю веб-приложения для подкастов, которые позволяют прослушивать и хранить файл подкаста .mp3.

Я пытаюсь реализовать базовый веб-интерфейс, в котором кто-то может добавить полный тег id3 со стороны клиента (файл будет храниться локально на стороне клиента: этот клиент не похож на клиент для всех, а просто парень, который получает необработанный файл подкаста без какого-либо тега id3, предпочтительно). Затем он размещает эту страницу локально, добавляет правильные теги id3, а затем копирует эти .mp3 в папку WebDav.

Я понимаю, что правки нужно делать на сервере, но было бы очень полезно, если бы все это можно было сделать локально в браузере.

Конечно, нет готовой библиотеки для редактирования файлов, поэтому я решил использовать API файловой системы HTML5, т.е. поместить файл в виртуальную файловую систему, отредактировать его там, а затем скопировать обратно в локальную систему. (для копирования есть готовая библиотека FileSaver.js).

Мне удалось сделать следующее: 1) связать файл mp3, сброшенный в зону перетаскивания, с API файловой системы с помощью webkitGetAsEntry

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);

Меня смущает, как добавить тег весь id3? . Я потерялся в этот момент, так как я не уверен в:

1) можем ли мы добавить весь тег id3 в файл из метода fileWriter? 2) Если да, это будет бинарное редактирование или как?? .

Любая помощь будет полезна. попробовал ниже, но я предполагаю, что ошибаюсь.

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

person anil keshav    schedule 12.06.2015    source источник


Ответы (1)


Вам нужно создать буфер ID3, затем создать буфер, достаточно большой для хранения файлов ID3 ​​и MP3, вставить ID3 и добавить данные MP3.

Для этого вам понадобится спецификация ID3 и используйте типизированные массивы с DataView для построить свой массив.

Общая структура ID3 определяется следующим образом (см. ссылку выше):

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

На данный момент длина буфера неизвестна, поэтому вам нужно делать это поэтапно. Есть несколько способов сделать это, вы можете создать небольшие сегменты буфера для каждого поля, а затем суммировать их в один буфер. Или вы можете создать больший буфер, который, как вы знаете, может содержать все поля, которые вы хотите включить, и скопировать сумму полей из этого буфера в окончательный.

Последний, как правило, проще, и, поскольку мы имеем дело с очень маленькими размерами, это может быть лучшим способом (учитывая, что каждый фрагмент в первом подходе имеет свои накладные расходы).

Итак, первое, что вам нужно сделать, это определить заголовок. Заголовок определяется следующим образом:

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

ID3 и версия являются фиксированными значениями (конечно, существуют и другие версии, но давайте следовать текущей).

Вероятно, вы можете игнорировать большинство флагов, если не все, установив для них значение 0. Но проверьте документацию для вашего варианта использования, например, если вы хотите использовать расширенные заголовки.

Размер определяется:

Размер тега ID3v2 хранится в виде 32-битного синхронизируемого целого числа (раздел 6.2), что в сумме составляет 28 эффективных битов (представляющих до 256 МБ).

Размер тега ID3v2 представляет собой сумму длины в байтах расширенного заголовка
, заполнения и кадров после десинхронизации. Если нижний колонтитул
присутствует, он равен ('общий размер' - 20) байтам, в противном случае ('общий размер' - 10) байтам.

Пример того, как вы можете построить свой буфер. Сначала определите буфер, достаточно большой для хранения всех данных, а также DataView:

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

DataView по умолчанию использует обратный порядок байтов, что идеально, поэтому все, что нам нужно сделать сейчас, это заполнить данные там, где они должны быть. Мы можем создать несколько вспомогательных методов, которые помогут нам перемещать позицию одновременно с записью. Позиции для DataView привязаны к байтам:

 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 например) и так далее.

Чтобы определить заголовок, мы можем написать «волшебное» слово ID3. Вы можете преобразовать строку или, поскольку она всего 3 байта, просто напишите ее прямо. 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 заполнен, 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);

Если все прошло нормально, теперь у вас есть MP3 с тегом ID3 (не забудьте проверить существующие ID3 - вам нужно отсканировать до конца).

Теперь вы можете отправить ArrayBuffer на сервер или преобразовать в Blob для IndexedDB или в URL-адрес объекта, если вы хотите предоставить ссылку для загрузки (ни один из показанных здесь ответов не выходит за рамки).

Этого должно быть достаточно для начала — как сказано, вам нужно изучить спецификации. Если вы не знакомы с типизированным массивом, ознакомьтесь с ними. также.

Также см. на сайте другие ресурсы (фреймы и т. д.).

Безопасные для синхронизации значения

В файлах «MP3» используются кадры, начинающиеся с 11 бит, для всех которых установлено значение 1. Если поле размера заголовка содержит 11 бит, для которых установлено значение 1, декодер может ошибочно интерпретировать его как звуковые данные. Чтобы избежать этого, используется концепция безопасных для синхронизации целых чисел, гарантирующая, что каждый байт MSB (самый старший бит, бит 7) всегда установлен в 0. Бит перемещается влево, следующий байт сдвигается на один бит для ID3. тег 4 раза (отсюда и 4x% 01111111).

Вот как кодировать и декодировать безопасные для синхронизации целые числа с помощью JavaScript (из источник Википедии C/C++) :

// 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;
}

Безопасное для синхронизации значение будет отображать такие биты, как: &b01111111011111110111111101111111 для примерного значения, используемого в приведенной выше демонстрации.

person Community    schedule 12.06.2015
comment
просто сомнение с размером тега тега заголовка. согласно спецификациям id3 он говорит 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.. В настоящее время я кодирую только заголовок, который должен присутствовать в теге, поэтому может ли это быть значение по умолчанию: 4 *% 0xxxxxxx - person anil keshav; 12.06.2015
comment
Это безопасное для синхронизации значение (см. 6.2 в документе). Его нужно закодировать особым образом, чтобы бит 7 (MSB) всегда был равен 0. Бит 7 сдвигается влево, затем делается то же самое с битом 7 этого байта, всего 4 раза. Добавлено решение этого в ответ. (и поскольку мы вставляем один бит, равный 0, мы теряем 4 бита, следовательно, максимальный размер 28 бит). Документы могли бы быть более четкими, поскольку это поле очень важно. - person ; 13.06.2015
comment
Просто наблюдаю проблему с файлом . view8 = new Uint8Array(mp3); . Когда я напрямую проверяю большой двоичный объект на view, т.е. var id3buffer = new ArrayBuffer(1024); view = new DataView(id3buffer);, шестнадцатеричный код для просмотра отображается правильно: file name: YourFileName (13).mp3 mime type: 0000-0010: 49 44 33 00-00 00 00 00-00 00 00 00-00 00 00 00 ID3. Но с типизированным массивом он отображается как все 0 var mp3 = new ArrayBuffer(pos); view8 = new Uint8Array(mp3);view8.set(view.buffer, 0); , когда я просматриваю значение mp3 Arraybuffer, все равно 0 . 'имя файла: YourFileName (14).mp3 mime type: 0000-0003: 00 00 00 ` - person anil keshav; 15.06.2015
comment
сделано: DataViewCopy — это место, где я напрямую копирую DataView в большой двоичный объект и предоставляю ObjectURL, и все работает нормально. TypedArray — это место, где я копирую содержимое DataView в typedArray, и все это 0. Как только файлы загружены. Я использую этот онлайн-инструмент, чтобы просмотреть побайтовый дамп файлов - person anil keshav; 15.06.2015
comment
См. это: jsfiddle.net/epistemex/vxw5hm9x/2 Похоже, исходный массив должен быть обрезанным, чтобы соответствовать целевому массиву. Вы можете сделать это, создав новое представление (Uin8) и установив начало и длину того, что оно должно представлять, а затем установите это. - person ; 15.06.2015
comment
спасибо, это сработало, мне даже пришлось обрезать буфер массива mp3, чтобы он соответствовал друг другу var segment = new Uint8Array(view.buffer, 0, pos); var mp3buffersegment = new Uint8Array(mp3buffer, 0, mp3buffer.byteLength); view8.set(segment, 0); view8.set(mp3buffersegment, pos); - person anil keshav; 15.06.2015