id3lib-ruby 0.6.0-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,418 @@
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
+ VERSION = '0.6.0'
13
+
14
+ # ID3 version 1. All V constants can be used with the methods
15
+ # new, update! or strip! of ID3Lib::Tag.
16
+ V1 = 1
17
+ # ID3 version 2
18
+ V2 = 2
19
+ # No tag type
20
+ V_NONE = 0
21
+ # All tag types
22
+ V_ALL = 0xff
23
+ # Both ID3 versions
24
+ V_BOTH = V1 | V2
25
+
26
+ NUM = 0
27
+ ID = 1
28
+ DESC = 2
29
+ FIELDS = 3
30
+
31
+ #
32
+ # This class is the main frontend of the library.
33
+ # Use it to read and write ID3 tag data of files.
34
+ #
35
+ # === Example of use
36
+ #
37
+ # tag = ID3Lib::Tag.new('shy_boy.mp3')
38
+ #
39
+ # # Remove comments
40
+ # tag.delete_if{ |frame| frame[:id] == :COMM }
41
+ #
42
+ # # Set year
43
+ # tag.year #=> 2000
44
+ # tag.year = 2005
45
+ #
46
+ # # Apply changes
47
+ # tag.update!
48
+ #
49
+ # === Working with tags
50
+ #
51
+ # You can use a ID3Lib::Tag object like an array. In fact, it is a subclass
52
+ # of Array. An ID3Lib::Tag contains frames which are stored as hashes,
53
+ # with field IDs as keys and field values as values. The frame IDs like TIT2
54
+ # are the ones specified by the ID3 standard. If you don't know these IDs,
55
+ # you probably want to use the accessor methods described afterwards, which
56
+ # have a more natural naming.
57
+ #
58
+ # tag.each do |frame|
59
+ # p frame
60
+ # end
61
+ # #=> {:id => :TIT2, :text => "Shy Boy", :textenc => 0}
62
+ # #=> {:id => :TPE1, :text => "Katie Melua", :textenc => 0}
63
+ # #=> {:id => :TALB, :text => "Piece By Piece", :textenc => 0}
64
+ # #=> {:id => :TRCK, :text => "1/12", :textenc => 0}
65
+ # #=> {:id => :TYER, :text => "2005", :textenc => 0}
66
+ # #=> {:id => :TCON, :text => "Jazz/Blues", :textenc => 0}
67
+ #
68
+ # === Get and set frames
69
+ #
70
+ # There are a number of accessors for text frames like
71
+ # title, performer, album, track, year, comment and genre. Have a look
72
+ # at ID3Lib::Accessors for a complete list. They can only be used for
73
+ # text that is encoded with ISO-8859-1.
74
+ #
75
+ # tag.title #=> "Shy Boi"
76
+ #
77
+ # tag.title = 'Shy Boy'
78
+ # tag.title #=> "Shy Boy"
79
+ #
80
+ # tag.track #=> [1,12]
81
+ # tag.year #=> 2005
82
+ #
83
+ # You can always read and write the raw text if you want. You just have
84
+ # to use the "manual access". It is generally encouraged to use the
85
+ # #frame_text method where possible, because the other two result in
86
+ # an exception when the frame isn't found.
87
+ #
88
+ # tag.frame_text(:TRCK) #=> "1/12"
89
+ # tag.frame_text(:TLAN) #=> nil
90
+ #
91
+ # tag.frame(:TRCK)[:text] #=> "1/12"
92
+ # # Raises an exception, because nil[:text] isn't possible:
93
+ # tag.frame(:TLAN)[:text]
94
+ #
95
+ # tag.find{ |f| f[:id] == :TRCK }[:text] #=> "1/12"
96
+ # # Also raises an exception:
97
+ # tag.find{ |f| f[:id] == :TLAN }[:text]
98
+ #
99
+ # Because only ISO-8859-1 encoded text frames can be set with accessors, you
100
+ # have to add special frames by hand.
101
+ #
102
+ # # Add two comments
103
+ # tag << {:id => :COMM, :text => 'chunky bacon'}
104
+ # tag << {:id => :COMM, :text => 'really.'}
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
+ #
110
+ # # Add an attached picture
111
+ # cover = {
112
+ # :id => :APIC,
113
+ # :mimetype => 'image/jpeg',
114
+ # :picturetype => 3,
115
+ # :description => 'A pretty picture',
116
+ # :textenc => 0,
117
+ # :data => File.read('cover.jpg')
118
+ # }
119
+ # tag << cover
120
+ #
121
+ # === Get information about frames
122
+ #
123
+ # In the last example we added an APIC frame. How can we know what data
124
+ # we have to store in the APIC hash?
125
+ #
126
+ # ID3Lib::Info.frame(:APIC)[3]
127
+ # #=> [:textenc, :mimetype, :picturetype, :description, :data]
128
+ #
129
+ # We see, the last element of the info array obtained through
130
+ # ID3Lib::Info.frame is an array of field IDs needed by APIC.
131
+ #
132
+ # Have a look at the ID3Lib::Info module for detailed information.
133
+ #
134
+ # === Write changes to file
135
+ #
136
+ # When you've finished modifying a tag, don't forget to call #update! to
137
+ # write the modifications back to the file. You have to check the return
138
+ # value of update!, it returns nil on failure. This probably means that
139
+ # the file is not writeable or cannot be created.
140
+ #
141
+ # tag.update!
142
+ #
143
+ # === Getting rid of a tag
144
+ #
145
+ # Use the #strip! method to completely remove a tag from a file.
146
+ #
147
+ # tag.strip!
148
+ #
149
+ class Tag < Array
150
+
151
+ include Accessors
152
+
153
+ attr_accessor :padding
154
+
155
+ #
156
+ # Create a new Tag. When a _filename_ is supplied, the tag of the file
157
+ # is read. _tagtype_ specifies the tag type to read and defaults to
158
+ # V_ALL.
159
+ # Use one of ID3Lib::V1, ID3Lib::V2, ID3Lib::V_BOTH or ID3Lib::V_ALL.
160
+ #
161
+ # tag = ID3Lib::Tag.new('shy_boy.mp3')
162
+ #
163
+ # Only read ID3v1 tag:
164
+ #
165
+ # id3v1_tag = ID3Lib::Tag.new('piece_by_piece.mp3', ID3Lib::V1)
166
+ #
167
+ def initialize(filename, readtype=V_ALL)
168
+ @filename = filename
169
+ @readtype = readtype
170
+ @padding = true
171
+
172
+ @tag = API::Tag.new
173
+ @tag.link(@filename, @readtype)
174
+ read_frames
175
+ end
176
+
177
+ #
178
+ # Returns an estimate of the number of bytes required to store the tag
179
+ # data.
180
+ #
181
+ def size
182
+ @tag.size
183
+ end
184
+
185
+ #
186
+ # Simple shortcut for getting a frame by its _id_.
187
+ #
188
+ # tag.frame(:TIT2)
189
+ # #=> {:id => :TIT2, :text => "Shy Boy", :textenc => 0}
190
+ #
191
+ # is the same as:
192
+ #
193
+ # tag.find{ |f| f[:id] == :TIT2 }
194
+ #
195
+ def frame(id)
196
+ find{ |f| f[:id] == id }
197
+ end
198
+
199
+ #
200
+ # Get the text of a frame specified by _id_. Returns nil if the
201
+ # frame can't be found.
202
+ #
203
+ # tag.find{ |f| f[:id] == :TIT2 }[:text] #=> "Shy Boy"
204
+ # tag.frame_text(:TIT2) #=> "Shy Boy"
205
+ #
206
+ # tag.find{ |f| f[:id] == :TLAN } #=> nil
207
+ # tag.frame_text(:TLAN) #=> nil
208
+ #
209
+ def frame_text(id)
210
+ f = frame(id)
211
+ f ? f[:text] : nil
212
+ end
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
+
229
+ #
230
+ # Set the text of a frame. First, all frames with the specified _id_ are
231
+ # deleted and then a new frame with _text_ is appended.
232
+ #
233
+ # tag.set_frame_text(:TLAN, 'eng')
234
+ #
235
+ def set_frame_text(id, text)
236
+ remove_frame(id)
237
+ if text
238
+ self << { :id => id, :text => text.to_s }
239
+ end
240
+ end
241
+
242
+ #
243
+ # Remove all frames with the specified _id_.
244
+ #
245
+ def remove_frame(id)
246
+ delete_if{ |f| f[:id] == id }
247
+ end
248
+
249
+ #
250
+ # Updates the tag. This change can't be undone. _writetype_ specifies
251
+ # which tag type to write and defaults to _readtype_ (see #new).
252
+ #
253
+ # Invalid frames or frame data is ignored. Use #invalid_frames before
254
+ # update! if you want to know if you have invalid data.
255
+ #
256
+ # Returns a number corresponding to the written tag type(s) or nil if
257
+ # the update failed.
258
+ #
259
+ # tag.update!
260
+ # id3v1_tag.update!(ID3Lib::V1)
261
+ #
262
+ def update!(writetype=@readtype)
263
+ @tag.strip(writetype)
264
+ # The following two lines are necessary because of the weird
265
+ # behaviour of id3lib.
266
+ @tag.clear
267
+ @tag.link(@filename, writetype)
268
+
269
+ delete_if do |frame|
270
+ frame_info = Info.frame(frame[:id])
271
+ next true if not frame_info
272
+ libframe = API::Frame.new(frame_info[NUM])
273
+ Frame.write(frame, libframe)
274
+ @tag.add_frame(libframe)
275
+ false
276
+ end
277
+
278
+ @tag.set_padding(@padding)
279
+ tags = @tag.update(writetype)
280
+ return tags == 0 ? nil : tags
281
+ end
282
+
283
+ #
284
+ # Strip tag from file. This is dangerous because you lose all tag
285
+ # information. Specify _striptag_ to only strip a certain tag type.
286
+ # You don't have to call #update! after #strip!.
287
+ #
288
+ # tag.strip!
289
+ # another_tag.strip!(ID3Lib::V1)
290
+ #
291
+ def strip!(striptype=V_ALL)
292
+ clear
293
+ tags = @tag.strip(striptype)
294
+ @tag.clear
295
+ @tag.link(@filename, @readtype)
296
+ tags
297
+ end
298
+
299
+ #
300
+ # Check if there is a tag of type _type_.
301
+ #
302
+ def has_tag?(type=V2)
303
+ @tag.link(@filename, V_ALL)
304
+ @tag.has_tag_type(type)
305
+ end
306
+
307
+ #
308
+ # Returns an Array of invalid frames and fields. If a frame ID is
309
+ # invalid, it alone is in the resulting array. If a frame ID is valid
310
+ # but has invalid fields, the frame ID and the invalid field IDs are
311
+ # included.
312
+ #
313
+ # tag.invalid_frames
314
+ # #=> [ [:TITS], [:TALB, :invalid] ]
315
+ #
316
+ def invalid_frames
317
+ invalid = []
318
+ each do |frame|
319
+ if not info = Info.frame(frame[:id])
320
+ # Frame ID doesn't exist.
321
+ invalid << [frame[:id]]
322
+ next
323
+ end
324
+ # Frame ID is ok, but are all fields ok?
325
+ invalid_fields = frame.keys.reject { |id|
326
+ info[FIELDS].include?(id) or id == :id
327
+ }
328
+ if not invalid_fields.empty?
329
+ invalid << [frame[:id], *invalid_fields]
330
+ end
331
+ end
332
+ invalid.empty? ? nil : invalid
333
+ end
334
+
335
+ private
336
+
337
+ def read_frames
338
+ iterator = @tag.create_iterator
339
+ while libframe = iterator.get_next
340
+ self << Frame.read(libframe)
341
+ end
342
+ end
343
+
344
+ end
345
+
346
+
347
+ module Frame #:nodoc:
348
+
349
+ def self.read(libframe)
350
+ frame = {}
351
+ info = Info.frame_num(libframe.get_id)
352
+ frame[:id] = info[ID]
353
+
354
+ info[FIELDS].each do |field_id|
355
+ libfield = field(libframe, field_id)
356
+ unless libfield
357
+ warn "id3lib-ruby: Invalid field #{field_id.inspect} in " \
358
+ "#{frame[:id].inspect}, please report this as a bug."
359
+ next
360
+ end
361
+ frame[field_id] =
362
+ case Info::FieldType[libfield.get_type]
363
+ when :integer
364
+ libfield.get_integer
365
+ when :binary
366
+ libfield.get_binary
367
+ when :text
368
+ if libfield.get_encoding > 0
369
+ libfield.get_unicode
370
+ else
371
+ libfield.get_ascii
372
+ end
373
+ end
374
+ end
375
+
376
+ frame
377
+ end
378
+
379
+ def self.write(frame, libframe)
380
+ if textenc = frame[:textenc]
381
+ field(libframe, :textenc).set_integer(textenc)
382
+ end
383
+
384
+ frame.each do |field_id, value|
385
+ next if field_id == :textenc
386
+ unless Info.frame(frame[:id])[FIELDS].include?(field_id)
387
+ # Ignore invalid fields
388
+ next
389
+ end
390
+
391
+ libfield = field(libframe, field_id)
392
+ next unless libfield
393
+ case Info::FieldType[libfield.get_type]
394
+ when :integer
395
+ libfield.set_integer(value)
396
+ when :binary
397
+ libfield.set_binary(value)
398
+ when :text
399
+ if textenc and textenc > 0 and
400
+ [:text, :description, :filename].include?(field_id)
401
+ # Special treatment for Unicode
402
+ libfield.set_encoding(textenc)
403
+ libfield.set_unicode(value)
404
+ else
405
+ libfield.set_ascii(value)
406
+ end
407
+ end
408
+ end
409
+ end
410
+
411
+ def self.field(libframe, id)
412
+ libframe.get_field(Info.field(id)[NUM])
413
+ end
414
+
415
+ end
416
+
417
+
418
+ end
@@ -0,0 +1,103 @@
1
+
2
+ module ID3Lib
3
+
4
+ #
5
+ # The Accessors module defines accessor methods for ID3 text frames.
6
+ #
7
+ # === Most used accessors
8
+ #
9
+ # Here's a list of the most used accessors:
10
+ #
11
+ # * #title
12
+ # * #performer or #artist
13
+ # * #album
14
+ # * #genre or #content_type
15
+ # * #year
16
+ # * #track
17
+ # * #part_of_set or #disc
18
+ # * #comment or #comment_frames
19
+ # * #composer
20
+ # * #bpm
21
+ #
22
+ module Accessors
23
+
24
+ def title() frame_text(:TIT2) end
25
+ def title=(v) set_frame_text(:TIT2, v) end
26
+
27
+ def performer() frame_text(:TPE1) end
28
+ def performer=(v) set_frame_text(:TPE1, v) end
29
+ alias artist performer
30
+ alias artist= performer=
31
+
32
+ def album() frame_text(:TALB) end
33
+ def album=(v) set_frame_text(:TALB, v) end
34
+
35
+ def genre() frame_text(:TCON) end
36
+ def genre=(v) set_frame_text(:TCON, v) end
37
+ alias content_type genre
38
+ alias content_type= genre=
39
+
40
+ def year() frame_text(:TYER) end
41
+ def year=(v) set_frame_text(:TYER, v) end
42
+
43
+ def track() frame_text(:TRCK) end
44
+ def track=(v) set_frame_text(:TRCK, v) end
45
+
46
+ def part_of_set() frame_text(:TPOS) end
47
+ def part_of_set=(v) set_frame_text(:TPOS, v) end
48
+ alias disc part_of_set
49
+ alias disc= part_of_set=
50
+
51
+ def comment() frame_text(:COMM) end
52
+ def comment=(v) set_frame_text(:COMM, v) end
53
+
54
+ # Returns an array of comment frames.
55
+ def comment_frames() select{ |f| f[:id] == :COMM } end
56
+
57
+ def composer() frame_text(:TCOM) end
58
+ def composer=(v) set_frame_text(:TCOM, v) end
59
+
60
+ def grouping() frame_text(:TIT1) end
61
+ def grouping=(v) set_frame_text(:TIT1, v) end
62
+
63
+ def bpm() frame_text(:TBPM) end
64
+ def bpm=(v) set_frame_text(:TBPM, v.to_s) end
65
+
66
+ def subtitle() frame_text(:TIT3) end
67
+ def subtitle=(v) set_frame_text(:TIT3, v) end
68
+
69
+ def date() frame_text(:TDAT) end
70
+ def date=(v) set_frame_text(:TDAT, v) end
71
+
72
+ def time() frame_text(:TIME) end
73
+ def time=(v) set_frame_text(:TIME, v) end
74
+
75
+ def language() frame_text(:TLAN) end
76
+ def language=(v) set_frame_text(:TLAN, v) end
77
+
78
+ def lyrics() frame_text(:USLT) end
79
+ def lyrics=(v) set_frame_text(:USLT, v) end
80
+
81
+ def lyricist() frame_text(:TEXT) end
82
+ def lyricist=(v) set_frame_text(:TEXT, v) end
83
+
84
+ def band() frame_text(:TPE2) end
85
+ def band=(v) set_frame_text(:TPE2, v) end
86
+
87
+ def conductor() frame_text(:TPE3) end
88
+ def conductor=(v) set_frame_text(:TPE3, v) end
89
+
90
+ def interpreted_by() frame_text(:TPE4) end
91
+ def interpreted_by=(v) set_frame_text(:TPE4, v) end
92
+ alias remixed_by interpreted_by
93
+ alias remixed_by= interpreted_by=
94
+
95
+ def publisher() frame_text(:TPUB) end
96
+ def publisher=(v) set_frame_text(:TPUB, v) end
97
+
98
+ def encoded_by() frame_text(:TENC) end
99
+ def encoded_by=(v) set_frame_text(:TENC, v) end
100
+
101
+ end
102
+
103
+ end