mp3info 0.6.17
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.
- data/.gitignore +12 -0
- data/History.txt +153 -0
- data/README.rdoc +68 -0
- data/lib/mp3info.rb +714 -0
- data/lib/mp3info/extension_modules.rb +46 -0
- data/lib/mp3info/id3v2.rb +437 -0
- data/mp3info.gemspec +17 -0
- data/test/fixtures.yml +86 -0
- data/test/test_ruby-mp3info.rb +572 -0
- metadata +76 -0
data/mp3info.gemspec
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = 'mp3info'
|
|
5
|
+
s.version = '0.6.17'
|
|
6
|
+
s.summary = %q{Read low-level informations and manipulate tags on mp3 files.}
|
|
7
|
+
s.homepage = "http://github.com/toy/#{s.name}"
|
|
8
|
+
s.authors = ['Guillaume Pierronnet', 'Ivan Kuchin']
|
|
9
|
+
s.license = 'ruby'
|
|
10
|
+
|
|
11
|
+
s.rubyforge_project = s.name
|
|
12
|
+
|
|
13
|
+
s.files = `git ls-files`.split("\n")
|
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
16
|
+
s.require_paths = %w[lib]
|
|
17
|
+
end
|
data/test/fixtures.yml
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
empty_mp3: !binary |
|
|
2
|
+
eJz7/3tCCgP/BwaGTAYGBg4GBl4FBgZGIFoC5AKZJg0MDCw+jr6uxnqW5qEj
|
|
3
|
+
EAy43///npSyrx9/BA2Y4wYDGBQx9H80hnCD0Rga7GA0hkYBAQAA8NuaUA==
|
|
4
|
+
2_2_tagged: !binary |
|
|
5
|
+
eNrtlrlOw0AQhn8QZ4E4CioK90jO2k58dGxs5xBxYiWbgiqKiAUWSmLZBFEi
|
|
6
|
+
XgDEM/AcPBN9imDHOBEIEB1H9tMW/8zO7szqb7ZqKcuI2T1gTAbWUR1chkMw
|
|
7
|
+
VwL2UQ69fiTQi67QGgVe2PdPkz1aAzZB/VBwutdgzWNgDVJO0uJjFFhNNNiJ
|
|
8
|
+
naRlQlQwswFsoeIHQmUY5JrdAGbDAXbgDc58Nhq4ZeqCAMyuA3tIUl4kXGki
|
|
9
|
+
EWVRUtPq86y63mg6EEiKlQnysZBmmdJ8y6ZToduKNK1R84oxE2m326xby3GL
|
|
10
|
+
eHe3LL0Kw5h3I6SgFaj6pr38nRE/EekcR9kcXtQxLavYqVoRJO1QpqZVMqgt
|
|
11
|
+
23aeqKqu5PXkRfNhdEUr6LIGDofD4XD+KpPxfQ/bz4AfBxvxX0IAluL1GIex
|
|
12
|
+
zN8AKzXq2IpoaO0F5MffPhk/9J7uvjaovcj8Cocm3CHuEHeIO/RveQFv4P/e
|
|
13
|
+
vbr: !binary |
|
|
14
|
+
eNrt0F1k1XEYB/DnWHI0x0kiTTIzZqyRTS8jlV4kHSMdLdJFjuaQKYmMlCUm
|
|
15
|
+
siSbSBcZqbGNGYnpIimzRBKJLpKYmJFIU6f/uegyZ1f/yOfr4fc8PHwev8rS
|
|
16
|
+
jVLUSE+5rzd58hENuVg1PB91K7P1ufyatevWb9jY1NzS2ta+uWPLtq4dO/fs
|
|
17
|
+
P3Cw0H34yNFjx0+cLPWWT/edPXf+Qv/FS1euDl67PnTz1sjtO3fvjd5/MDY+
|
|
18
|
+
OTX96PHMk6fPXsy+fPX6zdt37z98/PR5/svC4tdv33/8TLyuQ7sL+zrat29t
|
|
19
|
+
rJuonrHpV0TnmebsrkJk5qp3DGTGn8f/nsrS6lLkFyPKyZCNyDVGZJIaTcak
|
|
20
|
+
7bwcseLPRxX/lpoLxWLVaarhFGtlmc7elJxTKTmDKTkPU3LmUnIWUnIqQxwO
|
|
21
|
+
h8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8Ph
|
|
22
|
+
cDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgc
|
|
23
|
+
DofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD
|
|
24
|
+
4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4
|
|
25
|
+
HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6H
|
|
26
|
+
w+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+Fw
|
|
27
|
+
OBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwO
|
|
28
|
+
h8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8Ph
|
|
29
|
+
cDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgc
|
|
30
|
+
DofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD
|
|
31
|
+
4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4
|
|
32
|
+
HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6H
|
|
33
|
+
w+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+Fw
|
|
34
|
+
OBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwO
|
|
35
|
+
h8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8Ph
|
|
36
|
+
cDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgc
|
|
37
|
+
DofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD
|
|
38
|
+
4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4
|
|
39
|
+
HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6H
|
|
40
|
+
w+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+Fw
|
|
41
|
+
OBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwO
|
|
42
|
+
h8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8Ph
|
|
43
|
+
cDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgc
|
|
44
|
+
DofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD
|
|
45
|
+
4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4
|
|
46
|
+
HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6H
|
|
47
|
+
w+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+Fw
|
|
48
|
+
OBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwO
|
|
49
|
+
h8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8Ph
|
|
50
|
+
cDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgc
|
|
51
|
+
DofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD
|
|
52
|
+
4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4
|
|
53
|
+
HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6H
|
|
54
|
+
w+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+Fw
|
|
55
|
+
OBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwO
|
|
56
|
+
h8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8Ph
|
|
57
|
+
cDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgc
|
|
58
|
+
DofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD
|
|
59
|
+
4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4
|
|
60
|
+
HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6H
|
|
61
|
+
w+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+Fw
|
|
62
|
+
OBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwO
|
|
63
|
+
h8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8Ph
|
|
64
|
+
cDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgc
|
|
65
|
+
DofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD
|
|
66
|
+
4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4
|
|
67
|
+
HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6H
|
|
68
|
+
w+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+Fw
|
|
69
|
+
OBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwO
|
|
70
|
+
h8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8Ph
|
|
71
|
+
cDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgc
|
|
72
|
+
DofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD4XA4HA6Hw+FwOBwOh8PhcDgcDofD
|
|
73
|
+
4XA4HM4/dJYRDidN5zc6LQlL
|
|
74
|
+
audio_content_fixture: !binary |
|
|
75
|
+
SUQzAwAAAAABDFRBTEIAAAAnAAAB//5SAG8AZAByAGkAZwBvACAAeQAgAEcA
|
|
76
|
+
YQBiAHIAaQBlAGwAYQBUUkNLAAAABQAAAf/+MQBUSVQyAAAAEQAAAf/+VABh
|
|
77
|
+
AG0AYQBjAHUAbgBUUEUxAAAAJwAAAf/+UgBvAGQAcgBpAGcAbwAgAHkAIABH
|
|
78
|
+
AGEAYgByAGkAZQBsAGEA//uQZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
79
|
+
AAAAAAAAWGluZwAAAA8AAB7IAFw7mAADBQgKDQ8SFBcZHB4gIyUoKi4wMzU4
|
|
80
|
+
Oj1AQkVIS01QUlVYW11gYmRoam1vcnR3en1/gYSGiYuOkZSWmZueoKKmqKut
|
|
81
|
+
sLO2uLu9wMLFyMrNz9LU2Nrd3+Pl5+rs7/H19/r8//8AAABOTEFNRTMuOTAu
|
|
82
|
+
A74AAAAAAAAAADSAJAV8TQAAAABcO5gcQelNAAAAAAAAAAAAAAAAAAAAAAAA
|
|
83
|
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
84
|
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
85
|
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
86
|
+
AAAAAAAAAAAAAAAAAAAAAAA=
|
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
#!/usr/bin/env ruby1.9
|
|
2
|
+
# coding:utf-8
|
|
3
|
+
|
|
4
|
+
$:.unshift("lib/")
|
|
5
|
+
|
|
6
|
+
require "test/unit"
|
|
7
|
+
require "mp3info"
|
|
8
|
+
require "fileutils"
|
|
9
|
+
require "tempfile"
|
|
10
|
+
require "zlib"
|
|
11
|
+
require "yaml"
|
|
12
|
+
|
|
13
|
+
system("which id3v2 > /dev/null") || raise("id3v2 not found")
|
|
14
|
+
|
|
15
|
+
class Mp3InfoTest < Test::Unit::TestCase
|
|
16
|
+
|
|
17
|
+
TEMP_FILE = File.join(File.dirname(__FILE__), "test_mp3info.mp3")
|
|
18
|
+
|
|
19
|
+
DUMMY_TAG2 = {
|
|
20
|
+
"COMM" => "comments",
|
|
21
|
+
#"TCON" => "genre_s"
|
|
22
|
+
"TIT2" => "title",
|
|
23
|
+
"TPE1" => "artist",
|
|
24
|
+
"TALB" => "album",
|
|
25
|
+
"TYER" => "year",
|
|
26
|
+
"TRCK" => "tracknum"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
DUMMY_TAG1 = {
|
|
30
|
+
"title" => "toto",
|
|
31
|
+
"artist" => "artist 123",
|
|
32
|
+
"album" => "ALBUMM",
|
|
33
|
+
"year" => 1934,
|
|
34
|
+
"tracknum" => 14,
|
|
35
|
+
"comments" => "comment me",
|
|
36
|
+
"genre" => 233
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
FIXTURES = YAML::load_file( File.join(File.dirname(__FILE__), "fixtures.yml") )
|
|
40
|
+
|
|
41
|
+
def setup
|
|
42
|
+
# Command to create a gzip'ed dummy MP3
|
|
43
|
+
# $ dd if=/dev/zero bs=1024 count=15 | \
|
|
44
|
+
# lame --quiet --preset cbr 128 -r -s 44.1 --bitwidth 16 - - | \
|
|
45
|
+
# ruby -rbase64 -rzlib -ryaml -e 'print(Zlib::Deflate.deflate($stdin.read)'
|
|
46
|
+
# vbr:
|
|
47
|
+
# $ dd if=/dev/zero of=#{tempfile.path} bs=1024 count=30000 |
|
|
48
|
+
# system("lame -h -v -b 112 -r -s 44.1 --bitwidth 16 - /tmp/vbr.mp3
|
|
49
|
+
@valid_mp3, @valid_mp3_2_2, @vbr_mp3 = %w{empty_mp3 2_2_tagged vbr}.collect do |fixture_key|
|
|
50
|
+
Zlib::Inflate.inflate(FIXTURES[fixture_key])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
@tag = {
|
|
54
|
+
"title" => "title",
|
|
55
|
+
"artist" => "artist",
|
|
56
|
+
"album" => "album",
|
|
57
|
+
"year" => 1921,
|
|
58
|
+
"comments" => "comments",
|
|
59
|
+
"genre" => 0,
|
|
60
|
+
"genre_s" => "Blues",
|
|
61
|
+
"tracknum" => 36
|
|
62
|
+
}
|
|
63
|
+
File.open(TEMP_FILE, "w") { |f| f.write(@valid_mp3) }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def teardown
|
|
67
|
+
FileUtils.rm_f(TEMP_FILE)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_to_s
|
|
71
|
+
Mp3Info.open(TEMP_FILE) { |info| assert(info.to_s.is_a?(String)) }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_not_an_mp3
|
|
75
|
+
File.open(TEMP_FILE, "w") do |f|
|
|
76
|
+
str = "0"*1024*1024
|
|
77
|
+
f.write(str)
|
|
78
|
+
end
|
|
79
|
+
assert_raises(Mp3InfoError) do
|
|
80
|
+
mp3 = Mp3Info.new(TEMP_FILE)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def test_is_an_mp3
|
|
85
|
+
assert_nothing_raised do
|
|
86
|
+
Mp3Info.new(TEMP_FILE).close
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def test_detected_info
|
|
91
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
92
|
+
assert_mp3_info_are_ok(mp3)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_vbr_mp3_length
|
|
97
|
+
File.open(TEMP_FILE, "w") { |f| f.write(@vbr_mp3) }
|
|
98
|
+
|
|
99
|
+
Mp3Info.open(TEMP_FILE) do |info|
|
|
100
|
+
assert(info.vbr)
|
|
101
|
+
assert_in_delta(174.210612, info.length, 0.000001)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_removetag1
|
|
106
|
+
Mp3Info.open(TEMP_FILE) { |info| info.tag1 = @tag }
|
|
107
|
+
assert(Mp3Info.hastag1?(TEMP_FILE))
|
|
108
|
+
Mp3Info.removetag1(TEMP_FILE)
|
|
109
|
+
assert(! Mp3Info.hastag1?(TEMP_FILE))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_writetag1
|
|
113
|
+
Mp3Info.open(TEMP_FILE) { |info| info.tag1 = @tag }
|
|
114
|
+
Mp3Info.open(TEMP_FILE) { |info| assert_equal(info.tag1, @tag) }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def test_valid_tag1_1
|
|
118
|
+
tag = [ "title", "artist", "album", "1921", "comments", 36, 0].pack('A30A30A30A4a29CC')
|
|
119
|
+
valid_tag = {
|
|
120
|
+
"title" => "title",
|
|
121
|
+
"artist" => "artist",
|
|
122
|
+
"album" => "album",
|
|
123
|
+
"year" => 1921,
|
|
124
|
+
"comments" => "comments",
|
|
125
|
+
"genre" => "Blues",
|
|
126
|
+
#"version" => "1",
|
|
127
|
+
"tracknum" => 36
|
|
128
|
+
}
|
|
129
|
+
id3_test(tag, valid_tag)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def test_valid_tag1_0
|
|
133
|
+
tag = [ "title", "artist", "album", "1921", "comments", 0].pack('A30A30A30A4A30C')
|
|
134
|
+
valid_tag = {
|
|
135
|
+
"title" => "title",
|
|
136
|
+
"artist" => "artist",
|
|
137
|
+
"album" => "album",
|
|
138
|
+
"year" => 1921,
|
|
139
|
+
"comments" => "comments",
|
|
140
|
+
"genre" => "Blues",
|
|
141
|
+
#"version" => "0"
|
|
142
|
+
}
|
|
143
|
+
id3_test(tag, valid_tag)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def id3_test(tag_str, valid_tag)
|
|
147
|
+
tag = "TAG" + tag_str
|
|
148
|
+
File.open(TEMP_FILE, "w") do |f|
|
|
149
|
+
f.write(@valid_mp3)
|
|
150
|
+
f.write(tag)
|
|
151
|
+
end
|
|
152
|
+
assert(Mp3Info.hastag1?(TEMP_FILE))
|
|
153
|
+
#info = Mp3Info.new(TEMP_FILE)
|
|
154
|
+
#FIXME validate this test
|
|
155
|
+
#assert_equal(info.tag1, valid_tag)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def test_removetag2
|
|
159
|
+
w = write_tag2_to_temp_file({"TIT2" => "sdfqdsf"})
|
|
160
|
+
|
|
161
|
+
assert( Mp3Info.hastag2?(TEMP_FILE) )
|
|
162
|
+
Mp3Info.removetag2(TEMP_FILE)
|
|
163
|
+
assert( ! Mp3Info.hastag2?(TEMP_FILE) )
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def test_hastags
|
|
167
|
+
Mp3Info.open(TEMP_FILE) do |info|
|
|
168
|
+
info.tag1 = @tag
|
|
169
|
+
end
|
|
170
|
+
assert(Mp3Info.hastag1?(TEMP_FILE))
|
|
171
|
+
|
|
172
|
+
written_tag = write_tag2_to_temp_file(DUMMY_TAG2)
|
|
173
|
+
assert(Mp3Info.hastag2?(TEMP_FILE))
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def test_universal_tag
|
|
177
|
+
2.times do
|
|
178
|
+
tag = {"title" => "title"}
|
|
179
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
180
|
+
tag.each { |k,v| mp3.tag[k] = v }
|
|
181
|
+
end
|
|
182
|
+
w = Mp3Info.open(TEMP_FILE) { |m| m.tag }
|
|
183
|
+
assert_equal(tag, w)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def test_id3v2_universal_tag
|
|
188
|
+
tag = {}
|
|
189
|
+
%w{comments title artist album}.each { |k| tag[k] = k }
|
|
190
|
+
tag["tracknum"] = 34
|
|
191
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
192
|
+
tag.each { |k,v| mp3.tag[k] = v }
|
|
193
|
+
end
|
|
194
|
+
w = Mp3Info.open(TEMP_FILE) { |m| m.tag }
|
|
195
|
+
w.delete("genre")
|
|
196
|
+
w.delete("genre_s")
|
|
197
|
+
assert_equal(tag, w)
|
|
198
|
+
# id3v2_prog_test(tag, w)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def test_id3v2_version
|
|
202
|
+
written_tag = write_tag2_to_temp_file(DUMMY_TAG2)
|
|
203
|
+
assert_equal( "2.#{ID3v2::WRITE_VERSION}.0", written_tag.version )
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def test_id3v2_methods
|
|
207
|
+
tag = { "TIT2" => "tit2", "TPE1" => "tpe1" }
|
|
208
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
209
|
+
tag.each do |k, v|
|
|
210
|
+
mp3.tag2.send("#{k}=".to_sym, v)
|
|
211
|
+
end
|
|
212
|
+
assert_equal(tag, mp3.tag2)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def test_id3v2_basic
|
|
217
|
+
w = write_tag2_to_temp_file(DUMMY_TAG2)
|
|
218
|
+
assert_equal(DUMMY_TAG2, w)
|
|
219
|
+
id3v2_prog_test(DUMMY_TAG2, w)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
#test the tag with the "id3v2" program
|
|
223
|
+
def id3v2_prog_test(tag, written_tag)
|
|
224
|
+
return if RUBY_PLATFORM =~ /win32/
|
|
225
|
+
return if `which id3v2`.empty?
|
|
226
|
+
start = false
|
|
227
|
+
id3v2_output = {}
|
|
228
|
+
`id3v2 -l #{TEMP_FILE}`.split(/\n/).each do |line|
|
|
229
|
+
if m = /^(.{4}) \(.+\): (.+)$/.match(line)
|
|
230
|
+
k, v = m[1, 2]
|
|
231
|
+
case k
|
|
232
|
+
when "COMM"
|
|
233
|
+
v.sub!(/\(\)\[.{3}\]: (.+)/, '\1')
|
|
234
|
+
end
|
|
235
|
+
id3v2_output[k] = v
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
assert_equal( id3v2_output, written_tag, "id3v2 program output doesn't match")
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def test_id3v2_trash
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def test_id3v2_complex
|
|
246
|
+
tag = {}
|
|
247
|
+
#ID3v2::TAGS.keys.each do |k|
|
|
248
|
+
["PRIV", "APIC"].each do |k|
|
|
249
|
+
tag[k] = random_string(50)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
got_tag = write_tag2_to_temp_file(tag)
|
|
253
|
+
assert_equal(tag, got_tag)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def test_id3v2_bigtag
|
|
257
|
+
tag = {"APIC" => random_string(1024) }
|
|
258
|
+
assert_equal(tag, write_tag2_to_temp_file(tag))
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def test_infinite_loop_on_seek_to_v2_end
|
|
262
|
+
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def test_leading_char_gets_chopped
|
|
266
|
+
tag2 = DUMMY_TAG2.dup
|
|
267
|
+
tag2["WOAR"] = "http://foo.bar"
|
|
268
|
+
w = write_tag2_to_temp_file(tag2)
|
|
269
|
+
assert_equal("http://foo.bar", w["WOAR"])
|
|
270
|
+
|
|
271
|
+
system(%(id3v2 --WOAR "http://foo.bar" "#{TEMP_FILE}"))
|
|
272
|
+
|
|
273
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
274
|
+
assert_equal "http://foo.bar", mp3.tag2["WOAR"]
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def test_reading2_2_tags
|
|
279
|
+
File.open(TEMP_FILE, "w") { |f| f.write(@valid_mp3_2_2) }
|
|
280
|
+
|
|
281
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
282
|
+
assert_equal "2.2.0", mp3.tag2.version
|
|
283
|
+
expected_tag = {
|
|
284
|
+
"TCO" => "Hip Hop/Rap",
|
|
285
|
+
"TP1" => "Grems Aka Supermicro",
|
|
286
|
+
"TT2" => "Intro",
|
|
287
|
+
"TAL" => "Air Max",
|
|
288
|
+
"TEN" => "iTunes v7.0.2.16",
|
|
289
|
+
"TYE" => "2006",
|
|
290
|
+
"TRK" => "1/17",
|
|
291
|
+
"TPA" => "1/1" }
|
|
292
|
+
tag = mp3.tag2.dup
|
|
293
|
+
assert_equal 4, tag["COM"].size
|
|
294
|
+
tag.delete("COM")
|
|
295
|
+
assert_equal expected_tag, tag
|
|
296
|
+
|
|
297
|
+
expected_tag = {
|
|
298
|
+
"genre_s" => "Hip Hop/Rap",
|
|
299
|
+
"title" => "Intro",
|
|
300
|
+
#"comments" => "\000engiTunPGAP\0000\000\000",
|
|
301
|
+
"comments" => "0",
|
|
302
|
+
"year" => 2006,
|
|
303
|
+
"album" => "Air Max",
|
|
304
|
+
"artist" => "Grems Aka Supermicro",
|
|
305
|
+
"tracknum" => 1 }
|
|
306
|
+
# test universal tag
|
|
307
|
+
assert_equal expected_tag, mp3.tag
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def test_writing_universal_tag_from_2_2_tags
|
|
312
|
+
File.open(TEMP_FILE, "w") { |f| f.write(@valid_mp3_2_2) }
|
|
313
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
314
|
+
mp3.tag.artist = "toto"
|
|
315
|
+
mp3.tag.comments = "comments"
|
|
316
|
+
mp3.flush
|
|
317
|
+
expected_tag = {
|
|
318
|
+
"artist" => "toto",
|
|
319
|
+
"genre_s" => "Hip Hop/Rap",
|
|
320
|
+
"title" => "Intro",
|
|
321
|
+
"comments" => "comments",
|
|
322
|
+
"year" => 2006,
|
|
323
|
+
"album" => "Air Max",
|
|
324
|
+
"tracknum" => 1}
|
|
325
|
+
|
|
326
|
+
assert_equal expected_tag, mp3.tag
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def test_remove_tag
|
|
331
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
332
|
+
tag = mp3.tag
|
|
333
|
+
tag.title = "title"
|
|
334
|
+
tag.artist = "artist"
|
|
335
|
+
mp3.close
|
|
336
|
+
mp3.reload
|
|
337
|
+
assert !mp3.tag1.empty?, "tag is empty"
|
|
338
|
+
mp3.removetag1
|
|
339
|
+
mp3.flush
|
|
340
|
+
assert mp3.tag1.empty?, "tag is not empty"
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def test_good_parsing_of_a_pathname
|
|
345
|
+
fn = "Freak On `(Stone´s Club Mix).mp3"
|
|
346
|
+
FileUtils.cp(TEMP_FILE, fn)
|
|
347
|
+
begin
|
|
348
|
+
Mp3Info.open(fn) do |mp3|
|
|
349
|
+
mp3.tag.title = fn
|
|
350
|
+
mp3.flush
|
|
351
|
+
if RUBY_VERSION < "1.9.0"
|
|
352
|
+
assert_equal fn, mp3.tag.title
|
|
353
|
+
else
|
|
354
|
+
assert_equal fn, mp3.tag.title.force_encoding("utf-8")
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
ensure
|
|
358
|
+
File.delete(fn)
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def test_validity_of_id3v2_options
|
|
363
|
+
info = Mp3Info.new(TEMP_FILE)
|
|
364
|
+
expected_hash = { :lang => "ENG", :encoding => "iso-8859-1" }
|
|
365
|
+
assert_equal( expected_hash, info.tag2.options)
|
|
366
|
+
|
|
367
|
+
assert_raises(ArgumentError) do
|
|
368
|
+
Mp3Info.new(TEMP_FILE, :encoding => "bad encoding")
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def test_encoding_read
|
|
373
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
374
|
+
mp3.tag2['TEST'] = "all\xe9"
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
Mp3Info.open(TEMP_FILE, :encoding => "utf-8") do |mp3|
|
|
378
|
+
assert_equal "allé", mp3.tag2['TEST']
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
Mp3Info.open(TEMP_FILE, :encoding => "iso-8859-1") do |mp3|
|
|
382
|
+
if RUBY_VERSION < "1.9.0"
|
|
383
|
+
assert_equal "all\xe9", mp3.tag2['TEST']
|
|
384
|
+
else
|
|
385
|
+
assert_equal "all\xe9".force_encoding("binary"), mp3.tag2['TEST']
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def test_encoding_write
|
|
391
|
+
Mp3Info.open(TEMP_FILE, :encoding => 'utf-8') do |mp3|
|
|
392
|
+
mp3.tag2['TEST'] = "all\xc3\xa9"
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
Mp3Info.open(TEMP_FILE, :encoding => "iso-8859-1") do |mp3|
|
|
396
|
+
if RUBY_VERSION < "1.9.0"
|
|
397
|
+
assert_equal "all\xe9", mp3.tag2['TEST']
|
|
398
|
+
else
|
|
399
|
+
assert_equal "all\xe9".force_encoding("iso-8859-1"), mp3.tag2['TEST']
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
=begin
|
|
405
|
+
def test_should_raises_exception_when_writing_badly_encoded_frames
|
|
406
|
+
assert_raises(Iconv::Failure) do
|
|
407
|
+
Mp3Info.open(TEMP_FILE, :encoding => 'utf-8') do |mp3|
|
|
408
|
+
mp3.tag2['TEST'] = "all\xc3"
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
=end
|
|
413
|
+
|
|
414
|
+
def test_audio_content
|
|
415
|
+
require "digest/md5"
|
|
416
|
+
|
|
417
|
+
expected_digest = nil
|
|
418
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
419
|
+
mp3.tag1.update(DUMMY_TAG1)
|
|
420
|
+
mp3.tag2.update(DUMMY_TAG2)
|
|
421
|
+
mp3.flush
|
|
422
|
+
assert mp3.hastag1?
|
|
423
|
+
assert mp3.hastag2?
|
|
424
|
+
assert mp3.tag2.io_position != 0
|
|
425
|
+
expected_digest = compute_audio_content_mp3_digest(mp3)
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
429
|
+
mp3.removetag1
|
|
430
|
+
mp3.removetag2
|
|
431
|
+
mp3.flush
|
|
432
|
+
assert !mp3.hastag1?
|
|
433
|
+
assert !mp3.hastag2?
|
|
434
|
+
got_digest = compute_audio_content_mp3_digest(mp3)
|
|
435
|
+
assert_equal expected_digest, got_digest
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def test_audio_content_problematic
|
|
440
|
+
File.open(TEMP_FILE, "w") { |f| f.write(FIXTURES["audio_content_fixture"]) }
|
|
441
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
442
|
+
expected_pos = 150
|
|
443
|
+
audio_content_pos, audio_content_size = mp3.audio_content
|
|
444
|
+
assert_equal expected_pos, audio_content_pos
|
|
445
|
+
assert_equal File.size(TEMP_FILE) - expected_pos, audio_content_size
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
def test_headerless_vbr_file
|
|
450
|
+
mp3_length = 3
|
|
451
|
+
# this will generate a 15 sec mp3 file (44100hz*16bit*2channels) = 60/4 = 15
|
|
452
|
+
system("dd if=/dev/urandom bs=44100 count=#{mp3_length*4} 2>/dev/null | \
|
|
453
|
+
lame -v -m s --vbr-new --preset 128 -r -s 44.1 --bitwidth 16 - - > #{TEMP_FILE} 2>/dev/null")
|
|
454
|
+
|
|
455
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
456
|
+
assert mp3.vbr
|
|
457
|
+
assert_in_delta(mp3_length, mp3.length, 0.1)
|
|
458
|
+
assert_in_delta(128, mp3.bitrate, 8)
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
def test_parse_tags_disabled
|
|
463
|
+
write_tag2_to_temp_file(DUMMY_TAG2)
|
|
464
|
+
Mp3Info.open(TEMP_FILE, :parse_tags => false) do |mp3|
|
|
465
|
+
assert mp3.tag.empty?
|
|
466
|
+
assert mp3.tag1.empty?
|
|
467
|
+
assert mp3.tag2.empty?
|
|
468
|
+
mp3.tag["artist"] = "some dummy tag"
|
|
469
|
+
mp3.tag2["TIT2"] = "title 2"
|
|
470
|
+
mp3.flush
|
|
471
|
+
# tag should not be written
|
|
472
|
+
assert mp3.tag.empty?
|
|
473
|
+
assert mp3.tag1.empty?
|
|
474
|
+
assert mp3.tag2.empty?
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def test_string_io
|
|
479
|
+
io = load_string_io
|
|
480
|
+
Mp3Info.open(io) do |mp3|
|
|
481
|
+
assert_mp3_info_are_ok(mp3)
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def test_trying_to_rename_a_stringio_should_raise_an_error
|
|
486
|
+
io = load_string_io
|
|
487
|
+
Mp3Info.open(io) do |mp3|
|
|
488
|
+
assert_raises(Mp3InfoError) do
|
|
489
|
+
mp3.rename("whatever_filename_is_error_should_be_raised.mp3")
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def test_hastag_class_methods_with_a_stringio
|
|
495
|
+
Mp3Info.open(TEMP_FILE) do |info|
|
|
496
|
+
info.tag1 = @tag
|
|
497
|
+
end
|
|
498
|
+
io = load_string_io
|
|
499
|
+
assert(Mp3Info.hastag1?(io))
|
|
500
|
+
|
|
501
|
+
written_tag = write_tag2_to_temp_file(DUMMY_TAG2)
|
|
502
|
+
io = load_string_io
|
|
503
|
+
assert(Mp3Info.hastag2?(io))
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
def compute_audio_content_mp3_digest(mp3)
|
|
507
|
+
pos, size = mp3.audio_content
|
|
508
|
+
data = File.open(mp3.filename) do |f|
|
|
509
|
+
f.seek(pos, IO::SEEK_SET)
|
|
510
|
+
f.read(size)
|
|
511
|
+
end
|
|
512
|
+
Digest::MD5.new.update(data).hexdigest
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def write_tag2_to_temp_file(tag)
|
|
516
|
+
Mp3Info.open(TEMP_FILE) do |mp3|
|
|
517
|
+
mp3.tag2.update(tag)
|
|
518
|
+
end
|
|
519
|
+
return Mp3Info.open(TEMP_FILE) { |m| m.tag2 }
|
|
520
|
+
#system("cp -v #{TEMP_FILE} #{TEMP_FILE}.test")
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def random_string(size)
|
|
524
|
+
out = ""
|
|
525
|
+
size.times { out << rand(256).chr }
|
|
526
|
+
out
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
def assert_mp3_info_are_ok(mp3)
|
|
530
|
+
assert_equal(1, mp3.mpeg_version)
|
|
531
|
+
assert_equal(3, mp3.layer)
|
|
532
|
+
assert_equal(false, mp3.vbr)
|
|
533
|
+
assert_equal(128, mp3.bitrate)
|
|
534
|
+
assert_equal("JStereo", mp3.channel_mode)
|
|
535
|
+
assert_equal(44100, mp3.samplerate)
|
|
536
|
+
assert_equal(0.1305625, mp3.length)
|
|
537
|
+
assert_equal({:original => true,
|
|
538
|
+
:error_protection => false,
|
|
539
|
+
:padding => false,
|
|
540
|
+
:emphasis => 0,
|
|
541
|
+
:private => true,
|
|
542
|
+
:mode_extension => 2,
|
|
543
|
+
:copyright => false}, mp3.header)
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
def load_string_io(filename = TEMP_FILE)
|
|
547
|
+
io = StringIO.new
|
|
548
|
+
data = File.read(filename)
|
|
549
|
+
io.write(data)
|
|
550
|
+
io.rewind
|
|
551
|
+
io
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
=begin
|
|
555
|
+
|
|
556
|
+
def test_encoder
|
|
557
|
+
write_to_temp
|
|
558
|
+
info = Mp3Info.new(TEMP_FILE)
|
|
559
|
+
assert(info.encoder == "Lame 3.93")
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def test_vbr
|
|
563
|
+
mp3_vbr = Base64.decode64 <<EOF
|
|
564
|
+
|
|
565
|
+
EOF
|
|
566
|
+
File.open(TEMP_FILE, "w") { |f| f.write(mp3_vbr) }
|
|
567
|
+
info = Mp3Info.new(TEMP_FILE)
|
|
568
|
+
assert_equal(info.vbr, true)
|
|
569
|
+
assert_equal(info.bitrate, 128)
|
|
570
|
+
end
|
|
571
|
+
=end
|
|
572
|
+
end
|