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.
- data/CHANGES +67 -0
- data/INSTALL +75 -0
- data/README +84 -0
- data/Rakefile +181 -0
- data/TODO +10 -0
- data/ext/id3lib_api/Rakefile +21 -0
- data/ext/id3lib_api/extconf.rb +27 -0
- data/ext/id3lib_api/id3lib_api.i +182 -0
- data/ext/id3lib_api/id3lib_api_wrap.cxx +3648 -0
- data/lib/id3lib.rb +418 -0
- data/lib/id3lib/accessors.rb +103 -0
- data/lib/id3lib/info.rb +393 -0
- data/lib/id3lib_api.so +0 -0
- data/setup.rb +1585 -0
- data/test/data/cover.jpg +0 -0
- data/test/data/sample.mp3 +0 -0
- data/test/data/unicode.mp3 +0 -0
- data/test/test_reading.rb +82 -0
- data/test/test_unicode.rb +85 -0
- data/test/test_writing.rb +221 -0
- data/usage.rb +40 -0
- metadata +175 -0
data/lib/id3lib.rb
ADDED
@@ -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
|