wahwah 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/wahwah.rb +5 -0
- data/lib/wahwah/asf/object.rb +5 -10
- data/lib/wahwah/asf_tag.rb +2 -2
- data/lib/wahwah/flac/block.rb +5 -11
- data/lib/wahwah/flac/streaminfo_block.rb +2 -2
- data/lib/wahwah/flac_tag.rb +5 -5
- data/lib/wahwah/id3/frame.rb +11 -16
- data/lib/wahwah/id3/v2.rb +9 -8
- data/lib/wahwah/lazy_read.rb +26 -0
- data/lib/wahwah/mp3_tag.rb +0 -2
- data/lib/wahwah/mp4/atom.rb +7 -12
- data/lib/wahwah/mp4_tag.rb +17 -10
- data/lib/wahwah/ogg/flac_tag.rb +1 -1
- data/lib/wahwah/ogg/vorbis_comment.rb +1 -1
- data/lib/wahwah/ogg_tag.rb +8 -0
- data/lib/wahwah/riff/chunk.rb +5 -10
- data/lib/wahwah/riff_tag.rb +9 -9
- data/lib/wahwah/tag.rb +16 -7
- data/lib/wahwah/version.rb +1 -1
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e742f50b1a22c7cbbe63ba26506803720bbc44dbfe9943ac07a32e7efe048cbe
|
4
|
+
data.tar.gz: 47e1352b98217e34eb3e3fb3c258695a8f7cf2a4d579ce6baaeda1216703f4e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5450f35d5478a3a434ac0e366589edb3999b40eb1f321e53086ccd1d3d20c5542ce63ac9d8d7b380379cafd719bbc97c33d2768d585c9a2b1d71218950f1b36
|
7
|
+
data.tar.gz: 86a134f7ac39a2430fcc2a62756a23354aa42825bf1a97ad4b7ba6b71f42bb72c20f3dda4a884db06e77faa84517c7fe5f4004e317939bd36d4c60369067c93e
|
data/lib/wahwah.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'stringio'
|
4
|
+
require 'forwardable'
|
5
|
+
require 'zlib'
|
6
|
+
|
3
7
|
require 'wahwah/version'
|
4
8
|
require 'wahwah/errors'
|
5
9
|
require 'wahwah/helper'
|
6
10
|
require 'wahwah/tag_delegate'
|
11
|
+
require 'wahwah/lazy_read'
|
7
12
|
require 'wahwah/tag'
|
8
13
|
|
9
14
|
require 'wahwah/id3/v1'
|
data/lib/wahwah/asf/object.rb
CHANGED
@@ -11,29 +11,24 @@ module WahWah
|
|
11
11
|
# 8 bytes: Object size
|
12
12
|
# variable-sized: Object data
|
13
13
|
class Object
|
14
|
+
prepend LazyRead
|
15
|
+
|
14
16
|
HEADER_SIZE = 24
|
15
17
|
HEADER_FORMAT = 'a16Q<'
|
16
18
|
|
17
|
-
attr_reader :
|
19
|
+
attr_reader :guid
|
18
20
|
|
19
|
-
def initialize
|
20
|
-
guid_bytes, @size = file_io.read(HEADER_SIZE)&.unpack(HEADER_FORMAT)
|
21
|
+
def initialize
|
22
|
+
guid_bytes, @size = @file_io.read(HEADER_SIZE)&.unpack(HEADER_FORMAT)
|
21
23
|
return unless valid?
|
22
24
|
|
23
25
|
@size = @size - HEADER_SIZE
|
24
26
|
@guid = Helper.byte_string_to_guid(guid_bytes)
|
25
|
-
@file_io = file_io
|
26
|
-
@position = file_io.pos
|
27
27
|
end
|
28
28
|
|
29
29
|
def valid?
|
30
30
|
!@size.nil? && @size >= HEADER_SIZE
|
31
31
|
end
|
32
|
-
|
33
|
-
def data
|
34
|
-
@file_io.seek(@position)
|
35
|
-
@file_io.read(size)
|
36
|
-
end
|
37
32
|
end
|
38
33
|
end
|
39
34
|
end
|
data/lib/wahwah/asf_tag.rb
CHANGED
@@ -54,7 +54,7 @@ module WahWah
|
|
54
54
|
when CONTENT_DESCRIPTION_OBJECT_GUID
|
55
55
|
parse_content_description_object(sub_object)
|
56
56
|
else
|
57
|
-
|
57
|
+
sub_object.skip
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
@@ -187,7 +187,7 @@ module WahWah
|
|
187
187
|
|
188
188
|
return unless stream_type_guid == AUDIO_MEDIA_OBJECT_GUID
|
189
189
|
|
190
|
-
@sample_rate, bytes_per_second = object_data.read(type_specific_data_length).unpack('
|
190
|
+
@sample_rate, bytes_per_second, @bit_depth = object_data.read(type_specific_data_length).unpack('x4VVx2v')
|
191
191
|
@bitrate = (bytes_per_second * 8.0 / 1000).round
|
192
192
|
end
|
193
193
|
|
data/lib/wahwah/flac/block.rb
CHANGED
@@ -3,13 +3,15 @@
|
|
3
3
|
module WahWah
|
4
4
|
module Flac
|
5
5
|
class Block
|
6
|
+
prepend LazyRead
|
7
|
+
|
6
8
|
HEADER_SIZE = 4
|
7
9
|
HEADER_FORMAT = 'B*'
|
8
10
|
BLOCK_TYPE_INDEX = %w(STREAMINFO PADDING APPLICATION SEEKTABLE VORBIS_COMMENT CUESHEET PICTURE)
|
9
11
|
|
10
|
-
attr_reader :
|
12
|
+
attr_reader :type
|
11
13
|
|
12
|
-
def initialize
|
14
|
+
def initialize
|
13
15
|
# Block header structure:
|
14
16
|
#
|
15
17
|
# Length(bit) Meaning
|
@@ -30,14 +32,11 @@ module WahWah
|
|
30
32
|
#
|
31
33
|
# 24 Length (in bytes) of metadata to follow
|
32
34
|
# (does not include the size of the METADATA_BLOCK_HEADER)
|
33
|
-
header_bits = file_io.read(HEADER_SIZE).unpack(HEADER_FORMAT).first
|
35
|
+
header_bits = @file_io.read(HEADER_SIZE).unpack(HEADER_FORMAT).first
|
34
36
|
|
35
37
|
@last_flag = header_bits[0]
|
36
38
|
@type = BLOCK_TYPE_INDEX[header_bits[1..7].to_i(2)]
|
37
39
|
@size = header_bits[8..-1].to_i(2)
|
38
|
-
|
39
|
-
@file_io = file_io
|
40
|
-
@position = file_io.pos
|
41
40
|
end
|
42
41
|
|
43
42
|
def valid?
|
@@ -47,11 +46,6 @@ module WahWah
|
|
47
46
|
def is_last?
|
48
47
|
@last_flag.to_i == 1
|
49
48
|
end
|
50
|
-
|
51
|
-
def data
|
52
|
-
@file_io.seek(@position)
|
53
|
-
@file_io.read(size)
|
54
|
-
end
|
55
49
|
end
|
56
50
|
end
|
57
51
|
end
|
@@ -40,11 +40,11 @@ module WahWah
|
|
40
40
|
info_bits = block_data.unpack('x10B64').first
|
41
41
|
|
42
42
|
@sample_rate = info_bits[0..19].to_i(2)
|
43
|
-
|
43
|
+
@bit_depth = info_bits[23..27].to_i(2) + 1
|
44
44
|
total_samples = info_bits[28..-1].to_i(2)
|
45
45
|
|
46
46
|
@duration = (total_samples.to_f / @sample_rate).round if @sample_rate > 0
|
47
|
-
@bitrate = @sample_rate *
|
47
|
+
@bitrate = @sample_rate * @bit_depth / 1000
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
data/lib/wahwah/flac_tag.rb
CHANGED
@@ -38,9 +38,9 @@ module WahWah
|
|
38
38
|
when 'VORBIS_COMMENT'
|
39
39
|
parse_vorbis_comment(block.data)
|
40
40
|
when 'PICTURE'
|
41
|
-
|
41
|
+
@images_data.push(block); block.skip
|
42
42
|
else
|
43
|
-
|
43
|
+
block.skip
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
@@ -69,8 +69,8 @@ module WahWah
|
|
69
69
|
# 32 The length of the picture data in bytes.
|
70
70
|
#
|
71
71
|
# n*8 The binary picture data.
|
72
|
-
def
|
73
|
-
block_content = StringIO.new(
|
72
|
+
def parse_image_data(picture_block)
|
73
|
+
block_content = StringIO.new(picture_block.data)
|
74
74
|
|
75
75
|
type_index, mime_type_length = block_content.read(8).unpack('NN')
|
76
76
|
mime_type = Helper.encode_to_utf8(block_content.read(mime_type_length))
|
@@ -78,7 +78,7 @@ module WahWah
|
|
78
78
|
data_length = block_content.read(description_length + 20).unpack("#{'x' * (description_length + 16)}N").first
|
79
79
|
data = block_content.read(data_length)
|
80
80
|
|
81
|
-
|
81
|
+
{ data: data, mime_type: mime_type, type: ID3::ImageFrameBody::TYPES[type_index] }
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
data/lib/wahwah/id3/frame.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'zlib'
|
4
|
-
|
5
3
|
module WahWah
|
6
4
|
module ID3
|
7
5
|
class Frame
|
6
|
+
prepend LazyRead
|
7
|
+
|
8
8
|
ID_MAPPING = {
|
9
9
|
# ID3v2.2 frame id
|
10
10
|
COM: :comment,
|
@@ -78,10 +78,9 @@ module WahWah
|
|
78
78
|
array[15] = :data_length_indicator
|
79
79
|
end
|
80
80
|
|
81
|
-
attr_reader :name
|
81
|
+
attr_reader :name
|
82
82
|
|
83
|
-
def initialize(
|
84
|
-
@file_io = file_io
|
83
|
+
def initialize(version)
|
85
84
|
@version = version
|
86
85
|
|
87
86
|
parse_frame_header
|
@@ -98,8 +97,6 @@ module WahWah
|
|
98
97
|
@file_io.seek(4, IO::SEEK_CUR)
|
99
98
|
@size = @size - 4
|
100
99
|
end
|
101
|
-
|
102
|
-
parse_body
|
103
100
|
end
|
104
101
|
|
105
102
|
def valid?
|
@@ -114,6 +111,13 @@ module WahWah
|
|
114
111
|
@flags.include? :data_length_indicator
|
115
112
|
end
|
116
113
|
|
114
|
+
def value
|
115
|
+
return unless @size > 0
|
116
|
+
|
117
|
+
content = compressed? ? Zlib.inflate(data) : data
|
118
|
+
frame_body = frame_body_class.new(content, @version)
|
119
|
+
frame_body.value
|
120
|
+
end
|
117
121
|
|
118
122
|
private
|
119
123
|
# ID3v2.2 frame header structure:
|
@@ -154,15 +158,6 @@ module WahWah
|
|
154
158
|
end.compact
|
155
159
|
end
|
156
160
|
|
157
|
-
def parse_body
|
158
|
-
return unless @size > 0
|
159
|
-
(@file_io.seek(@size, IO::SEEK_CUR); return) if @name.nil?
|
160
|
-
|
161
|
-
content = compressed? ? Zlib.inflate(@file_io.read(@size)) : @file_io.read(@size)
|
162
|
-
frame_body = frame_body_class.new(content, @version)
|
163
|
-
@value = frame_body.value
|
164
|
-
end
|
165
|
-
|
166
161
|
def frame_body_class
|
167
162
|
case @name
|
168
163
|
when :comment
|
data/lib/wahwah/id3/v2.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
3
|
module WahWah
|
6
4
|
module ID3
|
7
5
|
class V2 < Tag
|
@@ -22,7 +20,7 @@ module WahWah
|
|
22
20
|
|
23
21
|
until end_of_tag? do
|
24
22
|
frame = ID3::Frame.new(@file_io, major_version)
|
25
|
-
next unless frame.valid?
|
23
|
+
(frame.skip; next) unless frame.valid?
|
26
24
|
|
27
25
|
update_attribute(frame)
|
28
26
|
end
|
@@ -30,31 +28,34 @@ module WahWah
|
|
30
28
|
|
31
29
|
def update_attribute(frame)
|
32
30
|
name = frame.name
|
33
|
-
value = frame.value
|
34
31
|
|
35
32
|
case name
|
36
33
|
when :comment
|
37
34
|
# Because there may be more than one comment frame in each tag,
|
38
35
|
# so push it into a array.
|
39
|
-
@comments.push(value)
|
36
|
+
@comments.push(frame.value)
|
40
37
|
when :image
|
41
38
|
# Because there may be more than one image frame in each tag,
|
42
39
|
# so push it into a array.
|
43
|
-
@
|
40
|
+
@images_data.push(frame); frame.skip
|
44
41
|
when :track, :disc
|
45
42
|
# Track and disc value may be extended with a "/" character
|
46
43
|
# and a numeric string containing the total numer.
|
47
|
-
count, total_count = value.split('/', 2)
|
44
|
+
count, total_count = frame.value.split('/', 2)
|
48
45
|
instance_variable_set("@#{name}", count)
|
49
46
|
instance_variable_set("@#{name}_total", total_count) unless total_count.nil?
|
50
47
|
else
|
51
|
-
instance_variable_set("@#{name}", value)
|
48
|
+
instance_variable_set("@#{name}", frame.value)
|
52
49
|
end
|
53
50
|
end
|
54
51
|
|
55
52
|
def end_of_tag?
|
56
53
|
size <= @file_io.pos || file_size <= @file_io.pos
|
57
54
|
end
|
55
|
+
|
56
|
+
def parse_image_data(image_frame)
|
57
|
+
image_frame.value
|
58
|
+
end
|
58
59
|
end
|
59
60
|
end
|
60
61
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WahWah
|
4
|
+
module LazyRead
|
5
|
+
def self.prepended(base)
|
6
|
+
base.class_eval do
|
7
|
+
attr_reader :size
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(file_io, *arg)
|
12
|
+
@file_io = file_io
|
13
|
+
super(*arg)
|
14
|
+
@position = @file_io.pos
|
15
|
+
end
|
16
|
+
|
17
|
+
def data
|
18
|
+
@file_io.seek(@position)
|
19
|
+
@file_io.read(size)
|
20
|
+
end
|
21
|
+
|
22
|
+
def skip
|
23
|
+
@file_io.seek(size, IO::SEEK_CUR)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/wahwah/mp3_tag.rb
CHANGED
data/lib/wahwah/mp4/atom.rb
CHANGED
@@ -3,13 +3,15 @@
|
|
3
3
|
module WahWah
|
4
4
|
module Mp4
|
5
5
|
class Atom
|
6
|
+
prepend LazyRead
|
7
|
+
|
6
8
|
VERSIONED_ATOMS = %w(meta stsd)
|
7
9
|
FLAGGED_ATOMS = %w(stsd)
|
8
10
|
HEADER_SIZE = 8
|
9
11
|
HEADER_SIZE_FIELD_SIZE = 4
|
10
12
|
EXTENDED_HEADER_SIZE = 8
|
11
13
|
|
12
|
-
attr_reader :
|
14
|
+
attr_reader :type
|
13
15
|
|
14
16
|
def self.find(file_io, *atom_path)
|
15
17
|
file_io.rewind
|
@@ -39,25 +41,18 @@ module WahWah
|
|
39
41
|
# Type:
|
40
42
|
# A 32-bit integer that contains the type of the atom.
|
41
43
|
# This can often be usefully treated as a four-character field with a mnemonic value .
|
42
|
-
def initialize
|
43
|
-
@size, @type = file_io.read(HEADER_SIZE)&.unpack('Na4')
|
44
|
+
def initialize
|
45
|
+
@size, @type = @file_io.read(HEADER_SIZE)&.unpack('Na4')
|
44
46
|
return unless valid?
|
45
47
|
|
46
48
|
# If the size field of an atom is set to 1, the type field is followed by a 64-bit extended size field,
|
47
49
|
# which contains the actual size of the atom as a 64-bit unsigned integer.
|
48
|
-
@size = file_io.read(EXTENDED_HEADER_SIZE).unpack('Q>').first - EXTENDED_HEADER_SIZE if @size == 1
|
50
|
+
@size = @file_io.read(EXTENDED_HEADER_SIZE).unpack('Q>').first - EXTENDED_HEADER_SIZE if @size == 1
|
49
51
|
|
50
52
|
# If the size field of an atom is set to 0, which is allowed only for a top-level atom,
|
51
53
|
# designates the last atom in the file and indicates that the atom extends to the end of the file.
|
52
|
-
@size = file_io.size if @size == 0
|
54
|
+
@size = @file_io.size if @size == 0
|
53
55
|
@size = @size - HEADER_SIZE
|
54
|
-
@file_io = file_io
|
55
|
-
@position = file_io.pos
|
56
|
-
end
|
57
|
-
|
58
|
-
def data
|
59
|
-
@file_io.seek(@position)
|
60
|
-
@file_io.read(size)
|
61
56
|
end
|
62
57
|
|
63
58
|
def valid?
|
data/lib/wahwah/mp4_tag.rb
CHANGED
@@ -59,7 +59,7 @@ module WahWah
|
|
59
59
|
# The metadata item list atom is of type ‘ilst’ and contains a number of metadata items, each of which is an atom.
|
60
60
|
# each metadata item atom contains a Value Atom, to hold the value of the metadata item
|
61
61
|
atom.children.each do |child_atom|
|
62
|
-
|
62
|
+
attr_name = META_ATOM_MAPPING[child_atom.type]
|
63
63
|
|
64
64
|
# The value of the metadata item is expressed as immediate data in a value atom.
|
65
65
|
# The value atom starts with two fields: a type indicator, and a locale indicator.
|
@@ -68,27 +68,30 @@ module WahWah
|
|
68
68
|
data_atom = child_atom.find('data')
|
69
69
|
return unless data_atom.valid?
|
70
70
|
|
71
|
-
|
72
|
-
encoded_data_value = META_ATOM_DECODE_BY_TYPE[data_type]&.call(data_value)
|
71
|
+
(@images_data.push(data_atom); next) if attr_name == :image
|
73
72
|
|
74
|
-
|
73
|
+
encoded_data_value = parse_meta_data_atom(data_atom)
|
74
|
+
next if attr_name.nil? || encoded_data_value.nil?
|
75
75
|
|
76
|
-
case
|
77
|
-
when :image
|
78
|
-
@images.push(encoded_data_value)
|
76
|
+
case attr_name
|
79
77
|
when :comment
|
80
78
|
@comments.push(encoded_data_value)
|
81
79
|
when :track, :disc
|
82
80
|
count, total_count = encoded_data_value.unpack('x2nn')
|
83
81
|
|
84
|
-
instance_variable_set("@#{
|
85
|
-
instance_variable_set("@#{
|
82
|
+
instance_variable_set("@#{attr_name}", count) unless count.zero?
|
83
|
+
instance_variable_set("@#{attr_name}_total", total_count) unless total_count.zero?
|
86
84
|
else
|
87
|
-
instance_variable_set("@#{
|
85
|
+
instance_variable_set("@#{attr_name}", encoded_data_value)
|
88
86
|
end
|
89
87
|
end
|
90
88
|
end
|
91
89
|
|
90
|
+
def parse_meta_data_atom(atom)
|
91
|
+
data_type, data_value = atom.data.unpack('Nx4a*')
|
92
|
+
META_ATOM_DECODE_BY_TYPE[data_type]&.call(data_value)
|
93
|
+
end
|
94
|
+
|
92
95
|
def parse_mvhd_atom(atom)
|
93
96
|
return unless atom.valid?
|
94
97
|
|
@@ -122,5 +125,9 @@ module WahWah
|
|
122
125
|
@sample_rate = mp4a_atom.data.unpack('x22I>').first if mp4a_atom.valid?
|
123
126
|
@bitrate = esds_atom.data.unpack('x26I>').first / 1000 if esds_atom.valid?
|
124
127
|
end
|
128
|
+
|
129
|
+
def parse_image_data(image_data_atom)
|
130
|
+
parse_meta_data_atom(image_data_atom)
|
131
|
+
end
|
125
132
|
end
|
126
133
|
end
|
data/lib/wahwah/ogg/flac_tag.rb
CHANGED
@@ -6,7 +6,7 @@ module WahWah
|
|
6
6
|
include VorbisComment
|
7
7
|
include Flac::StreaminfoBlock
|
8
8
|
|
9
|
-
attr_reader :bitrate, :duration, :sample_rate, *COMMET_FIELD_MAPPING.values
|
9
|
+
attr_reader :bitrate, :duration, :sample_rate, :bit_depth, *COMMET_FIELD_MAPPING.values
|
10
10
|
|
11
11
|
def initialize(identification_packet, comment_packet)
|
12
12
|
# Identification packet structure:
|
@@ -39,7 +39,7 @@ module WahWah
|
|
39
39
|
comment_length = comment_content.read(4).unpack('V').first
|
40
40
|
comment = Helper.encode_to_utf8(comment_content.read(comment_length))
|
41
41
|
field_name, field_value = comment.split('=', 2)
|
42
|
-
attr_name = COMMET_FIELD_MAPPING[field_name]
|
42
|
+
attr_name = COMMET_FIELD_MAPPING[field_name&.upcase]
|
43
43
|
|
44
44
|
field_value = field_value.to_i if %i(track disc).include? attr_name
|
45
45
|
|
data/lib/wahwah/ogg_tag.rb
CHANGED
@@ -24,6 +24,10 @@ module WahWah
|
|
24
24
|
@bitrate ||= parse_bitrate
|
25
25
|
end
|
26
26
|
|
27
|
+
def bit_depth
|
28
|
+
@bit_depth ||= parse_bit_depth
|
29
|
+
end
|
30
|
+
|
27
31
|
private
|
28
32
|
def packets
|
29
33
|
@packets ||= Ogg::Packets.new(@file_io)
|
@@ -62,5 +66,9 @@ module WahWah
|
|
62
66
|
return @tag.bitrate if @tag.respond_to? :bitrate
|
63
67
|
((file_size - @overhead_packets_size) * 8.0 / duration / 1000).round
|
64
68
|
end
|
69
|
+
|
70
|
+
def parse_bit_depth
|
71
|
+
@tag.bit_depth if @tag.respond_to? :bit_depth
|
72
|
+
end
|
65
73
|
end
|
66
74
|
end
|
data/lib/wahwah/riff/chunk.rb
CHANGED
@@ -16,19 +16,19 @@ module WahWah
|
|
16
16
|
# 4 bytes: an ASCII identifier for this particular RIFF or LIST chunk (for RIFF in the typical case, these 4 bytes describe the content of the entire file, such as "AVI " or "WAVE").
|
17
17
|
# rest of data: subchunks.
|
18
18
|
class Chunk
|
19
|
+
prepend LazyRead
|
20
|
+
|
19
21
|
HEADER_SIZE = 8
|
20
22
|
HEADER_FORMAT = 'A4V'
|
21
23
|
HEADER_TYPE_SIZE = 4
|
22
24
|
|
23
25
|
attr_reader :id, :type
|
24
26
|
|
25
|
-
def initialize
|
26
|
-
@id, @size = file_io.read(HEADER_SIZE)&.unpack(HEADER_FORMAT)
|
27
|
+
def initialize
|
28
|
+
@id, @size = @file_io.read(HEADER_SIZE)&.unpack(HEADER_FORMAT)
|
27
29
|
return unless valid?
|
28
30
|
|
29
|
-
@type = file_io.read(HEADER_TYPE_SIZE).unpack('A4').first if have_type?
|
30
|
-
@file_io = file_io
|
31
|
-
@position = file_io.pos
|
31
|
+
@type = @file_io.read(HEADER_TYPE_SIZE).unpack('A4').first if have_type?
|
32
32
|
end
|
33
33
|
|
34
34
|
def size
|
@@ -36,11 +36,6 @@ module WahWah
|
|
36
36
|
have_type? ? @size - HEADER_TYPE_SIZE : @size
|
37
37
|
end
|
38
38
|
|
39
|
-
def data
|
40
|
-
@file_io.seek(@position)
|
41
|
-
@file_io.read(size)
|
42
|
-
end
|
43
|
-
|
44
39
|
def valid?
|
45
40
|
!@id.empty? && !@size.nil? && @size > 0
|
46
41
|
end
|
data/lib/wahwah/riff_tag.rb
CHANGED
@@ -69,7 +69,7 @@ module WahWah
|
|
69
69
|
when 'id3', 'ID3'
|
70
70
|
parse_id3_chunk(sub_chunk)
|
71
71
|
else
|
72
|
-
|
72
|
+
sub_chunk.skip
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
@@ -92,13 +92,13 @@ module WahWah
|
|
92
92
|
#
|
93
93
|
# 2(little endian) BitsPerSample 8 bits = 8, 16 bits = 16, etc.
|
94
94
|
def parse_fmt_chunk(chunk)
|
95
|
-
_, @channel, @sample_rate, _, _, @
|
96
|
-
@bitrate = @sample_rate * @channel * @
|
95
|
+
_, @channel, @sample_rate, _, _, @bit_depth = chunk.data.unpack('vvVVvv')
|
96
|
+
@bitrate = @sample_rate * @channel * @bit_depth / 1000
|
97
97
|
end
|
98
98
|
|
99
99
|
def parse_data_chunk(chunk)
|
100
100
|
@duration = chunk.size * 8 / (@bitrate * 1000)
|
101
|
-
|
101
|
+
chunk.skip
|
102
102
|
end
|
103
103
|
|
104
104
|
def parse_list_chunk(chunk)
|
@@ -107,13 +107,13 @@ module WahWah
|
|
107
107
|
# RIFF can be tagged with metadata in the INFO chunk.
|
108
108
|
# And INFO chunk as a subchunk for LIST chunk.
|
109
109
|
if chunk.type != 'INFO'
|
110
|
-
|
110
|
+
chunk.skip
|
111
111
|
else
|
112
112
|
until list_chunk_end_position <= @file_io.pos do
|
113
113
|
info_chunk = Riff::Chunk.new(@file_io)
|
114
114
|
|
115
115
|
unless INFO_ID_MAPPING.keys.include? info_chunk.id.to_sym
|
116
|
-
|
116
|
+
info_chunk.skip; next
|
117
117
|
end
|
118
118
|
|
119
119
|
update_attribute(info_chunk)
|
@@ -126,14 +126,14 @@ module WahWah
|
|
126
126
|
end
|
127
127
|
|
128
128
|
def update_attribute(chunk)
|
129
|
-
|
129
|
+
attr_name = INFO_ID_MAPPING[chunk.id.to_sym]
|
130
130
|
chunk_data = Helper.encode_to_utf8(chunk.data)
|
131
131
|
|
132
|
-
case
|
132
|
+
case attr_name
|
133
133
|
when :comment
|
134
134
|
@comments.push(chunk_data)
|
135
135
|
else
|
136
|
-
instance_variable_set("@#{
|
136
|
+
instance_variable_set("@#{attr_name}", chunk_data)
|
137
137
|
end
|
138
138
|
end
|
139
139
|
end
|
data/lib/wahwah/tag.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module WahWah
|
4
4
|
class Tag
|
5
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)
|
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
|
|
@@ -45,15 +45,24 @@ module WahWah
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
def parse
|
49
|
-
raise WahWahNotImplementedError, 'The parse method is not implemented'
|
50
|
-
end
|
51
|
-
|
52
48
|
def inspect
|
53
49
|
inspect_id = ::Kernel.format '%x', (object_id * 2)
|
54
50
|
inspect_attributes_values = INSPECT_ATTRIBUTES.map { |attr_name| "#{attr_name}=#{self.send(attr_name)}" }.join(' ')
|
55
51
|
|
56
52
|
"<#{self.class.name}:0x#{inspect_id} #{inspect_attributes_values}>"
|
57
53
|
end
|
54
|
+
|
55
|
+
def images
|
56
|
+
return @images_data if @images_data.empty?
|
57
|
+
|
58
|
+
@images_data.map do |data|
|
59
|
+
parse_image_data(data)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def parse
|
65
|
+
raise WahWahNotImplementedError, 'The parse method is not implemented'
|
66
|
+
end
|
58
67
|
end
|
59
68
|
end
|
data/lib/wahwah/version.rb
CHANGED
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.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- aidewoode
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-07-
|
11
|
+
date: 2020-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 0.1.17
|
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
|