easytag 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,309 @@
1
+ # encoding: UTF-8
2
+ require 'easytag/attributes/base'
3
+
4
+ module EasyTag::Attributes
5
+ # type of TagLib::MP4::Item
6
+ module ItemType
7
+ STRING = 0 # not part of TagLib::MP4::Item, just for convenience
8
+ STRING_LIST = 1
9
+ BOOL = 2
10
+ INT = 3
11
+ INT_PAIR = 4
12
+ COVER_ART_LIST = 5
13
+ end
14
+
15
+ class MP4Attribute < BaseAttribute
16
+ attr_reader :name, :ivar
17
+
18
+ def initialize(args)
19
+ super(args)
20
+
21
+ @item_ids = args[:item_ids]
22
+ @item_type = args[:item_type] || ItemType::STRING
23
+ end
24
+
25
+ private
26
+
27
+ def item_for_id(id, iface)
28
+ iface.info.tag.item_list_map.fetch(id)
29
+ end
30
+
31
+ def data_from_item(item)
32
+ case @item_type
33
+ when ItemType::STRING
34
+ item.to_string_list[0]
35
+ when ItemType::STRING_LIST
36
+ item.to_string_list
37
+ when ItemType::BOOL
38
+ item.to_bool
39
+ when ItemType::INT
40
+ item.to_int
41
+ when ItemType::INT_PAIR
42
+ item.to_int_pair
43
+ when ItemType::COVER_ART_LIST
44
+ artwork = []
45
+ item.to_cover_art_list.each do |img|
46
+ artwork << EasyTag::Image.new(img.data)
47
+ end
48
+ artwork
49
+ else
50
+ nil
51
+ end
52
+ end
53
+
54
+ # read handlers
55
+
56
+ def read_first_item(iface)
57
+ item = nil
58
+ @item_ids.each do |id|
59
+ item = item_for_id(id, iface) if item.nil?
60
+ end
61
+
62
+ data_from_item(item) unless item.nil?
63
+ end
64
+
65
+ def read_user_info(iface)
66
+ kv_hash = {}
67
+ iface.info.tag.item_list_map.to_a.each do |key, value|
68
+ match_data = key.match(/\:com.apple.iTunes\:(.*)/)
69
+ if match_data
70
+ key = match_data[1]
71
+ key = Utilities.normalize_string(key) if @options[:normalize]
72
+ key = key.to_sym if @options[:to_sym]
73
+ kv_hash[key] = value.to_string_list[0]
74
+ end
75
+ end
76
+
77
+ kv_hash
78
+ end
79
+
80
+ end
81
+ end
82
+
83
+ module EasyTag::Attributes
84
+ MP4_ATTRIB_ARGS = [
85
+ # title
86
+ {
87
+ :name => :title,
88
+ :item_ids => ['©nam'],
89
+ :handler => :read_first_item,
90
+ },
91
+
92
+ # title_sort_order
93
+ {
94
+ :name => :title_sort_order,
95
+ :item_ids => ['sonm'],
96
+ :handler => :read_first_item,
97
+ },
98
+
99
+ # artist
100
+ {
101
+ :name => :artist,
102
+ :item_ids => ['©ART'],
103
+ :handler => :read_first_item,
104
+ },
105
+
106
+ # artist_sort_order
107
+ {
108
+ :name => :artist_sort_order,
109
+ :item_ids => ['soar'],
110
+ :handler => :read_first_item,
111
+ },
112
+
113
+ # album_artist
114
+ {
115
+ :name => :album_artist,
116
+ :item_ids => ['aART'],
117
+ :handler => :read_first_item,
118
+ },
119
+
120
+ # album_artist_sort_order
121
+ {
122
+ :name => :album_artist_sort_order,
123
+ :item_ids => ['soaa'],
124
+ :handler => :read_first_item,
125
+ },
126
+
127
+ # album
128
+ {
129
+ :name => :album,
130
+ :item_ids => ['©alb'],
131
+ :handler => :read_first_item,
132
+ },
133
+
134
+ # album_sort_order
135
+ {
136
+ :name => :album_sort_order,
137
+ :item_ids => ['soal'],
138
+ :handler => :read_first_item,
139
+ },
140
+
141
+ # genre
142
+ {
143
+ :name => :genre,
144
+ :item_ids => ['©gen'],
145
+ :handler => :read_first_item,
146
+ },
147
+
148
+ # comments
149
+ {
150
+ :name => :comments,
151
+ :item_ids => ['©cmt'],
152
+ :handler => :read_first_item,
153
+ :item_type => ItemType::STRING_LIST,
154
+ :default => [],
155
+ },
156
+
157
+ # comment
158
+ {
159
+ :name => :comment,
160
+ :handler => lambda { |iface| iface.comments.first }
161
+ },
162
+
163
+ # lyrics
164
+ {
165
+ :name => :lyrics,
166
+ :item_ids => ['©lyr'],
167
+ :handler => :read_first_item,
168
+ },
169
+
170
+ # date
171
+ {
172
+ :name => :date,
173
+ :item_ids => ['©day'],
174
+ :handler => :read_first_item,
175
+ :type => Type::DATETIME,
176
+ },
177
+
178
+ # year
179
+ {
180
+ :name => :year,
181
+ :handler => lambda { |iface| iface.date.nil? ? 0 : iface.date.year }
182
+ },
183
+
184
+ # apple_id
185
+ {
186
+ :name => :apple_id,
187
+ :item_ids => ['apid'],
188
+ :handler => :read_first_item,
189
+ },
190
+
191
+ # encoded_by
192
+ {
193
+ :name => :encoded_by,
194
+ :item_ids => ['©enc'],
195
+ :handler => :read_first_item,
196
+ },
197
+
198
+ # encoder_settings
199
+ {
200
+ :name => :encoder_settings,
201
+ :item_ids => ['©too'],
202
+ :handler => :read_first_item,
203
+ },
204
+
205
+ # group
206
+ {
207
+ :name => :group,
208
+ :item_ids => ['©grp'],
209
+ :handler => :read_first_item,
210
+ },
211
+
212
+ # compilation?
213
+ {
214
+ :name => :compilation?,
215
+ :item_ids => ['cpil'],
216
+ :handler => :read_first_item,
217
+ :item_type => ItemType::BOOL,
218
+ :default => false,
219
+ },
220
+
221
+ # bpm
222
+ {
223
+ :name => :bpm,
224
+ :item_ids => ['tmpo'],
225
+ :handler => :read_first_item,
226
+ :item_type => ItemType::INT,
227
+ :default => 0,
228
+ },
229
+
230
+ # copyright
231
+ {
232
+ :name => :copyright,
233
+ :item_ids => ['cprt'],
234
+ :handler => :read_first_item,
235
+ },
236
+
237
+ # track_num
238
+ {
239
+ :name => :track_num,
240
+ :item_ids => ['trkn'],
241
+ :handler => :read_first_item,
242
+ :item_type => ItemType::INT_PAIR,
243
+ :default => [0, 0],
244
+ },
245
+
246
+ # disc_num
247
+ {
248
+ :name => :disc_num,
249
+ :item_ids => ['disk'],
250
+ :handler => :read_first_item,
251
+ :item_type => ItemType::INT_PAIR,
252
+ :default => [0, 0],
253
+ },
254
+
255
+ # album_art
256
+ {
257
+ :name => :album_art,
258
+ :item_ids => ['covr'],
259
+ :handler => :read_first_item,
260
+ :item_type => ItemType::COVER_ART_LIST,
261
+ :default => [],
262
+ },
263
+
264
+ # user_info
265
+ {
266
+ :name => :user_info,
267
+ :handler => :read_user_info,
268
+ :default => {},
269
+ :options => {:normalize => true, :to_sym => true},
270
+ },
271
+
272
+ # subtitle
273
+ {
274
+ :name => :subtitle,
275
+ :handler => lambda { |iface| iface.user_info[:subtitle] }
276
+ },
277
+
278
+ # disc_subtitle
279
+ {
280
+ :name => :disc_subtitle,
281
+ :handler => lambda { |iface| iface.user_info[:discsubtitle] }
282
+ },
283
+
284
+ # media
285
+ {
286
+ :name => :media,
287
+ :handler => lambda { |iface| iface.user_info[:media] }
288
+ },
289
+
290
+ # label
291
+ {
292
+ :name => :label,
293
+ :handler => lambda { |iface| iface.user_info[:label] }
294
+ },
295
+
296
+ # composer
297
+ {
298
+ :name => :composer,
299
+ :handler => lambda { |iface| iface.user_info[:composer] }
300
+ },
301
+
302
+ # lyricist
303
+ {
304
+ :name => :lyricist,
305
+ :handler => lambda { |iface| iface.user_info[:lyricist] }
306
+ },
307
+
308
+ ]
309
+ end
@@ -1,186 +1,53 @@
1
1
  require 'mp3info'
