ruby-mp3info 0.7.2 → 0.8

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.
@@ -1,3 +1,9 @@
1
+ === 0.8 / 2013-01-28
2
+
3
+ * allow higher level of reading and writing to APIC tag:
4
+ * tag2.pictures, tag2.remove_pictures, tag2.add_picture
5
+ * added "<<...snip...>>>" to shorten APIC tag when Hash.inspect called (not a shallow copy hash, deep copied hash so does not effect data) tag2.inspect
6
+
1
7
  === 0.7.2 / 2012-12-24
2
8
 
3
9
  * do not try to commit changes to IO or StringIO
@@ -1,6 +1,6 @@
1
1
  History.txt
2
2
  Manifest.txt
3
- README.txt
3
+ README.md
4
4
  Rakefile
5
5
  lib/mp3info.rb
6
6
  lib/mp3info/extension_modules.rb
@@ -0,0 +1,97 @@
1
+ # ruby-mp3info
2
+
3
+ * http://github.com/moumar/ruby-mp3info
4
+
5
+ ## Description
6
+
7
+ ruby-mp3info read low-level informations and manipulate tags on mp3 files.
8
+
9
+ ## Features/Problems
10
+
11
+ * Written in pure ruby
12
+ * Read low-level informations like bitrate, length, samplerate, etc...
13
+ * Read, write, remove id3v1 and id3v2 tags
14
+ * Correctly read VBR files (with or without Xing header)
15
+ * Only 2.3 version is supported for writings id3v2 tags
16
+ * id3v2 tags are always written in UTF-16 encoding
17
+
18
+ ## Synopsis
19
+
20
+ ```ruby
21
+ require "mp3info"
22
+
23
+ # Read and display infos & tags
24
+
25
+ Mp3Info.open("myfile.mp3") do |mp3info|
26
+ puts mp3info
27
+ end
28
+
29
+ # read/write tag1 and tag2 with Mp3Info#tag attribute
30
+ # when reading tag2 have priority over tag1
31
+ # when writing, each tag is written.
32
+
33
+ Mp3Info.open("myfile.mp3") do |mp3|
34
+ puts mp3.tag.title
35
+ puts mp3.tag.artist
36
+ puts mp3.tag.album
37
+ puts mp3.tag.tracknum
38
+ mp3.tag.title = "track title"
39
+ mp3.tag.artist = "artist name"
40
+ end
41
+
42
+ # tags are written when calling Mp3Info#close or at the end of the #open block
43
+
44
+ ### access id3v2 tags
45
+
46
+ Mp3Info.open("myfile.mp3") do |mp3|
47
+ # you can access four letter v2 tags like this
48
+ puts mp3.tag2.TIT2
49
+ mp3.tag2.TIT2 = "new TIT2"
50
+ # or like that
51
+ mp3.tag2["TIT2"]
52
+ # at this time, only COMM tag is processed after reading and before writing
53
+ # according to ID3v2#options hash
54
+ mp3.tag2.options[:lang] = "FRE"
55
+ mp3.tag2.COMM = "my comment in french, correctly handled when reading and writing"
56
+ end
57
+
58
+ ### image manipulation
59
+
60
+ file = File.new('input_img','rb')
61
+ Mp3Info.open '1.mp3' do |m|
62
+ pictures = m.tag2.get_pictures # array of images :
63
+ pictures.each do |description, data|
64
+ File.open(description, "wb") { |f| f.write data }
65
+ end
66
+ m.tag2.remove_pictures # remove all images
67
+ m.tag2.add_picture(file.read) # optionally, you may include options for the header:
68
+ # options = { mime: 'gif', description: "description",
69
+ # pic_type: 0 }
70
+ end
71
+ ```
72
+
73
+ ## Requirements
74
+
75
+ * iconv when using ruby 1.8
76
+
77
+ ## Install
78
+
79
+ gem install ruby-mp3info
80
+
81
+ ## Developers
82
+
83
+ After checking out the source, run:
84
+
85
+ $ rake newb
86
+
87
+ This task will install any missing dependencies, run the tests/specs, and generate the RDoc.
88
+
89
+ ## License
90
+
91
+ ruby
92
+
93
+ ## TODO:
94
+
95
+ * encoder detection
96
+ * support for more tags in id3v2
97
+ * generalize id3v2 with other audio formats (APE, MPC, OGG, etc...)
@@ -5,8 +5,8 @@
5
5
 
6
6
  require "fileutils"
7
7
  require "stringio"
8
- require "mp3info/extension_modules"
9
8
  require "mp3info/id3v2"
9
+ require "mp3info/extension_modules"
10
10
 
