id3lib-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/id3lib.rb ADDED
@@ -0,0 +1,349 @@
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
+ self << { :id => id, :text => text }
217
+ end
218
+
219
+ #
220
+ # Remove all frames with the specified _id_.
221
+ #
222
+ def remove_frame(id)
223
+ delete_if{ |f| f[:id] == id }
224
+ end
225
+
226
+ #
227
+ # Updates the tag. This change can't be undone. _writetype_ specifies
228
+ # which tag type to write and defaults to _readtype_ (see #new).
229
+ #
230
+ # Returns a number corresponding to the written tag type(s) or nil if
231
+ # the update failed.
232
+ #
233
+ # tag.update!
234
+ # id3v1_tag.update!(ID3Lib::V1)
235
+ #
236
+ def update!(writetype=@readtype)
237
+ @tag.strip(writetype)
238
+ # The following two lines are necessary because of the weird
239
+ # behaviour of id3lib.
240
+ @tag.clear
241
+ @tag.link(@filename, writetype)
242
+
243
+ delete_if do |frame|
244
+ frame_info = Info.frame(frame[:id])
245
+ next true if not frame_info
246
+ libframe = API::Frame.new(frame_info[NUM])
247
+ Frame.write(frame, libframe)
248
+ @tag.add_frame(libframe)
249
+ false
250
+ end
251
+
252
+ @tag.set_padding(@padding)
253
+ tags = @tag.update(writetype)
254
+ return tags == 0 ? nil : tags
255
+ end
256
+
257
+ #
258
+ # Strip tag from file. This is dangerous because you lose all tag
259
+ # information. Specify _striptag_ to only strip a certain tag type.
260
+ # You don't have to call #update! after #strip!.
261
+ #
262
+ # tag.strip!
263
+ # another_tag.strip!(ID3Lib::V1)
264
+ #
265
+ def strip!(striptype=V_ALL)
266
+ clear
267
+ tags = @tag.strip(striptype)
268
+ @tag.clear
269
+ @tag.link(@filename, @readtype)
270
+ tags
271
+ end
272
+
273
+ #
274
+ # Check if there is a tag of type _type_.
275
+ #
276
+ def has_tag?(type=V2)
277
+ @tag.link(@filename, V_ALL)
278
+ @tag.has_tag_type(type)
279
+ end
280
+
281
+ private
282
+
283
+ def read_frames
284
+ iterator = @tag.iterator_new
285
+ while libframe = @tag.iterator_next_frame(iterator)
286
+ self << Frame.read(libframe)
287
+ end
288
+ end
289
+
290
+ end
291
+
292
+
293
+ module Frame #:nodoc:
294
+
295
+ def self.read(libframe)
296
+ frame = {}
297
+ info = Info.frame(libframe.num)
298
+ frame[:id] = info[ID]
299
+ if info[FIELDS].include?(:textenc)
300
+ textenc = field(libframe, :textenc).integer
301
+ frame[:textenc] = textenc
302
+ end
303
+ info[FIELDS].each do |field_id|
304
+ next if field_id == :textenc
305
+ libfield = field(libframe, field_id)
306
+ frame[field_id] = if textenc and textenc > 0
307
+ libfield.unicode
308
+ else
309
+ case Info::FieldType[libfield.type]
310
+ when :integer : libfield.integer
311
+ when :binary : libfield.binary
312
+ when :text : libfield.ascii
313
+ end
314
+ end
315
+ end
316
+ frame
317
+ end
318
+
319
+ def self.write(frame, libframe)
320
+ textenc = frame[:textenc]
321
+ field(libframe, :textenc).set_integer(textenc) if textenc
322
+ frame.each do |field_id, value|
323
+ unless Info.frame(frame[:id])[FIELDS].include?(field_id)
324
+ # TODO: Add method to check if frames are valid.
325
+ next
326
+ end
327
+ next if field_id == :textenc
328
+ libfield = field(libframe, field_id)
329
+ if textenc and textenc > 0
330
+ libfield.set_encoding(textenc)
331
+ libfield.set_unicode(value)
332
+ else
333
+ case Info::FieldType[libfield.type]
334
+ when :integer : libfield.set_integer(value)
335
+ when :binary : libfield.set_binary(value)
336
+ when :text : libfield.set_ascii(value)
337
+ end
338
+ end
339
+ end
340
+ end
341
+
342
+ def self.field(libframe, id)
343
+ libframe.field(Info.field(id)[NUM])
344
+ end
345
+
346
+ end
347
+
348
+
349
+ end
@@ -0,0 +1,260 @@
1
+
2
+ module ID3Lib
3
+
4
+ #
5
+ # The Accessors module defines accessor methods for ID3 text frames.
6
+ #
7
+ # Have a look at the source code to see the ID3 frame ID behind an accessor.
8
+ #
9
+ # === Most used accessors
10
+ #
11
+ # Here's a list of the probably most used accessors:
12
+ #
13
+ # * #title
14
+ # * #performer or #artist
15
+ # * #album
16
+ # * #genre
17
+ # * #year
18
+ # * #track
19
+ # * #part_of_set or #disc
20
+ # * #comment or #comment_frames
21
+ # * #composer
22
+ # * #bpm
23
+ #
24
+ # === Automatic conversion
25
+ #
26
+ # Some accessors automatically convert the raw text to a more
27
+ # suitable type (when reading) and back (when writing). For example,
28
+ # the track information is converted to an array of one or two items.
29
+ #
30
+ # tag.track #=> [1,12]
31
+ #
32
+ # and the year is an integer.
33
+ #
34
+ # tag.year #=> 2005
35
+ #
36
+ # Use the Tag#frame_text method to get the raw text.
37
+ #
38
+ # tag.frame_text(:TRCK) #=> "1/12"
39
+ #
40
+ module Accessors
41
+
42
+ def title() frame_text(:TIT2) end
43
+ def title=(v) set_frame_text(:TIT2, v) end
44
+
45
+ def performer() frame_text(:TPE1) end
46
+ def performer=(v) set_frame_text(:TPE1, v) end
47
+ alias_method :artist, :performer
48
+ alias_method :artist=, :performer=
49
+
50
+ def album() frame_text(:TALB) end
51
+ def album=(v) set_frame_text(:TALB, v) end
52
+
53
+ def comment() frame_text(:COMM) end
54
+ def comment=(v) set_frame_text(:COMM, v) end
55
+
56
+ def genre() frame_text(:TCON) end
57
+ def genre=(v) set_frame_text(:TCON, v) end
58
+ alias_method :content_type, :genre
59
+ alias_method :content_type=, :genre=
60
+
61
+ def bpm() frame_text(:TBPM) end
62
+ def bpm=(v) set_frame_text(:TBPM, v.to_s) end
63
+
64
+ def composer() frame_text(:TCOM) end
65
+ def composer=(v) set_frame_text(:TCOM, v) end
66
+
67
+ def copyright() frame_text(:TCOP) end
68
+ def copyright=(v) set_frame_text(:TCOP, v) end
69
+
70
+ def date() frame_text(:TDAT) end
71
+ def date=(v) set_frame_text(:TDAT, v) end
72
+
73
+ def encoding_time() frame_text(:TDEN) end
74
+ def encoding_time=(v) set_frame_text(:TDEN, v) end
75
+
76
+ def playlist_delay() frame_text(:TDLY) end
77
+ def playlist_delay=(v) set_frame_text(:TDLY, v) end
78
+
79
+ def original_release_time() frame_text(:TDOR) end
80
+ def original_release_time=(v) set_frame_text(:TDOR, v) end
81
+
82
+ def recording_time() frame_text(:TDRC) end
83
+ def recording_time=(v) set_frame_text(:TDRC, v) end
84
+
85
+ def release_time() frame_text(:TDRL) end
86
+ def release_time=(v) set_frame_text(:TDRL, v) end
87
+
88
+ def tagging_time() frame_text(:TDTG) end
89
+ def tagging_time=(v) set_frame_text(:TDTG, v) end
90
+
91
+ def involved_people() frame_text(:TIPL) end
92
+ def involved_people=(v) set_frame_text(:TIPL, v) end
93
+
94
+ def encoded_by() frame_text(:TENC) end
95
+ def encoded_by=(v) set_frame_text(:TENC, v) end
96
+
97
+ def lyricist() frame_text(:TEXT) end
98
+ def lyricist=(v) set_frame_text(:TEXT, v) end
99
+
100
+ def file_type() frame_text(:TFLT) end
101
+ def file_type=(v) set_frame_text(:TFLT, v) end
102
+
103
+ def time() frame_text(:TIME) end
104
+ def time=(v) set_frame_text(:TIME, v) end
105
+
106
+ def content_group() frame_text(:TIT1) end
107
+ def content_group=(v) set_frame_text(:TIT1, v) end
108
+
109
+ def subtitle() frame_text(:TIT3) end
110
+ def subtitle=(v) set_frame_text(:TIT3, v) end
111
+
112
+ def initial_key() frame_text(:TKEY) end
113
+ def initial_key=(v) set_frame_text(:TKEY, v) end
114
+
115
+ def language() frame_text(:TLAN) end
116
+ def language=(v) set_frame_text(:TLAN, v) end
117
+
118
+ def audio_length() frame_text(:TLEN) end
119
+ def audio_length=(v) set_frame_text(:TLEN, v) end
120
+
121
+ def musician_credits() frame_text(:TMCL) end
122
+ def musician_credits=(v) set_frame_text(:TMCL, v) end
123
+
124
+ def media_type() frame_text(:TMED) end
125
+ def media_type=(v) set_frame_text(:TMED, v) end
126
+
127
+ def mood() frame_text(:TMOO) end
128
+ def mood=(v) set_frame_text(:TMOO, v) end
129
+
130
+ def original_title() frame_text(:TOAL) end
131
+ def original_title=(v) set_frame_text(:TOAL, v) end
132
+
133
+ def original_filename() frame_text(:TOFN) end
134
+ def original_filename=(v) set_frame_text(:TOFN, v) end
135
+
136
+ def original_lyricist() frame_text(:TOLY) end
137
+ def original_lyricist=(v) set_frame_text(:TOLY, v) end
138
+
139
+ def original_performer() frame_text(:TOPE) end
140
+ def original_performer=(v) set_frame_text(:TOPE, v) end
141
+
142
+ def file_owner() frame_text(:TOWN) end
143
+ def file_owner=(v) set_frame_text(:TOWN, v) end
144
+
145
+ def band() frame_text(:TPE2) end
146
+ def band=(v) set_frame_text(:TPE2, v) end
147
+
148
+ def conductor() frame_text(:TPE3) end
149
+ def conductor=(v) set_frame_text(:TPE3, v) end
150
+
151
+ def produced_notice() frame_text(:TPRO) end
152
+ def produced_notice=(v) set_frame_text(:TPRO, v) end
153
+
154
+ def publisher() frame_text(:TPUB) end
155
+ def publisher=(v) set_frame_text(:TPUB, v) end
156
+
157
+ def internet_radio_station() frame_text(:TRSN) end
158
+ def internet_radio_station=(v) set_frame_text(:TRSN, v) end
159
+
160
+ def internet_radio_owner() frame_text(:TRSO) end
161
+ def internet_radio_owner=(v) set_frame_text(:TRSO, v) end
162
+
163
+ def album_sort_order() frame_text(:TSOA) end
164
+ def album_sort_order=(v) set_frame_text(:TSOA, v) end
165
+
166
+ def performer_sort_order() frame_text(:TSOP) end
167
+ def performer_sort_order=(v) set_frame_text(:TSOP, v) end
168
+
169
+ def title_sort_order() frame_text(:TSOT) end
170
+ def title_sort_order=(v) set_frame_text(:TSOT, v) end
171
+
172
+ def isrc() frame_text(:TSRC) end
173
+ def isrc=(v) set_frame_text(:TSRC, v) end
174
+
175
+ def encoder_settings() frame_text(:TSSE) end
176
+ def encoder_settings=(v) set_frame_text(:TSSE, v) end
177
+
178
+ def set_subtitle() frame_text(:TSST) end
179
+ def set_subtitle=(v) set_frame_text(:TSST, v) end
180
+
181
+ def terms_of_use() frame_text(:TPE2) end
182
+ def terms_of_use=(v) set_frame_text(:TPE2, v) end
183
+
184
+ def commercial_url() frame_text(:WCOM) end
185
+ def commercial_url=(v) set_frame_text(:WCOM, v) end
186
+
187
+ def copyright_url() frame_text(:WCOP) end
188
+ def copyright_url=(v) set_frame_text(:WCOP, v) end
189
+
190
+ def audio_file_url() frame_text(:WOAF) end
191
+ def audio_file_url=(v) set_frame_text(:WOAF, v) end
192
+
193
+ def performer_url() frame_text(:WOAR) end
194
+ def performer_url=(v) set_frame_text(:WOAR, v) end
195
+
196
+ def audio_source_url() frame_text(:WOAS) end
197
+ def audio_source_url=(v) set_frame_text(:WOAS, v) end
198
+
199
+ def internet_radio_url() frame_text(:WORS) end
200
+ def internet_radio_url=(v) set_frame_text(:WORS, v) end
201
+
202
+ def payment_url() frame_text(:WPAY) end
203
+ def payment_url=(v) set_frame_text(:WPAY, v) end
204
+
205
+ def publisher_url() frame_text(:WPUB) end
206
+ def publisher_url=(v) set_frame_text(:WPUB, v) end
207
+
208
+ # Returns an array of comment frames.
209
+ def comment_frames() select{ |f| f[:id] == :COMM } end
210
+
211
+ #
212
+ # Returns an array of one or two numbers.
213
+ #
214
+ # tag.track #=> [3,11]
215
+ # tag.frame_text(:TRCK) #=> "3/11"
216
+ #
217
+ # tag_2.track #=> [5]
218
+ # tag_2.frame_text(:TRCK) #=> "5"
219
+ #
220
+ def track() frame_text(:TRCK).split('/').collect{ |s| s.to_i } end
221
+
222
+ #
223
+ # Assign either an array or a string.
224
+ #
225
+ # tag.track = [4,11]
226
+ # tag.track = [5]
227
+ # tag.track = "4/11"
228
+ #
229
+ def track=(v) set_frame_text(:TRCK, (v.join('/') rescue v.to_s)) end
230
+
231
+ #
232
+ # Returns an array of one or two numbers like #track.
233
+ #
234
+ def part_of_set() frame_text(:TPOS).split('/').collect{ |s| s.to_i } end
235
+ alias_method :disc, :part_of_set
236
+
237
+ #
238
+ # Assign either an array or a string like #track=.
239
+ #
240
+ def part_of_set=(v) set_frame_text(:TPOS, (v.join('/') rescue v.to_s)) end
241
+ alias_method :disc=, :part_of_set=
242
+
243
+ #
244
+ # Returns a number.
245
+ #
246
+ # tag.year #=> 2004
247
+ #
248
+ def year() frame_text(:TYER).to_i end
249
+
250
+ #
251
+ # Assign a number or a string.
252
+ #
253
+ # tag.year = 2005
254
+ # tag.year = "2005"
255
+ #
256
+ def year=(v) set_frame_text(:TYER, v.to_s) end
257
+
258
+ end
259
+
260
+ end