id3lib-ruby 0.3.0-mswin32
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 +28 -0
- data/README +95 -0
- data/Rakefile +119 -0
- data/TODO +4 -0
- data/ext/mswin32/id3lib_api.so +0 -0
- data/lib/id3lib.rb +384 -0
- data/lib/id3lib/accessors.rb +103 -0
- data/lib/id3lib/info.rb +389 -0
- data/setup.rb +1585 -0
- data/test/data/cover.jpg +0 -0
- data/test/data/sample.mp3 +0 -0
- data/test/data/unicode.mp3 +0 -0
- data/test/test_reading.rb +70 -0
- data/test/test_writing.rb +237 -0
- metadata +64 -0
data/CHANGES
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
= id3lib-ruby changes
|
2
|
+
|
3
|
+
=== 0.3.0 (r29)
|
4
|
+
|
5
|
+
* Added generation of mswin32 binary gem. This means that
|
6
|
+
installing on Windows is a piece of cake. No dependencies, no
|
7
|
+
compiling, just a gem installation.
|
8
|
+
* Changed Info.frame to use hash access instead of find -> faster.
|
9
|
+
|
10
|
+
=== 0.2.1 (r23)
|
11
|
+
|
12
|
+
* Fixed extconf.rb to print a message and abort if id3lib can't be
|
13
|
+
found.
|
14
|
+
|
15
|
+
=== 0.2.0 (r21)
|
16
|
+
|
17
|
+
* Overhauled direct access methods
|
18
|
+
* Remove frames by setting them to nil, e.g. tag.title = nil
|
19
|
+
* Removed methods for rarely used frames.
|
20
|
+
* All methods use strings now, no special cases like track anymore.
|
21
|
+
* Explicit call of to_s in set_frame_text.
|
22
|
+
* More unit tests
|
23
|
+
* Added method invalid_frames which is useful to detect invalid frame
|
24
|
+
data before calling update!.
|
25
|
+
|
26
|
+
=== 0.1.0 (r19)
|
27
|
+
|
28
|
+
* First release :)
|
data/README
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
= id3lib-ruby
|
3
|
+
|
4
|
+
id3lib-ruby provides a Ruby interface to the id3lib C++ library for easily
|
5
|
+
editing ID3 tags (v1 and v2) like with pyid3lib.
|
6
|
+
|
7
|
+
The class documentation starts at ID3Lib::Tag.
|
8
|
+
|
9
|
+
|
10
|
+
== Features
|
11
|
+
|
12
|
+
* Read and write ID3v1 or ID3v2 tags
|
13
|
+
* Simple interface for adding, changing and removing frames
|
14
|
+
* Quick access to common text frames like title and performer
|
15
|
+
* Custom data frames like attached picture (APIC)
|
16
|
+
* Pretty complete coverage of id3lib's features
|
17
|
+
* UTF-16 support
|
18
|
+
* Windows binary gem available
|
19
|
+
|
20
|
+
See TODO for planned features.
|
21
|
+
|
22
|
+
The CHANGES file contains a list of changes between versions.
|
23
|
+
|
24
|
+
|
25
|
+
== Online Information
|
26
|
+
|
27
|
+
The home of id3lib-ruby is http://id3lib-ruby.rubyforge.org
|
28
|
+
|
29
|
+
|
30
|
+
== Installation
|
31
|
+
|
32
|
+
Note that id3lib has to be installed before running any of the following
|
33
|
+
commands, unless you use the Windows binary gem.
|
34
|
+
|
35
|
+
Installation through RubyGems:
|
36
|
+
|
37
|
+
gem install id3lib-ruby
|
38
|
+
|
39
|
+
Manual installation:
|
40
|
+
|
41
|
+
ruby setup.rb
|
42
|
+
|
43
|
+
== Usage
|
44
|
+
|
45
|
+
require 'rubygems'
|
46
|
+
require 'id3lib'
|
47
|
+
|
48
|
+
# Load a tag from a file
|
49
|
+
tag = ID3Lib::Tag.new('talk.mp3')
|
50
|
+
|
51
|
+
# Get and set text frames with convenience methods
|
52
|
+
tag.title #=> "Talk"
|
53
|
+
tag.album = 'X&Y'
|
54
|
+
tag.track = '5/13'
|
55
|
+
|
56
|
+
# Tag is a subclass of Array and each frame is a Hash
|
57
|
+
tag[0]
|
58
|
+
#=> { :id => :TPE1, :textenc => 0, :text => "Coldplay" }
|
59
|
+
|
60
|
+
# Get the number of frames
|
61
|
+
tag.length #=> 7
|
62
|
+
|
63
|
+
# Remove all comment frames
|
64
|
+
tag.delete_if{ |frame| frame[:id] == :COMM }
|
65
|
+
|
66
|
+
# Get info about APIC frame to see which fields are allowed
|
67
|
+
ID3Lib::Info.frame(:APIC)
|
68
|
+
#=> [ 2, :APIC, "Attached picture",
|
69
|
+
#=> [:textenc, :mimetype, :picturetype, :description, :data] ]
|
70
|
+
|
71
|
+
# Add an attached picture frame
|
72
|
+
cover = {
|
73
|
+
:id => :APIC,
|
74
|
+
:mimetype => 'image/jpeg',
|
75
|
+
:picturetype => 3,
|
76
|
+
:description => 'A pretty picture',
|
77
|
+
:textenc => 0,
|
78
|
+
:data => File.read('cover.jpg')
|
79
|
+
}
|
80
|
+
tag << cover
|
81
|
+
|
82
|
+
# Last but not least, apply changes
|
83
|
+
tag.update!
|
84
|
+
|
85
|
+
|
86
|
+
== Licence
|
87
|
+
|
88
|
+
This library has Ruby's licence:
|
89
|
+
|
90
|
+
http://www.ruby-lang.org/en/LICENSE.txt
|
91
|
+
|
92
|
+
|
93
|
+
== Author
|
94
|
+
|
95
|
+
Robin Stocker <robinstocker at rubyforge.org>
|
data/Rakefile
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
|
2
|
+
begin
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
rescue Exception
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rake/testtask'
|
10
|
+
require 'rake/rdoctask'
|
11
|
+
|
12
|
+
|
13
|
+
PKG_VERSION = '0.3.0'
|
14
|
+
|
15
|
+
PKG_COMMON = FileList[
|
16
|
+
'lib/**/*.rb',
|
17
|
+
'test/test_*.rb',
|
18
|
+
'test/data/*.mp3',
|
19
|
+
'test/data/cover.jpg',
|
20
|
+
'Rakefile',
|
21
|
+
'setup.rb'
|
22
|
+
]
|
23
|
+
|
24
|
+
|
25
|
+
desc "Build extension."
|
26
|
+
task :ext do
|
27
|
+
sh "cd ext && rake"
|
28
|
+
puts "(end)"
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Build mswin32 extension."
|
32
|
+
task :ext_mswin32 do
|
33
|
+
sh 'cd ext/mswin32; rake'
|
34
|
+
puts "(end)"
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
Rake::TestTask.new do |t|
|
39
|
+
t.libs = ['lib', 'ext']
|
40
|
+
t.test_files = FileList['test/test_*.rb']
|
41
|
+
t.verbose = true
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
RDOC_OPTS = ['--line-numbers', '--main', 'README']
|
46
|
+
|
47
|
+
desc "Generate RDOC documentation."
|
48
|
+
Rake::RDocTask.new :rdoc do |rdoc|
|
49
|
+
rdoc.rdoc_dir = 'doc'
|
50
|
+
rdoc.title = 'id3lib-ruby'
|
51
|
+
rdoc.options = RDOC_OPTS
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
rdoc.rdoc_files.include('README', 'TODO', 'CHANGES')
|
54
|
+
end
|
55
|
+
task :doc => [:rdoc]
|
56
|
+
|
57
|
+
|
58
|
+
if defined? Gem
|
59
|
+
spec = Gem::Specification.new do |s|
|
60
|
+
s.name = 'id3lib-ruby'
|
61
|
+
s.version = PKG_VERSION
|
62
|
+
s.summary =
|
63
|
+
'id3lib-ruby provides a Ruby interface to the id3lib C++ library for ' +
|
64
|
+
'easily editing ID3 tags (v1 and v2) like with pyid3lib.'
|
65
|
+
s.requirements << 'id3lib C++ library'
|
66
|
+
s.files = PKG_COMMON + FileList['ext/extconf.rb', 'ext/*.cxx']
|
67
|
+
s.extensions = ['ext/extconf.rb']
|
68
|
+
s.test_files = FileList['test/test_*.rb']
|
69
|
+
s.has_rdoc = true
|
70
|
+
s.extra_rdoc_files = FileList['README', 'CHANGES', 'TODO']
|
71
|
+
s.rdoc_options = RDOC_OPTS
|
72
|
+
s.author = 'Robin Stocker'
|
73
|
+
s.email = 'robinstocker@rubyforge.org'
|
74
|
+
s.homepage = 'http://id3lib-ruby.rubyforge.org'
|
75
|
+
s.rubyforge_project = "id3lib-ruby"
|
76
|
+
end
|
77
|
+
|
78
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
79
|
+
pkg.need_tar_gz = true
|
80
|
+
pkg.need_zip = true
|
81
|
+
end
|
82
|
+
|
83
|
+
spec_mswin32 = spec.clone
|
84
|
+
spec_mswin32.files = PKG_COMMON + FileList['ext/mswin32/id3lib_api.so']
|
85
|
+
spec_mswin32.extensions = []
|
86
|
+
spec_mswin32.require_paths = ['lib', 'ext/mswin32']
|
87
|
+
spec_mswin32.platform = Gem::Platform::WIN32
|
88
|
+
|
89
|
+
desc "Build mswin32 gem."
|
90
|
+
task :gem_mswin32 => [:ext_mswin32] do
|
91
|
+
Gem::Builder.new(spec_mswin32).build
|
92
|
+
mkdir_p "pkg"
|
93
|
+
mv "id3lib-ruby-#{PKG_VERSION}-mswin32.gem", "pkg/"
|
94
|
+
end
|
95
|
+
|
96
|
+
end # defined? Gem
|
97
|
+
|
98
|
+
|
99
|
+
task :web => [:web_doc] do
|
100
|
+
puts "# Now execute the following:"
|
101
|
+
puts "scp web/* robinstocker@rubyforge.org:/var/www/gforge-projects/id3lib-ruby/"
|
102
|
+
puts "scp -r web/doc robinstocker@rubyforge.org:/var/www/gforge-projects/id3lib-ruby/doc"
|
103
|
+
end
|
104
|
+
|
105
|
+
desc "Generate RDOC documentation on web."
|
106
|
+
Rake::RDocTask.new :web_doc do |rdoc|
|
107
|
+
rdoc.rdoc_dir = 'web/doc'
|
108
|
+
rdoc.title = 'id3lib-ruby'
|
109
|
+
rdoc.options << '--line-numbers' << '--main' << 'ID3Lib::Tag'
|
110
|
+
rdoc.rdoc_files.include('README', 'TODO', 'CHANGES')
|
111
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
112
|
+
end
|
113
|
+
|
114
|
+
task :usage_html do
|
115
|
+
require 'syntax/convertors/html'
|
116
|
+
convertor = Syntax::Convertors::HTML.for_syntax('ruby')
|
117
|
+
html = convertor.convert(File.read('usage.rb'))
|
118
|
+
puts html
|
119
|
+
end
|
data/TODO
ADDED
Binary file
|
data/lib/id3lib.rb
ADDED
@@ -0,0 +1,384 @@
|
|
1
|
+
|
2
|
+
require 'id3lib_api'
|
3
|
+
require 'id3lib/info'
|
4
|
+
require 'id3lib/accessors'
|
5
|
+
|
6
|
+
|
7
|
+
#
|
8
|
+
# This module includes all the classes and constants of id3lib-ruby.
|
9
|
+
# Have a look at ID3Lib::Tag for an introduction on how to use this library.
|
10
|
+
#
|
11
|
+
module ID3Lib
|
12
|
+
|
13
|
+
# ID3 version 1. All V constants can be used with the methods
|
14
|
+
# new, update! or strip! of ID3Lib::Tag.
|
15
|
+
V1 = 1
|
16
|
+
# ID3 version 2
|
17
|
+
V2 = 2
|
18
|
+
# No tag type
|
19
|
+
V_NONE = 0
|
20
|
+
# All tag types
|
21
|
+
V_ALL = -1
|
22
|
+
# Both ID3 versions
|
23
|
+
V_BOTH = V1 | V2
|
24
|
+
|
25
|
+
NUM = 0
|
26
|
+
ID = 1
|
27
|
+
DESC = 2
|
28
|
+
FIELDS = 3
|
29
|
+
|
30
|
+
#
|
31
|
+
# This class is the main frontend of the library.
|
32
|
+
# Use it to read and write ID3 tag data of files.
|
33
|
+
#
|
34
|
+
# === Example of use
|
35
|
+
#
|
36
|
+
# tag = ID3Lib::Tag.read('shy_boy.mp3')
|
37
|
+
#
|
38
|
+
# # Remove comments
|
39
|
+
# tag.delete_if{ |frame| frame[:id] == :COMM }
|
40
|
+
#
|
41
|
+
# # Set year
|
42
|
+
# tag.year #=> 2000
|
43
|
+
# tag.year = 2005
|
44
|
+
#
|
45
|
+
# # Apply changes
|
46
|
+
# tag.update!
|
47
|
+
#
|
48
|
+
# === Working with tags
|
49
|
+
#
|
50
|
+
# You can use a ID3Lib::Tag object like an array. In fact, it is a subclass
|
51
|
+
# of Array. An ID3Lib::Tag contains frames which are stored as hashes,
|
52
|
+
# with field IDs as keys and field values as values. The frame IDs like TIT2
|
53
|
+
# are the ones specified by the ID3 standard. If you don't know these IDs,
|
54
|
+
# you probably want to use the accessor methods described afterwards, which
|
55
|
+
# have a more natural naming.
|
56
|
+
#
|
57
|
+
# tag.each do |frame|
|
58
|
+
# p frame
|
59
|
+
# end
|
60
|
+
# #=> {:id => :TIT2, :text => "Shy Boy", :textenc => 0}
|
61
|
+
# #=> {:id => :TPE1, :text => "Katie Melua", :textenc => 0}
|
62
|
+
# #=> {:id => :TALB, :text => "Piece By Piece", :textenc => 0}
|
63
|
+
# #=> {:id => :TRCK, :text => "1/12", :textenc => 0}
|
64
|
+
# #=> {:id => :TYER, :text => "2005", :textenc => 0}
|
65
|
+
# #=> {:id => :TCON, :text => "Jazz/Blues", :textenc => 0}
|
66
|
+
#
|
67
|
+
# === Get and set frames
|
68
|
+
#
|
69
|
+
# There are a number of accessors for text frames like
|
70
|
+
# title, performer, album, track, year, comment and genre. Have a look
|
71
|
+
# at ID3Lib::Accessors for a complete list.
|
72
|
+
#
|
73
|
+
# tag.title #=> "Shy Boi"
|
74
|
+
#
|
75
|
+
# tag.title = 'Shy Boy'
|
76
|
+
# tag.title #=> "Shy Boy"
|
77
|
+
#
|
78
|
+
# tag.track #=> [1,12]
|
79
|
+
# tag.year #=> 2005
|
80
|
+
#
|
81
|
+
# You can always read and write the raw text if you want. You just have
|
82
|
+
# to use the "manual access". It is generally encouraged to use the
|
83
|
+
# #frame_text method where possible, because the other two result in
|
84
|
+
# an exception when the frame isn't found.
|
85
|
+
#
|
86
|
+
# tag.frame_text(:TRCK) #=> "1/12"
|
87
|
+
# tag.frame_text(:TLAN) #=> nil
|
88
|
+
#
|
89
|
+
# tag.frame(:TRCK)[:text] #=> "1/12"
|
90
|
+
# # Raises an exception, because nil[:text] isn't possible:
|
91
|
+
# tag.frame(:TLAN)[:text]
|
92
|
+
#
|
93
|
+
# tag.find{ |f| f[:id] == :TRCK }[:text] #=> "1/12"
|
94
|
+
# # Also raises an exception:
|
95
|
+
# tag.find{ |f| f[:id] == :TLAN }[:text]
|
96
|
+
#
|
97
|
+
# Because only text frames can be set with accessors, you have to add
|
98
|
+
# special frames by hand.
|
99
|
+
#
|
100
|
+
# # Add two comments
|
101
|
+
# tag << {:id => :COMM, :text => 'chunky bacon'}
|
102
|
+
# tag << {:id => :COMM, :text => 'really.'}
|
103
|
+
#
|
104
|
+
# # Add an attached picture
|
105
|
+
# cover = {
|
106
|
+
# :id => :APIC,
|
107
|
+
# :mimetype => 'image/jpeg',
|
108
|
+
# :picturetype => 3,
|
109
|
+
# :description => 'A pretty picture',
|
110
|
+
# :textenc => 0,
|
111
|
+
# :data => File.read('cover.jpg')
|
112
|
+
# }
|
113
|
+
# tag << cover
|
114
|
+
#
|
115
|
+
# === Get information about frames
|
116
|
+
#
|
117
|
+
# In the last example we added an APIC frame. How can we know what data
|
118
|
+
# we have to store in the APIC hash?
|
119
|
+
#
|
120
|
+
# ID3Lib::Info.frame(:APIC)[3]
|
121
|
+
# #=> [:textenc, :mimetype, :picturetype, :description, :data]
|
122
|
+
#
|
123
|
+
# We see, the last element of the info array obtained through
|
124
|
+
# ID3Lib::Info.frame is an array of field IDs needed by APIC.
|
125
|
+
#
|
126
|
+
# Have a look at the ID3Lib::Info module for detailed information.
|
127
|
+
#
|
128
|
+
# === Write changes to file
|
129
|
+
#
|
130
|
+
# When you've finished modifying a tag, don't forget to call #update! to
|
131
|
+
# write the modifications back to the file. You have to check the return
|
132
|
+
# value of update!, it returns nil on failure. This probably means that
|
133
|
+
# the file is not writeable or cannot be created.
|
134
|
+
#
|
135
|
+
# tag.update!
|
136
|
+
#
|
137
|
+
# === Getting rid of a tag
|
138
|
+
#
|
139
|
+
# Use the #strip! method to completely remove a tag from a file.
|
140
|
+
#
|
141
|
+
# tag.strip!
|
142
|
+
#
|
143
|
+
class Tag < Array
|
144
|
+
|
145
|
+
include Accessors
|
146
|
+
|
147
|
+
attr_accessor :padding
|
148
|
+
|
149
|
+
#
|
150
|
+
# Create a new Tag. When a _filename_ is supplied, the tag of the file
|
151
|
+
# is read. _tagtype_ specifies the tag type to read and defaults to
|
152
|
+
# V_ALL.
|
153
|
+
# Use one of ID3Lib::V1, ID3Lib::V2, ID3Lib::V_BOTH or ID3Lib::V_ALL.
|
154
|
+
#
|
155
|
+
# tag = ID3Lib::Tag.new('shy_boy.mp3')
|
156
|
+
#
|
157
|
+
# Only read ID3v1 tag:
|
158
|
+
#
|
159
|
+
# id3v1_tag = ID3Lib::Tag.new('piece_by_piece.mp3', ID3Lib::V1)
|
160
|
+
#
|
161
|
+
def initialize(filename, readtype=V_ALL)
|
162
|
+
@filename = filename
|
163
|
+
@readtype = readtype
|
164
|
+
@padding = true
|
165
|
+
|
166
|
+
@tag = API::Tag.new
|
167
|
+
@tag.link(@filename, @readtype)
|
168
|
+
read_frames
|
169
|
+
end
|
170
|
+
|
171
|
+
#
|
172
|
+
# Returns an estimate of the number of bytes required to store the tag
|
173
|
+
# data.
|
174
|
+
#
|
175
|
+
def size
|
176
|
+
@tag.size
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# Simple shortcut for getting a frame by its _id_.
|
181
|
+
#
|
182
|
+
# tag.frame(:TIT2)
|
183
|
+
# #=> {:id => :TIT2, :text => "Shy Boy", :textenc => 0}
|
184
|
+
#
|
185
|
+
# is the same as:
|
186
|
+
#
|
187
|
+
# tag.find{ |f| f[:id] == :TIT2 }
|
188
|
+
#
|
189
|
+
def frame(id)
|
190
|
+
find{ |f| f[:id] == id }
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
# Get the text of a frame specified by _id_. Returns nil if the
|
195
|
+
# frame can't be found.
|
196
|
+
#
|
197
|
+
# tag.find{ |f| f[:id] == :TIT2 }[:text] #=> "Shy Boy"
|
198
|
+
# tag.frame_text(:TIT2) #=> "Shy Boy"
|
199
|
+
#
|
200
|
+
# tag.find{ |f| f[:id] == :TLAN } #=> nil
|
201
|
+
# tag.frame_text(:TLAN) #=> nil
|
202
|
+
#
|
203
|
+
def frame_text(id)
|
204
|
+
f = frame(id)
|
205
|
+
f ? f[:text] : nil
|
206
|
+
end
|
207
|
+
|
208
|
+
#
|
209
|
+
# Set the text of a frame. First, all frames with the specified _id_ are
|
210
|
+
# deleted and then a new frame with _text_ is appended.
|
211
|
+
#
|
212
|
+
# tag.set_frame_text(:TLAN, 'eng')
|
213
|
+
#
|
214
|
+
def set_frame_text(id, text)
|
215
|
+
remove_frame(id)
|
216
|
+
if text
|
217
|
+
self << { :id => id, :text => text.to_s }
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
#
|
222
|
+
# Remove all frames with the specified _id_.
|
223
|
+
#
|
224
|
+
def remove_frame(id)
|
225
|
+
delete_if{ |f| f[:id] == id }
|
226
|
+
end
|
227
|
+
|
228
|
+
#
|
229
|
+
# Updates the tag. This change can't be undone. _writetype_ specifies
|
230
|
+
# which tag type to write and defaults to _readtype_ (see #new).
|
231
|
+
#
|
232
|
+
# Invalid frames or frame data is ignored. Use #invalid_frames before
|
233
|
+
# update! if you want to know if you have invalid data.
|
234
|
+
#
|
235
|
+
# Returns a number corresponding to the written tag type(s) or nil if
|
236
|
+
# the update failed.
|
237
|
+
#
|
238
|
+
# tag.update!
|
239
|
+
# id3v1_tag.update!(ID3Lib::V1)
|
240
|
+
#
|
241
|
+
def update!(writetype=@readtype)
|
242
|
+
@tag.strip(writetype)
|
243
|
+
# The following two lines are necessary because of the weird
|
244
|
+
# behaviour of id3lib.
|
245
|
+
@tag.clear
|
246
|
+
@tag.link(@filename, writetype)
|
247
|
+
|
248
|
+
delete_if do |frame|
|
249
|
+
frame_info = Info.frame(frame[:id])
|
250
|
+
next true if not frame_info
|
251
|
+
libframe = API::Frame.new(frame_info[NUM])
|
252
|
+
Frame.write(frame, libframe)
|
253
|
+
@tag.add_frame(libframe)
|
254
|
+
false
|
255
|
+
end
|
256
|
+
|
257
|
+
@tag.set_padding(@padding)
|
258
|
+
tags = @tag.update(writetype)
|
259
|
+
return tags == 0 ? nil : tags
|
260
|
+
end
|
261
|
+
|
262
|
+
#
|
263
|
+
# Strip tag from file. This is dangerous because you lose all tag
|
264
|
+
# information. Specify _striptag_ to only strip a certain tag type.
|
265
|
+
# You don't have to call #update! after #strip!.
|
266
|
+
#
|
267
|
+
# tag.strip!
|
268
|
+
# another_tag.strip!(ID3Lib::V1)
|
269
|
+
#
|
270
|
+
def strip!(striptype=V_ALL)
|
271
|
+
clear
|
272
|
+
tags = @tag.strip(striptype)
|
273
|
+
@tag.clear
|
274
|
+
@tag.link(@filename, @readtype)
|
275
|
+
tags
|
276
|
+
end
|
277
|
+
|
278
|
+
#
|
279
|
+
# Check if there is a tag of type _type_.
|
280
|
+
#
|
281
|
+
def has_tag?(type=V2)
|
282
|
+
@tag.link(@filename, V_ALL)
|
283
|
+
@tag.has_tag_type(type)
|
284
|
+
end
|
285
|
+
|
286
|
+
#
|
287
|
+
# Returns an Array of invalid frames and fields. If a frame ID is
|
288
|
+
# invalid, it alone is in the resulting array. If a frame ID is valid
|
289
|
+
# but has invalid fields, the frame ID and the invalid field IDs are
|
290
|
+
# included.
|
291
|
+
#
|
292
|
+
# tag.invalid_frames
|
293
|
+
# #=> [ [:TITS], [:TALB, :invalid] ]
|
294
|
+
#
|
295
|
+
def invalid_frames
|
296
|
+
invalid = []
|
297
|
+
each do |frame|
|
298
|
+
if not info = Info.frame(frame[:id])
|
299
|
+
# Frame ID doesn't exist.
|
300
|
+
invalid << [frame[:id]]
|
301
|
+
next
|
302
|
+
end
|
303
|
+
# Frame ID is ok, but are all fields ok?
|
304
|
+
invalid_fields = frame.keys.reject { |id|
|
305
|
+
info[FIELDS].include?(id) or id == :id
|
306
|
+
}
|
307
|
+
if not invalid_fields.empty?
|
308
|
+
invalid << [frame[:id], *invalid_fields]
|
309
|
+
end
|
310
|
+
end
|
311
|
+
invalid.empty? ? nil : invalid
|
312
|
+
end
|
313
|
+
|
314
|
+
private
|
315
|
+
|
316
|
+
def read_frames
|
317
|
+
iterator = @tag.iterator_new
|
318
|
+
while libframe = @tag.iterator_next_frame(iterator)
|
319
|
+
self << Frame.read(libframe)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
module Frame #:nodoc:
|
327
|
+
|
328
|
+
def self.read(libframe)
|
329
|
+
frame = {}
|
330
|
+
info = Info.frame_num(libframe.num)
|
331
|
+
frame[:id] = info[ID]
|
332
|
+
if info[FIELDS].include?(:textenc)
|
333
|
+
textenc = field(libframe, :textenc).integer
|
334
|
+
frame[:textenc] = textenc
|
335
|
+
end
|
336
|
+
info[FIELDS].each do |field_id|
|
337
|
+
next if field_id == :textenc
|
338
|
+
libfield = field(libframe, field_id)
|
339
|
+
frame[field_id] = if textenc and textenc > 0
|
340
|
+
libfield.unicode
|
341
|
+
else
|
342
|
+
case Info::FieldType[libfield.type]
|
343
|
+
when :integer : libfield.integer
|
344
|
+
when :binary : libfield.binary
|
345
|
+
when :text : libfield.ascii
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
frame
|
350
|
+
end
|
351
|
+
|
352
|
+
def self.write(frame, libframe)
|
353
|
+
if textenc = frame[:textenc]
|
354
|
+
field(libframe, :textenc).set_integer(textenc)
|
355
|
+
end
|
356
|
+
frame.each do |field_id, value|
|
357
|
+
next if field_id == :textenc
|
358
|
+
unless Info.frame(frame[:id])[FIELDS].include?(field_id)
|
359
|
+
# Ignore invalid fields
|
360
|
+
next
|
361
|
+
end
|
362
|
+
libfield = field(libframe, field_id)
|
363
|
+
if textenc and textenc > 0
|
364
|
+
# Special treatment for Unicode
|
365
|
+
libfield.set_encoding(textenc)
|
366
|
+
libfield.set_unicode(value)
|
367
|
+
else
|
368
|
+
case Info::FieldType[libfield.type]
|
369
|
+
when :integer : libfield.set_integer(value)
|
370
|
+
when :binary : libfield.set_binary(value)
|
371
|
+
when :text : libfield.set_ascii(value)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
def self.field(libframe, id)
|
378
|
+
libframe.field(Info.field(id)[NUM])
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|
382
|
+
|
383
|
+
|
384
|
+
end
|