wahwah 1.0.0 → 1.3.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 +4 -4
- data/lib/wahwah/asf/object.rb +7 -12
- data/lib/wahwah/asf_tag.rb +199 -198
- data/lib/wahwah/errors.rb +2 -1
- data/lib/wahwah/flac/block.rb +8 -14
- data/lib/wahwah/flac/streaminfo_block.rb +5 -5
- data/lib/wahwah/flac_tag.rb +65 -63
- data/lib/wahwah/helper.rb +8 -8
- data/lib/wahwah/id3/comment_frame_body.rb +1 -1
- data/lib/wahwah/id3/frame.rb +57 -61
- data/lib/wahwah/id3/frame_body.rb +6 -7
- data/lib/wahwah/id3/image_frame_body.rb +5 -5
- data/lib/wahwah/id3/text_frame_body.rb +1 -1
- data/lib/wahwah/id3/v1.rb +59 -58
- data/lib/wahwah/id3/v2.rb +42 -35
- data/lib/wahwah/id3/v2_header.rb +4 -4
- data/lib/wahwah/lazy_read.rb +40 -0
- data/lib/wahwah/mp3/mpeg_frame_header.rb +39 -37
- data/lib/wahwah/mp3/vbri_header.rb +2 -2
- data/lib/wahwah/mp3/xing_header.rb +4 -4
- data/lib/wahwah/mp3_tag.rb +52 -51
- data/lib/wahwah/mp4/atom.rb +30 -34
- data/lib/wahwah/mp4_tag.rb +102 -91
- data/lib/wahwah/ogg/flac_tag.rb +3 -3
- data/lib/wahwah/ogg/opus_tag.rb +3 -3
- data/lib/wahwah/ogg/packets.rb +2 -2
- data/lib/wahwah/ogg/page.rb +3 -3
- data/lib/wahwah/ogg/vorbis_comment.rb +15 -15
- data/lib/wahwah/ogg/vorbis_tag.rb +2 -2
- data/lib/wahwah/ogg_tag.rb +34 -34
- data/lib/wahwah/riff/chunk.rb +11 -15
- data/lib/wahwah/riff_tag.rb +81 -79
- data/lib/wahwah/tag.rb +22 -10
- data/lib/wahwah/version.rb +1 -1
- data/lib/wahwah.rb +58 -53
- metadata +28 -14
data/lib/wahwah/riff_tag.rb
CHANGED
@@ -17,7 +17,7 @@ module WahWah
|
|
17
17
|
TRCK: :track
|
18
18
|
}
|
19
19
|
|
20
|
-
CHANNEL_MODE_INDEX = %w
|
20
|
+
CHANNEL_MODE_INDEX = %w[Mono Stereo]
|
21
21
|
|
22
22
|
tag_delegate :@id3_tag,
|
23
23
|
:title,
|
@@ -39,102 +39,104 @@ module WahWah
|
|
39
39
|
end
|
40
40
|
|
41
41
|
private
|
42
|
-
def parse
|
43
|
-
top_chunk = Riff::Chunk.new(@file_io)
|
44
|
-
return unless top_chunk.valid?
|
45
42
|
|
46
|
-
|
43
|
+
def parse
|
44
|
+
top_chunk = Riff::Chunk.new(@file_io)
|
45
|
+
return unless top_chunk.valid?
|
47
46
|
|
48
|
-
|
49
|
-
# This additional field provides the form type of the field.
|
50
|
-
# For wav file, the value of the type field is 'WAVE'
|
51
|
-
return unless top_chunk.id == 'RIFF' && top_chunk.type == 'WAVE'
|
47
|
+
total_chunk_size = top_chunk.size + Riff::Chunk::HEADER_SIZE
|
52
48
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
49
|
+
# The top "RIFF" chunks include an additional field in the first four bytes of the data field.
|
50
|
+
# This additional field provides the form type of the field.
|
51
|
+
# For wav file, the value of the type field is 'WAVE'
|
52
|
+
return unless top_chunk.id == "RIFF" && top_chunk.type == "WAVE"
|
58
53
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
case sub_chunk.id
|
63
|
-
when 'fmt'
|
64
|
-
parse_fmt_chunk(sub_chunk)
|
65
|
-
when 'data'
|
66
|
-
parse_data_chunk(sub_chunk)
|
67
|
-
when 'LIST'
|
68
|
-
parse_list_chunk(sub_chunk)
|
69
|
-
when 'id3', 'ID3'
|
70
|
-
parse_id3_chunk(sub_chunk)
|
71
|
-
else
|
72
|
-
@file_io.seek(sub_chunk.size, IO::SEEK_CUR)
|
73
|
-
end
|
54
|
+
until total_chunk_size <= @file_io.pos || @file_io.eof?
|
55
|
+
sub_chunk = Riff::Chunk.new(@file_io)
|
56
|
+
parse_sub_chunk(sub_chunk)
|
74
57
|
end
|
58
|
+
end
|
75
59
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
# The number of bytes for one sample including
|
91
|
-
# all channels.
|
92
|
-
#
|
93
|
-
# 2(little endian) BitsPerSample 8 bits = 8, 16 bits = 16, etc.
|
94
|
-
def parse_fmt_chunk(chunk)
|
95
|
-
_, @channel, @sample_rate, _, _, @bits_per_sample = chunk.data.unpack('vvVVvv')
|
96
|
-
@bitrate = @sample_rate * @channel * @bits_per_sample / 1000
|
60
|
+
def parse_sub_chunk(sub_chunk)
|
61
|
+
return unless sub_chunk.valid?
|
62
|
+
|
63
|
+
case sub_chunk.id
|
64
|
+
when "fmt"
|
65
|
+
parse_fmt_chunk(sub_chunk)
|
66
|
+
when "data"
|
67
|
+
parse_data_chunk(sub_chunk)
|
68
|
+
when "LIST"
|
69
|
+
parse_list_chunk(sub_chunk)
|
70
|
+
when "id3", "ID3"
|
71
|
+
parse_id3_chunk(sub_chunk)
|
72
|
+
else
|
73
|
+
sub_chunk.skip
|
97
74
|
end
|
75
|
+
end
|
98
76
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
77
|
+
# The fmt chunk data structure:
|
78
|
+
# Length Meaning Description
|
79
|
+
#
|
80
|
+
# 2(little endian) AudioFormat PCM = 1 (i.e. Linear quantization)
|
81
|
+
# Values other than 1 indicate some
|
82
|
+
# form of compression.
|
83
|
+
#
|
84
|
+
# 2(little endian) NumChannels Mono = 1, Stereo = 2, etc.
|
85
|
+
#
|
86
|
+
# 4(little endian) SampleRate 8000, 44100, etc.
|
87
|
+
#
|
88
|
+
# 4(little endian) ByteRate == SampleRate * NumChannels * BitsPerSample/8
|
89
|
+
#
|
90
|
+
# 2(little endian) BlockAlign == NumChannels * BitsPerSample/8
|
91
|
+
# The number of bytes for one sample including
|
92
|
+
# all channels.
|
93
|
+
#
|
94
|
+
# 2(little endian) BitsPerSample 8 bits = 8, 16 bits = 16, etc.
|
95
|
+
def parse_fmt_chunk(chunk)
|
96
|
+
_, @channel, @sample_rate, _, _, @bit_depth = chunk.data.unpack("vvVVvv")
|
97
|
+
@bitrate = @sample_rate * @channel * @bit_depth / 1000
|
98
|
+
end
|
103
99
|
|
104
|
-
|
105
|
-
|
100
|
+
def parse_data_chunk(chunk)
|
101
|
+
@duration = chunk.size * 8 / (@bitrate * 1000).to_f
|
102
|
+
chunk.skip
|
103
|
+
end
|
106
104
|
|
107
|
-
|
108
|
-
|
109
|
-
if chunk.type != 'INFO'
|
110
|
-
@file_io.seek(chunk.size, IO::SEEK_CUR)
|
111
|
-
else
|
112
|
-
until list_chunk_end_position <= @file_io.pos do
|
113
|
-
info_chunk = Riff::Chunk.new(@file_io)
|
105
|
+
def parse_list_chunk(chunk)
|
106
|
+
list_chunk_end_position = @file_io.pos + chunk.size
|
114
107
|
|
115
|
-
|
116
|
-
|
117
|
-
|
108
|
+
# RIFF can be tagged with metadata in the INFO chunk.
|
109
|
+
# And INFO chunk as a subchunk for LIST chunk.
|
110
|
+
if chunk.type != "INFO"
|
111
|
+
chunk.skip
|
112
|
+
else
|
113
|
+
until list_chunk_end_position <= @file_io.pos
|
114
|
+
info_chunk = Riff::Chunk.new(@file_io)
|
118
115
|
|
119
|
-
|
116
|
+
unless INFO_ID_MAPPING.key? info_chunk.id.to_sym
|
117
|
+
info_chunk.skip
|
118
|
+
next
|
120
119
|
end
|
120
|
+
|
121
|
+
update_attribute(info_chunk)
|
121
122
|
end
|
122
123
|
end
|
124
|
+
end
|
123
125
|
|
124
|
-
|
125
|
-
|
126
|
-
|
126
|
+
def parse_id3_chunk(chunk)
|
127
|
+
@id3_tag = ID3::V2.new(StringIO.new(chunk.data))
|
128
|
+
end
|
127
129
|
|
128
|
-
|
129
|
-
|
130
|
-
|
130
|
+
def update_attribute(chunk)
|
131
|
+
attr_name = INFO_ID_MAPPING[chunk.id.to_sym]
|
132
|
+
chunk_data = Helper.encode_to_utf8(chunk.data)
|
131
133
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
end
|
134
|
+
case attr_name
|
135
|
+
when :comment
|
136
|
+
@comments.push(chunk_data)
|
137
|
+
else
|
138
|
+
instance_variable_set("@#{attr_name}", chunk_data)
|
138
139
|
end
|
140
|
+
end
|
139
141
|
end
|
140
142
|
end
|
data/lib/wahwah/tag.rb
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
module WahWah
|
4
4
|
class Tag
|
5
|
-
INTEGER_ATTRIBUTES = %i
|
6
|
-
INSPECT_ATTRIBUTES = %i
|
5
|
+
INTEGER_ATTRIBUTES = %i[disc disc_total track track_total]
|
6
|
+
INSPECT_ATTRIBUTES = %i[title artist album albumartist composer track track_total genre year disc disc_total duration bitrate sample_rate bit_depth]
|
7
7
|
|
8
8
|
attr_reader(
|
9
9
|
:title,
|
@@ -18,10 +18,10 @@ module WahWah
|
|
18
18
|
:year,
|
19
19
|
:disc,
|
20
20
|
:disc_total,
|
21
|
-
:images,
|
22
21
|
:duration,
|
23
22
|
:bitrate,
|
24
23
|
:sample_rate,
|
24
|
+
:bit_depth,
|
25
25
|
:file_size
|
26
26
|
)
|
27
27
|
|
@@ -35,7 +35,7 @@ module WahWah
|
|
35
35
|
end
|
36
36
|
|
37
37
|
@comments = []
|
38
|
-
@
|
38
|
+
@images_data = []
|
39
39
|
|
40
40
|
parse if @file_size > 0
|
41
41
|
|
@@ -43,17 +43,29 @@ module WahWah
|
|
43
43
|
value = instance_variable_get("@#{attr_name}")&.to_i
|
44
44
|
instance_variable_set("@#{attr_name}", value)
|
45
45
|
end
|
46
|
-
|
47
|
-
|
48
|
-
def parse
|
49
|
-
raise WahWahNotImplementedError, 'The parse method is not implemented'
|
46
|
+
ensure
|
47
|
+
@file_io.close
|
50
48
|
end
|
51
49
|
|
52
50
|
def inspect
|
53
|
-
inspect_id = ::Kernel.format
|
54
|
-
inspect_attributes_values = INSPECT_ATTRIBUTES.map { |attr_name| "#{attr_name}=#{
|
51
|
+
inspect_id = ::Kernel.format "%x", (object_id * 2)
|
52
|
+
inspect_attributes_values = INSPECT_ATTRIBUTES.map { |attr_name| "#{attr_name}=#{send(attr_name)}" }.join(" ")
|
55
53
|
|
56
54
|
"<#{self.class.name}:0x#{inspect_id} #{inspect_attributes_values}>"
|
57
55
|
end
|
56
|
+
|
57
|
+
def images
|
58
|
+
return @images_data if @images_data.empty?
|
59
|
+
|
60
|
+
@images_data.map do |data|
|
61
|
+
parse_image_data(data)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def parse
|
68
|
+
raise WahWahNotImplementedError, "The parse method is not implemented"
|
69
|
+
end
|
58
70
|
end
|
59
71
|
end
|
data/lib/wahwah/version.rb
CHANGED
data/lib/wahwah.rb
CHANGED
@@ -1,57 +1,62 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
|
7
|
-
require
|
8
|
-
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
require
|
26
|
-
require
|
27
|
-
|
28
|
-
require
|
29
|
-
|
30
|
-
require
|
31
|
-
require
|
32
|
-
|
33
|
-
require
|
34
|
-
require
|
35
|
-
|
36
|
-
require
|
37
|
-
|
38
|
-
require
|
39
|
-
|
40
|
-
|
41
|
-
require
|
42
|
-
|
43
|
-
require
|
44
|
-
|
45
|
-
require
|
3
|
+
require "stringio"
|
4
|
+
require "forwardable"
|
5
|
+
require "zlib"
|
6
|
+
|
7
|
+
require "wahwah/version"
|
8
|
+
require "wahwah/errors"
|
9
|
+
require "wahwah/helper"
|
10
|
+
require "wahwah/tag_delegate"
|
11
|
+
require "wahwah/lazy_read"
|
12
|
+
require "wahwah/tag"
|
13
|
+
|
14
|
+
require "wahwah/id3/v1"
|
15
|
+
require "wahwah/id3/v2"
|
16
|
+
require "wahwah/id3/v2_header"
|
17
|
+
require "wahwah/id3/frame"
|
18
|
+
require "wahwah/id3/frame_body"
|
19
|
+
require "wahwah/id3/text_frame_body"
|
20
|
+
require "wahwah/id3/genre_frame_body"
|
21
|
+
require "wahwah/id3/comment_frame_body"
|
22
|
+
require "wahwah/id3/image_frame_body"
|
23
|
+
|
24
|
+
require "wahwah/mp3/mpeg_frame_header"
|
25
|
+
require "wahwah/mp3/xing_header"
|
26
|
+
require "wahwah/mp3/vbri_header"
|
27
|
+
|
28
|
+
require "wahwah/riff/chunk"
|
29
|
+
|
30
|
+
require "wahwah/flac/block"
|
31
|
+
require "wahwah/flac/streaminfo_block"
|
32
|
+
|
33
|
+
require "wahwah/ogg/page"
|
34
|
+
require "wahwah/ogg/pages"
|
35
|
+
require "wahwah/ogg/packets"
|
36
|
+
require "wahwah/ogg/vorbis_comment"
|
37
|
+
require "wahwah/ogg/vorbis_tag"
|
38
|
+
require "wahwah/ogg/opus_tag"
|
39
|
+
require "wahwah/ogg/flac_tag"
|
40
|
+
|
41
|
+
require "wahwah/asf/object"
|
42
|
+
|
43
|
+
require "wahwah/mp4/atom"
|
44
|
+
|
45
|
+
require "wahwah/mp3_tag"
|
46
|
+
require "wahwah/mp4_tag"
|
47
|
+
require "wahwah/ogg_tag"
|
48
|
+
require "wahwah/riff_tag"
|
49
|
+
require "wahwah/asf_tag"
|
50
|
+
require "wahwah/flac_tag"
|
46
51
|
|
47
52
|
module WahWah
|
48
53
|
FORMATE_MAPPING = {
|
49
|
-
Mp3Tag: [
|
50
|
-
OggTag: [
|
51
|
-
RiffTag: [
|
52
|
-
FlacTag: [
|
53
|
-
AsfTag: [
|
54
|
-
Mp4Tag: [
|
54
|
+
Mp3Tag: ["mp3"],
|
55
|
+
OggTag: ["ogg", "oga", "opus"],
|
56
|
+
RiffTag: ["wav"],
|
57
|
+
FlacTag: ["flac"],
|
58
|
+
AsfTag: ["wma"],
|
59
|
+
Mp4Tag: ["m4a"]
|
55
60
|
}.freeze
|
56
61
|
|
57
62
|
def self.open(file_path)
|
@@ -60,10 +65,10 @@ module WahWah
|
|
60
65
|
|
61
66
|
file_format = Helper.file_format(file_path)
|
62
67
|
|
63
|
-
raise WahWahArgumentError,
|
64
|
-
raise WahWahArgumentError,
|
65
|
-
raise WahWahArgumentError,
|
66
|
-
raise WahWahArgumentError,
|
68
|
+
raise WahWahArgumentError, "File is not exists" unless File.exist? file_path
|
69
|
+
raise WahWahArgumentError, "File is unreadable" unless File.readable? file_path
|
70
|
+
raise WahWahArgumentError, "File is empty" unless File.size(file_path) > 0
|
71
|
+
raise WahWahArgumentError, "No supported format found" unless support_formats.include? file_format
|
67
72
|
|
68
73
|
FORMATE_MAPPING.each do |tag, formats|
|
69
74
|
break const_get(tag).new(file_path) if formats.include?(file_format)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wahwah
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aidewoode
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -53,47 +53,61 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: standard
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 1.4.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 1.4.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: simplecov
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 0.21.2
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 0.21.2
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: simplecov-lcov
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
89
|
+
version: 0.8.0
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.
|
96
|
+
version: 0.8.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: memory_profiler
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.9.14
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.9.14
|
97
111
|
description: WahWah is an audio metadata reader ruby gem, it supports many popular
|
98
112
|
formats including mp3(ID3 v1, v2.2, v2.3, v2.4), m4a, ogg, oga, opus, wav, flac
|
99
113
|
and wma.
|
@@ -121,6 +135,7 @@ files:
|
|
121
135
|
- lib/wahwah/id3/v1.rb
|
122
136
|
- lib/wahwah/id3/v2.rb
|
123
137
|
- lib/wahwah/id3/v2_header.rb
|
138
|
+
- lib/wahwah/lazy_read.rb
|
124
139
|
- lib/wahwah/mp3/mpeg_frame_header.rb
|
125
140
|
- lib/wahwah/mp3/vbri_header.rb
|
126
141
|
- lib/wahwah/mp3/xing_header.rb
|
@@ -152,15 +167,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
152
167
|
requirements:
|
153
168
|
- - ">="
|
154
169
|
- !ruby/object:Gem::Version
|
155
|
-
version: 2.
|
170
|
+
version: 2.6.0
|
156
171
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
172
|
requirements:
|
158
173
|
- - ">="
|
159
174
|
- !ruby/object:Gem::Version
|
160
175
|
version: '0'
|
161
176
|
requirements: []
|
162
|
-
|
163
|
-
rubygems_version: 2.7.6.2
|
177
|
+
rubygems_version: 3.2.32
|
164
178
|
signing_key:
|
165
179
|
specification_version: 4
|
166
180
|
summary: Audio metadata reader ruby gem
|