easytag 0.3.1 → 0.4.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: d4c3f241efadea1f57ee59da86e825e2ab3e16f1
4
- data.tar.gz: a14c85a009253c9f8b0fd468107a9e4cc0f39cee
3
+ metadata.gz: 00e00f1b32f68a4b82926812d67662a241f3ab90
4
+ data.tar.gz: 80062af54050c116c899d079df4ad98c0db23a70
5
5
  SHA512:
6
- metadata.gz: 70ac3fa28f1bcef558a017bce6e58d2d09028fe38f24677873cbc178720e298e49a228d4b2335e9720baeb3638e70b937669999237b9f6e80ef7da5e62b2aa4f
7
- data.tar.gz: 53fe63add857a0bed84914337587486a24c22fc27c1af50cc0db3a45cc0b6849b35f447e035ca7bbee1fbc5a349069f73c980bb4472e5302286390b54c208056
6
+ metadata.gz: 18effe5cc310fb3bfc02757404f82fa6fdc84848c23c720e6c020bea7157e948c8a27e0a18495e233180a3b4af4dcd9c8acd7ef02886ab7d0d29ae83e6f2428c
7
+ data.tar.gz: 701971062dfe40d066cc4ad1a61c5bbc906ea05c9f481059a55aa1fcecf6a79fee06cc4d194cbdababd9547a80cdbe89c025c2991a21bcc4270ef9aaafc2fcde
@@ -1,3 +1,36 @@
1
+ ##### v0.4.0 (2013-06-07) #####
2
+ * added: general attributes
3
+ - `#asin`
4
+ - `#conductor`
5
+ - `#remixer`
6
+ - `#mood`
7
+ * added: musicbrainz attributes
8
+ - `#musicbrainz_track_id`
9
+ - `#musicbrainz_album_id`
10
+ - `#musicbrainz_album_status`
11
+ - `#musicbrainz_album_type`
12
+ - `#musicbrainz_album_release_country`
13
+ - `#musicbrainz_artist_id`
14
+ - `#musicbrainz_album_artist_id`
15
+ - `#musicbrainz_release_group_id`
16
+ * added: audio property attributes
17
+ - `#length` (alias: `#duration`)
18
+ - `#bitrate`
19
+ - `#sample_rate`
20
+ - `#channels`
21
+ - `MP3#layer`
22
+ - `MP3#copyrighted?`
23
+ - `MP3#original?`
24
+ - `MP3#protection_enabled?`
25
+ - `MP4#bits_per_sample`
26
+ * added: support for attribute aliases
27
+ * changed: attributes that return `String` now default to an empty string
28
+ instead of `nil`
29
+ * changed: unsupported attributes now return a default value instead of always
30
+ `nil` (ex: `MP3#apple_id`)
31
+ * fixed: `#user_info` stores an array of values if item has multiple value
32
+ * fixed: improvements to `#date` (issues #1 and #2)
33
+
1
34
  ##### v0.3.1 (2013-05-31) #####
2
35
  * fixed: `MP3#subtitle` now points to correct ID3 frame
3
36
  * fixed: restored `MP3#user_info`
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- EasyTag is an abstraction layer to the [TagLib](http://taglib.github.io/) audio tagging library. It is designed to provide a simple and consistent API regardless of file format being read.
1
+ EasyTag is an abstract interface to the [TagLib](http://taglib.github.io/) audio tagging library. It is designed to provide a simple and consistent API regardless of file format being read.
2
2
 
3
3
  A quick reference to which attributes are currently supported can be found [here](https://github.com/cjlucas/ruby-easytag/wiki/Currently-Supported-Attributes).
4
4
 
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.version = EasyTag::VERSION
8
8
  s.summary = 'A simple audio metadata tagging interface'
9
9
  s.description = <<-EOF
10
- EasyTag is an abstraction layer to the TagLib audio tagging library.
10
+ EasyTag is an abstract interface to the TagLib audio tagging library.
11
11
  It is designed to provide a simple and consistent API regardless
12
12
  of file format being read.
13
13
  EOF
@@ -8,16 +8,21 @@ module EasyTag::Attributes
8
8
  STRING_LIST = 4
9
9
  BOOLEAN = 5
10
10
  DATETIME = 6
11
+ LIST = 7
11
12
  end
12
13
  class BaseAttribute
13
14
  Utilities = EasyTag::Utilities
15
+
16
+ attr_reader :name, :aliases, :ivar
14
17
 
15
18
  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)
19
+ @name = args[:name]
20
+ @type = args[:type]
21
+ @default = args[:default] || self.class.default_for_type(@type)
22
+ @options = args[:options] || {}
23
+ @handler_opts = args[:handler_opts] || {}
24
+ @aliases = args[:aliases] || []
25
+ @ivar = BaseAttribute.name_to_ivar(@name)
21
26
 
22
27
  if args[:handler].is_a?(Symbol)
23
28
  @handler = method(args[:handler])
@@ -28,11 +33,13 @@ module EasyTag::Attributes
28
33
  # fill default options
29
34
 
30
35
  # 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
+ @options[:compact] ||= false
37
+ # Delete empty objects in array (post process)
38
+ @options[:delete_empty] ||= false
39
+ # normalizes key (if hash) (handler or post process)
40
+ @options[:normalize] ||= false
41
+ # cast key (if hash) to symbol (handler or post process)
42
+ @options[:to_sym] ||= false
36
43
 
37
44
  end
38
45
 
@@ -44,13 +51,29 @@ module EasyTag::Attributes
44
51
  Marshal.load(Marshal.dump(obj))
45
52
  end
46
53
 
54
+ def self.default_for_type(type)
55
+ case type
56
+ when Type::STRING
57
+ ''
58
+ when Type::DATETIME
59
+ nil
60
+ when Type::INT
61
+ 0
62
+ when Type::INT_LIST
63
+ [0, 0] # TODO: don't assume an INT_LIST is always an int pair
64
+ when Type::BOOLEAN
65
+ false
66
+ when Type::LIST
67
+ []
68
+ end
69
+ end
70
+
47
71
  def default
48
72
  BaseAttribute.can_clone?(@default) ?
49
73
  BaseAttribute.deep_copy(@default) : @default
50
74
  end
51
75
 
52
76
  def call(iface)
53
- #puts 'entered call()'
54
77
  data = @handler.call(iface)
55
78
  data = type_cast(data)
56
79
  post_process(data)
@@ -62,6 +85,8 @@ module EasyTag::Attributes
62
85
  data = data.to_i
63
86
  when Type::DATETIME
64
87
  data = Utilities.get_datetime(data.to_s)
88
+ when Type::LIST
89
+ data = Array(data)
65
90
  end
66
91
 
67
92
  data
@@ -73,31 +98,46 @@ module EasyTag::Attributes
73
98
  end
74
99
 
75
100
  # fall back to default if data is nil
76
- data = BaseAttribute.obj_or_nil(data) || default
101
+ data ||= default
102
+
103
+ # REVIEW: the compact option may not be needed anymore
104
+ # since we've done away with casting empty strings to nil
105
+ data.compact! if @options[:compact] && data.respond_to?(:compact!)
106
+
107
+ if @options[:delete_empty]
108
+ data.select! { |item| !item.empty? if item.respond_to?(:empty?) }
109
+ end
77
110
 
78
- # run obj_or_nil on each item in array
79
- data.map! { |item| BaseAttribute.obj_or_nil(item) } if data.is_a?(Array)
111
+ data = Utilities.normalize_object(data) if @options[:normalize]
80
112
 
81
- if @options[:compact] && data.respond_to?(:compact!)
82
- data.compact!
113
+ # TODO: roll this out to a method that supports more than just array
114
+ if @options[:to_sym] && data.is_a?(Array)
115
+ data.map! { |item| item.to_sym if item.respond_to?(:to_sym) }
83
116
  end
84
117
 
85
118
  data
86
119
  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
120
+
121
+ def self.name_to_ivar(name)
122
+ name.to_s.gsub(/\?/, '').insert(0, '@').to_sym
94
123
  end
95
124
 
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
125
+ # read handlers
126
+
127
+ def read_default(iface)
128
+ default
129
+ end
130
+
131
+ def read_audio_property(iface)
132
+ key = @handler_opts[:key]
133
+ warn "@handler_opts[:key] doesn't exist" if key.nil?
134
+ iface.info.audio_properties.send(key)
135
+ end
136
+
137
+ def user_info_lookup(iface)
138
+ key = @handler_opts[:key]
139
+ warn "@handler_opts[:key] doesn't exist" if key.nil?
140
+ iface.user_info_normalized[key]
101
141
  end
102
142
  end
103
143
  end
@@ -105,10 +105,12 @@ module EasyTag::Attributes
105
105
  frame_data = read_all_id3(iface)
106
106
 
107
107
  frame_data.each do |data|
108
- key, value = data
108
+ key = data[0]
109
+ values = data[1..-1]
110
+
109
111
  key = Utilities.normalize_string(key) if @options[:normalize]
110
112
  key = key.to_sym if @options[:to_sym]
111
- kv_hash[key] = value
113
+ kv_hash[key] = values.count > 1 ? values : values.first
112
114
  end
113
115
 
114
116
  kv_hash
@@ -130,6 +132,20 @@ module EasyTag::Attributes
130
132
 
131
133
  date_str
132
134
  end
135
+
136
+ def read_ufid(iface)
137
+ frames = iface.info.id3v2_tag.frame_list('UFID')
138
+ ufid = nil
139
+
140
+ frames.each do |frame|
141
+ if @handler_opts[:owner].eql?(frame.owner)
142
+ ufid = frame.identifier
143
+ break
144
+ end
145
+ end
146
+
147
+ ufid
148
+ end
133
149
  end
134
150
  end
135
151
 
@@ -151,6 +167,7 @@ module EasyTag::Attributes
151
167
  :name => :title_sort_order,
152
168
  :id3v2_frames => ['TSOT', 'XSOT'],
153
169
  :handler => :read_first_id3,
170
+ :type => Type::STRING,
154
171
  },
155
172
 
156
173
  # subtitle
@@ -158,6 +175,7 @@ module EasyTag::Attributes
158
175
  :name => :subtitle,
159
176
  :id3v2_frames => ['TIT3'],
160
177
  :handler => :read_first_id3,
178
+ :type => Type::STRING,
161
179
  },
162
180
 
163
181
  # artist
@@ -166,6 +184,7 @@ module EasyTag::Attributes
166
184
  :id3v2_frames => ['TPE1'],
167
185
  :id3v1_tag => :artist,
168
186
  :handler => :read_first_id3,
187
+ :type => Type::STRING,
169
188
  },
