id3lib-ruby 0.5.0 → 0.6.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.
- data/CHANGES +10 -0
- data/INSTALL +33 -15
- data/README +1 -3
- data/Rakefile +83 -32
- data/TODO +4 -0
- data/ext/{Rakefile → id3lib_api/Rakefile} +1 -1
- data/ext/id3lib_api/extconf.rb +27 -0
- data/ext/{id3lib_api.i → id3lib_api/id3lib_api.i} +10 -8
- data/ext/{id3lib_api_wrap.cxx → id3lib_api/id3lib_api_wrap.cxx} +594 -287
- data/lib/id3lib.rb +24 -4
- data/test/data/sample.mp3 +0 -0
- data/test/data/unicode.mp3 +0 -0
- data/test/test_reading.rb +17 -0
- data/test/test_unicode.rb +10 -0
- data/test/test_writing.rb +1 -1
- metadata +145 -45
- data/ext/extconf.rb +0 -20
- data/ext/generate_info.rb +0 -127
data/lib/id3lib.rb
CHANGED
@@ -9,7 +9,7 @@ require 'id3lib/accessors'
|
|
9
9
|
# Have a look at ID3Lib::Tag for an introduction on how to use this library.
|
10
10
|
#
|
11
11
|
module ID3Lib
|
12
|
-
VERSION = '0.
|
12
|
+
VERSION = '0.6.0'
|
13
13
|
|
14
14
|
# ID3 version 1. All V constants can be used with the methods
|
15
15
|
# new, update! or strip! of ID3Lib::Tag.
|
@@ -69,7 +69,8 @@ module ID3Lib
|
|
69
69
|
#
|
70
70
|
# There are a number of accessors for text frames like
|
71
71
|
# title, performer, album, track, year, comment and genre. Have a look
|
72
|
-
# at ID3Lib::Accessors for a complete list.
|
72
|
+
# at ID3Lib::Accessors for a complete list. They can only be used for
|
73
|
+
# text that is encoded with ISO-8859-1.
|
73
74
|
#
|
74
75
|
# tag.title #=> "Shy Boi"
|
75
76
|
#
|
@@ -95,13 +96,17 @@ module ID3Lib
|
|
95
96
|
# # Also raises an exception:
|
96
97
|
# tag.find{ |f| f[:id] == :TLAN }[:text]
|
97
98
|
#
|
98
|
-
# Because only text frames can be set with accessors, you
|
99
|
-
# special frames by hand.
|
99
|
+
# Because only ISO-8859-1 encoded text frames can be set with accessors, you
|
100
|
+
# have to add special frames by hand.
|
100
101
|
#
|
101
102
|
# # Add two comments
|
102
103
|
# tag << {:id => :COMM, :text => 'chunky bacon'}
|
103
104
|
# tag << {:id => :COMM, :text => 'really.'}
|
104
105
|
#
|
106
|
+
# # Add an UTF-16 text frame with BOM (byte order mark)
|
107
|
+
# tag << {:id => :TIT2, :text => "\xff\xfe\x60\x4f\x7d\x59",
|
108
|
+
# :textenc => 1}
|
109
|
+
#
|
105
110
|
# # Add an attached picture
|
106
111
|
# cover = {
|
107
112
|
# :id => :APIC,
|
@@ -206,6 +211,21 @@ module ID3Lib
|
|
206
211
|
f ? f[:text] : nil
|
207
212
|
end
|
208
213
|
|
214
|
+
#
|
215
|
+
# Get the text of a user frame specified by _description_.
|
216
|
+
# Returns nil if the frame can't be found.
|
217
|
+
#
|
218
|
+
# tag.user_frame_text('MusicBrainz Album Id')
|
219
|
+
# #=> "f0d6c31f-8f9f-47fe-b5f5-3b96746b48fa"
|
220
|
+
#
|
221
|
+
# tag.user_frame_text('MusicBrainz Album Artist Id')
|
222
|
+
# #=> nil
|
223
|
+
#
|
224
|
+
def user_frame_text(description)
|
225
|
+
f = find{ |f| f[:id] == :TXXX && f[:description] == description }
|
226
|
+
f ? f[:text] : nil
|
227
|
+
end
|
228
|
+
|
209
229
|
#
|
210
230
|
# Set the text of a frame. First, all frames with the specified _id_ are
|
211
231
|
# deleted and then a new frame with _text_ is appended.
|
data/test/data/sample.mp3
CHANGED
Binary file
|
data/test/data/unicode.mp3
CHANGED
Binary file
|
data/test/test_reading.rb
CHANGED
@@ -31,6 +31,23 @@ class TestReading < Test::Unit::TestCase
|
|
31
31
|
assert_equal 'Pop', @tag.genre
|
32
32
|
end
|
33
33
|
|
34
|
+
def test_frame_text
|
35
|
+
assert_equal 'Dummy Title', @tag.frame_text(:TIT2)
|
36
|
+
assert_equal 'Dummy Artist', @tag.frame_text(:TPE1)
|
37
|
+
assert_equal 'Dummy Album', @tag.frame_text(:TALB)
|
38
|
+
assert_equal '1/10', @tag.frame_text(:TRCK)
|
39
|
+
assert_equal '2000', @tag.frame_text(:TYER)
|
40
|
+
assert_equal 'Dummy Comment', @tag.frame_text(:COMM)
|
41
|
+
assert_equal 'Pop', @tag.frame_text(:TCON)
|
42
|
+
assert_equal nil, @tag.frame_text(:AENC)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_user_frame_text
|
46
|
+
album_id = @tag.user_frame_text('MusicBrainz Album Id')
|
47
|
+
assert_equal '992dc19a-5631-40f5-b252-fbfedbc328a9', album_id
|
48
|
+
assert_equal nil, @tag.user_frame_text('Inexistent')
|
49
|
+
end
|
50
|
+
|
34
51
|
def test_comments
|
35
52
|
one, two = @tag.comment_frames
|
36
53
|
assert_not_nil one
|
data/test/test_unicode.rb
CHANGED
@@ -54,6 +54,16 @@ class TestUnicode < Test::Unit::TestCase
|
|
54
54
|
assert_equal "\x4f\x60\x59\x7d", @tag.title
|
55
55
|
end
|
56
56
|
|
57
|
+
def test_reading_lyrics
|
58
|
+
@tag = ID3Lib::Tag.new('test/data/unicode.mp3', ID3Lib::V2)
|
59
|
+
lyrics = @tag.find{ |f| f[:id] == :USLT }
|
60
|
+
assert_equal 1, lyrics[:textenc]
|
61
|
+
assert_equal "zho", lyrics[:language]
|
62
|
+
# This is "U+6771 U+4EAC" (Tokyo) in UTF-16BE
|
63
|
+
tokyo = "\x67\x71\x4e\xac"
|
64
|
+
assert_equal tokyo, lyrics[:text]
|
65
|
+
end
|
66
|
+
|
57
67
|
def test_invalid_data
|
58
68
|
nonstr = 1
|
59
69
|
@tag.reject!{ |f| f[:id] == :TIT2 }
|
data/test/test_writing.rb
CHANGED
metadata
CHANGED
@@ -1,37 +1,123 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.0
|
3
|
-
specification_version: 1
|
4
2
|
name: id3lib-ruby
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
-
|
11
|
-
|
12
|
-
homepage: http://id3lib-ruby.rubyforge.org
|
13
|
-
rubyforge_project: id3lib-ruby
|
14
|
-
description:
|
15
|
-
autorequire:
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 6
|
8
|
+
- 0
|
9
|
+
version: 0.6.0
|
25
10
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
11
|
authors:
|
30
12
|
- Robin Stocker
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-16 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: |
|
22
|
+
= id3lib-ruby
|
23
|
+
|
24
|
+
id3lib-ruby provides a Ruby interface to the id3lib C++ library for easily
|
25
|
+
editing ID3 tags (v1 and v2) of MP3 audio files.
|
26
|
+
|
27
|
+
The class documentation starts at ID3Lib::Tag.
|
28
|
+
|
29
|
+
|
30
|
+
== Features
|
31
|
+
|
32
|
+
* Read and write ID3v1 and ID3v2 tags
|
33
|
+
* Simple interface for adding, changing and removing frames
|
34
|
+
* Quick access to common text frames like title and performer
|
35
|
+
* Custom data frames like attached picture (APIC)
|
36
|
+
* Pretty complete coverage of id3lib's features
|
37
|
+
* UTF-16 support (warning: id3lib writes broken UTF-16 frames)
|
38
|
+
* Windows binary gem available
|
39
|
+
|
40
|
+
The CHANGES file contains a list of changes between versions.
|
41
|
+
|
42
|
+
|
43
|
+
== Installation
|
44
|
+
|
45
|
+
See INSTALL.
|
46
|
+
|
47
|
+
|
48
|
+
== Online Information
|
49
|
+
|
50
|
+
The home of id3lib-ruby is http://id3lib-ruby.rubyforge.org
|
51
|
+
|
52
|
+
|
53
|
+
== Usage
|
54
|
+
|
55
|
+
require 'rubygems'
|
56
|
+
require 'id3lib'
|
57
|
+
|
58
|
+
# Load a tag from a file
|
59
|
+
tag = ID3Lib::Tag.new('talk.mp3')
|
60
|
+
|
61
|
+
# Get and set text frames with convenience methods
|
62
|
+
tag.title #=> "Talk"
|
63
|
+
tag.album = 'X&Y'
|
64
|
+
tag.track = '5/13'
|
65
|
+
|
66
|
+
# Tag is a subclass of Array and each frame is a Hash
|
67
|
+
tag[0]
|
68
|
+
#=> { :id => :TPE1, :textenc => 0, :text => "Coldplay" }
|
69
|
+
|
70
|
+
# Get the number of frames
|
71
|
+
tag.length #=> 7
|
72
|
+
|
73
|
+
# Remove all comment frames
|
74
|
+
tag.delete_if{ |frame| frame[:id] == :COMM }
|
75
|
+
|
76
|
+
# Get info about APIC frame to see which fields are allowed
|
77
|
+
ID3Lib::Info.frame(:APIC)
|
78
|
+
#=> [ 2, :APIC, "Attached picture",
|
79
|
+
#=> [:textenc, :mimetype, :picturetype, :description, :data] ]
|
80
|
+
|
81
|
+
# Add an attached picture frame
|
82
|
+
cover = {
|
83
|
+
:id => :APIC,
|
84
|
+
:mimetype => 'image/jpeg',
|
85
|
+
:picturetype => 3,
|
86
|
+
:description => 'A pretty picture',
|
87
|
+
:textenc => 0,
|
88
|
+
:data => File.read('cover.jpg')
|
89
|
+
}
|
90
|
+
tag << cover
|
91
|
+
|
92
|
+
# Last but not least, apply changes
|
93
|
+
tag.update!
|
94
|
+
|
95
|
+
|
96
|
+
== Licence
|
97
|
+
|
98
|
+
This library has Ruby's licence:
|
99
|
+
|
100
|
+
http://www.ruby-lang.org/en/LICENSE.txt
|
101
|
+
|
102
|
+
|
103
|
+
== Author
|
104
|
+
|
105
|
+
Robin Stocker <robinstocker at rubyforge.org>
|
106
|
+
|
107
|
+
email: robinstocker@rubyforge.org
|
108
|
+
executables: []
|
109
|
+
|
110
|
+
extensions:
|
111
|
+
- ext/id3lib_api/extconf.rb
|
112
|
+
extra_rdoc_files:
|
113
|
+
- README
|
114
|
+
- INSTALL
|
115
|
+
- TODO
|
116
|
+
- CHANGES
|
31
117
|
files:
|
32
|
-
- lib/id3lib.rb
|
33
118
|
- lib/id3lib/accessors.rb
|
34
119
|
- lib/id3lib/info.rb
|
120
|
+
- lib/id3lib.rb
|
35
121
|
- test/test_unicode.rb
|
36
122
|
- test/test_writing.rb
|
37
123
|
- test/test_reading.rb
|
@@ -41,34 +127,48 @@ files:
|
|
41
127
|
- Rakefile
|
42
128
|
- usage.rb
|
43
129
|
- setup.rb
|
44
|
-
- ext/extconf.rb
|
45
|
-
- ext/
|
46
|
-
- ext/
|
47
|
-
- ext/id3lib_api
|
48
|
-
- ext/Rakefile
|
130
|
+
- ext/id3lib_api/extconf.rb
|
131
|
+
- ext/id3lib_api/id3lib_api_wrap.cxx
|
132
|
+
- ext/id3lib_api/id3lib_api.i
|
133
|
+
- ext/id3lib_api/Rakefile
|
49
134
|
- README
|
50
135
|
- INSTALL
|
51
136
|
- TODO
|
52
137
|
- CHANGES
|
53
|
-
|
54
|
-
-
|
55
|
-
|
56
|
-
|
138
|
+
has_rdoc: true
|
139
|
+
homepage: http://id3lib-ruby.rubyforge.org
|
140
|
+
licenses: []
|
141
|
+
|
142
|
+
post_install_message:
|
57
143
|
rdoc_options:
|
58
144
|
- --inline-source
|
59
145
|
- --line-numbers
|
60
146
|
- --main
|
61
147
|
- README
|
62
|
-
|
63
|
-
-
|
64
|
-
|
65
|
-
|
66
|
-
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
148
|
+
require_paths:
|
149
|
+
- lib
|
150
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
segments:
|
155
|
+
- 0
|
156
|
+
version: "0"
|
157
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
segments:
|
162
|
+
- 0
|
163
|
+
version: "0"
|
71
164
|
requirements:
|
72
165
|
- id3lib C++ library
|
73
|
-
|
74
|
-
|
166
|
+
rubyforge_project: id3lib-ruby
|
167
|
+
rubygems_version: 1.3.6
|
168
|
+
signing_key:
|
169
|
+
specification_version: 3
|
170
|
+
summary: id3lib-ruby provides a Ruby interface to the id3lib C++ library for easily editing ID3 tags (v1 and v2) of MP3 audio files.
|
171
|
+
test_files:
|
172
|
+
- test/test_unicode.rb
|
173
|
+
- test/test_writing.rb
|
174
|
+
- test/test_reading.rb
|
data/ext/extconf.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'mkmf'
|
2
|
-
|
3
|
-
def error msg
|
4
|
-
message msg + "\n"
|
5
|
-
abort
|
6
|
-
end
|
7
|
-
|
8
|
-
unless have_library('stdc++')
|
9
|
-
error "You must have libstdc++ installed."
|
10
|
-
end
|
11
|
-
|
12
|
-
unless have_library('z')
|
13
|
-
error "You must have zlib installed."
|
14
|
-
end
|
15
|
-
|
16
|
-
unless have_header('id3.h') and have_library('id3', 'ID3Tag_New')
|
17
|
-
error "You must have id3lib installed in order to use id3lib-ruby."
|
18
|
-
end
|
19
|
-
|
20
|
-
create_makefile('id3lib_api')
|
data/ext/generate_info.rb
DELETED
@@ -1,127 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
|
3
|
-
require 'enumerator'
|
4
|
-
require 'pathname'
|
5
|
-
|
6
|
-
|
7
|
-
id3lib_dir = Pathname.new(ARGV.first || '/home/robin/tmp/id3lib-3.8.3')
|
8
|
-
|
9
|
-
globals_file = File.read(id3lib_dir + 'include/id3/globals.h')
|
10
|
-
field_file = File.read(id3lib_dir + 'src/field.cpp')
|
11
|
-
|
12
|
-
def field_symbol(enum_name)
|
13
|
-
f = enum_name[/ID3FN_(.*)/, 1].downcase
|
14
|
-
if f == 'id'
|
15
|
-
:identifier
|
16
|
-
else
|
17
|
-
f.to_sym
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
|
22
|
-
field_groups = {}
|
23
|
-
allowed_fields = {}
|
24
|
-
|
25
|
-
field_file.scan(/ID3_FieldDef (\w+)[^\{]+\{(.+?)\};/m) do |group, body|
|
26
|
-
fields = []
|
27
|
-
body.scan(/\{\W+(\w+)/) do |field_name, _|
|
28
|
-
next if field_name == "ID3FN_NOFIELD"
|
29
|
-
fields << field_name
|
30
|
-
end
|
31
|
-
fields.uniq!
|
32
|
-
fields.map!{ |f| field_symbol(f) }
|
33
|
-
field_groups[group] = fields
|
34
|
-
end
|
35
|
-
|
36
|
-
field_file.scan(/ID3_FrameDef ID3_FrameDefs[^\{]+\{(.+?)\};/m) do |body, _|
|
37
|
-
body.each_line do |line|
|
38
|
-
values = line.split(/\s*,\s*/)
|
39
|
-
next unless values.size > 1
|
40
|
-
frame = values[2].delete('"')
|
41
|
-
group = values[5]
|
42
|
-
next if frame.empty?
|
43
|
-
allowed_fields[frame.to_sym] = field_groups[group]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
field_info = []
|
49
|
-
frame_info = []
|
50
|
-
genre_info = []
|
51
|
-
|
52
|
-
globals_file.scan(/ID3_ENUM\((\w+)\)\s+\{\s+(.+?)\s+\}/m) do |name, enum|
|
53
|
-
case name
|
54
|
-
when 'ID3_FieldID'
|
55
|
-
id = 0
|
56
|
-
enum.scan(/([^\s,]+)(?: = (\d+),|,)\s*\/\*\*< ([^*]+)\s+\*\//m) do |field, newid, description|
|
57
|
-
id = newid.to_i if newid
|
58
|
-
|
59
|
-
field = field_symbol(field)
|
60
|
-
field_info << [id, field, description]
|
61
|
-
|
62
|
-
id += 1
|
63
|
-
end
|
64
|
-
when 'ID3_FrameID'
|
65
|
-
id = 0
|
66
|
-
enum.scan(/\/\* (\S+) \*\/ [^\s,]+(?: = (\d+),|,)\s*\/\*\*< ([^*]+)\s+\*\//m) do |frame, newid, description|
|
67
|
-
id = newid.to_i if newid
|
68
|
-
frame = (frame == '????') ? :____ : frame.to_sym
|
69
|
-
|
70
|
-
fields = allowed_fields[frame] || []
|
71
|
-
|
72
|
-
frame_info << [id, frame, description, fields]
|
73
|
-
id += 1
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
globals_file.scan(/(ID3_v1_genre_description).+?\{(.+?)\}/m) do |name, list|
|
79
|
-
id = 0
|
80
|
-
list.scan(/"([^"]+)"/) do |genre|
|
81
|
-
genre_info << genre.first
|
82
|
-
id += 1
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
|
87
|
-
def indent level, text
|
88
|
-
puts ' ' * level + text
|
89
|
-
end
|
90
|
-
|
91
|
-
indent 4, "Frames = ["
|
92
|
-
frame_info.each do |f|
|
93
|
-
comment = case f[1]
|
94
|
-
when :____ : "# Special frames"
|
95
|
-
when :TALB : "# Text information frames"
|
96
|
-
when :UFID : "# Special frames again"
|
97
|
-
when :WCOM : "# URL link frames"
|
98
|
-
end
|
99
|
-
indent 6, comment if comment
|
100
|
-
indent 6, f.inspect + ","
|
101
|
-
end
|
102
|
-
indent 4, "]"
|
103
|
-
|
104
|
-
indent 4, "FramesByID = {"
|
105
|
-
frame_info.each do |f|
|
106
|
-
indent 6, f[1].inspect + " => Frames[" + f[0].to_s + "],"
|
107
|
-
end
|
108
|
-
indent 4, "}"
|
109
|
-
|
110
|
-
indent 4, "Fields = ["
|
111
|
-
field_info.each do |f|
|
112
|
-
indent 6, f.inspect + ","
|
113
|
-
end
|
114
|
-
indent 4, "]"
|
115
|
-
|
116
|
-
indent 4, "FieldsByID = {"
|
117
|
-
field_info.each do |f|
|
118
|
-
indent 6, f[1].inspect.ljust(16) + " => Fields[" + f[0].to_s + "],"
|
119
|
-
end
|
120
|
-
indent 4, "}"
|
121
|
-
|
122
|
-
indent 4, "Genres = ["
|
123
|
-
genre_info.each_slice(4) do |gs|
|
124
|
-
indent 6, "# Winamp extensions" if gs.first == "Folk"
|
125
|
-
indent 6, gs.map{ |g| g.inspect }.join(", ") + ","
|
126
|
-
end
|
127
|
-
indent 4, "]"
|