2
+ require 'yaml'
2
3
 
3
- require 'easytag'
4
+ require 'easytag/attributes/mp3'
4
5
 
5
6
  module EasyTag::Interfaces
6
7
 
7
8
  class MP3 < Base
8
9
  def initialize(file)
9
10
  @info = TagLib::MPEG::File.new(file)
10
- @id3v1 = @info.id3v1_tag
11
- @id3v2 = @info.id3v2_tag
12
11
 
13
- # this is required because taglib hash issues with the TDAT/TYER
12
+ add_tdat_to_taglib(file)
13
+ end
14
+
15
+ private
16
+
17
+ def add_tdat_to_taglib(file)
18
+ # this is required because taglib hash issues with the TDAT+TYER
14
19
  # frame (https://github.com/taglib/taglib/issues/127)
15
- @id3v2_hash = ID3v2.new
20
+ id3v2_hash = ID3v2.new
16
21
 
17
22
  File.open(file) do |fp|
18
23
  fp.read(3) # read past ID3 identifier
19
24
  begin
20
- @id3v2_hash.from_io(fp)
25
+ id3v2_hash.from_io(fp)
21
26
  rescue ID3v2Error => e
22
27
  warn 'no id3v2 tags found'
23
28
  end
24
29
  end
25
30
 
