easytag 0.2.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d32bfe7e7828920f2801fd17d56c52362eeb9a97
4
- data.tar.gz: 2e8739c3b7ed170e5db24ab52e75fb6494d777c6
3
+ metadata.gz: 1eef251238ecbdd6cc6b83991a3aad154a2a9007
4
+ data.tar.gz: 844c9b1b74f2a9cd67127e4a8d56a79f0387de48
5
5
  SHA512:
6
- metadata.gz: fc522bd6064e15d3e08bfc13d0e0a5ff659730f7a684c4141ad3551e8dbce792bec92f9cee40a8772c64a991352d6d8be600d67b9c02fb696779c47e1485bb52
7
- data.tar.gz: 41a958d976e06dd70321632b9b080d350ed82b2069124f26092b79f732f24fb90eadc0556368b277d977116a7196b78ca6d610468130f86a2eeac1a85a016b78
6
+ metadata.gz: 0a54438c37cade246206bc4f7bf0d0f0458e7d1fb2d002f09c642aca7a8f818aa7a3e9f54ceba51bbca22cb99fd9c75a330d11fb666fcae830706fe69062f10d
7
+ data.tar.gz: 5dbbd2002796e72d4c8884950e066be03054e88eee2de85d0f91539b8b84e4bfc347136afce68f9caf288097d0259f44bfb49c282f45d927f4737ec191247d37
data/CHANGELOG.md CHANGED
@@ -1,4 +1,22 @@
1
- ##### v0.2.0 (Not yet released) #####
1
+ ##### v0.3.0 (2013-05-29) #####
2
+ * added:
3
+ - `#encoded_by`
4
+ - `#encoder_settings`
5
+ - `#group`
6
+ - `#composer`
7
+ - `#lyrics`
8
+ - `#compilation?`
9
+ - `#subtitle`
10
+ - `#bpm`
11
+ - `#lyricist`
12
+ - `#copyright`
13
+ - `#comment`
14
+
15
+ * changed:
16
+ - `#comments` now returns an array, `#comment` is the
17
+ equivalent of the old behavior
18
+
19
+ ##### v0.2.0 (2013-05-25) #####
2
20
  * added:
3
21
  - `#track_num`
4
22
  - `#disc_num`
data/easytag.gemspec CHANGED
@@ -6,7 +6,11 @@ Gem::Specification.new do |s|
6
6
  s.name = 'easytag'
7
7
  s.version = EasyTag::VERSION
8
8
  s.summary = 'A simple audio metadata tagging interface'
9
- s.description = `head -n1 README.md | ./bin/strip_md.rb`
9
+ s.description = <<-EOF
10
+ EasyTag is an abstraction layer to the TagLib audio tagging library.
11
+ It is designed to provide a simple and consistent API regardless
12
+ of file format being read.
13
+ EOF
10
14
  s.authors = ['Chris Lucas']
11
15
  s.email = ['chris@chrisjlucas.com']
12
16
  s.homepage = 'https://github.com/cjlucas/ruby-easytag'