170
189
 
171
190
  # artist_sort_order
@@ -175,6 +194,7 @@ module EasyTag::Attributes
175
194
  :name => :artist_sort_order,
176
195
  :id3v2_frames => ['TSOP', 'XSOP'],
177
196
  :handler => :read_first_id3,
197
+ :type => Type::STRING,
178
198
  },
179
199
 
180
200
  # album_artist
@@ -182,12 +202,15 @@ module EasyTag::Attributes
182
202
  :name => :album_artist,
183
203
  :id3v2_frames => ['TPE2'],
184
204
  :handler => :read_first_id3,
205
+ :type => Type::STRING,
185
206
  },
186
207
 
187
208
  # album_artist_sort_order
188
209
  {
189
210
  :name => :album_artist_sort_order,
190
- :handler => lambda { |iface| iface.user_info[:albumartistsort] }
211
+ :handler => :user_info_lookup,
212
+ :handler_opts => {:key => :albumartistsort},
213
+ :type => Type::STRING,
191
214
  },
192
215
 
193
216
  # album
@@ -196,14 +219,16 @@ module EasyTag::Attributes
196
219
  :id3v2_frames => ['TALB'],
197
220
  :id3v1_tag => :album,
198
221
  :handler => :read_first_id3,
222
+ :type => Type::STRING,
199
223
  },