26
- end
27
-
28
- def title
29
- obj_for_frame_id('TIT2') or Base.obj_or_nil(@id3v1.title)
30
- end
31
-
32
- def title_sort_order
33
- # TSOT - (v2.4 only)
34
- # XSOT - Musicbrainz Picard custom
35
- obj_for_frame_id('TSOT') || obj_for_frame_id('XSOT')
36
- end
37
-
38
- def artist
39
- obj_for_frame_id('TPE1') or Base.obj_or_nil(@id3v1.artist)
40
- end
41
-
42
- def artist_sort_order
43
- # TSOP - (v2.4 only)
44
- # XSOP - Musicbrainz Picard custom
45
- obj_for_frame_id('TSOP') || obj_for_frame_id('XSOP')
46
- end
47
-
48
- def album_artist
49
- obj_for_frame_id('TPE2')
50
- end
51
-
52
- def album_artist_sort_order
53
- user_info[:albumartistsort]
54
- end
55
-
56
- def album
57
- obj_for_frame_id('TALB') or Base.obj_or_nil(@id3v1.album)
58
- end
59
-
60
- def album_sort_order
61
- # TSOA - (v2.4 only)
62
- # XSOA - Musicbrainz Picard custom
63
- obj_for_frame_id('TSOA') || obj_for_frame_id('XSOA')
64
- end
65
-
66
- # REVIEW: TCON supports genre refining, which we currently don't utilize
67
- def genre
68
- obj_for_frame_id('TCON') or Base.obj_or_nil(@id3v1.genre)
69
- end
70
-
71
- def comments
72
- return @comments unless @comments.nil?
73
-
74
- comm_frame = lookup_frames('COMM').first
75
- comm_str = comm_frame ? comm_frame.text : @id3v1.comment
76
-
77
- @comments = Base.obj_or_nil(comm_str)
78
- end
79
-
80
- def year
81
- date.nil? ? 0 : date.year
82
- end
83
-
84
- def date
85
- return @date unless @date.nil?
86
-
87
- v10_year = @id3v1.year.to_s if @id3v1.year > 0
88
- v23_year = obj_for_frame_id('TYER')
89
- v23_date = Base.obj_or_nil(@id3v2_hash['TDAT'])
90
- v24_date = obj_for_frame_id('TDRC')
31
+ # delete all TDAT frames (taglib-ruby segfaults when trying to read)
32
+ frames = @info.id3v2_tag.frame_list('TDAT')
33
+ frames.each { |frame| @info.id3v2_tag.remove_frame(frame) }
91
34
 
