id3lib-ruby 0.3.0-mswin32
Sign up to get free protection for your applications and to get access to all the features.
- 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
|