200
224
 
201
225
  # compilation?
202
226
  {
203
227
  :name => :compilation?,
204
228
  :id3v2_frames => ['TCMP'],
205
- :default => false,
206
229
  :handler => :read_first_id3,
230
+ :type => Type::BOOLEAN,
231
+ # TODO: remove is_flag option, determine boolean value implicitly
207
232
  :options => {:is_flag => true},
208
233
  },
209
234
 
@@ -214,6 +239,7 @@ module EasyTag::Attributes
214
239
  :name => :album_sort_order,
215
240
  :id3v2_frames => ['TSOA', 'XSOA'],
216
241
  :handler => :read_first_id3,
242
+ :type => Type::STRING,
217
243
  },
218
244
 
219
245
  # genre
@@ -222,6 +248,7 @@ module EasyTag::Attributes
222
248
  :id3v2_frames => ['TCON'],
223
249
  :id3v1_tag => :genre,
224
250
  :handler => :read_first_id3,
251
+ :type => Type::STRING,
225
252
  },
226
253
 
227
254
  # disc_subtitle
@@ -229,6 +256,7 @@ module EasyTag::Attributes
229
256
  :name => :disc_subtitle,
230
257
  :id3v2_frames => ['TSST'],
231
258
  :handler => :read_first_id3,
