первый коммит
This commit is contained in:
commit
81034325a1
13 files changed
+810
No files matched your search
@@ -0,0 +1,3 @@
|
|||||||
|
data\
|
||||||
|
temp.v
|
||||||
|
path.text
|
||||||
@@ -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
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -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]
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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 () {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
$env:CFLAGS = (pkg-config --cflags pango pangocairo pangoft2 freetype2)
|
||||||
@@ -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
|
||||||
|
}
|
||||||
Reference in new issue
Block a user