92
- # check variables in order of importance
93
- date_str = v24_date || v23_year || v10_year
94
- # only append v23_date if date_str is currently a year
95
- date_str << v23_date unless v23_date.nil? or date_str.length > 4
96
- puts "MP3#date: date_str = \"#{date_str}\"" if $DEBUG
35
+ if id3v2_hash['TDAT']
36
+ frame = TagLib::ID3v2::TextIdentificationFrame
37
+ .new('TDAT', TagLib::String::UTF8)
97
38
 
98
- @date = EasyTag::Utilities.get_datetime(date_str)
99
- end
100
-
101
- def original_date
102
- return @original_date unless @original_date.nil?
103
-
104
- # TDOR - orig release date (v2.4 only)
105
- # TORY - orig release year (v2.3)
106
- date_str = obj_for_frame_id('TDOR') || obj_for_frame_id('TORY')
107
- @original_date ||= EasyTag::Utilities.get_datetime(date_str)
108
- end
109
-
110
- def album_art
111
- return @album_art unless @album_art.nil?
112
-
113
- @album_art = []
114
- @id3v2.frame_list('APIC').each do |apic|
115
- img = EasyTag::Image.new(apic.picture)
116
- img.desc = apic.description
117
- img.type = apic.type
118
- img.mime_type = apic.mime_type
119
-
120
- @album_art << img
39
+ frame.text = id3v2_hash['TDAT']
40
+ @info.id3v2_tag.add_frame(frame)
121
41
  end
122
-
123
- @album_art
124
42
  end
125
43
 
126
- def track_num
127
- return @track_num unless @track_num.nil?
128
-
129
- pair = int_pair_for_frame_id('TRCK')
130
- @track_num = (pair == [0, 0]) ? [@id3v1.track, 0] : pair
131
- end
132
-
133
- def disc_num
134
- int_pair_for_frame_id('TPOS')
135
- end
136
-
137
- def user_info
138
- return @user_info unless @user_info.nil?
139
-
140
- @user_info = {}
141
- lookup_frames('TXXX').each do |frame|
142
- key, value = frame.field_list
143
- key = EasyTag::Utilities.normalize_string(key)
144
- @user_info[key.to_sym] = value
44
+ EasyTag::Attributes::MP3_ATTRIB_ARGS.each do |attrib_args|
45
+ attrib = EasyTag::Attributes::MP3Attribute.new(attrib_args)
46
+ define_method(attrib.name) do
47
+ instance_variable_get(attrib.ivar) ||
48
+ instance_variable_set(attrib.ivar, attrib.call(self))
145
49
  end
146
-
147
- @user_info
148
50
  end
149
51
 
150
- def disc_subtitle
151
- obj_for_frame_id('TSST')
152
- end
153
-
154
- def media
155
- obj_for_frame_id('TMED')
156
- end
157
-
158
- def label
159
- obj_for_frame_id('TPUB')
160
- end
161
-
162
- private
163
-
164
- # for TPOS and TRCK
165
- def int_pair_for_frame_id(frame_id)
166
- str = obj_for_frame_id(frame_id)
167
- EasyTag::Utilities.get_int_pair(str)
168
- end
169
-
170
- def obj_for_frame_id(frame_id)
171
- Base.obj_or_nil(lookup_first_field(frame_id))
172
- end
173
-
174
- def lookup_frames(frame_id)
175
- frames = @id3v2.frame_list(frame_id)
176
- end
177
-
178
- # get the first field in the first frame
179
- def lookup_first_field(frame_id)
180
- frame = lookup_frames(frame_id).first
181
- warn "frame '#{frame_id}' is not present" if frame.nil?
182
- frame.field_list.first unless frame.nil?
183
- end
184
52
  end
185
53
  end
186
-