apetag 1.1.4 → 1.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/apetag.rb +65 -43
- data/test/test_apetag.rb +161 -12
- metadata +33 -57
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 36144161c33f413d750ae832dbec6ba9612a0001
|
4
|
+
data.tar.gz: b9831a6a7e4b30aa0796a2a8fa9f0a229cdf6ce0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 45ed5df49bcd11636c6bf51d37c6eee643b283536850b2f91b3ef3459ba61cc162cf01f4da32252e36b86082ea654df292b4fc5043e5bf5b65da7d2bf5f04e07
|
7
|
+
data.tar.gz: 844c79409f01a86b9cf21becc3c62a5b643bb023314ea0c90ef08b8659f78f23cca193de5c84af87a5a0bc31297971411ed28e85086e15b40848e42460f35930
|
data/apetag.rb
CHANGED
@@ -69,7 +69,9 @@ class ApeItem < Array
|
|
69
69
|
return value
|
70
70
|
end
|
71
71
|
value = [value] unless value.is_a?(Array)
|
72
|
-
new(key, value)
|
72
|
+
item = new(key, value)
|
73
|
+
item.valid?
|
74
|
+
item
|
73
75
|
end
|
74
76
|
|
75
77
|
# Parse an ApeItem from the given data string starting at the provided offset.
|
@@ -84,12 +86,10 @@ class ApeItem < Array
|
|
84
86
|
raise ApeTagError, "Missing key-value separator at offset #{offset}" unless key_end
|
85
87
|
raise ApeTagError, "Invalid item length at offset #{offset}" if (next_item_start=length + key_end + 1) > data.length
|
86
88
|
begin
|
87
|
-
item = ApeItem.new_from_parse(data[offset...key_end], data[(key_end+1)...next_item_start].split("\0"))
|
89
|
+
item = ApeItem.new_from_parse(data[offset...key_end], data[(key_end+1)...next_item_start].split("\0"), flags)
|
88
90
|
rescue ArgumentError =>e
|
89
91
|
raise ApeTagError, "ArgumentError: #{e.message}"
|
90
92
|
end
|
91
|
-
item.read_only = flags & 1 > 0
|
92
|
-
item.ape_type = ITEM_TYPES[flags/2]
|
93
93
|
return [item, next_item_start]
|
94
94
|
end
|
95
95
|
|
@@ -101,7 +101,6 @@ class ApeItem < Array
|
|
101
101
|
self.read_only = false
|
102
102
|
self.ape_type = ITEM_TYPES[0]
|
103
103
|
super(value)
|
104
|
-
raise ApeTagError, "Invalid item value encoding (non UTF-8)" unless valid_value?
|
105
104
|
end
|
106
105
|
|
107
106
|
# Set ape_type if valid, otherwise raise ApeTagError.
|
@@ -162,14 +161,16 @@ class ApeItem < Array
|
|
162
161
|
# Check if the string value is valid UTF-8.
|
163
162
|
def valid_value?
|
164
163
|
begin
|
165
|
-
if
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
164
|
+
if ape_type == 'utf8' || ape_type == 'external'
|
165
|
+
if RUBY_VERSION >= '1.9'
|
166
|
+
begin
|
167
|
+
map!{|v| v.to_s.encode('UTF-8')}
|
168
|
+
rescue EncodingError
|
169
|
+
return false
|
170
|
+
end
|
170
171
|
end
|
172
|
+
string_value.unpack('U*')
|
171
173
|
end
|
172
|
-
string_value.unpack('U*') if ape_type == 'utf8' || ape_type == 'external'
|
173
174
|
rescue ArgumentError
|
174
175
|
false
|
175
176
|
else
|
@@ -177,11 +178,26 @@ class ApeItem < Array
|
|
177
178
|
end
|
178
179
|
end
|
179
180
|
|
180
|
-
|
181
|
-
|
182
|
-
|
181
|
+
def self.new_from_parse(key, value, flags)
|
182
|
+
ape_type = flags/2
|
183
|
+
if RUBY_VERSION >= '1.9.0'
|
184
|
+
key.force_encoding('US-ASCII')
|
185
|
+
case ape_type
|
186
|
+
when 0, 2
|
187
|
+
value = value.map{|v| v.to_s.force_encoding('UTF-8')}
|
188
|
+
when 1
|
189
|
+
value = value.map{|v| v.to_s.force_encoding('BINARY')}
|
190
|
+
end
|
183
191
|
end
|
192
|
+
value = [''] if value.empty?
|
193
|
+
item = new(key, value)
|
194
|
+
item.read_only = flags & 1 > 0
|
195
|
+
item.ape_type = ITEM_TYPES[ape_type]
|
196
|
+
raise ApeTagError, "Invalid item value encoding (non UTF-8)" unless item.valid_value?
|
197
|
+
item
|
198
|
+
end
|
184
199
|
|
200
|
+
if RUBY_VERSION >= '1.9.0'
|
185
201
|
def encoded_key(key)
|
186
202
|
begin
|
187
203
|
key.encode('US-ASCII')
|
@@ -191,14 +207,12 @@ class ApeItem < Array
|
|
191
207
|
end
|
192
208
|
|
193
209
|
def normalize_encodings
|
194
|
-
map!{|v| v.to_s.encode('UTF-8')}
|
210
|
+
map!{|v| v.to_s.encode('UTF-8')} if ape_type == 'utf8' || ape_type == 'external'
|
195
211
|
self
|
212
|
+
rescue Encoding::UndefinedConversionError => e
|
213
|
+
raise ApeTagError, "#{e.class}: #{e.message}"
|
196
214
|
end
|
197
215
|
else
|
198
|
-
def self.new_from_parse(key, value)
|
199
|
-
new(key, value)
|
200
|
-
end
|
201
|
-
|
202
216
|
def encoded_key(key)
|
203
217
|
key
|
204
218
|
end
|
@@ -242,7 +256,7 @@ class ApeTag
|
|
242
256
|
Goa, Drum & Bass, Club-House, Hardcore, Terror, Indie, BritPop, Negerpunk,
|
243
257
|
Polsk Punk, Beat, Christian Gangsta Rap, Heavy Metal, Black Metal,
|
244
258
|
Crossover, Contemporary Christian, Christian Rock, Merengue, Salsa,
|
245
|
-
|
259
|
+
Thrash Metal, Anime, Jpop, Synthpop'.split(',').collect{|g| g.strip}
|
246
260
|
ID3_GENRES_HASH = CICPHash.new(255.chr)
|
247
261
|
ID3_GENRES.each_with_index{|g,i| ID3_GENRES_HASH[g] = i.chr }
|
248
262
|
FILE_OBJ_METHODS = %w'close seek read pos write truncate'
|
@@ -275,7 +289,7 @@ class ApeTag
|
|
275
289
|
@check_id3 = check_id3.nil? ? @@check_id3 : check_id3
|
276
290
|
else
|
277
291
|
@filename = filename.to_s
|
278
|
-
@check_id3 = check_id3.nil? ?
|
292
|
+
@check_id3 = check_id3.nil? ? (MP3_RE.match(@filename) ? true : nil) : check_id3
|
279
293
|
end
|
280
294
|
end
|
281
295
|
|
@@ -284,10 +298,16 @@ class ApeTag
|
|
284
298
|
@has_tag.nil? ? access_file('rb'){has_tag} : @has_tag
|
285
299
|
end
|
286
300
|
|
301
|
+
# Check the file for an ID3 tag. Returns true or false. Raises ApeTagError for corrupt tags.
|
302
|
+
def has_id3?
|
303
|
+
exists?
|
304
|
+
id3 != ''
|
305
|
+
end
|
306
|
+
|
287
307
|
# Remove an APE tag from a file, if one exists.
|
288
308
|
# Returns true. Raises ApeTagError for corrupt tags.
|
289
309
|
def remove!
|
290
|
-
access_file('rb+'){file.truncate(tag_start) if has_tag}
|
310
|
+
access_file('rb+'){file.truncate(tag_start) if has_tag || has_id3?}
|
291
311
|
@has_tag, @fields, @id3, @tag_size, @tag_start, @tag_data, @tag_header, @tag_footer, @tag_item_count = []
|
292
312
|
true
|
293
313
|
end
|
@@ -302,8 +322,8 @@ class ApeTag
|
|
302
322
|
def pretty_print
|
303
323
|
begin
|
304
324
|
fields.values.sort_by{|value| value.key}.collect{|value| "#{value.key}: #{value.join(', ')}"}.join("\n")
|
305
|
-
rescue ApeTagError
|
306
|
-
"CORRUPT TAG
|
325
|
+
rescue ApeTagError => e
|
326
|
+
"CORRUPT TAG!: #{e.message}"
|
307
327
|
rescue Errno::ENOENT, Errno::EINVAL
|
308
328
|
"FILE NOT FOUND!"
|
309
329
|
end
|
@@ -464,27 +484,29 @@ class ApeTag
|
|
464
484
|
# If the file doesn't have an ID3 and the file already has an APE tag or
|
465
485
|
# check_id3 is not set, an ID3 won't be added.
|
466
486
|
def update_id3
|
467
|
-
return if id3.
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
487
|
+
return if id3.empty? && has_tag
|
488
|
+
if !id3.empty? || check_id3
|
489
|
+
id3_fields = CICPHash.new('')
|
490
|
+
id3_fields['genre'] = 255.chr
|
491
|
+
fields.values.each do |value|
|
492
|
+
case value.key
|
493
|
+
when /\Atrack/i
|
494
|
+
id3_fields['track'] = value.string_value.to_i
|
495
|
+
id3_fields['track'] = 0 if id3_fields['track'] > 255
|
496
|
+
id3_fields['track'] = id3_fields['track'].chr
|
497
|
+
when /\Agenre/i
|
498
|
+
id3_fields['genre'] = ID3_GENRES_HASH[value.first]
|
499
|
+
when /\Adate\z/i
|
500
|
+
match = YEAR_RE.match(value.string_value)
|
501
|
+
id3_fields['year'] = match[0] if match
|
502
|
+
when /\A(title|artist|album|year|comment)\z/i
|
503
|
+
id3_fields[value.key] = value.join(', ')
|
504
|
+
end
|
483
505
|
end
|
506
|
+
@id3 = ["TAG", id3_fields['title'], id3_fields['artist'], id3_fields['album'],
|
507
|
+
id3_fields['year'], id3_fields['comment'], "\0", id3_fields['track'],
|
508
|
+
id3_fields['genre']].pack("a3a30a30a30a4a28a1a1a1")
|
484
509
|
end
|
485
|
-
@id3 = ["TAG", id3_fields['title'], id3_fields['artist'], id3_fields['album'],
|
486
|
-
id3_fields['year'], id3_fields['comment'], "\0", id3_fields['track'],
|
487
|
-
id3_fields['genre']].pack("a3a30a30a30a4a28a1a1a1")
|
488
510
|
end
|
489
511
|
|
490
512
|
# Write the APEv2 and ID3v1.1 tags to disk.
|
data/test/test_apetag.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'rubygems'
|
3
3
|
require 'apetag'
|
4
|
-
require 'stringio'
|
5
4
|
require 'test/unit'
|
5
|
+
require 'stringio'
|
6
|
+
require 'fileutils'
|
6
7
|
|
7
8
|
EMPTY_APE_TAG = "APETAGEX\320\a\0\0 \0\0\0\0\0\0\0\0\0\0\240\0\0\0\0\0\0\0\0APETAGEX\320\a\0\0 \0\0\0\0\0\0\0\0\0\0\200\0\0\0\0\0\0\0\0TAG\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377"
|
8
9
|
EXAMPLE_APE_TAG = "APETAGEX\xd0\x07\x00\x00\xb0\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00Track\x001\x04\x00\x00\x00\x00\x00\x00\x00Date\x002007\t\x00\x00\x00\x00\x00\x00\x00Comment\x00XXXX-0000\x0b\x00\x00\x00\x00\x00\x00\x00Title\x00Love Cheese\x0b\x00\x00\x00\x00\x00\x00\x00Artist\x00Test Artist\x16\x00\x00\x00\x00\x00\x00\x00Album\x00Test Album\x00Other AlbumAPETAGEX\xd0\x07\x00\x00\xb0\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00TAGLove Cheese\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Test Artist\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Test Album, Other Album\x00\x00\x00\x00\x00\x00\x002007XXXX-0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff"
|
@@ -13,19 +14,169 @@ EXAMPLE_APE_FIELDS = {"Track"=>["1"], "Comment"=>["XXXX-0000"], "Album"=>["Test
|
|
13
14
|
EXAMPLE_APE_FIELDS2 = {"Blah"=>["Blah"], "Comment"=>["XXXX-0000"], "Album"=>["Test Album", "Other Album"], "Artist"=>["Test Artist"], "Date"=>["2007"]}
|
14
15
|
EXAMPLE_APE_TAG_PRETTY_PRINT = "Album: Test Album, Other Album\nArtist: Test Artist\nComment: XXXX-0000\nDate: 2007\nTitle: Love Cheese\nTrack: 1"
|
15
16
|
|
16
|
-
class
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
class ApeTagTest < Test::Unit::TestCase
|
18
|
+
def binary(str)
|
19
|
+
str.force_encoding('BINARY') if str.respond_to?(:force_encoding)
|
20
|
+
str
|
21
|
+
end
|
22
|
+
|
23
|
+
def utf8(str)
|
24
|
+
str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
|
25
|
+
str
|
26
|
+
end
|
27
|
+
|
28
|
+
def tagname(name)
|
29
|
+
"../test-files/#{name}#{'.tag' unless name =~ /\./}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def tag(name)
|
33
|
+
ApeTag.new(tagname(name))
|
34
|
+
end
|
35
|
+
|
36
|
+
def assert_apetag_raised(name, msg)
|
37
|
+
yield tag(name)
|
38
|
+
rescue ApeTagError => e
|
39
|
+
assert(e.message.include?(msg), "Expected: #{msg.inspect}, received: #{e.message.inspect}")
|
21
40
|
else
|
22
|
-
|
23
|
-
|
41
|
+
assert(false, "#{name} did not raise ApeTagError: #{msg}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def corrupt(name, msg)
|
45
|
+
assert_apetag_raised(name, msg){|tag| tag.fields}
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_test_file(before, opts={})
|
49
|
+
temp_name = tagname(opts[:name] || 'test')
|
50
|
+
FileUtils.copy(tagname(before), temp_name)
|
51
|
+
yield temp_name
|
52
|
+
File.delete(temp_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def assert_files_equal(before, after, opts={})
|
56
|
+
with_test_file(before, opts) do |temp_name|
|
57
|
+
yield(opts[:yield] == :name ? temp_name : ApeTag.new(temp_name))
|
58
|
+
assert_equal(File.read(tagname(after)), File.read(temp_name))
|
24
59
|
end
|
25
60
|
end
|
26
|
-
end
|
27
61
|
|
28
|
-
|
62
|
+
def test_corrupt
|
63
|
+
corrupt("corrupt-count-larger-than-possible", "Item count (1) is larger than possible")
|
64
|
+
corrupt("corrupt-count-mismatch", "Header and footer item count does not match")
|
65
|
+
corrupt("corrupt-count-over-max-allowed", "Item count (97) is larger than 64")
|
66
|
+
corrupt("corrupt-data-remaining", "Data remaining after specified number of items parsed")
|
67
|
+
corrupt("corrupt-duplicate-item-key", "Multiple items with same key (\"name\")")
|
68
|
+
corrupt("corrupt-finished-without-parsing-all-items", "End of tag reached but more items specified")
|
69
|
+
corrupt("corrupt-footer-flags", "Tag has bad footer flags")
|
70
|
+
corrupt("corrupt-header", "Missing header")
|
71
|
+
corrupt("corrupt-item-flags-invalid", "Invalid item flags at offset 0")
|
72
|
+
corrupt("corrupt-item-length-invalid", "Invalid item length at offset 0")
|
73
|
+
corrupt("corrupt-key-invalid", "Invalid APE key")
|
74
|
+
corrupt("corrupt-key-too-short", "Invalid APE key")
|
75
|
+
corrupt("corrupt-key-too-long", "Invalid APE key")
|
76
|
+
corrupt("corrupt-min-size", "Tag size (57) smaller than minimum size")
|
77
|
+
corrupt("corrupt-missing-key-value-separator", "Missing key-value separator at offset 8")
|
78
|
+
corrupt("corrupt-next-start-too-large", "Invalid item length at offset 8")
|
79
|
+
corrupt("corrupt-size-larger-than-possible", "Tag size (65) larger than possible")
|
80
|
+
corrupt("corrupt-size-mismatch", "Header and footer size does not match")
|
81
|
+
corrupt("corrupt-size-over-max-allowed", "Tag size (61504) larger than possible")
|
82
|
+
corrupt("corrupt-value-not-utf8", "Invalid item value encoding (non UTF-8)")
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_exists?
|
86
|
+
assert_equal(false, tag("missing-ok").exists?)
|
87
|
+
assert_equal(true, tag("good-empty").exists?)
|
88
|
+
assert_equal(false, tag("good-empty-id3-only").exists?)
|
89
|
+
assert_equal(true, tag("good-empty-id3").exists?)
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_has_id3?
|
93
|
+
assert_equal(false, tag("missing-ok").has_id3?)
|
94
|
+
assert_equal(false, tag("good-empty").has_id3?)
|
95
|
+
assert_equal(true, tag("good-empty-id3-only").has_id3?)
|
96
|
+
assert_equal(true, tag("good-empty-id3").has_id3?)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_parsing
|
100
|
+
assert_equal({}, tag("good-empty").fields)
|
101
|
+
assert_equal({'name'=>['value']}, tag("good-simple-1").fields)
|
102
|
+
assert_equal(['value'], tag("good-simple-1").fields['Name'])
|
103
|
+
|
104
|
+
assert_equal(63, tag("good-many-items").fields.size)
|
105
|
+
assert_equal([''], tag("good-many-items").fields['0n'])
|
106
|
+
assert_equal(['a'], tag("good-many-items").fields['1n'])
|
107
|
+
assert_equal(['a' * 62], tag("good-many-items").fields['62n'])
|
108
|
+
|
109
|
+
assert_equal({'name'=>['va', 'ue']}, tag("good-multiple-values").fields)
|
110
|
+
|
111
|
+
assert_equal('name', tag("good-simple-1").fields['name'].key)
|
112
|
+
assert_equal('utf8', tag("good-simple-1").fields['name'].ape_type)
|
113
|
+
assert_equal(false, tag("good-simple-1").fields['name'].read_only)
|
114
|
+
|
115
|
+
assert_equal('name', tag("good-simple-1-ro-external").fields['name'].key)
|
116
|
+
assert_equal(['value'], tag("good-simple-1-ro-external").fields['name'])
|
117
|
+
assert_equal('external', tag("good-simple-1-ro-external").fields['name'].ape_type)
|
118
|
+
assert_equal(true, tag("good-simple-1-ro-external").fields['name'].read_only)
|
119
|
+
|
120
|
+
assert_equal('name', tag("good-binary-non-utf8-value").fields['name'].key)
|
121
|
+
assert_equal(binary("v\x81lue"), tag("good-binary-non-utf8-value").fields['name'][0])
|
122
|
+
assert_equal('binary', tag("good-binary-non-utf8-value").fields['name'].ape_type)
|
123
|
+
assert_equal(false, tag("good-binary-non-utf8-value").fields['name'].read_only)
|
124
|
+
|
125
|
+
assert_equal({'name'=>['value']}, ApeTag.new(File.open(tagname("good-simple-1"), 'rb')).fields)
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_remove!
|
129
|
+
assert_files_equal('good-empty', 'missing-ok'){|tag| tag.remove!}
|
130
|
+
assert_files_equal('good-empty-id3', 'missing-ok'){|tag| tag.remove!}
|
131
|
+
assert_files_equal('good-empty-id3-only', 'missing-ok'){|tag| tag.remove!}
|
132
|
+
assert_files_equal('missing-10k', 'missing-10k'){|tag| tag.remove!}
|
133
|
+
assert_files_equal('good-empty-id3', 'missing-ok', :yield=>:name){|temp_name| ApeTag.new(File.open(temp_name, 'rb+')).remove!}
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_update
|
137
|
+
assert_files_equal('good-empty', 'good-empty'){|tag| tag.update{|f|}}
|
138
|
+
assert_files_equal('missing-ok', 'good-empty'){|tag| tag.update{|f|}}
|
139
|
+
assert_files_equal('good-empty', 'good-simple-1'){|tag| tag.update{|f| f['name'] = 'value'}}
|
140
|
+
assert_files_equal('good-simple-1', 'good-empty'){|tag| tag.update{|f| f.delete('name')}}
|
141
|
+
assert_files_equal('good-simple-1', 'good-empty'){|tag| tag.update{|f| f.delete('Name')}}
|
142
|
+
assert_files_equal('good-empty', 'good-simple-1-ro-external'){|tag| tag.update{|f| ai = ApeItem.new('name', ['value']); ai.read_only = true; ai.ape_type = 'external'; f['name'] = ai}}
|
143
|
+
assert_files_equal('good-empty', 'good-binary-non-utf8-value'){|tag| tag.update{|f| ai = ApeItem.new('name', [binary("v\x81lue")]); ai.ape_type = 'binary'; f['name'] = ai}}
|
144
|
+
assert_files_equal('good-empty', 'good-many-items'){|tag| tag.update{|f| 63.times{|i| f["#{i}n"] = "a" * i}}}
|
145
|
+
assert_files_equal('good-empty', 'good-multiple-values'){|tag| tag.update{|f| f['name'] = ['va', 'ue']}}
|
146
|
+
assert_files_equal('good-multiple-values', 'good-simple-1-uc'){|tag| tag.update{|f| f['NAME'] = 'value'}}
|
147
|
+
assert_files_equal('good-empty', 'good-simple-1-utf8'){|tag| tag.update{|f| f['name'] = [utf8("v\xc3\x82\xc3\x95")]}}
|
148
|
+
|
149
|
+
assert_apetag_raised('good-empty', 'Updated tag has too many items (65)'){|tag| tag.update{|f| 65.times{|i| f["#{i}n"] = "a" * i}}}
|
150
|
+
assert_apetag_raised('good-empty', 'Updated tag too large (8193)'){|tag| tag.update{|f| f['xn'] = "a" * 8118}}
|
151
|
+
assert_apetag_raised('good-empty', 'Invalid APE key'){|tag| tag.update{|f| f['n'] = "a"}}
|
152
|
+
assert_apetag_raised('good-empty', 'Invalid APE key'){|tag| tag.update{|f| f['n' * 256] = "a"}}
|
153
|
+
assert_apetag_raised('good-empty', 'Invalid APE key'){|tag| tag.update{|f| f["n\0"] = "a"}}
|
154
|
+
assert_apetag_raised('good-empty', 'Invalid APE key'){|tag| tag.update{|f| f["n\x1f"] = "a"}}
|
155
|
+
assert_apetag_raised('good-empty', 'Invalid APE key'){|tag| tag.update{|f| f[binary("n\x80")] = "a"}}
|
156
|
+
assert_apetag_raised('good-empty', 'Invalid APE key'){|tag| tag.update{|f| f[binary("n\xff")] = "a"}}
|
157
|
+
assert_apetag_raised('good-empty', 'Invalid APE key'){|tag| tag.update{|f| f["tag"] = "a"}}
|
158
|
+
assert_apetag_raised('good-empty', 'Invalid key, value, APE type, or Read-Only Flag'){|tag| tag.update{|f| f["ab"] = utf8("v\xff")}}
|
159
|
+
assert_apetag_raised('good-empty', 'Invalid APE type'){|tag| tag.update{|f| ai = ApeItem.new('name', [binary("v\x81lue")]); ai.ape_type = 'foo'; f['name'] = ai}}
|
160
|
+
|
161
|
+
assert_files_equal('good-empty', 'good-simple-1', :yield=>:name){|temp_name| ApeTag.new(File.open(temp_name, 'rb+'), false).update{|f| f["name"] = 'value'}}
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_id3
|
165
|
+
assert_files_equal('missing-ok', 'good-empty'){|tag| tag.update{|f|}}
|
166
|
+
assert_files_equal('missing-ok', 'good-empty-id3', :yield=>:name){|temp_name| ApeTag.new(temp_name, true).update{|f|}}
|
167
|
+
assert_files_equal('missing-ok', 'good-empty-id3', :name=>'test.mp3'){|tag| tag.update{|f|}}
|
168
|
+
assert_files_equal('missing-ok', 'good-empty', :name=>'test.mp3', :yield=>:name){|temp_name| ApeTag.new(temp_name, false).update{|f|}}
|
169
|
+
|
170
|
+
assert_files_equal('good-empty-id3-only', 'good-empty-id3'){|tag| tag.update{|f|}}
|
171
|
+
assert_files_equal('good-empty-id3', 'good-simple-4'){|tag| tag.update{|f| f.merge!('track'=>'1', 'genre'=>'Game', 'year'=>'1999', 'title'=>'Test Title', 'artist'=>'Test Artist', 'album'=>'Test Album', 'comment'=>'Test Comment')}}
|
172
|
+
assert_files_equal('good-empty-id3', 'good-simple-4-uc'){|tag| tag.update{|f| f.merge!('Track'=>'1', 'Genre'=>'Game', 'Year'=>'1999', 'Title'=>'Test Title', 'Artist'=>'Test Artist', 'Album'=>'Test Album', 'Comment'=>'Test Comment')}}
|
173
|
+
assert_files_equal('good-empty-id3', 'good-simple-4-date'){|tag| tag.update{|f| f.merge!('track'=>'1', 'genre'=>'Game', 'date'=>'12/31/1999', 'title'=>'Test Title', 'artist'=>'Test Artist', 'album'=>'Test Album', 'comment'=>'Test Comment')}}
|
174
|
+
assert_files_equal('good-empty-id3', 'good-simple-4-long'){|tag| tag.update{|f| f.merge!('track'=>'1', 'genre'=>'Game', 'year'=>'1999'*2, 'title'=>'Test Title'*5, 'artist'=>'Test Artist'*5, 'album'=>'Test Album'*5, 'comment'=>'Test Comment'*5)}}
|
175
|
+
|
176
|
+
assert_equal(false, ApeTag.new(tagname('good-empty-id3'), false).exists?)
|
177
|
+
assert_equal(false, ApeTag.new(tagname('good-empty-id3'), false).has_id3?)
|
178
|
+
end
|
179
|
+
|
29
180
|
def get_ape_tag(f, check_id3)
|
30
181
|
f.is_a?(ApeTag) ? f : ApeTag.new(f, check_id3)
|
31
182
|
end
|
@@ -165,8 +316,6 @@ class ApeTagTest < Test::Unit::TestCase
|
|
165
316
|
|
166
317
|
# Test create fails with invalid key
|
167
318
|
assert_raises(ApeTagError){ApeItem.create('', ai)}
|
168
|
-
# Test create fails with invalid UTF-8 value
|
169
|
-
assert_raises(ApeTagError){ApeItem.create('xx',["\xfe"])}
|
170
319
|
# Test create doesn't fail with valid UTF-8 value
|
171
320
|
assert_nothing_raised{ApeItem.create('xx',[[12345, 1345].pack('UU')])}
|
172
321
|
|
metadata
CHANGED
@@ -1,83 +1,59 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: apetag
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease: false
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 1
|
9
|
-
- 4
|
10
|
-
version: 1.1.4
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.5
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- Jeremy Evans
|
14
8
|
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
11
|
+
date: 2013-02-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
22
14
|
name: cicphash
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 23
|
30
|
-
segments:
|
31
|
-
- 1
|
32
|
-
- 0
|
33
|
-
- 0
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
34
19
|
version: 1.0.0
|
35
20
|
type: :runtime
|
36
|
-
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.0
|
37
27
|
description:
|
38
28
|
email: code@jeremyevans.net
|
39
29
|
executables: []
|
40
|
-
|
41
30
|
extensions: []
|
42
|
-
|
43
31
|
extra_rdoc_files: []
|
44
|
-
|
45
|
-
files:
|
32
|
+
files:
|
46
33
|
- apetag.rb
|
47
34
|
- test/test_apetag.rb
|
48
|
-
has_rdoc: true
|
49
35
|
homepage: http://apetag.rubyforge.org
|
50
36
|
licenses: []
|
51
|
-
|
37
|
+
metadata: {}
|
52
38
|
post_install_message:
|
53
39
|
rdoc_options: []
|
54
|
-
|
55
|
-
require_paths:
|
40
|
+
require_paths:
|
56
41
|
- .
|
57
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
none: false
|
68
|
-
requirements:
|
69
|
-
- - ">="
|
70
|
-
- !ruby/object:Gem::Version
|
71
|
-
hash: 3
|
72
|
-
segments:
|
73
|
-
- 0
|
74
|
-
version: "0"
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
75
52
|
requirements: []
|
76
|
-
|
77
53
|
rubyforge_project: apetag
|
78
|
-
rubygems_version:
|
54
|
+
rubygems_version: 2.0.0
|
79
55
|
signing_key:
|
80
|
-
specification_version:
|
56
|
+
specification_version: 4
|
81
57
|
summary: APEv2 Tag Reader/Writer
|
82
|
-
test_files:
|
58
|
+
test_files:
|
83
59
|
- test/test_apetag.rb
|