File without changes
@@ -0,0 +1,103 @@
1
+ module EasyTag::Attributes
2
+ # for type casting
3
+ module Type
4
+ STRING = 0
5
+ INT = 1
6
+ FLOAT = 2
7
+ INT_LIST = 3
8
+ STRING_LIST = 4
9
+ BOOLEAN = 5
10
+ DATETIME = 6
11
+ end
12
+ class BaseAttribute
13
+ Utilities = EasyTag::Utilities
14
+
15
+ def initialize(args)
16
+ @name = args[:name]
17
+ @default = args[:default]
18
+ @type = args[:type] || Type::STRING
19
+ @options = args[:options] || {}
20
+ @ivar = BaseAttribute.name_to_ivar(@name)
21
+
22
+ if args[:handler].is_a?(Symbol)
23
+ @handler = method(args[:handler])
24
+ elsif args[:handler].is_a?(Proc)
25
+ @handler = args[:handler]
26
+ end
27
+
28
+ # fill default options
29
+
30
+ # Remove nil objects from array (post process)
31
+ @options[:compact] ||= false
32
+ # normalizes key (if hash) (handler)
33
+ @options[:normalize] ||= false
34
+ # cast key (if hash) to symbol (handler)
35
+ @options[:to_sym] ||= false
36
+
37
+ end
38
+
39
+ def self.can_clone?(obj)
40
+ obj.is_a?(String) || obj.is_a?(Array) || obj.is_a?(Hash)
41
+ end
42
+
43
+ def self.deep_copy(obj)
44
+ Marshal.load(Marshal.dump(obj))
45
+ end
46
+
47
+ def default
48
+ BaseAttribute.can_clone?(@default) ?
49
+ BaseAttribute.deep_copy(@default) : @default
50
+ end
51
+
52
+ def call(iface)
53
+ #puts 'entered call()'
54
+ data = @handler.call(iface)
55
+ data = type_cast(data)
56
+ post_process(data)
57
+ end
58
+
59
+ def type_cast(data)
60
+ case @type
61
+ when Type::INT
62
+ data = data.to_i
63
+ when Type::DATETIME
64
+ data = Utilities.get_datetime(data.to_s)
65
+ end
66
+
67
+ data
68
+ end
69
+
70
+ def post_process(data)
71
+ if @options[:is_flag]
72
+ data = data.to_i == 1 ? true : false
73
+ end
74
+
75
+ # fall back to default if data is nil
76
+ data = BaseAttribute.obj_or_nil(data) || default
77
+
78
+ # run obj_or_nil on each item in array
79
+ data.map! { |item| BaseAttribute.obj_or_nil(item) } if data.is_a?(Array)
80
+
81
+ if @options[:compact] && data.respond_to?(:compact!)
82
+ data.compact!
83
+ end
84
+
85
+ data
86
+ end
87
+ # avoid returing empty objects
88
+ def self.obj_or_nil(o)
89
+ if o.class == String
90
+ ret = o.empty? ? nil : o
91
+ else
92
+ o
93
+ end
94
+ end
95
+
96
+ def self.name_to_ivar(name)
97
+ name = name.to_s if name.class == Symbol
98
+ name.gsub!(/\?/, '')
99
+ name.insert(0, '@')
100
+ name.to_sym
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,382 @@
1
+ require 'taglib'
2
+
3
+ require 'easytag/image'
4
+ require 'easytag/util'
5
+ require 'easytag/attributes/base'
6
+
7
+ module EasyTag::Attributes
8
+ class MP3Attribute < BaseAttribute
9
+ attr_reader :name, :ivar
10
+
11
+ def initialize(args)
12
+ super(args)
13
+ @id3v2_frames = args[:id3v2_frames] || []
14
+ @id3v1_tag = args[:id3v1_tag] || nil
15
+
16
+ # fill default options
17
+
18
+ # ID3 stores boolean values as numeric strings
19
+ # set to true to enable type casting (post process)
20
+ @options[:is_flag] ||= false
21
+ # return entire field list instead of first item in field list
22
+ @options[:field_list] ||= false
23
+ end
24
+
25
+
26
+ def frames_for_id(id, iface)
27
+ iface.info.id3v2_tag.frame_list(id)
28
+ end
29
+
30
+ def first_frame_for_id(id, iface)
31
+ frames_for_id(id, iface).first
32
+ end
33
+
34
+ def data_from_frame(frame)
35
+ data = nil
36
+ if frame.is_a?(TagLib::ID3v2::TextIdentificationFrame)
37
+ field_list = frame.field_list
38
+ data = @options[:field_list] ? field_list : field_list.first
39
+ elsif frame.is_a?(TagLib::ID3v2::UnsynchronizedLyricsFrame)
40
+ data = frame.text
41
+ elsif frame.is_a?(TagLib::ID3v2::CommentsFrame)
42
+ data = frame.text
43
+ elsif frame.is_a?(TagLib::ID3v2::AttachedPictureFrame)
44
+ data = EasyTag::Image.new(frame.picture)
45
+ data.desc = frame.description
46
+ data.type = frame.type
47
+ data.mime_type = frame.mime_type
48
+ else
49
+ warn 'no defined frames match the given frame'
50
+ end
51
+
52
+ data
53
+ end
54
+
55
+ #
56
+ # read handlers
57
+ #
58
+
59
+ # read_all_id3
60
+ #
61
+ # gets data from each frame id given
62
+ # only falls back to the id3v1 tag if none found
63
+ def read_all_id3(iface)
64
+ frames = []
65
+ @id3v2_frames.each do |f|
66
+ frames += frames_for_id(f, iface)
67
+ end
68
+
69
+ data = []
70
+ # only check id3v1 if no id3v2 frames found
71
+ if frames.empty?
72
+ data << iface.info.id3v1_tag.send(@id3v1_tag) unless @id3v1_tag.nil?
73
+ else
74
+ frames.each { |frame| data << data_from_frame(frame) }
75
+ end
76
+
77
+ data
78
+ end
79
+
80
+ # read_first_id3
81
+ #
82
+ # Similar to read_all_id3, but optimized for reading only one frame at max
83
+ def read_first_id3(iface)
84
+ frame = nil
85
+ @id3v2_frames.each do |f|
86
+ frame = first_frame_for_id(f, iface) if frame.nil?
87
+ end
88
+
89
+ if frame.nil?
90
+ data = iface.info.id3v1_tag.send(@id3v1_tag) unless @id3v1_tag.nil?
91
+ else
92
+ data = data_from_frame(frame)
93
+ end
94
+
95
+ data
96
+ end
97
+
98
+ def read_int_pair(iface)
99
+ int_pair_str = read_first_id3(iface).to_s
100
+ EasyTag::Utilities.get_int_pair(int_pair_str)
101
+ end
102
+
103
+ def read_field_list_as_key_value(iface)
104
+ kv_hash = {}
105
+ frame_data = read_all_id3(iface)
106
+
107
+ frame_data.each do |data|
108
+ key, value = data
109
+ key = Utilities.normalize_string(key) if @options[:normalize]
110
+ key = key.to_sym if @options[:to_sym]
111
+ kv_hash[key] = value
112
+ end
113
+
114
+ kv_hash
115
+ end
116
+
117
+ def read_date(iface)
118
+ id3v1 = iface.info.id3v1_tag
119
+
120
+ v10_year = id3v1.year.to_s if id3v1.year > 0
121
+ v23_year = data_from_frame(first_frame_for_id('TYER', iface))
122
+ v23_date = data_from_frame(first_frame_for_id('TDAT', iface))
123
+ v24_date = data_from_frame(first_frame_for_id('TDRC', iface))
124
+
125
+ # check variables in order of importance
126
+ date_str = v24_date || v23_year || v10_year
127
+ # only append v23_date if date_str is currently a year
128
+ date_str << v23_date unless v23_date.nil? or date_str.length > 4
129
+ puts "MP3#date: date_str = \"#{date_str}\"" if $DEBUG
130
+
131
+ date_str
132
+ end
133
+ end
134
+ end
135
+
136
+ module EasyTag::Attributes
137
+ MP3_ATTRIB_ARGS = [
138
+ # title
139
+ {
140
+ :name => :title,
141
+ :id3v2_frames => ['TIT2'],
142
+ :id3v1_tag => :title,
143
+ :handler => :read_first_id3,
144
+ :type => Type::STRING,
145
+ },
146
+
147
+ # title_sort_order
148
+ # TSOT - (v2.4 only)
149
+ # XSOT - Musicbrainz Picard custom
150
+ {
151
+ :name => :title_sort_order,
152
+ :id3v2_frames => ['TSOT', 'XSOT'],
153
+ :handler => :read_first_id3,
154
+ },
155
+
156
+ # subtitle
157
+ {
158
+ :name => :subtitle,
159
+ :id3v2_frames => ['TIT1'],
160
+ :handler => :read_first_id3,
161
+ },
162
+
163
+ # artist
164
+ {
165
+ :name => :artist,
166
+ :id3v2_frames => ['TPE1'],
167
+ :id3v1_tag => :artist,
168
+ :handler => :read_first_id3,
169
+ },
170
+
171
+ # artist_sort_order
172
+ # TSOP - (v2.4 only)
173
+ # XSOP - Musicbrainz Picard custom
174
+ {
175
+ :name => :artist_sort_order,
176
+ :id3v2_frames => ['TSOP', 'XSOP'],
177
+ :handler => :read_first_id3,
178
+ },
179
+
180
+ # album_artist
181
+ {
182
+ :name => :album_artist,
183
+ :id3v2_frames => ['TPE2'],
184
+ :handler => :read_first_id3,
185
+ },
186
+
187
+ # album_artist_sort_order
188
+ {
189
+ :name => :album_artist_sort_order,
190
+ :handler => lambda { |iface| iface.user_info[:albumartistsort] }
191
+ },
192
+
193
+ # album
194
+ {
195
+ :name => :album,
196
+ :id3v2_frames => ['TALB'],
197
+ :id3v1_tag => :album,
198
+ :handler => :read_first_id3,
199
+ },
200
+
201
+ # compilation?
202
+ {
203
+ :name => :compilation?,
204
+ :id3v2_frames => ['TCMP'],
205
+ :default => false,
206
+ :handler => :read_first_id3,
207
+ :options => {:is_flag => true},
208
+ },
209
+
210
+ # album_sort_order
211
+ # TSOA - (v2.4 only)
212
+ # XSOA - Musicbrainz Picard custom
213
+ {
214
+ :name => :album_sort_order,
215
+ :id3v2_frames => ['TSOA', 'XSOA'],
216
+ :handler => :read_first_id3,
217
+ },
218
+
219
+ # genre
220
+ {
221
+ :name => :genre,
222
+ :id3v2_frames => ['TCON'],
223
+ :id3v1_tag => :genre,
224
+ :handler => :read_first_id3,
225
+ },
226
+
227
+ # disc_subtitle
228
+ {
229
+ :name => :disc_subtitle,
230
+ :id3v2_frames => ['TSST'],
231
+ :handler => :read_first_id3,
232
+ },
233
+
234
+ # media
235
+ {
236
+ :name => :media,
237
+ :id3v2_frames => ['TMED'],
238
+ :handler => :read_first_id3,
239
+ },
240
+
241
+ # label
242
+ {
243
+ :name => :label,
244
+ :id3v2_frames => ['TPUB'],
245
+ :handler => :read_first_id3,
246
+ },
247
+
248
+ # encoded_by
249
+ {
250
+ :name => :encoded_by,
251
+ :id3v2_frames => ['TENC'],
252
+ :handler => :read_first_id3,
253
+ },
254
+
255
+ # encoder_settings
256
+ {
257
+ :name => :encoder_settings,
258
+ :id3v2_frames => ['TSSE'],
259
+ :handler => :read_first_id3,
260
+ },
261
+
262
+ # group
263
+ {
264
+ :name => :group,
265
+ :id3v2_frames => ['TIT1'],
266
+ :handler => :read_first_id3,
267
+ },
268
+
269
+ # composer
270
+ {
271
+ :name => :composer,
272
+ :id3v2_frames => ['TCOM'],
273
+ :handler => :read_first_id3,
274
+ },
275
+
276
+ # lyrics
277
+ {
278
+ :name => :lyrics,
279
+ :id3v2_frames => ['USLT'],
280
+ :handler => :read_first_id3,
281
+ },
282
+
283
+ # lyricist
284
+ {
285
+ :name => :lyricist,
286
+ :id3v2_frames => ['TEXT'],
287
+ :handler => :read_first_id3,
288
+ },
289
+
290
+ # copyright
291
+ {
292
+ :name => :copyright,
293
+ :id3v2_frames => ['TCOP'],
294
+ :handler => :read_first_id3,
295
+ },
296
+
297
+ # bpm
298
+ {
299
+ :name => :bpm,
300
+ :id3v2_frames => ['TBPM'],
301
+ :handler => :read_first_id3,
302
+ :type => Type::INT,
303
+ },
304
+
305
+ # track_num
306
+ {
307
+ :name => :track_num,
308
+ :id3v2_frames => ['TRCK'],
309
+ :id3v1_tag => :track,
310
+ :default => [0, 0],
311
+ :handler => :read_int_pair,
312
+ :type => Type::INT_LIST, # don't know if this will ever be useful
313
+ },
314
+
315
+ # disc_num
316
+ {
317
+ :name => :disc_num,
318
+ :id3v2_frames => ['TPOS'],
319
+ :default => [0, 0],
320
+ :handler => :read_int_pair,
321
+ :type => Type::INT_LIST, # don't know if this will ever be useful
322
+ },
323
+
324
+ # original_date
325
+ # TDOR - orig release date (v2.4 only)
326
+ # TORY - orig release year (v2.3)
327
+ {
328
+ :name => :original_date,
329
+ :id3v2_frames => ['TDOR', 'TORY'],
330
+ :handler => :read_first_id3,
331
+ :type => Type::DATETIME,
332
+ },
333
+
334
+ # comments
335
+ {
336
+ :name => :comments,
337
+ :id3v2_frames => ['COMM'],
338
+ :id3v1_tag => :comment,
339
+ :handler => :read_all_id3,
340
+ :default => [],
341
+ :options => { :compact => true }
342
+ },
343
+
344
+ # comment
345
+ {
346
+ :name => :comment,
347
+ :handler => lambda { |iface| iface.comments.first }
348
+ },
349
+
350
+ # album_art
351
+ {
352
+ :name => :album_art,
353
+ :id3v2_frames => ['APIC'],
354
+ :handler => :read_all_id3,
355
+ :default => [],
356
+ },
357
+
358
+ # date
359
+ {
360
+ :name => :date,
361
+ :handler => :read_date,
362
+ :type => Type::DATETIME,
363
+ },
364
+
365
+ # year
366
+ {
367
+ :name => :year,
368
+ :handler => lambda { |iface| iface.date.nil? ? 0 : iface.date.year }
369
+ },
370
+
371
+ # user_info
372
+ {
373
+ :name => :user_info_new,
374
+ :id3v2_frames => ['TXXX'],
375
+ :handler => :read_field_list_as_key_value,
376
+ :default => {},
377
+ :options => { :normalize => true,
378
+ :to_sym => true,
379
+ :field_list => true },
380
+ },
381
+ ]
382
+ end