259
+ :type => Type::STRING,
232
260
  },
233
261
 
234
262
  # media
@@ -236,6 +264,7 @@ module EasyTag::Attributes
236
264
  :name => :media,
237
265
  :id3v2_frames => ['TMED'],
238
266
  :handler => :read_first_id3,
267
+ :type => Type::STRING,
239
268
  },
240
269
 
241
270
  # label
@@ -243,6 +272,7 @@ module EasyTag::Attributes
243
272
  :name => :label,
244
273
  :id3v2_frames => ['TPUB'],
245
274
  :handler => :read_first_id3,
275
+ :type => Type::STRING,
246
276
  },
247
277
 
248
278
  # encoded_by
@@ -250,6 +280,7 @@ module EasyTag::Attributes
250
280
  :name => :encoded_by,
251
281
  :id3v2_frames => ['TENC'],
252
282
  :handler => :read_first_id3,
283
+ :type => Type::STRING,
253
284
  },
254
285
 
255
286
  # encoder_settings
@@ -257,6 +288,7 @@ module EasyTag::Attributes
257
288
  :name => :encoder_settings,
258
289
  :id3v2_frames => ['TSSE'],
259
290
  :handler => :read_first_id3,
291
+ :type => Type::STRING,
260
292
  },
261
293
 
262
294
  # group
@@ -264,6 +296,7 @@ module EasyTag::Attributes
264
296
  :name => :group,
265
297
  :id3v2_frames => ['TIT1'],
266
298
  :handler => :read_first_id3,
299
+ :type => Type::STRING,
267
300
  },
268
301
 
269
302
  # composer
@@ -271,6 +304,23 @@ module EasyTag::Attributes
271
304
  :name => :composer,
272
305
  :id3v2_frames => ['TCOM'],
273
306
  :handler => :read_first_id3,
307
+ :type => Type::STRING,
308
+ },
309
+
310
+ # conductor
311
+ {
312
+ :name => :conductor,
313
+ :id3v2_frames => ['TPE3'],
314
+ :handler => :read_first_id3,
315
+ :type => Type::STRING,
316
+ },
317
+
318
+ # remixer
319
+ {
320
+ :name => :remixer,
321
+ :id3v2_frames => ['TPE4'],
322
+ :handler => :read_first_id3,
323
+ :type => Type::STRING,
274
324
  },