11
11
  # ruby -d to display debugging infos
12
12
 
@@ -18,7 +18,7 @@ end
18
18
 
19
19
  class Mp3Info
20
20
 
21
- VERSION = "0.7.2"
21
+ VERSION = "0.8"
22
22
 
23
23
  LAYER = [ nil, 3, 2, 1]
24
24
  BITRATE = {
@@ -471,7 +471,7 @@ class Mp3Info
471
471
  def to_s
472
472
  s = "MPEG #{@mpeg_version} Layer #{@layer} #{@vbr ? "VBR" : "CBR"} #{@bitrate} Kbps #{@channel_mode} #{@samplerate} Hz length #{@length} sec. header #{@header.inspect} "
473
473
  s << "tag1: "+@tag1.to_hash.inspect+"\n" if hastag1?
474
- s << "tag2: "+@tag2.to_hash.inspect+"\n" if hastag2?
474
+ s << "tag2: "+@tag2.to_inspect_hash.inspect+"\n" if hastag2?
475
475
  s
476
476
  end
477
477
 
@@ -217,6 +217,136 @@ class ID3v2 < DelegateClass(Hash)
217
217
  end
218
218
  end
219
219
 
220
+ ### cuts out long tag values from hash for display on screen
221
+ def to_inspect_hash
222
+ result = Marshal.load(Marshal.dump(self.to_hash))
223
+ result.each do |k,v|
224
+ if v.is_a? Array
225
+ v.each_index do |i, item|
226
+ if (v[i].is_a? String and v[i].length > 128)
227
+ result[k][i] = pretty_header(v[i])
228
+ end
229
+ end
230
+ elsif v.is_a? String and v.length > 128
231
+ result[k] = pretty_header(v) # this method 'snips' long data
232
+ end
233
+ end
234
+ result
235
+ end
236
+
237
+ ### ID3V2::add_picture
238
+ ### Takes an image string as input and writes it with header.
239
+ ### Mime type is automatically guessed by default.
240
+ ### It is possible but not necessary to include:
241
+ ### :pic_type => 0 - 14 (see http://id3.org/id3v2.3.0#Attached_picture)
242
+ ### :mime => 'gif'
243
+ ### :description => "Image description"
244
+ def add_picture(data, opts = {})
245
+ options = {
246
+ :pic_type => 0,
247
+ :mime => nil,
248
+ :description => "image"
249
+ }
250
+ options.update(opts)
251
+ jpg = Regexp.new( "^\xFF".force_encoding("BINARY"),
252
+ Regexp::FIXEDENCODING )
253
+ png = Regexp.new( "^\x89PNG".force_encoding("BINARY"),
254
+ Regexp::FIXEDENCODING )
255
+ gif = Regexp.new( "^\x89GIF".force_encoding("BINARY"),
256
+ Regexp::FIXEDENCODING )
257
+
258
+ mime = options[:mime]
259
+ mime ||= "jpg" if data.match jpg
260
+ mime ||= "png" if data.match png
261
+ mime ||= "gif" if data.match gif
262
+ pic_type = options[:pic_type]
263
+ pic_type = ["%02i" % pic_type].pack('H*')
264
+ desc = "#{options[:description]}"
265
+ header = "\x00image/#{mime}\x00#{pic_type}#{desc}\x00"
266
+ self["APIC"] = header + data.force_encoding('BINARY')
267
+ end
268
+
269
+ ### Returns an array of images:
270
+ ### [ ["01_.jpg", "Image Data in Binary String"],
271
+ ### ["02_.png", "Another Image in a String"] ]
272
+ ###
273
+ ### e.g. to write all images:
274
+ ### mp3.tag2.pictures.each do |image|
275
+ ### File.open(img[0], 'wb'){|f| f.write img[1])
276
+ ### end
277
+ def pictures
278
+ apic_images = [self["APIC"]].flatten.dup
279
+ result = []
280
+ apic_images.each_index do |index|
281
+ pic = apic_images[index]
282
+ next if !pic.is_a?(String) or pic == ""
283
+ pic.force_encoding 'BINARY'
284
+ picture = []
285
+ jpg = Regexp.new("jpg|JPG|jpeg|JPEG".force_encoding("BINARY"),
286
+ Regexp::FIXEDENCODING )
287
+ png = Regexp.new("png|PNG".force_encoding("BINARY"),
288
+ Regexp::FIXEDENCODING )
289
+ header = pic.unpack('a120').first.force_encoding "BINARY"
290
+ mime_pos = 0
291
+
292
+ # safest way to correctly extract jpg and png is finding mime
293
+ if header.match jpg
294
+ mime = "jpg"
295
+ mime_pos = header =~ jpg
296
+ start = Regexp.new("^\377".force_encoding("BINARY"),
297
+ Regexp::FIXEDENCODING )
298
+ elsif header.match png
299
+ mime = "png"
300
+ mime_pos = header =~ png
301
+ start = Regexp.new("^\x89PNG".force_encoding("BINARY"),
302
+ Regexp::FIXEDENCODING )
303
+ else
304
+ mime = "dat"
305
+ end
306
+
307
+ puts "analysing image: #{header.inspect}..." if $DEBUG
308
+ mim, pic_type, desc, data = pic[mime_pos, pic.length].unpack('Z*hZ*a*')
309
+
310
+ if mime != "dat" and (!data.match(start) or data.nil?)
311
+ real_start = pic =~ start
312
+ data = pic[real_start, pic.length]
313
+ end
314
+
315
+ if mime == "dat"
316
+ # if no jpg or png, extract data anyway e.g. gif
317
+ mime, desc, data = pic.unpack('h Z* h Z* a*').values_at(1,3,4)
318
+ end
319
+
320
+ if mime == "jpg"
321
+ # inspect jpg image header (first 10 chars) for "\xFF\x00" (expect "\xFF")
322
+ trailing_null_byte = Regexp.new("(\377)(\000)".force_encoding('BINARY'),
323
+ Regexp::FIXEDENCODING)
324
+ if (data =~ trailing_null_byte) < 10
325
+ data.gsub!(trailing_null_byte, "\xff".force_encoding('BINARY'))
326
+ end
327
+ end
328
+
329
+ desc = "%02i_#{desc[0,25]}" % (index + 1)
330
+
331
+ filename = desc.match("#{mime}$") ? desc : "#{desc}.#{mime}"
332
+ filename.gsub!('/','')
333
+
334
+ picture[0] = filename
335
+ picture[1] = data
336
+ result << picture
337
+ end
338
+ result
339
+ end
340
+
341
+ def inspect
342
+ self.to_inspect_hash
343
+ end
344
+
345
+ def remove_pictures
346
+ self.APIC = ""
347
+ self.PIC = ""
348
+ end
349
+
220
350
  ### gets id3v2 tag information from io object (must support #seek() method)
221
351
  def from_io(io)
222
352
  @io = io
@@ -461,6 +591,14 @@ class ID3v2 < DelegateClass(Hash)
461
591
  def to_syncsafe(num)
462
592
  ( (num<<3) & 0x7f000000 ) + ( (num<<2) & 0x7f0000 ) + ( (num<<1) & 0x7f00 ) + ( num & 0x7f )
463
593
  end
594
+
595
+ ### this is especially useful for printing out APIC data because
596
+ ### only the header of the APIC tag is of interest
597
+ ### The result also shows some bytes escaped for cleaner display
598
+ def pretty_header(str, chars=128)
599
+ "#{str.unpack("a#{chars}").first}<<<...snip...>>>".force_encoding('BINARY').inspect[1..-2]
600
+ end
601
+
464
602
 
465
603
  end
466
604
 
@@ -189,6 +189,39 @@ class Mp3InfoTest < Test::Unit::TestCase
189
189
  assert_equal( "2.3.0", written_tag.version )
190
190
  end
191
191
 
192
+ # test for good output of APIC tag inspect (escaped and snipped)
193
+ def test_id3v2_to_inspect_hash
194
+ Mp3Info.open(TEMP_FILE) do |mp3|
195
+ mp3.tag2.APIC = "\x00testing.jpg\x00" * 20
196
+ escaped_str = "\\x00testing.jpg\\x00" * 20
197
+ snipped_str = escaped_str.unpack('a185').first + "<<<...snip...>>>"
198
+ assert_equal(snipped_str, mp3.tag2.to_inspect_hash["APIC"])
199
+ end
200
+ end
201
+
202
+ def test_id3v2_get_pictures
203
+ img = "\x89PNG".force_encoding('BINARY') +
204
+ random_string(120).force_encoding('BINARY')
205
+ Mp3Info.open(TEMP_FILE) do |mp3|
206
+ mp3.tag2.add_picture(img, :description => 'example image.png')
207
+ end
208
+ Mp3Info.open(TEMP_FILE) do |mp3|
209
+ assert_equal(["01_example image.png", img], mp3.tag2.pictures[0])
210
+ end
211
+ end
212
+
213
+ def test_id3v2_remove_pictures
214
+ jpg_data = "\xFF".force_encoding('BINARY') +
215
+ random_string(123).force_encoding('BINARY')
216
+ Mp3Info.open(TEMP_FILE) do |mp|
217
+ mp.tag2.add_picture(jpg_data)
218
+ end
219
+ Mp3Info.open(TEMP_FILE) do |mp|
220
+ mp.tag2.remove_pictures
221
+ assert_equal([], mp.tag2.pictures)
222
+ end
223
+ end
224
+
192
225
  def test_id3v2_methods
193
226
  tag = { "TIT2" => "tit2", "TPE1" => "tpe1" }
194
227
  Mp3Info.open(TEMP_FILE) do |mp3|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-mp3info
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: '0.8'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-25 00:00:00.000000000 Z
12
+ date: 2013-01-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: hoe-yard
@@ -34,7 +34,7 @@ dependencies:
34
34
  requirements:
35
35
  - - ~>
36
36
  - !ruby/object:Gem::Version
37
- version: '3.4'
37
+ version: '3.5'
38
38
  type: :development
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,10 +42,8 @@ dependencies:
42
42
  requirements:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
- version: '3.4'
46
- description: ! 'ruby-mp3info read low-level informations and manipulate tags on
47
-
48
- mp3 files.'
45
+ version: '3.5'
46
+ description: ruby-mp3info read low-level informations and manipulate tags on mp3 files.
49
47
  email:
50
48
  - guillaume.pierronnet@gmail.com
51
49
  executables: []
@@ -53,11 +51,10 @@ extensions: []
53
51
  extra_rdoc_files:
54
52
  - History.txt
55
53
  - Manifest.txt
56
- - README.txt
57
54
  files:
58
55
  - History.txt
59
56
  - Manifest.txt
60
- - README.txt
57
+ - README.md
61
58
  - Rakefile
62
59
  - lib/mp3info.rb
63
60
  - lib/mp3info/extension_modules.rb
data/README.txt DELETED
@@ -1,82 +0,0 @@
1
- = ruby-mp3info
2
-
3
- * http://github.com/moumar/ruby-mp3info
4
-
5
- == DESCRIPTION:
6
-
7
- ruby-mp3info read low-level informations and manipulate tags on
8
- mp3 files.
9
-
10
- == FEATURES/PROBLEMS:
11
-
12
- * written in pure ruby
13
- * read low-level informations like bitrate, length, samplerate, etc...
14
- * read, write, remove id3v1 and id3v2 tags
15
- * correctly read VBR files (with or without Xing header)
16
- * only 2.3 version is supported for writings id3v2 tags
17
- * id3v2 tags are always written in UTF-16 encoding
18
-
19
- == SYNOPSIS:
20
-
21
- ### read and display infos & tags
22
-
23
- require "mp3info"
24
- # read and display infos & tags
25
- Mp3Info.open("myfile.mp3") do |mp3info|
26
- puts mp3info
27
- end
28
-
29
- # read/write tag1 and tag2 with Mp3Info#tag attribute
30
- # when reading tag2 have priority over tag1
31
- # when writing, each tag is written.
32
- Mp3Info.open("myfile.mp3") do |mp3|
33
- puts mp3.tag.title
34
- puts mp3.tag.artist
35
- puts mp3.tag.album
36
- puts mp3.tag.tracknum
37
- mp3.tag.title = "track title"
38
- mp3.tag.artist = "artist name"
39
- end
40
-
41
- # tags are written when calling Mp3Info#close or at the end of the #open block
42
-
43
- ### access id3v2 tags
44
-
45
- Mp3Info.open("myfile.mp3") do |mp3|
46
- # you can access four letter v2 tags like this
47
- puts mp3.tag2.TIT2
48
- mp3.tag2.TIT2 = "new TIT2"
49
- # or like that
50
- mp3.tag2["TIT2"]
51
- # at this time, only COMM tag is processed after reading and before writing
52
- # according to ID3v2#options hash
53
- mp3.tag2.options[:lang] = "FRE"
54
- mp3.tag2.COMM = "my comment in french, correctly handled when reading and writing"
55
- end
56
-
57
- == REQUIREMENTS:
58
-
59
- * iconv when using ruby 1.8
60
-
61
- == INSTALL:
62
-
63
- * gem install ruby-mp3info
64
-
65
- == DEVELOPERS:
66
-
67
- After checking out the source, run:
68
-
69
- $ rake newb
70
-
71
- This task will install any missing dependencies, run the tests/specs,
72
- and generate the RDoc.
73
-
74
- == LICENSE:
75
-
76
- ruby
77
-
78
- == TODO:
79
-
80
- * encoder detection
81
- * support for more tags in id3v2
82
- * generalize id3v2 with other audio formats (APE, MPC, OGG, etc...)