diff --git a/main.v b/main.v index 983d931..e69de29 100644 --- a/main.v +++ b/main.v @@ -1,128 +0,0 @@ -module main - -import os -// import io -import metadata - -fn id3(){ - // mut audio := os.open("./data/АЛИСА 11000.mp3") or { panic(err) } - // mut audio := os.open("./data/Глава 2. 16 и 17 июля.mp3") or { panic(err) } - // mut audio := os.open("./out.mp3") or { panic(err) } - mut audio := os.open("./data/test_id3v23.mp3") or { panic(err) } - // mut audio := os.open("./data/01_01.mp3") or { panic(err) } - // mut audio := os.open("./test.mp3") or { panic(err) } - - defer { - audio.close() - } - - mut wav := metadata.Audio{} - wav.detect_tags(mut audio) - - println(wav.id3v1.embedded) - println(wav.id3v23.embedded) - println(wav.id3v24.embedded) - - if wav.id3v1.embedded { - last_bytes := metadata.get_bytes(mut audio, -128, 128) - wav.id3v1.get_metadata(last_bytes) - - // wav.id3v1.genre = "Rock" - // wav.id3v1.title = "Test title" - // wav.id3v1.artist = "Test artist" - // wav.id3v1.album = "Test albulm" - // wav.id3v1.comment = "2/5" - // wav.id3v1.track = "2/5" - - println(wav.id3v1) - - } - - - if wav.id3v23.embedded { - wav.id3v23.get_full_size(mut audio) - wav.id3v23.fill_tags(mut audio) - println(wav.id3v23.to_v1()) - wav.id3v23.tit2.encode_size(3) - tit2 := wav.id3v23.tit2.get_encoded_frame_tag(3) - println(tit2) - println(wav.id3v23) - } - - if wav.id3v24.embedded { - wav.id3v24.get_full_size(mut audio) - wav.id3v24.fill_tags(mut audio) - println(wav.id3v24.to_v1()) - // wav.id3v24.tit2.encode_size(4) - - // flex := wav.id3v24.talb.get_encoded_frame_tag(4) - - // println(flex) - - // println(wav.id3v24.tdrc.data) - // wav.id3v24.tdrc.set_data("2017") - - // // enc_size := metadata.encode_header_size(wav.id3v24.size, wav.id3v24.version) - // // println(enc_size) - // wav.id3v24.tit2.set_data("2017") - // wav.id3v24.tpe1.init("tpe1") - // wav.id3v24.tpe1.set_data("Hello") - // wav.id3v24.tpe2.init("tpe2") - // wav.id3v24.tpe2.set_data("Hello2") - // // metadata.encode_header(mut wav.id3v24) - // // println(wav.id3v24.encoded_header) - // // println(wav.id3v24.tpe1.name) - - // wav.id3v24.encode_embed_tags() - // println(wav.id3v24.encoded_header.len) - - // wav.id3v24.insert_tags_into_audio(mut audio) - - // // println(wav.id3v24) - - // mut f := os.create("./temp.mp3") or { panic(err) } - - // f.write(wav.id3v24.encoded_header) or { panic(err) } - // f.close() - - // println(wav.id3v24) - // mut audio_2 := os.open("./out.mp3") or { panic(err) } - - // data := io.read_all(reader: audio_2) or { [u8(0), u8(0), u8(0), u8(0)] } - // println(data) - println(wav.id3v24.size) - println(wav.id3v24.encoded_header) - } - - - - // println(wav.id3v23) - // println(wav.id3v23.tit2) - - - // converted_idv31 := wav.id3v1.to_v23() - // println(converted_idv31) - // println(last_bytes) - - // flex := wav.id3v1.to_bytes() - - // audio.seek(-1, os.SeekMode.end) or { panic(err) } - - // flex1 := audio.tell() or { panic(err) } - - // mut all_bytes := audio.read_bytes(int(flex1))#[..-128] - - // all_bytes << flex - - // println(all_bytes.len) - // println(int(flex1)) - - // all_bytes := wav.id3v1.insert(mut audio, -128) - - // os.write_bytes("./test.mp3", new_bytes) or { panic(err) } - -} - -fn main(){ - id3() -} diff --git a/metadata/frame.v b/metadata/frame.v index e7263ef..7f808ac 100644 --- a/metadata/frame.v +++ b/metadata/frame.v @@ -123,7 +123,6 @@ pub fn (mut frame Frame) get_encoded_frame_tag(idv3_version u8) []u8 { // Название тэга mut encoded_tag := frame.name.bytes() - // Четыре числа на закодированный размер encoded_tag << frame.encoded_size // Флаги, оставляем пустыми diff --git a/metadata/id3v23.v b/metadata/id3v23.v index 51c6268..aacb75c 100644 --- a/metadata/id3v23.v +++ b/metadata/id3v23.v @@ -1,6 +1,7 @@ module metadata import os { File } +import io { read_all } // 00: ISO-8859-1 (1 байт на символ). // 01: UTF-16 с BOM (2 байта на символ + 2 байта маркер). @@ -103,3 +104,13 @@ 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) } + + // Размер старого тэга берётся из файла, 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 +} diff --git a/metadata/struct.v b/metadata/struct.v index c4f1256..61b58ba 100644 --- a/metadata/struct.v +++ b/metadata/struct.v @@ -4,14 +4,22 @@ module metadata @[heap; minify] pub struct ID3v1 { pub mut: - genre string// Жанр - title string // Название аудиодорожки - artist string // Актёр озучания - album string // Название альбома - track string // Записывается как 2/5, что значит 2 трэк из 5 - year string // Год записи, может int нужно - comment string // Комментарий - embedded bool // Присутвует ли тэг + // Жанр + genre string + // Название аудиодорожки + title string + // Актёр озучания + artist string + // Название альбома + album string + // Записывается как 2/5, что значит 2 трэк из 5 + track string + // Год записи, может int нужно + year string + // Комментарий + comment string + // Присутвует ли тэг + embedded bool } @[heap; minify] @@ -21,21 +29,33 @@ pub struct ID3v23 { size u64 // Название аудиодорожки tit2 Frame - - tpe1 Frame // Автор - tpe2 Frame // Исполнитель - talb Frame // Название альбома - tyer Frame // Год выпуска - trck Frame // Номер трека - tcon Frame // Жанр - tcop Frame // Композитор (автор) - apic Frame // Обложка - txxx Frame // Комментарий - unknown Frame // Затычка - others []Frame // Остальные фреймы + // Автор + tpe1 Frame + // Исполнитель + tpe2 Frame + // Название альбома + talb Frame + // Год выпуска + tyer Frame + // Номер трека + trck Frame + // Жанр + tcon Frame + // Композитор (автор) + tcop Frame + // Обложка + apic Frame + // Комментарий + txxx Frame + // Затычка + unknown Frame + // Остальные фреймы + others []Frame encoded_header []u8 - embedded bool // Присутвует ли тэг - version u8 = 3 // Версия тэга + // Присутвует ли тэг + embedded bool + // Версия тэга + version u8 = 3 title string = "ID3" } diff --git a/metadata/utils.v b/metadata/utils.v index 4435668..b09b32d 100644 --- a/metadata/utils.v +++ b/metadata/utils.v @@ -99,7 +99,8 @@ pub fn encode_header[T](mut id3v T) { // Минорная версия и спец. флаги id3v.encoded_header << []u8{len: 2} - id3v.encoded_header << metadata.encode_header_size(id3v.size, id3v.version) + // Размер всего тэга в заголовке всегда syncsafe (по 7 бит в байте), в том числе в v2.3 + id3v.encoded_header << metadata.encode_header_size(id3v.size, 4) } // Количество нулей в конце тэга @@ -110,7 +111,8 @@ pub fn encode_embed_tags[T](mut id3v2 T) { // Проход по всем полям стуктуры mut tag_bytes := []u8{} - $for field in id3v2.fields { + // Именно T.fields: с id3v2.fields V пропускает поля, которых нет в другом типе (tyer в ID3v24) + $for field in T.fields { $if field.typ is Frame { // Читаются только заполненные тэги if id3v2.$(field.name).cur_size > 0 { @@ -133,4 +135,4 @@ pub fn encode_embed_tags[T](mut id3v2 T) { id3v2.encoded_header = []u8{} encode_header(mut id3v2) id3v2.encoded_header << tag_bytes -} \ No newline at end of file +} diff --git a/test/id3v2_test.v b/test/id3v2_test.v index b757c06..7f2ea14 100644 --- a/test/id3v2_test.v +++ b/test/id3v2_test.v @@ -37,6 +37,72 @@ fn test_idv3v23() { assert id3v1.comment == "comment" } +fn test_writeidv3v23() { + mut audio := os.open("./data/test_id3v23.mp3") or { panic(err) } + defer { + audio.close() + } + mut wav := metadata.Audio{} + wav.detect_tags(mut audio) + + assert wav.id3v1.embedded == false, 'Тег показан false, а должен быть true' + assert wav.id3v23.embedded == true, 'Тег показан true, а должен быть false' + assert wav.id3v24.embedded == false, 'Тег показан true, а должен быть false' + wav.id3v23.get_full_size(mut audio) + wav.id3v23.fill_tags(mut audio) + + // Изменение значений тэга + wav.id3v23.tit2.set_data("Test Title2") + wav.id3v23.tpe1.set_data("Test Author2") + wav.id3v23.tpe2.set_data("Test Narator2") + wav.id3v23.talb.set_data("Test Album2") + wav.id3v23.tyer.set_data("2027") + wav.id3v23.trck.set_data("7/7") + wav.id3v23.tcon.set_data("Rock2") + wav.id3v23.tcop.set_data("Test2") + wav.id3v23.txxx.set_data("comment2") + + wav.id3v23.encode_embed_tags() + wav.id3v23.insert_tags_into_audio(mut audio) + + os.write_bytes("./data/temp_v23.mp3", wav.id3v23.encoded_header) or { panic(err) } + + // Чтение файла и проверка записи + mut temp_audio := os.open("./data/temp_v23.mp3") or { panic(err) } + defer { + temp_audio.close() + } + + mut temp_wav := metadata.Audio{} + temp_wav.detect_tags(mut temp_audio) + + assert temp_wav.id3v1.embedded == false, 'Тег показан false, а должен быть true' + assert temp_wav.id3v23.embedded == true, 'Тег показан true, а должен быть false' + assert temp_wav.id3v24.embedded == false, 'Тег показан true, а должен быть false' + temp_wav.id3v23.get_full_size(mut temp_audio) + temp_wav.id3v23.fill_tags(mut temp_audio) + + assert temp_wav.id3v23.tit2.data == "Test Title2" + assert temp_wav.id3v23.tpe1.data == "Test Author2" + assert temp_wav.id3v23.tpe2.data == "Test Narator2" + assert temp_wav.id3v23.talb.data == "Test Album2" + assert temp_wav.id3v23.tyer.data == "2027" + assert temp_wav.id3v23.trck.data == "7/7" + assert temp_wav.id3v23.tcon.data == "Rock2" + assert temp_wav.id3v23.tcop.data == "Test2" + assert temp_wav.id3v23.txxx.data == "comment2" + + id3v1 := wav.id3v23.to_v1() + + assert id3v1.genre == "Rock2" + assert id3v1.title == "Test Title2" + assert id3v1.artist == "Test Narator2" + assert id3v1.album == "Test Album2" + assert id3v1.track == "7" + assert id3v1.year == "2027" + assert id3v1.comment == "comment2" +} + // Проверка чтения id3v24 fn test_idv3v24() { mut audio := os.open("./data/test_id3v24.mp3") or { panic(err) }