275
325
 
276
326
  # lyrics
@@ -278,6 +328,7 @@ module EasyTag::Attributes
278
328
  :name => :lyrics,
279
329
  :id3v2_frames => ['USLT'],
280
330
  :handler => :read_first_id3,
331
+ :type => Type::STRING,
281
332
  },
282
333
 
283
334
  # lyricist
@@ -285,6 +336,7 @@ module EasyTag::Attributes
285
336
  :name => :lyricist,
286
337
  :id3v2_frames => ['TEXT'],
287
338
  :handler => :read_first_id3,
339
+ :type => Type::STRING,
288
340
  },
289
341
 
290
342
  # copyright
@@ -292,6 +344,7 @@ module EasyTag::Attributes
292
344
  :name => :copyright,
293
345
  :id3v2_frames => ['TCOP'],
294
346
  :handler => :read_first_id3,
347
+ :type => Type::STRING,
295
348
  },
296
349
 
297
350
  # bpm
@@ -302,6 +355,14 @@ module EasyTag::Attributes
302
355
  :type => Type::INT,
303
356
  },
304
357
 
358
+ # mood
359
+ {
360
+ :name => :mood,
361
+ :id3v2_frames => ['TMOO'],
362
+ :handler => :read_first_id3,
363
+ :type => Type::STRING,
364
+ },
365
+
305
366
  # track_num
306
367
  {
307
368
  :name => :track_num,
@@ -338,13 +399,14 @@ module EasyTag::Attributes
338
399
  :id3v1_tag => :comment,
339
400
  :handler => :read_all_id3,
340
401
  :default => [],
341
- :options => { :compact => true }
402
+ :options => { :compact => true, :delete_empty => true }
342
403
  },
343
404
 
344
405
  # comment
345
406
  {
346
407
  :name => :comment,
347
- :handler => lambda { |iface| iface.comments.first }
408
+ :handler => lambda { |iface| iface.comments.first },
409
+ :type => Type::STRING,
348
410
  },
349
411
 
350
412
  # album_art
@@ -368,15 +430,177 @@ module EasyTag::Attributes
368
430
  :handler => lambda { |iface| iface.date.nil? ? 0 : iface.date.year }
369
431
  },
370
432
 
433
+ # apple_id
434
+ {
435
+ :name => :apple_id,
436
+ :handler => :read_default,
437
+ :type => Type::STRING,
438
+ },
439
+
371
440
  # user_info
