audio_tag 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/.rubocop.yml +78 -0
- data/.ruby-version +1 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +127 -0
- data/LICENSE.txt +21 -0
- data/README.md +38 -0
- data/Rakefile +10 -0
- data/Steepfile +12 -0
- data/audio_tag.gemspec +24 -0
- data/lib/audio_tag/id3/header.rb +36 -0
- data/lib/audio_tag/id3/stream_reader.rb +98 -0
- data/lib/audio_tag/id3/stream_section.rb +57 -0
- data/lib/audio_tag/id3/synchsafe.rb +19 -0
- data/lib/audio_tag/id3/v2/frame.rb +43 -0
- data/lib/audio_tag/id3/v2/frame_header.rb +35 -0
- data/lib/audio_tag/id3/v2/frame_merger.rb +30 -0
- data/lib/audio_tag/id3/v2/frame_type.rb +81 -0
- data/lib/audio_tag/id3/v2/frames/comment_frame.rb +32 -0
- data/lib/audio_tag/id3/v2/frames/picture_frame.rb +63 -0
- data/lib/audio_tag/id3/v2/frames/user_frame.rb +29 -0
- data/lib/audio_tag/id3/v2/tag.rb +39 -0
- data/lib/audio_tag/id3/v2/tag_header.rb +71 -0
- data/lib/audio_tag/id3/v2/text_decoder.rb +33 -0
- data/lib/audio_tag/version.rb +3 -0
- data/lib/audio_tag.rb +7 -0
- data/sig/audio_tag/id3/header.rbs +17 -0
- data/sig/audio_tag/id3/stream_reader.rbs +33 -0
- data/sig/audio_tag/id3/stream_section.rbs +16 -0
- data/sig/audio_tag/id3/synchsafe.rbs +12 -0
- data/sig/audio_tag/id3/v2/frame.rbs +24 -0
- data/sig/audio_tag/id3/v2/frame_header.rbs +18 -0
- data/sig/audio_tag/id3/v2/frame_merger.rbs +20 -0
- data/sig/audio_tag/id3/v2/frame_type.rbs +22 -0
- data/sig/audio_tag/id3/v2/frames/comment_frame.rbs +23 -0
- data/sig/audio_tag/id3/v2/frames/picture_frame.rbs +27 -0
- data/sig/audio_tag/id3/v2/frames/user_frame.rbs +22 -0
- data/sig/audio_tag/id3/v2/tag.rbs +20 -0
- data/sig/audio_tag/id3/v2/tag_header.rbs +36 -0
- data/sig/audio_tag/id3/v2/text_decoder.rbs +13 -0
- data/sig/audio_tag.rbs +5 -0
- data/sig/zeitwerk/gem_inflector.rbs +7 -0
- data/sig/zeitwerk/loader.rbs +13 -0
- metadata +100 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
class FrameType
|
5
|
+
LOOKUP = {
|
6
|
+
# 2.3.0
|
7
|
+
APIC: { name: :picture, class: Frames::PictureFrame },
|
8
|
+
COMM: { name: :comments, class: Frames::CommentFrame },
|
9
|
+
TALB: { name: :album },
|
10
|
+
TBPM: { name: :tempo },
|
11
|
+
TCOM: { name: :composer },
|
12
|
+
TCON: { name: :genre },
|
13
|
+
TCOP: { name: :copyright },
|
14
|
+
TPE1: { name: :artist },
|
15
|
+
TKEY: { name: :key },
|
16
|
+
TXXX: { name: :user_frames, class: Frames::UserFrame },
|
17
|
+
TDAT: { name: :date },
|
18
|
+
TDLY: { name: :playlist_delay },
|
19
|
+
TENC: { name: :encoded_by },
|
20
|
+
TEXT: { name: :text },
|
21
|
+
TFLT: { name: :custom },
|
22
|
+
TIME: { name: :time },
|
23
|
+
TIT1: { name: :content_group_description },
|
24
|
+
TIT2: { name: :title },
|
25
|
+
TIT3: { name: :subtitle_description_refinement },
|
26
|
+
TLAN: { name: :language },
|
27
|
+
TLEN: { name: :length },
|
28
|
+
TMED: { name: :custom },
|
29
|
+
TOAL: { name: :original_album },
|
30
|
+
TOFN: { name: :original_filename },
|
31
|
+
TOLY: { name: :original_writer },
|
32
|
+
TOPE: { name: :original_artist },
|
33
|
+
TOWN: { name: :file_owner },
|
34
|
+
TPE2: { name: :album_artist },
|
35
|
+
TPE3: { name: :conductor },
|
36
|
+
TPE4: { name: :other_artist },
|
37
|
+
TPOS: { name: :part_in_set },
|
38
|
+
TPUB: { name: :label },
|
39
|
+
TRCK: { name: :track },
|
40
|
+
TRDA: { name: :recording_date },
|
41
|
+
TRSN: { name: :radio_station_name },
|
42
|
+
TRSO: { name: :radio_station_owner },
|
43
|
+
TSIZ: { name: :size },
|
44
|
+
TSRC: { name: :isrc },
|
45
|
+
TSSE: { name: :encoder },
|
46
|
+
|
47
|
+
# 2.4.0
|
48
|
+
TDEN: { name: :encoded_at },
|
49
|
+
TDOR: { name: :originally_released_at },
|
50
|
+
TDRC: { name: :date },
|
51
|
+
TDRL: { name: :released_at },
|
52
|
+
TDTG: { name: :tagged_at },
|
53
|
+
TIPL: { name: :involved_people },
|
54
|
+
TMCL: { name: :musician_credits },
|
55
|
+
TMOO: { name: :mood },
|
56
|
+
TPRO: { name: :produced_notice },
|
57
|
+
TSOA: { name: :album_sort },
|
58
|
+
TSOP: { name: :performer_sort },
|
59
|
+
TSOT: { name: :title_sort },
|
60
|
+
TSST: { name: :set_subtitle },
|
61
|
+
TYER: { name: :year }
|
62
|
+
}.freeze
|
63
|
+
|
64
|
+
DEFAULT_FRAME_CLASS = Frame
|
65
|
+
|
66
|
+
attr_reader :name, :frame_class
|
67
|
+
|
68
|
+
def initialize(**attributes)
|
69
|
+
@name = attributes[:name]
|
70
|
+
@frame_class = attributes[:class] || DEFAULT_FRAME_CLASS
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.find(key)
|
74
|
+
attributes = LOOKUP[key] || { name: key }
|
75
|
+
|
76
|
+
new(**attributes)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
module Frames
|
5
|
+
class CommentFrame < Frame
|
6
|
+
def data
|
7
|
+
{ language => { description => text } }
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
attr_reader :encoding, :text
|
13
|
+
|
14
|
+
def description
|
15
|
+
@description.empty? ? :_ : @description.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def language
|
19
|
+
@language.to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
def extract!
|
23
|
+
@encoding = read_byte!
|
24
|
+
@language = read!(3)
|
25
|
+
@description = read_until!(0x00)
|
26
|
+
@text = read_until!(0x00)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
module Frames
|
5
|
+
class PictureFrame < Frame
|
6
|
+
ENCODINGS = %i[
|
7
|
+
ascii
|
8
|
+
utf_16
|
9
|
+
utf_16be
|
10
|
+
utf_8
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
TYPES = %i[
|
14
|
+
other
|
15
|
+
thumbnail
|
16
|
+
other_thumbnail
|
17
|
+
front_cover
|
18
|
+
back_cover
|
19
|
+
leaflet_page
|
20
|
+
media_page
|
21
|
+
lead_artist
|
22
|
+
artist
|
23
|
+
conductor
|
24
|
+
band
|
25
|
+
composer
|
26
|
+
lyricist
|
27
|
+
recording_location
|
28
|
+
during_recording
|
29
|
+
during_performance
|
30
|
+
movie_capture
|
31
|
+
bright_colored_fish
|
32
|
+
illustration
|
33
|
+
band_logo
|
34
|
+
publisher_logo
|
35
|
+
].freeze
|
36
|
+
|
37
|
+
def data
|
38
|
+
{ encoding:, mime:, type:, description: }
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :mime, :description
|
44
|
+
|
45
|
+
def encoding
|
46
|
+
ENCODINGS[@encoding]
|
47
|
+
end
|
48
|
+
|
49
|
+
def type
|
50
|
+
TYPES[@type]
|
51
|
+
end
|
52
|
+
|
53
|
+
def extract!
|
54
|
+
@encoding = read_byte!
|
55
|
+
@mime = read_until!(0x00)
|
56
|
+
@type = read_byte!
|
57
|
+
@description = read_until!(0x00)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
module Frames
|
5
|
+
class UserFrame < Frame
|
6
|
+
DESCRIPTIONS = {}.freeze
|
7
|
+
|
8
|
+
def data
|
9
|
+
{ description => value }
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :encoding, :value
|
15
|
+
|
16
|
+
def description
|
17
|
+
DESCRIPTIONS[@description.to_sym] || @description.downcase.to_sym
|
18
|
+
end
|
19
|
+
|
20
|
+
def extract!
|
21
|
+
@encoding = read_byte!
|
22
|
+
@description = read_until!(0x00)
|
23
|
+
@value = read_until!(0x00)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
class Tag < StreamSection
|
5
|
+
attr_reader :header, :frames
|
6
|
+
|
7
|
+
def initialize(stream)
|
8
|
+
@header = TagHeader.new(stream)
|
9
|
+
|
10
|
+
super(stream)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_h
|
14
|
+
FrameMerger.new(frames).merge
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_file(path)
|
18
|
+
File.open(path) { |file| new(file) }
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def end_of_tag?
|
24
|
+
stream.pos >= last_pos || read_byte.zero?
|
25
|
+
end
|
26
|
+
|
27
|
+
def size
|
28
|
+
header.tag_size
|
29
|
+
end
|
30
|
+
|
31
|
+
def extract!
|
32
|
+
@frames = [].tap do |frames|
|
33
|
+
frames << Frame.build(stream) until end_of_tag?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
class TagHeader < Header
|
5
|
+
STRUCTURE = %i[
|
6
|
+
identifier identifier identifier
|
7
|
+
version
|
8
|
+
revision
|
9
|
+
flags
|
10
|
+
size size size size
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
IDENTIFIER = "ID3".freeze
|
14
|
+
|
15
|
+
INVALID_FLAG_MASK = 0x8F
|
16
|
+
EXPERIMENTAL_MASK = 0x10
|
17
|
+
EXTENDED_MASK = 0x20
|
18
|
+
UNSYNCHRONISED_MASK = 0x40
|
19
|
+
|
20
|
+
def initialize(stream)
|
21
|
+
super
|
22
|
+
|
23
|
+
validate_identifier!
|
24
|
+
validate_flags!
|
25
|
+
end
|
26
|
+
|
27
|
+
def experimental?
|
28
|
+
(flags & EXPERIMENTAL_MASK).positive?
|
29
|
+
end
|
30
|
+
|
31
|
+
def extended?
|
32
|
+
(flags & EXTENDED_MASK).positive?
|
33
|
+
end
|
34
|
+
|
35
|
+
def unsynchronised?
|
36
|
+
(flags & UNSYNCHRONISED_MASK).positive?
|
37
|
+
end
|
38
|
+
|
39
|
+
def tag_size
|
40
|
+
@tag_size ||= Synchsafe.parse(*bytes_for(:size))
|
41
|
+
end
|
42
|
+
|
43
|
+
def total_size
|
44
|
+
@total_size ||= tag_size + size
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def identifier
|
50
|
+
@identifier ||= bytes_for(:identifier).pack("c*")
|
51
|
+
end
|
52
|
+
|
53
|
+
def flags
|
54
|
+
@flags ||= bytes_for(:flags).first || raise
|
55
|
+
end
|
56
|
+
|
57
|
+
def validate_identifier!
|
58
|
+
return if identifier == IDENTIFIER
|
59
|
+
|
60
|
+
raise "invalid ID3 tag identifier: #{identifier}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_flags!
|
64
|
+
return if (flags & INVALID_FLAG_MASK).zero?
|
65
|
+
|
66
|
+
raise "invalid flags set"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
class TextDecoder
|
5
|
+
ENCODINGS = {
|
6
|
+
ascii: /^\x00(.*?)(\x00)?$/,
|
7
|
+
utf_16: /^\x01(.*?)(\x00\x00)?$/,
|
8
|
+
utf_16be: /^\x02(.*?)(\x00\x00)?$/,
|
9
|
+
utf_8: /^\x03(.*?)(\x00)?$/
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def self.encoding(string)
|
13
|
+
case string
|
14
|
+
when ENCODINGS[:ascii] then :ascii
|
15
|
+
when ENCODINGS[:utf_16] then :utf_16
|
16
|
+
when ENCODINGS[:utf_16be] then :utf_16be
|
17
|
+
when ENCODINGS[:utf_8] then :utf_8
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.decode(string)
|
22
|
+
case encoding(string)
|
23
|
+
when :ascii then string.gsub(ENCODINGS[:ascii], "\\1")
|
24
|
+
when :utf_16 then string.gsub(ENCODINGS[:utf_16], "\\1")
|
25
|
+
when :utf_16be then string.gsub(ENCODINGS[:utf_16be], "\\1")
|
26
|
+
when :utf_8 then string.gsub(ENCODINGS[:utf_8], "\\1")
|
27
|
+
else string
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/audio_tag.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
class Header < StreamSection
|
4
|
+
attr_reader bytes: bytes
|
5
|
+
|
6
|
+
def bytes_for: (Symbol) -> bytes
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
UNEXTENDED_SIZE: Integer
|
11
|
+
|
12
|
+
def structure: -> Array[Symbol]
|
13
|
+
|
14
|
+
def structure_defined?: -> bool
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
class StreamReader
|
4
|
+
attr_reader stream: IO
|
5
|
+
|
6
|
+
def initialize: (IO) -> void
|
7
|
+
|
8
|
+
def read!: (Integer) -> String
|
9
|
+
|
10
|
+
def read: (Integer) -> String
|
11
|
+
|
12
|
+
def read_bytes!: (Integer) -> bytes
|
13
|
+
|
14
|
+
def read_bytes: (Integer) -> bytes
|
15
|
+
|
16
|
+
def read_byte!: -> Integer
|
17
|
+
|
18
|
+
def read_byte: -> Integer
|
19
|
+
|
20
|
+
def read_until!: (Integer, ?consume_delimiter: bool) -> String
|
21
|
+
|
22
|
+
def read_until: (Integer) -> String
|
23
|
+
|
24
|
+
def read_bytes_until!: (Integer, ?consume_delimiter: bool) -> bytes
|
25
|
+
|
26
|
+
def read_bytes_until: (Integer) -> bytes
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def end_of_stream?: -> bool
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
class Frame < StreamSection
|
5
|
+
attr_reader header: FrameHeader
|
6
|
+
attr_reader raw_data: String
|
7
|
+
|
8
|
+
def initialize: (IO, ?header: FrameHeader) -> void
|
9
|
+
|
10
|
+
def key: -> Symbol
|
11
|
+
|
12
|
+
def data: -> untyped
|
13
|
+
|
14
|
+
def to_h: -> Hash[Symbol, untyped]
|
15
|
+
|
16
|
+
def self.build: (IO) -> Frame
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def size: -> Integer
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
class FrameHeader < Header
|
5
|
+
STRUCTURE: Array[Symbol]
|
6
|
+
|
7
|
+
attr_reader id: Symbol
|
8
|
+
attr_reader frame_size: Integer
|
9
|
+
attr_reader key: Symbol
|
10
|
+
attr_reader frame_class: singleton(Frame)
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader frame_type: FrameType
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
class FrameMerger
|
5
|
+
|
6
|
+
type frames = Array[Frame]
|
7
|
+
|
8
|
+
attr_reader frames: frames
|
9
|
+
|
10
|
+
def initialize: (frames) -> void
|
11
|
+
|
12
|
+
def merge: -> Hash[Symbol, untyped]
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def pairs: -> Array[[Symbol, untyped]]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
class FrameType
|
5
|
+
type frame_class = singleton(Frame)
|
6
|
+
|
7
|
+
type frame_attributes = Hash[Symbol, untyped]
|
8
|
+
|
9
|
+
LOOKUP: Hash[Symbol, frame_attributes]
|
10
|
+
|
11
|
+
DEFAULT_FRAME_CLASS: frame_class
|
12
|
+
|
13
|
+
attr_reader name: Symbol
|
14
|
+
attr_reader frame_class: frame_class
|
15
|
+
|
16
|
+
def initialize: (**untyped) -> void
|
17
|
+
|
18
|
+
def self.find: (Symbol) -> instance
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
module Frames
|
5
|
+
class CommentFrame < Frame
|
6
|
+
def data: -> Hash[Symbol, Hash[Symbol, String]]
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
attr_reader encoding: Integer
|
11
|
+
attr_reader text: String
|
12
|
+
|
13
|
+
@description: String
|
14
|
+
@language: String
|
15
|
+
|
16
|
+
def description: -> Symbol
|
17
|
+
|
18
|
+
def language: -> Symbol
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
module Frames
|
5
|
+
class PictureFrame < Frame
|
6
|
+
ENCODINGS: Array[Symbol]
|
7
|
+
|
8
|
+
TYPES: Array[Symbol]
|
9
|
+
|
10
|
+
def data: -> Hash[Symbol, untyped]
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader mime: String
|
15
|
+
attr_reader description: String
|
16
|
+
|
17
|
+
@encoding: Integer
|
18
|
+
@type: Integer
|
19
|
+
|
20
|
+
def encoding: -> Symbol
|
21
|
+
|
22
|
+
def type: -> Symbol
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
module Frames
|
5
|
+
class UserFrame < Frame
|
6
|
+
DESCRIPTIONS: Hash[Symbol, Symbol]
|
7
|
+
|
8
|
+
def data: -> Hash[Symbol, untyped]
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
attr_reader encoding: Integer
|
13
|
+
attr_reader value: String
|
14
|
+
|
15
|
+
@description: String
|
16
|
+
|
17
|
+
def description: -> Symbol
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
class Tag < StreamSection
|
5
|
+
attr_reader header: TagHeader
|
6
|
+
attr_reader frames: Array[Frame]
|
7
|
+
|
8
|
+
def initialize: (IO) -> void
|
9
|
+
|
10
|
+
def to_h: -> Hash[Symbol, untyped]
|
11
|
+
|
12
|
+
def self.from_file: (String) -> Tag
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def end_of_tag?: -> bool
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module AudioTag
|
2
|
+
module ID3
|
3
|
+
module V2
|
4
|
+
class TagHeader < Header
|
5
|
+
STRUCTURE: Array[Symbol]
|
6
|
+
|
7
|
+
IDENTIFIER: String
|
8
|
+
|
9
|
+
INVALID_FLAG_MASK: Integer
|
10
|
+
EXPERIMENTAL_MASK: Integer
|
11
|
+
EXTENDED_MASK: Integer
|
12
|
+
UNSYNCHRONISED_MASK: Integer
|
13
|
+
|
14
|
+
def experimental?: -> bool
|
15
|
+
|
16
|
+
def extended?: -> bool
|
17
|
+
|
18
|
+
def unsynchronised?: -> bool
|
19
|
+
|
20
|
+
attr_reader tag_size: Integer
|
21
|
+
|
22
|
+
attr_reader total_size: Integer
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader identifier: String
|
27
|
+
|
28
|
+
attr_reader flags: Integer
|
29
|
+
|
30
|
+
def validate_identifier!: -> void
|
31
|
+
|
32
|
+
def validate_flags!: -> void
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|