From 81034325a161d533f276683b21a5308b1d92f87b Mon Sep 17 00:00:00 2001 From: K1ndWha1e Date: Sat, 11 Jul 2026 20:46:19 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B2=D1=8B=D0=B9=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BC=D0=B8=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + TODO.md | 8 ++ main.v | 75 ++++++++++++++++ metadata/frame.v | 72 +++++++++++++++ metadata/genres.v | 197 ++++++++++++++++++++++++++++++++++++++++++ metadata/id3v1.v | 110 +++++++++++++++++++++++ metadata/id3v2.3.v | 114 ++++++++++++++++++++++++ metadata/id3v2.4.v | 107 +++++++++++++++++++++++ metadata/struct.v | 62 +++++++++++++ metadata/utils.v | 44 ++++++++++ path.txt | 1 + utils/code_detector.v | 10 +++ v.mod | 7 ++ 13 files changed, 810 insertions(+) create mode 100644 .gitignore create mode 100644 TODO.md create mode 100644 main.v create mode 100644 metadata/frame.v create mode 100644 metadata/genres.v create mode 100644 metadata/id3v1.v create mode 100644 metadata/id3v2.3.v create mode 100644 metadata/id3v2.4.v create mode 100644 metadata/struct.v create mode 100644 metadata/utils.v create mode 100644 path.txt create mode 100644 utils/code_detector.v create mode 100644 v.mod diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7330bfb --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +data\ +temp.v +path.text \ No newline at end of file diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..2d7d3d4 --- /dev/null +++ b/TODO.md @@ -0,0 +1,8 @@ +- [x] Извлечение ID3 v1 +- [x] Запись ID3 v1 +- [x] Извлечение ID3 v2.3 +- [ ] Запись ID3 v2.3 +- [x] Извлечение ID3 v2.4 +- [ ] Запись ID3 v2.4 +- [x] Конвертация из ID3 v1 в v2.3 и обратно +1 \ No newline at end of file diff --git a/main.v b/main.v new file mode 100644 index 0000000..790de2a --- /dev/null +++ b/main.v @@ -0,0 +1,75 @@ +module main + +import os +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("./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) + + println(wav.id3v1) + } + + if wav.id3v23.embedded { + wav.id3v23.get_full_size(mut audio) + wav.id3v23.fill_tag(mut audio) + println(wav.id3v23.to_v1()) + wav.id3v23.tit2.encode_size(3) + println(wav.id3v23.tit2) + } + + if wav.id3v24.embedded { + wav.id3v24.get_full_size(mut audio) + wav.id3v24.fill_tag(mut audio) + println(wav.id3v24.to_v1()) + wav.id3v24.tit2.encode_size(4) + println(wav.id3v24.tit2) + } + + // 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", all_bytes) or { panic(err) } + +} + +fn main(){ + id3() +} diff --git a/metadata/frame.v b/metadata/frame.v new file mode 100644 index 0000000..846bfe1 --- /dev/null +++ b/metadata/frame.v @@ -0,0 +1,72 @@ +module metadata + +import encoding.iconv + +@[heap] +pub struct Frame { + mut: + size u64 + start u64 + end u64 + apic ?[]u8 + coded_size ?[]u64 + addition []u8 + encoding string + text string + name string +} + +// Во врейме первый байт указывает кодировку 0 - ISO-8859-1 (Latin-1), 1 - UTF-16LE +@[inline] +fn (mut frame Frame) fill_frame(bytes []u8, start_position u64, idv3_version u8) { + // Начало фрейма + frame.start = start_position + // Название фрейма + frame.name = bytes[frame.start..frame.start + 4].bytestr() + + // Размер фрейма + frame.size = if idv3_version == 3 { + get_size_v23(bytes[frame.start+4..frame.start+8]) + } else { + get_size_v24(bytes[frame.start+4..frame.start+8]) + } + // Конец фрейма + frame.end = frame.start + frame.size + 10 + + // Содержимое фрейма + frame.encoding = match bytes[frame.start+10] { + 0 { "ISO-8859-1" } + 1 { "UTF-16LE" } + 2 { "UTF-16BE" } + else { "UTF-8" } + } + + if frame.name.to_lower() == "apic" { + // Дополнительные байты с информацией (MIME) + frame.addition = bytes[frame.start+10..frame.start+24] + frame.apic = bytes[frame.start+24..frame.end] + } else { + // Выбор шага в записимости от версии + stride := if idv3_version == 3 { 13 } else { 11 } + frame.text = iconv.encoding_to_vstring(bytes[frame.start+u64(stride)..frame.end], frame.encoding) or { "" } + + frame.addition = bytes[frame.start..frame.start + 10] + } +} + +// Кодируем длину фрейма с нужным сдвигом +@[inline] +pub fn (mut frame Frame) encode_size(idv3_version u8) []u64 { + stride := if idv3_version == 3 { + [u64(24), u64(16), u64(8), u64(0xFF)] + } else { + [u64(21), u64(14), u64(7), u64(0x7F)] + } + + return [ + (frame.size >> stride[0]) & stride[3], + (frame.size >> stride[1]) & stride[3], + (frame.size >> stride[2]) & stride[3], + frame.size & stride[3] + ] +} diff --git a/metadata/genres.v b/metadata/genres.v new file mode 100644 index 0000000..1768c72 --- /dev/null +++ b/metadata/genres.v @@ -0,0 +1,197 @@ +module metadata + +@[heap] +enum Genre as u8 { + blues = 0 + classic_rock = 1 + country = 2 + dance = 3 + disco = 4 + funk = 5 + grunge = 6 + hip_hop = 7 + jazz = 8 + metal = 9 + new_age = 10 + oldies = 11 + other = 12 + pop = 13 + r_and_b = 14 + rap = 15 + reggae = 16 + rock = 17 + techno = 18 + industrial = 19 + alternative = 20 + ska = 21 + death_metal = 22 + pranks = 23 + soundtrack = 24 + euro_techno = 25 + ambient = 26 + trip_hop = 27 + vocal = 28 + jazz_funk = 29 + fusion = 30 + trance = 31 + classical = 32 + instrumental = 33 + acid = 34 + house = 35 + game = 36 + sound_clip = 37 + gospel = 38 + noise = 39 + alternrock = 40 + bass = 41 + soul = 42 + punk = 43 + space = 44 + meditative = 45 + instrumental_pop = 46 + instrumental_rock = 47 + ethnic = 48 + gothic = 49 + darkwave = 50 + techno_industrial = 51 + electronic = 52 + pop_folk = 53 + eurodance = 54 + dream = 55 + southern_rock = 56 + comedy = 57 + cult = 58 + gangsta = 59 + top_40 = 60 + christian_rap = 61 + pop_funk = 62 + jungle = 63 + native_american = 64 + cabaret = 65 + new_wave = 66 + psychadelic = 67 + rave = 68 + showtunes = 69 + trailer = 70 + lo_fi = 71 + tribal = 72 + acid_punk = 73 + acid_jazz = 74 + polka = 75 + retro = 76 + musical = 77 + rock_and_roll = 78 + hard_rock = 79 + // Winamp Extended Genres + folk = 80 + folk_rock = 81 + national_folk = 82 + swing = 83 + fast_fusion = 84 + bebob = 85 + latin = 86 + revival = 87 + celtic = 88 + bluegrass = 89 + avantgarde = 90 + gothic_rock = 91 + progressive_rock = 92 + psychedelic_rock = 93 + symphonic_rock = 94 + slow_rock = 95 + big_band = 96 + chorus = 97 + easy_listening = 98 + acoustic = 99 + humour = 100 + speech = 101 + chanson = 102 + opera = 103 + chamber_music = 104 + sonata = 105 + symphony = 106 + booty_bass = 107 + primus = 108 + porn_groove = 109 + satire = 110 + slow_jam = 111 + club = 112 + tango = 113 + samba = 114 + folklore = 115 + ballad = 116 + power_ballad = 117 + rhythmic_soul = 118 + freestyle = 119 + duet = 120 + punk_ballad = 121 + drum_solo = 122 + a_cappella = 123 + euro_house = 124 + dance_hall = 125 + goa = 126 + drum_and_bass = 127 + club_house = 128 + hardcore = 129 + terror = 130 + indie = 131 + brit_pop = 132 + negerpunk = 133 + polsk_punk = 134 + beat = 135 + christian_gangsta = 136 + heavy_metal = 137 + black_metal = 138 + crossover = 139 + contemporary_c = 140 + christian_rock = 141 + merengue = 142 + salsa = 143 + thrash_metal = 144 + anime = 145 + jpop = 146 + synthpop = 147 + + unknown = 255 +} + +const genres_name = [ + 'Blues', 'Classic Rock', 'Country', 'Dance', 'Disco', 'Funk', 'Grunge', + 'Hip-Hop', 'Jazz', 'Metal', 'New Age', 'Oldies', 'Other', 'Pop', 'R&B', + 'Rap', 'Reggae', 'Rock', 'Techno', 'Industrial', 'Alternative', 'Ska', + 'Death Metal', 'Pranks', 'Soundtrack', 'Euro-Techno', 'Ambient', + 'Trip-Hop', 'Vocal', 'Jazz+Funk', 'Fusion', 'Trance', 'Classical', + 'Instrumental', 'Acid', 'House', 'Game', 'Sound Clip', 'Gospel', + 'Noise', 'AlternRock', 'Bass', 'Soul', 'Punk', 'Space', 'Meditative', + 'Instrumental Pop', 'Instrumental Rock', 'Ethnic', 'Gothic', 'Darkwave', + 'Techno-Industrial', 'Electronic', 'Pop-Folk', 'Eurodance', 'Dream', + 'Southern Rock', 'Comedy', 'Cult', 'Gangsta', 'Top 40', 'Christian Rap', + 'Pop/Funk', 'Jungle', 'Native American', 'Cabaret', 'New Wave', + 'Psychadelic', 'Rave', 'Showtunes', 'Trailer', 'Lo-Fi', 'Tribal', + 'Acid Punk', 'Acid Jazz', 'Polka', 'Retro', 'Musical', 'Rock & Roll', + 'Hard Rock', + // Winamp + 'Folk', 'Folk-Rock', 'National Folk', 'Swing', 'Fast Fusion', 'Bebob', + 'Latin', 'Revival', 'Celtic', 'Bluegrass', 'Avantgarde', 'Gothic Rock', + 'Progressive Rock', 'Psychedelic Rock', 'Symphonic Rock', 'Slow Rock', + 'Big Band', 'Chorus', 'Easy Listening', 'Acoustic', 'Humour', 'Speech', + 'Chanson', 'Opera', 'Chamber Music', 'Sonata', 'Symphony', 'Booty Bass', + 'Primus', 'Porn Groove', 'Satire', 'Slow Jam', 'Club', 'Tango', 'Samba', + 'Folklore', 'Ballad', 'Power Ballad', 'Rhythmic Soul', 'Freestyle', 'Duet', + 'Punk Ballad', 'Drum Solo', 'A Cappella', 'Euro-House', 'Dance Hall', + 'Goa', 'Drum & Bass', 'Club-House', 'Hardcore', 'Terror', 'Indie', + 'BritPop', 'Negerpunk', 'Polsk Punk', 'Beat', 'Christian Gangsta', + 'Heavy Metal', 'Black Metal', 'Crossover', 'Contemporary Christian', + 'Christian Rock', 'Merengue', 'Salsa', 'Thrash Metal', 'Anime', 'JPop', + 'Synthpop' +] + +pub fn (genre Genre) str() string{ + match genre { + .unknown { + return "Unknown" + } else { + return genres_name[u8(genre)] + } + } +} \ No newline at end of file diff --git a/metadata/id3v1.v b/metadata/id3v1.v new file mode 100644 index 0000000..e353b43 --- /dev/null +++ b/metadata/id3v1.v @@ -0,0 +1,110 @@ +module metadata + +import os { File } +import encoding.iconv +import utils + + +// Определить кодировку utf-8 или cp-1251 +@[inline] +fn decode_bytes(bytes []u8) string { + format := if utils.is_utf8(bytes) { "UTF-8" } else { "CP1251" } + text := iconv.encoding_to_vstring(bytes, format) or { "Can't decode" } + + return text +} + +// Извлечение метаданных из ID3 v1 +pub fn (mut tag ID3v1) get_metadata(last_bytes []u8) { + tag.title = decode_bytes(last_bytes[3..33]) + + tag.artist = decode_bytes(last_bytes[33..63]) + tag.album = decode_bytes(last_bytes[63..92]) + tag.year = decode_bytes(last_bytes[93..97]) + tag.comment = decode_bytes(last_bytes[97..125]) + tag.track = if last_bytes[125] == u8(0) {last_bytes[126].str()} else { "" } + + genre := Genre.from(last_bytes[127..128][0]) or { Genre.unknown } + tag.genre = genre.str()} + +// Конвертировать ID3 v2.3 в ID3 v1 +pub fn (id3v1 ID3v1) to_v23() ID3v23 { + return ID3v23{ + tcon: metadata.Frame{ + text: id3v1.genre + } + tit2: metadata.Frame{ + text: id3v1.title + } + tpe2: metadata.Frame{ + text: id3v1.artist + } + talb: metadata.Frame{ + text: id3v1.album + } + trck: metadata.Frame{ + text: id3v1.track + } + tyer: metadata.Frame{ + text: id3v1.year + } + comment: metadata.Frame{ + text: id3v1.comment + } + } +} + +// По ситуации отрезаем или добиваем нулями +@[inline] +fn cut_or_trim(data string, capacity i8) []u8 { + mut final_data := []u8{len: capacity, init: 0} + + + bytes_data := iconv.vstring_to_encoding(data, "CP1251") or { []u8{len: capacity} } + + diff := capacity - bytes_data.len + if diff > 0 { + for i, d in bytes_data { + final_data[i] = d + } + } else { + final_data = bytes_data.clone()[..capacity] + } + + return final_data +} + +// Перевести тэг в байты +pub fn (id3v1 ID3v1) to_bytes() []u8 { + mut bytes := []u8{cap: 128} + + bytes << "TAG".bytes() + bytes << cut_or_trim(id3v1.title, i8(30)) + bytes << cut_or_trim(id3v1.artist, i8(30)) + bytes << cut_or_trim(id3v1.album, i8(30)) + bytes << cut_or_trim(id3v1.year, i8(4)) + bytes << cut_or_trim(id3v1.comment, i8(28)) + + // bytes << cut_or_trim(id3v1.track, i8(2)) + bytes << if id3v1.track.len != 0 { [u8(0), id3v1.track.u8()]} else { [u8(1), u8(0)] } + + id_genre := Genre.from(id3v1.genre) or { Genre.unknown } + bytes << u8(id_genre) // cut_or_trim([u8(id_genre)], i8(2)) + + return bytes +} + +// Добавление изменённого тэга +pub fn (id3v1 ID3v1) insert(mut audio File, position i8) []u8 { + bytes_tag := id3v1.to_bytes() + // Уcтановка указателя на последний байт + audio.seek(0, os.SeekMode.end) or { panic(err) } + // Положение указателя + size := audio.tell() or { panic(err) } + // Чтение всех байта, кроме последних 128 + mut all_bytes := audio.read_bytes(int(size))#[..position] + + all_bytes << bytes_tag + + return all_bytes +} diff --git a/metadata/id3v2.3.v b/metadata/id3v2.3.v new file mode 100644 index 0000000..634d9da --- /dev/null +++ b/metadata/id3v2.3.v @@ -0,0 +1,114 @@ +module metadata + +import os { File } + +// 00: ISO-8859-1 (1 байт на символ). +// 01: UTF-16 с BOM (2 байта на символ + 2 байта маркер). +// 03: UTF-8 (переменная длина). + +// Пример из пункта 3 (13 байт размера): +// Если первый байт данных 01 (UTF-16), то реальный текст занимает: +// 13 - 1 (байт кодировки) - 2 (BOM) = 10 байт. +// В UTF-16 это будет всего 5 символов (так как 1 символ = 2 байта). + +enum EnumID3v23 as u8 { + tit2 + tpe1 + tpe2 + talb + tyer + trck + tcon + tcop + apic + comment + unknown +} + +const start_idx = 10 + + +pub fn (mut tag ID3v23) add_frame(frame Frame) { + name := EnumID3v23.from(frame.name.to_lower()) or { EnumID3v23.unknown } + + match name { + .tit2 { + tag.tit2 = frame + } + .tpe1 { + tag.tpe1 = frame + } + .tpe2 { + tag.tpe2 = frame + } + .talb { + tag.talb = frame + } + .tyer { + tag.tyer = frame + } + .trck { + tag.trck = frame + } + .tcon { + tag.tcon = frame + } + .tcop { + tag.tcop = frame + } + .apic { + tag.apic = frame + } + else { + tag.others << frame + } + } +} + +// [ID3v23] Получение размера всего тэга / фрейма +@[inline] +fn get_size_v23(size_bytes []u8) u64 { + return (u64(size_bytes[0]) << 24) | (u64(size_bytes[1]) << 16) | (u64(size_bytes[2]) << 8) | u64(size_bytes[3]) +} + +pub fn (mut id3v23 ID3v23) get_full_size(mut audio File) { + // Первые 10 байт тэга + first_ten_bytes := get_bytes(mut audio, 0, 10) + + id3v23.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 (mut id3v23 ID3v23) fill_tag(mut audio File) { + mut current_frame := &Frame{} + // Самая первая позиция + mut start_position := u64(10) + // Версия IDv3 + idv3_version := u8(3) + // Получаем вне возможные байты + all_tag_bytes := get_bytes(mut audio, 10, int(id3v23.size + start_position)) + + for id3v23.size > start_position { + current_frame.fill_frame(all_tag_bytes, start_position, idv3_version) + start_position = current_frame.end + id3v23.size -= current_frame.size + + id3v23.add_frame(current_frame) + } +} + +// Конвертировать ID3 v2.3 в ID3 v1 +pub fn (id3v23 ID3v23) to_v1() ID3v1 { + return ID3v1{ + genre: id3v23.tcon.text + title: id3v23.tit2.text + artist: id3v23.tpe2.text + album: id3v23.talb.text + track: id3v23.trck.text.split("/")[0] + year: id3v23.tyer.text + comment: id3v23.comment.text + } +} + +pub fn to_bytes () { + +} \ No newline at end of file diff --git a/metadata/id3v2.4.v b/metadata/id3v2.4.v new file mode 100644 index 0000000..d508feb --- /dev/null +++ b/metadata/id3v2.4.v @@ -0,0 +1,107 @@ +module metadata + +import os { File } + + +enum EnumID3v24 as u8 { + tit2 + tpe1 + tpe2 + talb + tdrc + trck + tcon + tcop + apic + comment + unknown +} + +pub fn (mut tag ID3v24) add_frame(frame Frame) { + name := EnumID3v24.from(frame.name.to_lower()) or { EnumID3v24.unknown } + + match name { + .tit2 { + tag.tit2 = frame + } + .tpe1 { + tag.tpe1 = frame + } + .tpe2 { + tag.tpe2 = frame + } + .talb { + tag.talb = frame + } + .tdrc { + tag.tdrc = frame + } + .trck { + tag.trck = frame + } + .tcon { + tag.tcon = frame + } + .tcop { + tag.tcop = frame + } + .apic { + tag.apic = frame + } + else { + tag.others << frame + } + } +} + +@[inline] +fn get_size_v24(size_bytes []u8) u64 { + return (u64(size_bytes[0]) << 21) | (u64(size_bytes[1]) << 14) | (u64(size_bytes[2]) << 7) | u64(size_bytes[3]) +} + +pub fn (mut id3v23 ID3v24) get_full_size(mut audio File) { + // Первые 10 байт тэга + first_ten_bytes := get_bytes(mut audio, 0, 10) + + id3v23.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 (mut id3v24 ID3v24) fill_tag(mut audio File) { + mut current_frame := &Frame{} + // Самая первая позиция + mut start_position := u64(10) + // Версия IDv3 + idv3_version := u8(4) + // Получаем вне возможные байты + all_tag_bytes := get_bytes(mut audio, 10, int(id3v24.size + start_position)) + // TODO Подумать как правильно считать размер + // Скорее всего просто скадлывать байтики, если нуль - вырубать + for id3v24.size > start_position { + // Заполнение кадров + current_frame.fill_frame(all_tag_bytes, start_position, idv3_version) + + start_position = current_frame.end + id3v24.size -= current_frame.size + + // Добавление кадра + id3v24.add_frame(current_frame) + } +} + +// Конвертировать ID3 v2.3 в ID3 v1 +pub fn (id3v23 ID3v24) to_v1() ID3v1 { + return ID3v1{ + genre: id3v23.tcon.text + title: id3v23.tit2.text + artist: id3v23.tpe2.text + album: id3v23.talb.text + track: id3v23.trck.text.split("/")[0] + year: id3v23.tdrc.text + comment: id3v23.comment.text + } +} + +pub fn insert(mut audio File) { + +} \ No newline at end of file diff --git a/metadata/struct.v b/metadata/struct.v new file mode 100644 index 0000000..702062e --- /dev/null +++ b/metadata/struct.v @@ -0,0 +1,62 @@ +module metadata + + +@[heap] +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 // Присутвует ли тэг +} + +@[heap; minify] +pub struct ID3v23 { + pub mut: + size u64 // Размер тэга + tit2 Frame // Название аудиодорожки + tpe1 Frame // Автор + tpe2 Frame // Исполнитель + talb Frame // Название альбома + tyer Frame // Год выпуска + trck Frame // Номер трека + tcon Frame // Жанр + tcop Frame // Композитор (автор) + apic Frame // Обложка + comment Frame // Комментарий + others []Frame + embedded bool // Присутвует ли тэг +} + +@[heap; minify] +pub struct ID3v24 { + pub mut: + size u64 // Размер тэга + tit2 Frame // Название аудиодорожки + tpe1 Frame // Автор + tpe2 Frame // Исполнитель + talb Frame // Название альбома + tdrc Frame // Год выпуска + trck Frame // Номер трека + tcon Frame // Жанр + tcop Frame // Композитор (автор) + apic Frame // Обложка + comment Frame // Комментарий + others []Frame + embedded bool // Присутвует ли тэг +} + + +@[heap] +pub struct Audio { + pub mut: + id3v1 ID3v1 + id3v23 ID3v23 + id3v24 ID3v24 + path string + extension string = ".mp3" +} diff --git a/metadata/utils.v b/metadata/utils.v new file mode 100644 index 0000000..8258a9d --- /dev/null +++ b/metadata/utils.v @@ -0,0 +1,44 @@ +module metadata + +import os + + +pub fn (mut audio Audio) detect_tags(mut data os.File) { + // ? --- Проверка версии ID3v1 --- + last_bytes := get_bytes(mut data, -128, 3) + + if last_bytes[0..3].bytestr() == "TAG" { + audio.id3v1.embedded = true + } + // =============================== + + // ? --- Проверка версии ID3v23.3 --- + first_ten_bytes := get_bytes(mut data, 0, 4) + + if first_ten_bytes[..3].bytestr() == "ID3" && first_ten_bytes[3] == 3 { + audio.id3v23.embedded = true + } + // =============================== + + // ? --- Проверка версии ID3v23.4 --- + if first_ten_bytes[..3].bytestr() == "ID3" && first_ten_bytes[3] == 4 { + audio.id3v24.embedded = true + } + // =============================== +} + +pub fn get_bytes(mut audio os.File, position int, size int) []u8 { + // ? ---------Работа с расположением указателя-------------- + // Ставим указатель на 128 байт с конца + if position < 0 { + audio.seek(position, os.SeekMode.end) or { panic(err) } + } + // Получаем номер 128 байта с конца + pos := audio.tell() or { panic(err) } + // Читаем байты с конца + last_bytes := audio.read_bytes_at(size, u64(pos)) + audio.seek(0, os.SeekMode.start) or { panic(err) } + // ========================================================= + + return last_bytes +} diff --git a/path.txt b/path.txt new file mode 100644 index 0000000..e7d4ebf --- /dev/null +++ b/path.txt @@ -0,0 +1 @@ +$env:CFLAGS = (pkg-config --cflags pango pangocairo pangoft2 freetype2) \ No newline at end of file diff --git a/utils/code_detector.v b/utils/code_detector.v new file mode 100644 index 0000000..d8e6ca2 --- /dev/null +++ b/utils/code_detector.v @@ -0,0 +1,10 @@ +module utils + +pub fn is_utf8(bytes []u8) bool { + first_byte_amount := bytes.str().count(bytes[0].str()) + percent := f32(first_byte_amount) / f32(bytes.len) + + result := if percent >= 0.1 { true } else { false } + + return result +} diff --git a/v.mod b/v.mod new file mode 100644 index 0000000..958d82f --- /dev/null +++ b/v.mod @@ -0,0 +1,7 @@ +Module { + name: 'Vbooks' + description: '' + version: '0.0.1' + license: 'MIT' + dependencies: [] +} \ No newline at end of file