372
441
  {
373
442
  :name => :user_info,
374
443
  :id3v2_frames => ['TXXX'],
375
444
  :handler => :read_field_list_as_key_value,
376
445
  :default => {},
377
- :options => { :normalize => true,
446
+ :options => {:field_list => true},
447
+ },
448
+
449
+ # user_info_normalized
450
+ {
451
+ :name => :user_info_normalized,
452
+ :id3v2_frames => ['TXXX'],
453
+ :handler => :read_field_list_as_key_value,
454
+ :default => {},
455
+ :options => {:normalize => true,
378
456
  :to_sym => true,
379
457
  :field_list => true },
380
458
  },
459
+
460
+ # asin
461
+ {
462
+ :name => :asin,
463
+ :handler => :user_info_lookup,
464
+ :handler_opts => {:key => :asin},
465
+ :type => Type::STRING,
466
+ },
467
+
468
+ #
469
+ # MusicBrainz Attributes
470
+ #
471
+
472
+ # musicbrainz_track_id
473
+ {
474
+ :name => :musicbrainz_track_id,
475
+ :handler => :read_ufid,
476
+ :handler_opts => {:owner => 'http://musicbrainz.org'},
477
+ :type => Type::STRING,
478
+ },
479
+
480
+ # musicbrainz_album_artist_id
481
+ {
482
+ :name => :musicbrainz_album_artist_id,
483
+ :handler => :user_info_lookup,
484
+ :handler_opts => {:key => :musicbrainz_album_artist_id},
485
+ :type => Type::STRING,
486
+ },
487
+
488
+ # musicbrainz_artist_id
489
+ {
490
+ :name => :musicbrainz_artist_id,
491
+ :handler => :user_info_lookup,
492
+ :handler_opts => {:key => :musicbrainz_artist_id},
493
+ :type => Type::LIST,
494
+ },
495
+
496
+ # musicbrainz_album_id
497
+ {
498
+ :name => :musicbrainz_album_id,
499
+ :handler => :user_info_lookup,
500
+ :handler_opts => {:key => :musicbrainz_album_id},
501
+ :type => Type::STRING,
502
+ },
503
+
504
+ # musicbrainz_album_status
505
+ {
506
+ :name => :musicbrainz_album_status,
507
+ :handler => :user_info_lookup,
508
+ :handler_opts => {:key => :musicbrainz_album_status},
509
+ :type => Type::STRING,
510
+ },
511
+
512
+ # musicbrainz_album_type
513
+ {
514
+ :name => :musicbrainz_album_type,
515
+ :handler => :user_info_lookup,
516
+ :handler_opts => {:key => :musicbrainz_album_type},
517
+ :type => Type::LIST,
518
+ },
519
+
520
+
521
+ # musicbrainz_release_group_id
522
+ {
523
+ :name => :musicbrainz_release_group_id,
524
+ :handler => :user_info_lookup,
525
+ :handler_opts => {:key => :musicbrainz_release_group_id},
526
+ :type => Type::STRING,
527
+ },
528
+
529
+ # musicbrainz_album_release_country
530
+ {
531
+ :name => :musicbrainz_album_release_country,
532
+ :handler => :user_info_lookup,
533
+ :handler_opts => {:key => :musicbrainz_album_release_country},
534
+ :type => Type::STRING,
535
+ },
536
+
537
+ #
538
+ # Audio Properties
539
+ #
540
+
541
+ # length
542
+ {
543
+ :name => :length,
544
+ :aliases => [:duration],
545
+ :handler => :read_audio_property,
546
+ :handler_opts => {:key => :length},
547
+ :type => Type::INT,
548
+ },
549
+
550
+ # bitrate
551
+ {
552
+ :name => :bitrate,
553
+ :handler => :read_audio_property,
554
+ :handler_opts => {:key => :bitrate},
555
+ :type => Type::INT,
556
+ },
557
+
558
+ # sample_rate
559
+ {
560
+ :name => :sample_rate,
561
+ :handler => :read_audio_property,
562
+ :handler_opts => {:key => :sample_rate},
563
+ :type => Type::INT,
564
+ },
565
+
566
+ # channels
567
+ {
568
+ :name => :channels,
569
+ :handler => :read_audio_property,
570
+ :handler_opts => {:key => :channels},
571
+ :type => Type::INT,
572
+ },
573
+
574
+ # copyrighted?
575
+ {
576
+ :name => :copyrighted?,
577
+ :handler => :read_audio_property,
578
+ :handler_opts => {:key => :copyrighted?},
579
+ :type => Type::BOOLEAN,
580
+ },
581
+
582
+ # layer
583
+ {
584
+ :name => :layer,
585
+ :handler => :read_audio_property,
586
+ :handler_opts => {:key => :layer},
587
+ :type => Type::INT,
588
+ },
589
+
590
+ # original?
591
+ {
592
+ :name => :original?,
593
+ :handler => :read_audio_property,
594
+ :handler_opts => {:key => :original?},
595
+ :type => Type::BOOLEAN,
596
+ },
597
+
598
+ # protection_enabled?
599
+ {
600
+ :name => :protection_enabled?,
601
+ :handler => :read_audio_property,
602
+ :handler_opts => {:key => :protection_enabled},
603
+ :type => Type::BOOLEAN,
604
+ },
381
605
  ]
382
606
  end