*исправление ошибок: двой перезаписи, исправление записи с BOM, добавлена проверка в insert_tags_into_audio
This commit is contained in:
1 parent
ffa8d80875
commit
078e68efa8
5 files changed
+179
-53
No files matched your search
+47
-3
@@ -19,6 +19,8 @@ pub struct Frame {
|
|||||||
apic ?[]u8
|
apic ?[]u8
|
||||||
encoded_size []u8
|
encoded_size []u8
|
||||||
modified bool
|
modified bool
|
||||||
|
// Фрейм целиком в том виде, в каком он был прочитан из файла
|
||||||
|
raw []u8
|
||||||
}
|
}
|
||||||
|
|
||||||
// Во фрейме первый байт указывает кодировку 0 - ISO-8859-1 (Latin-1), 1 - UTF-16LE
|
// Во фрейме первый байт указывает кодировку 0 - ISO-8859-1 (Latin-1), 1 - UTF-16LE
|
||||||
@@ -38,6 +40,9 @@ fn (mut frame Frame) fill(bytes []u8, start_position u64, idv3_version u8) {
|
|||||||
// Конец фрейма
|
// Конец фрейма
|
||||||
frame.end = frame.start + frame.cur_size + 10
|
frame.end = frame.start + frame.cur_size + 10
|
||||||
|
|
||||||
|
frame.raw = bytes[frame.start..frame.end]
|
||||||
|
frame.modified = false
|
||||||
|
|
||||||
// Содержимое фрейма
|
// Содержимое фрейма
|
||||||
frame.encoding_name = match bytes[frame.start + 10] {
|
frame.encoding_name = match bytes[frame.start + 10] {
|
||||||
0 { "ISO-8859-1" }
|
0 { "ISO-8859-1" }
|
||||||
@@ -72,11 +77,21 @@ fn (mut frame Frame) fill(bytes []u8, start_position u64, idv3_version u8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut frame Frame) init(tag_name string) {
|
pub fn (mut frame Frame) init(tag_name string, idv3_version u8) {
|
||||||
frame.name = tag_name.to_upper()
|
frame.name = tag_name.to_upper()
|
||||||
|
frame.set_unicode_encoding(idv3_version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Кодировка, в которой представим любой текст
|
||||||
|
fn (mut frame Frame) set_unicode_encoding(idv3_version u8) {
|
||||||
|
// В v2.3 нет UTF-8, там Юникод только UTF-16 с BOM
|
||||||
|
if idv3_version == 3 {
|
||||||
|
frame.encoding_name = "UTF-16LE"
|
||||||
|
frame.encoding_name_idx = 1
|
||||||
|
} else {
|
||||||
frame.encoding_name = "UTF-8"
|
frame.encoding_name = "UTF-8"
|
||||||
frame.encoding_name_idx = 3
|
frame.encoding_name_idx = 3
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Кодируем длину фрейма с нужным сдвигом
|
// Кодируем длину фрейма с нужным сдвигом
|
||||||
@@ -99,9 +114,27 @@ pub fn (mut frame Frame) encode_size(idv3_version u8) {
|
|||||||
|
|
||||||
// Кодируем кадр для дальнейшей записи
|
// Кодируем кадр для дальнейшей записи
|
||||||
pub fn (mut frame Frame) encode_frame(idv3_version u8) {
|
pub fn (mut frame Frame) encode_frame(idv3_version u8) {
|
||||||
encoded_data := iconv.vstring_to_encoding(frame.data, frame.encoding_name) or { []u8{} }
|
// В ISO-8859-1 есть только символы до U+00FF, остальные iconv молча заменяет на '?'
|
||||||
|
if frame.encoding_name_idx == 0 && frame.data.runes().any(it > 0xFF) {
|
||||||
|
frame.set_unicode_encoding(idv3_version)
|
||||||
|
}
|
||||||
|
|
||||||
frame.encoded_data = encoded_data
|
encoded_data := iconv.vstring_to_encoding(frame.data, frame.encoding_name) or {
|
||||||
|
// Не удалось закодировать в исходной кодировке фрейма, переходим на Юникод
|
||||||
|
frame.set_unicode_encoding(idv3_version)
|
||||||
|
iconv.vstring_to_encoding(frame.data, frame.encoding_name) or {
|
||||||
|
panic("не удалось закодировать фрейм ${frame.name}: ${err}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Для UTF-16 с BOM (01) перед текстом нужен маркер порядка байт
|
||||||
|
frame.encoded_data = if frame.encoding_name_idx == 1 {
|
||||||
|
mut bom := if frame.encoding_name == "UTF-16BE" { [u8(0xFE), 0xFF] } else { [u8(0xFF), 0xFE] }
|
||||||
|
bom << encoded_data
|
||||||
|
bom
|
||||||
|
} else {
|
||||||
|
encoded_data
|
||||||
|
}
|
||||||
|
|
||||||
// Размер тела фрейма: байт кодировки + данные + замыкающие нули
|
// Размер тела фрейма: байт кодировки + данные + замыкающие нули
|
||||||
frame.cur_size = u64(1 + frame.encoded_data.len + frame.terminator_len())
|
frame.cur_size = u64(1 + frame.encoded_data.len + frame.terminator_len())
|
||||||
@@ -137,6 +170,17 @@ pub fn (mut frame Frame) get_encoded_frame_tag(idv3_version u8) []u8 {
|
|||||||
return encoded_tag
|
return encoded_tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Байты фрейма для записи в файл
|
||||||
|
pub fn (mut frame Frame) to_bytes(idv3_version u8) []u8 {
|
||||||
|
// Картинка и нетронутые фреймы пишутся как были прочитаны,
|
||||||
|
// чтобы не потерять то, что не разбирается (язык в COMM, значение в TXXX, бинарные фреймы)
|
||||||
|
if frame.raw.len > 0 && (!frame.modified || frame.name == "APIC") {
|
||||||
|
return frame.raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame.get_encoded_frame_tag(idv3_version)
|
||||||
|
}
|
||||||
|
|
||||||
// Запись данных в тэг
|
// Запись данных в тэг
|
||||||
@[inline]
|
@[inline]
|
||||||
pub fn (mut frame Frame) set_data(data string) {
|
pub fn (mut frame Frame) set_data(data string) {
|
||||||
|
|||||||
+3
-10
@@ -1,7 +1,6 @@
|
|||||||
module metadata
|
module metadata
|
||||||
|
|
||||||
import os { File }
|
import os { File }
|
||||||
import io { read_all }
|
|
||||||
|
|
||||||
// 00: ISO-8859-1 (1 байт на символ).
|
// 00: ISO-8859-1 (1 байт на символ).
|
||||||
// 01: UTF-16 с BOM (2 байта на символ + 2 байта маркер).
|
// 01: UTF-16 с BOM (2 байта на символ + 2 байта маркер).
|
||||||
@@ -104,13 +103,7 @@ pub fn (id3v23 ID3v23) to_v1() ID3v1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut id3v23 ID3v23) insert_tags_into_audio(mut audio File) {
|
// Добавление аудио после нового тэга
|
||||||
full_part := read_all(reader: audio) or { panic(err) }
|
pub fn (mut id3v23 ID3v23) insert_tags_into_audio(mut audio File) ! {
|
||||||
|
insert_tags_into_audio[ID3v23](mut id3v23, mut audio)!
|
||||||
// Размер старого тэга берётся из файла, id3v23.size уже хранит размер нового.
|
|
||||||
// В заголовке тэга размер syncsafe даже в v2.3, поэтому get_size_v24
|
|
||||||
old_size := get_size_v24(full_part[6..10])
|
|
||||||
sound_part := full_part[int(old_size) + 10 .. ]
|
|
||||||
|
|
||||||
id3v23.encoded_header << sound_part
|
|
||||||
}
|
}
|
||||||
+3
-9
@@ -1,7 +1,6 @@
|
|||||||
module metadata
|
module metadata
|
||||||
|
|
||||||
import os { File }
|
import os { File }
|
||||||
import io { read_all }
|
|
||||||
|
|
||||||
|
|
||||||
pub fn (mut tag ID3v24) add_frame(frame Frame) {
|
pub fn (mut tag ID3v24) add_frame(frame Frame) {
|
||||||
@@ -79,12 +78,7 @@ pub fn (id3v24 ID3v24) to_v1() ID3v1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn (mut id3v24 ID3v24) insert_tags_into_audio(mut audio File) {
|
// Добавление аудио после нового тэга
|
||||||
full_part := read_all(reader: audio) or { panic(err) }
|
pub fn (mut id3v24 ID3v24) insert_tags_into_audio(mut audio File) ! {
|
||||||
|
insert_tags_into_audio[ID3v24](mut id3v24, mut audio)!
|
||||||
// Размер старого тэга берётся из файла, id3v24.size уже хранит размер нового
|
|
||||||
old_size := get_size_v24(full_part[6..10])
|
|
||||||
sound_part := full_part[int(old_size) + 10 .. ]
|
|
||||||
|
|
||||||
id3v24.encoded_header << sound_part
|
|
||||||
}
|
}
|
||||||
+59
-21
@@ -72,22 +72,16 @@ pub fn get_current_tag_size[T](mut tag T, mut audio os.File) {
|
|||||||
tag.size = (u64(first_ten_bytes[6]) << 21) | (u64(first_ten_bytes[7]) << 14) | (u64(first_ten_bytes[8]) << 7) | u64(first_ten_bytes[9])
|
tag.size = (u64(first_ten_bytes[6]) << 21) | (u64(first_ten_bytes[7]) << 14) | (u64(first_ten_bytes[8]) << 7) | u64(first_ten_bytes[9])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Кодирование размера заголовка со всеми тэгами
|
// Кодирование размера заголовка со всеми тэгами.
|
||||||
pub fn encode_header_size(size u64, idv3_version u8) []u8 {
|
// В заголовке размер всегда syncsafe (по 7 бит в байте), в том числе в v2.3
|
||||||
stride := if idv3_version == 3 {
|
pub fn encode_header_size(size u64) []u8 {
|
||||||
[u64(24), u64(16), u64(8), u64(0xFF)]
|
|
||||||
} else {
|
|
||||||
[u64(21), u64(14), u64(7), u64(0x7F)]
|
|
||||||
}
|
|
||||||
|
|
||||||
mut encoded_size := []u8{len: 4}
|
mut encoded_size := []u8{len: 4}
|
||||||
encoded_size[0] = u8((size >> stride[0]) & stride[3])
|
encoded_size[0] = u8((size >> 21) & 0x7F)
|
||||||
encoded_size[1] = u8((size >> stride[1]) & stride[3])
|
encoded_size[1] = u8((size >> 14) & 0x7F)
|
||||||
encoded_size[2] = u8((size >> stride[2]) & stride[3])
|
encoded_size[2] = u8((size >> 7) & 0x7F)
|
||||||
encoded_size[3] = u8(size & stride[3])
|
encoded_size[3] = u8(size & 0x7F)
|
||||||
|
|
||||||
return encoded_size
|
return encoded_size
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Кодирование первых 10 байт ID3
|
// Кодирование первых 10 байт ID3
|
||||||
@@ -99,8 +93,7 @@ pub fn encode_header[T](mut id3v T) {
|
|||||||
// Минорная версия и спец. флаги
|
// Минорная версия и спец. флаги
|
||||||
id3v.encoded_header << []u8{len: 2}
|
id3v.encoded_header << []u8{len: 2}
|
||||||
|
|
||||||
// Размер всего тэга в заголовке всегда syncsafe (по 7 бит в байте), в том числе в v2.3
|
id3v.encoded_header << metadata.encode_header_size(id3v.size)
|
||||||
id3v.encoded_header << metadata.encode_header_size(id3v.size, 4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Количество нулей в конце тэга
|
// Количество нулей в конце тэга
|
||||||
@@ -116,16 +109,23 @@ pub fn encode_embed_tags[T](mut id3v2 T) {
|
|||||||
$if field.typ is Frame {
|
$if field.typ is Frame {
|
||||||
// Читаются только заполненные тэги
|
// Читаются только заполненные тэги
|
||||||
if id3v2.$(field.name).cur_size > 0 {
|
if id3v2.$(field.name).cur_size > 0 {
|
||||||
if id3v2.$(field.name).name.to_lower() != "apic" {
|
// Фрейма не было в файле и init не вызывался: имя берётся из поля структуры
|
||||||
// Кодируем значения полей
|
if id3v2.$(field.name).name == "" && field.name.len == 4 {
|
||||||
tag_bytes << id3v2.$(field.name).get_encoded_frame_tag(id3v2.version)
|
id3v2.$(field.name).init(field.name, id3v2.version)
|
||||||
} else {
|
}
|
||||||
// Картинка не трогается
|
|
||||||
tag_bytes << id3v2.$(field.name).apic or { []u8{} }
|
// Без 4-символьного ID фрейм испортит весь тэг
|
||||||
|
if id3v2.$(field.name).name.len == 4 {
|
||||||
|
tag_bytes << id3v2.$(field.name).to_bytes(id3v2.version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Фреймы, для которых нет отдельного поля в структуре
|
||||||
|
for mut frame in id3v2.others {
|
||||||
|
tag_bytes << frame.to_bytes(id3v2.version)
|
||||||
|
}
|
||||||
// Запас нулей чтобы не пересчитывать весь тэг при маленьком редактировании
|
// Запас нулей чтобы не пересчитывать весь тэг при маленьком редактировании
|
||||||
tag_bytes << []u8{len: tag_padding}
|
tag_bytes << []u8{len: tag_padding}
|
||||||
|
|
||||||
@@ -136,3 +136,41 @@ pub fn encode_embed_tags[T](mut id3v2 T) {
|
|||||||
encode_header(mut id3v2)
|
encode_header(mut id3v2)
|
||||||
id3v2.encoded_header << tag_bytes
|
id3v2.encoded_header << tag_bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Добавляет аудио из файла после нового тэга в encoded_header, старый тэг пропускается
|
||||||
|
pub fn insert_tags_into_audio[T](mut id3v2 T, mut audio os.File) ! {
|
||||||
|
header := get_bytes(mut audio, 0, 10)
|
||||||
|
if header.len < 10 || header[..3].bytestr() != "ID3" {
|
||||||
|
return error("в файле нет тэга ID3v2")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Размер старого тэга берётся из файла, id3v2.size уже хранит размер нового.
|
||||||
|
// В заголовке он syncsafe в любой версии, плюс 10 байт самого заголовка
|
||||||
|
mut audio_start := get_size_v24(header[6..10]) + 10
|
||||||
|
// В v2.4 после тэга может идти футер ещё на 10 байт
|
||||||
|
if header[3] == 4 && header[5] & 0x10 != 0 {
|
||||||
|
audio_start += 10
|
||||||
|
}
|
||||||
|
|
||||||
|
audio.seek(0, .end)!
|
||||||
|
file_size := u64(audio.tell()!)
|
||||||
|
if audio_start > file_size {
|
||||||
|
return error("размер тэга (${audio_start}) больше размера файла (${file_size})")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Аудио читается сразу в конец encoded_header, без промежуточной копии всего файла
|
||||||
|
sound_len := int(file_size - audio_start)
|
||||||
|
header_len := id3v2.encoded_header.len
|
||||||
|
unsafe { id3v2.encoded_header.grow_len(sound_len) }
|
||||||
|
|
||||||
|
audio.seek(i64(audio_start), .start)!
|
||||||
|
mut read := 0
|
||||||
|
for read < sound_len {
|
||||||
|
n := unsafe { audio.read_into_ptr(&u8(id3v2.encoded_header.data) + header_len + read, sound_len - read)! }
|
||||||
|
if n == 0 {
|
||||||
|
return error("файл закончился раньше ожидаемого")
|
||||||
|
}
|
||||||
|
read += n
|
||||||
|
}
|
||||||
|
audio.seek(0, .start)!
|
||||||
|
}
|
||||||
+67
-10
@@ -10,8 +10,8 @@ fn test_idv3v23() {
|
|||||||
mut wav := metadata.Audio{}
|
mut wav := metadata.Audio{}
|
||||||
wav.detect_tags(mut audio)
|
wav.detect_tags(mut audio)
|
||||||
|
|
||||||
assert wav.id3v1.embedded == false, 'Тег показан false, а должен быть true'
|
assert wav.id3v1.embedded == false, 'Тег показан true, а должен быть false'
|
||||||
assert wav.id3v23.embedded == true, 'Тег показан true, а должен быть false'
|
assert wav.id3v23.embedded == true, 'Тег показан false, а должен быть true'
|
||||||
assert wav.id3v24.embedded == false, 'Тег показан true, а должен быть false'
|
assert wav.id3v24.embedded == false, 'Тег показан true, а должен быть false'
|
||||||
wav.id3v23.get_full_size(mut audio)
|
wav.id3v23.get_full_size(mut audio)
|
||||||
wav.id3v23.fill_tags(mut audio)
|
wav.id3v23.fill_tags(mut audio)
|
||||||
@@ -45,8 +45,8 @@ fn test_writeidv3v23() {
|
|||||||
mut wav := metadata.Audio{}
|
mut wav := metadata.Audio{}
|
||||||
wav.detect_tags(mut audio)
|
wav.detect_tags(mut audio)
|
||||||
|
|
||||||
assert wav.id3v1.embedded == false, 'Тег показан false, а должен быть true'
|
assert wav.id3v1.embedded == false, 'Тег показан true, а должен быть false'
|
||||||
assert wav.id3v23.embedded == true, 'Тег показан true, а должен быть false'
|
assert wav.id3v23.embedded == true, 'Тег показан false, а должен быть true'
|
||||||
assert wav.id3v24.embedded == false, 'Тег показан true, а должен быть false'
|
assert wav.id3v24.embedded == false, 'Тег показан true, а должен быть false'
|
||||||
wav.id3v23.get_full_size(mut audio)
|
wav.id3v23.get_full_size(mut audio)
|
||||||
wav.id3v23.fill_tags(mut audio)
|
wav.id3v23.fill_tags(mut audio)
|
||||||
@@ -63,7 +63,7 @@ fn test_writeidv3v23() {
|
|||||||
wav.id3v23.txxx.set_data("comment2")
|
wav.id3v23.txxx.set_data("comment2")
|
||||||
|
|
||||||
wav.id3v23.encode_embed_tags()
|
wav.id3v23.encode_embed_tags()
|
||||||
wav.id3v23.insert_tags_into_audio(mut audio)
|
wav.id3v23.insert_tags_into_audio(mut audio) or { panic(err) }
|
||||||
|
|
||||||
os.write_bytes("./data/temp_v23.mp3", wav.id3v23.encoded_header) or { panic(err) }
|
os.write_bytes("./data/temp_v23.mp3", wav.id3v23.encoded_header) or { panic(err) }
|
||||||
|
|
||||||
@@ -76,8 +76,8 @@ fn test_writeidv3v23() {
|
|||||||
mut temp_wav := metadata.Audio{}
|
mut temp_wav := metadata.Audio{}
|
||||||
temp_wav.detect_tags(mut temp_audio)
|
temp_wav.detect_tags(mut temp_audio)
|
||||||
|
|
||||||
assert temp_wav.id3v1.embedded == false, 'Тег показан false, а должен быть true'
|
assert temp_wav.id3v1.embedded == false, 'Тег показан true, а должен быть false'
|
||||||
assert temp_wav.id3v23.embedded == true, 'Тег показан true, а должен быть false'
|
assert temp_wav.id3v23.embedded == true, 'Тег показан false, а должен быть true'
|
||||||
assert temp_wav.id3v24.embedded == false, 'Тег показан true, а должен быть false'
|
assert temp_wav.id3v24.embedded == false, 'Тег показан true, а должен быть false'
|
||||||
temp_wav.id3v23.get_full_size(mut temp_audio)
|
temp_wav.id3v23.get_full_size(mut temp_audio)
|
||||||
temp_wav.id3v23.fill_tags(mut temp_audio)
|
temp_wav.id3v23.fill_tags(mut temp_audio)
|
||||||
@@ -92,7 +92,8 @@ fn test_writeidv3v23() {
|
|||||||
assert temp_wav.id3v23.tcop.data == "Test2"
|
assert temp_wav.id3v23.tcop.data == "Test2"
|
||||||
assert temp_wav.id3v23.txxx.data == "comment2"
|
assert temp_wav.id3v23.txxx.data == "comment2"
|
||||||
|
|
||||||
id3v1 := wav.id3v23.to_v1()
|
// Проверяется прочитанный с диска тэг, а не тот, что в памяти
|
||||||
|
id3v1 := temp_wav.id3v23.to_v1()
|
||||||
|
|
||||||
assert id3v1.genre == "Rock2"
|
assert id3v1.genre == "Rock2"
|
||||||
assert id3v1.title == "Test Title2"
|
assert id3v1.title == "Test Title2"
|
||||||
@@ -112,9 +113,9 @@ fn test_idv3v24() {
|
|||||||
mut wav := metadata.Audio{}
|
mut wav := metadata.Audio{}
|
||||||
wav.detect_tags(mut audio)
|
wav.detect_tags(mut audio)
|
||||||
|
|
||||||
assert wav.id3v1.embedded == false, 'Тег показан false, а должен быть true'
|
assert wav.id3v1.embedded == false, 'Тег показан true, а должен быть false'
|
||||||
assert wav.id3v23.embedded == false, 'Тег показан true, а должен быть false'
|
assert wav.id3v23.embedded == false, 'Тег показан true, а должен быть false'
|
||||||
assert wav.id3v24.embedded == true, 'Тег показан true, а должен быть false'
|
assert wav.id3v24.embedded == true, 'Тег показан false, а должен быть true'
|
||||||
wav.id3v24.get_full_size(mut audio)
|
wav.id3v24.get_full_size(mut audio)
|
||||||
wav.id3v24.fill_tags(mut audio)
|
wav.id3v24.fill_tags(mut audio)
|
||||||
|
|
||||||
@@ -139,3 +140,59 @@ fn test_idv3v24() {
|
|||||||
assert id3v1.comment == "comment"
|
assert id3v1.comment == "comment"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Кириллица во фрейме с кодировкой ISO-8859-1 не должна превращаться в '?'
|
||||||
|
fn test_write_unicode_into_latin1_frame() {
|
||||||
|
mut audio := os.open("./data/test_id3v23.mp3") or { panic(err) }
|
||||||
|
defer {
|
||||||
|
audio.close()
|
||||||
|
}
|
||||||
|
mut wav := metadata.Audio{}
|
||||||
|
wav.detect_tags(mut audio)
|
||||||
|
wav.id3v23.get_full_size(mut audio)
|
||||||
|
wav.id3v23.fill_tags(mut audio)
|
||||||
|
old_size := int(wav.id3v23.size)
|
||||||
|
|
||||||
|
wav.id3v23.tit2.set_data("Глава 2")
|
||||||
|
wav.id3v23.encode_embed_tags()
|
||||||
|
wav.id3v23.insert_tags_into_audio(mut audio) or { panic(err) }
|
||||||
|
|
||||||
|
temp_path := os.join_path(os.temp_dir(), "id3_editor_unicode_test.mp3")
|
||||||
|
os.write_bytes(temp_path, wav.id3v23.encoded_header) or { panic(err) }
|
||||||
|
defer {
|
||||||
|
os.rm(temp_path) or {}
|
||||||
|
}
|
||||||
|
|
||||||
|
mut temp_audio := os.open(temp_path) or { panic(err) }
|
||||||
|
defer {
|
||||||
|
temp_audio.close()
|
||||||
|
}
|
||||||
|
mut temp_wav := metadata.Audio{}
|
||||||
|
temp_wav.detect_tags(mut temp_audio)
|
||||||
|
temp_wav.id3v23.get_full_size(mut temp_audio)
|
||||||
|
temp_wav.id3v23.fill_tags(mut temp_audio)
|
||||||
|
|
||||||
|
assert temp_wav.id3v23.tit2.data == "Глава 2"
|
||||||
|
// Нетронутые фреймы и аудио сохраняются без изменений
|
||||||
|
assert temp_wav.id3v23.tpe1.data == "Test Author"
|
||||||
|
assert temp_wav.id3v23.others.len == 1
|
||||||
|
|
||||||
|
original := os.read_bytes("./data/test_id3v23.mp3") or { panic(err) }
|
||||||
|
written := os.read_bytes(temp_path) or { panic(err) }
|
||||||
|
assert written[int(temp_wav.id3v23.size) + 10..] == original[old_size + 10..]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вставка в файл без тэга ID3v2 возвращает ошибку, а не падает
|
||||||
|
fn test_insert_without_id3v2() {
|
||||||
|
mut audio := os.open("./data/test_id3v1.mp3") or { panic(err) }
|
||||||
|
defer {
|
||||||
|
audio.close()
|
||||||
|
}
|
||||||
|
mut wav := metadata.Audio{}
|
||||||
|
wav.id3v23.encode_embed_tags()
|
||||||
|
|
||||||
|
wav.id3v23.insert_tags_into_audio(mut audio) or {
|
||||||
|
assert err.msg() == "в файле нет тэга ID3v2"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert false, 'Ошибка не возвращена'
|
||||||
|
}
|
||||||
Reference in new issue
Block a user