первый коммит
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