flacinfo-rb 0.3.1 → 0.4
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.
- data/README +12 -6
- data/lib/flacinfo.rb +131 -25
- data/test/test-flacinfo.rb +80 -0
- data/test/test.flac +0 -0
- metadata +7 -6
data/README
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
::
|
1
|
+
:: FlacInfo-rb ::
|
2
2
|
Author: Darren Kirby
|
3
3
|
mailto:bulliver@badcomputer.org
|
4
4
|
License: Ruby
|
@@ -14,7 +14,7 @@ foo = FlacInfo.new("someSong.flac")
|
|
14
14
|
|
15
15
|
streaminfo :: hash of STREAMINFO block metadata
|
16
16
|
seektable :: hash of arrays of seek points
|
17
|
-
comment :: array of VORBIS COMMENT block metadata
|
17
|
+
comment :: array of VORBIS COMMENT block metadata
|
18
18
|
tags :: user-friendly hash of Vorbis comment metadata key=value pairs
|
19
19
|
application :: hash of APPLICATION block metadata
|
20
20
|
padding :: hash of PADDING block metadata
|
@@ -24,13 +24,19 @@ flac_file :: hash of APPLICATION Id 0x41544348 (Flac File) metadata if presen
|
|
24
24
|
|
25
25
|
== Public methods ==
|
26
26
|
|
27
|
-
|
27
|
+
comment_add :: adds a comment
|
28
|
+
comment_del :: deletes a comment
|
28
29
|
hastag('str') :: returns true if tags['str'] exists
|
29
|
-
print_tags :: pretty-print tags hash
|
30
|
-
print_seektable :: pretty-print seektable hash
|
31
30
|
meta_flac :: prints all META BLOCKS. (Mostly) equivelant to 'metaflac --list'
|
31
|
+
padding_add!(b) :: adds a PADDING block of size 'b' or 4096 bytes
|
32
|
+
padding_del! :: deletes the PADDING block
|
33
|
+
padding_resize! :: resizes (grow or shrink) a padding block to size 'b' or 4096 bytes
|
34
|
+
print_seektable :: pretty-print seektable hash
|
35
|
+
print_streaminfo :: pretty-print streaminfo hash
|
36
|
+
print_tags :: pretty-print tags hash
|
32
37
|
raw_data_dump(?) :: if passed a filename it will dump flac_file['raw_data'] to that file,
|
33
|
-
|
38
|
+
otherwise it will dump it to the console (even if binary!)
|
39
|
+
update! :: writes comment changes to disk
|
34
40
|
write_picture(?) :: write image from PICTURE block(s) to optional file
|
35
41
|
|
36
42
|
For more/different documentation see http://badcomputer.org/unix/code/flacinfo/
|
data/lib/flacinfo.rb
CHANGED
@@ -17,9 +17,10 @@
|
|
17
17
|
# = Copyright and Disclaimer
|
18
18
|
#
|
19
19
|
# Copyright:: (c) 2006, 2007 Darren Kirby
|
20
|
-
#
|
21
|
-
# No warranty is provided and the author
|
22
|
-
# for lost or damaged files.
|
20
|
+
#
|
21
|
+
# FlacInfo is free software. No warranty is provided and the author
|
22
|
+
# cannot accept responsibility for lost or damaged files.
|
23
|
+
#
|
23
24
|
# License:: Ruby
|
24
25
|
# Author:: Darren Kirby (mailto:bulliver@badcomputer.org)
|
25
26
|
# Website:: http://badcomputer.org/unix/code/flacinfo/index.bot
|
@@ -53,7 +54,7 @@ end
|
|
53
54
|
# All attributes will be present but empty if the associated block is not present in the Flac file,
|
54
55
|
# except for 'picture' which will have the key 'n' with the value '0'.
|
55
56
|
# All 'offset' and 'block_size' values do not include the block header. All block headers are 4 bytes
|
56
|
-
# no matter the type, so if you need the offset including the header, subtract 4. If you need the size
|
57
|
+
# no matter the type, so if you need the offset including the header, subtract 4. If you need the size
|
57
58
|
# including the header, add 4.
|
58
59
|
class FlacInfo
|
59
60
|
# A list of 'standard field names' according to the Vorbis Comment specification. It is certainly
|
@@ -321,19 +322,15 @@ class FlacInfo
|
|
321
322
|
nil
|
322
323
|
end
|
323
324
|
|
324
|
-
# Writes changes to disk
|
325
|
+
# Writes Vorbis tag changes to disk
|
325
326
|
#
|
326
327
|
# :call-seq:
|
327
|
-
# FlacInfo.update -> bool
|
328
|
+
# FlacInfo.update! -> bool
|
328
329
|
#
|
329
330
|
# Returns true if write was successful, false otherwise.
|
330
331
|
#
|
331
|
-
def update
|
332
|
-
|
333
|
-
return true
|
334
|
-
else
|
335
|
-
return false
|
336
|
-
end
|
332
|
+
def update!
|
333
|
+
write_to_disk ? true : false
|
337
334
|
end
|
338
335
|
|
339
336
|
# Adds a new comment to the comment array
|
@@ -342,8 +339,8 @@ class FlacInfo
|
|
342
339
|
# FlacInfo.comment_add(str) -> bool
|
343
340
|
#
|
344
341
|
# 'str' must be in the form 'name=value', or 'name=' if you want to
|
345
|
-
# set an empty value for a particular tag.
|
346
|
-
#
|
342
|
+
# set an empty value for a particular tag. Returns 'true' if successful,
|
343
|
+
# false otherwise. Remember to call 'update!' to write changes to the file.
|
347
344
|
#
|
348
345
|
def comment_add(name)
|
349
346
|
if name !~ /\w=/ # We accept 'name=' in case you want to leave the value empty
|
@@ -366,7 +363,8 @@ class FlacInfo
|
|
366
363
|
# If 'str' is in the form 'name=value' only exact matches
|
367
364
|
# will be deleted. If 'str' is in the form 'name' any and all
|
368
365
|
# comments named 'name' will be deleted. Returns 'true' if a
|
369
|
-
# comment was deleted, false otherwise.
|
366
|
+
# comment was deleted, false otherwise. Remember to call
|
367
|
+
# 'update!' to write changes to the file.
|
370
368
|
#
|
371
369
|
def comment_del(name)
|
372
370
|
bc = Array.new(@comment) # We need a copy
|
@@ -384,6 +382,55 @@ class FlacInfo
|
|
384
382
|
end
|
385
383
|
end
|
386
384
|
|
385
|
+
# Adds a padding block
|
386
|
+
#
|
387
|
+
# :call-seq:
|
388
|
+
# FlacInfo.padding_add!(size) --> Boolean
|
389
|
+
#
|
390
|
+
# 'size' is an optional integer argument for the
|
391
|
+
# size of the padding block. It defaults to 4096 bytes.
|
392
|
+
# Returns true if successful, else false.
|
393
|
+
#
|
394
|
+
def padding_add!(size=4096)
|
395
|
+
@metadata_blocks.each do |type|
|
396
|
+
if type[0] == "padding"
|
397
|
+
raise FlacInfoError, "PADDING block exists. Use 'padding_resize!'"
|
398
|
+
end
|
399
|
+
end
|
400
|
+
build_padding_block(size) ? true : false
|
401
|
+
end
|
402
|
+
|
403
|
+
# Removes a padding block
|
404
|
+
#
|
405
|
+
# :call-seq:
|
406
|
+
# FlacInfo.padding_del!() --> Boolean
|
407
|
+
#
|
408
|
+
# Returns true if the padding block is
|
409
|
+
# successfully removed else false.
|
410
|
+
#
|
411
|
+
def padding_del!
|
412
|
+
remove_padding_block ? true : false
|
413
|
+
end
|
414
|
+
|
415
|
+
# Resizes a padding block
|
416
|
+
#
|
417
|
+
# :call-seq:
|
418
|
+
# FlacInfo.padding_resize!(size) --> Boolean
|
419
|
+
#
|
420
|
+
# 'size' is an optional integer argument for the
|
421
|
+
# size of the new padding block. It defaults to 4096 bytes.
|
422
|
+
# Returns true if successful, else false.
|
423
|
+
#
|
424
|
+
def padding_resize!(size=4096)
|
425
|
+
begin
|
426
|
+
remove_padding_block
|
427
|
+
build_padding_block(size)
|
428
|
+
true
|
429
|
+
rescue
|
430
|
+
false
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
387
434
|
#--
|
388
435
|
# This cleans up the output when using FlacInfo in irb
|
389
436
|
def inspect #:nodoc:
|
@@ -486,7 +533,7 @@ class FlacInfo
|
|
486
533
|
header = @fp.read(4)
|
487
534
|
# First 4 bytes must be 0x66, 0x4C, 0x61, and 0x43
|
488
535
|
if header != 'fLaC'
|
489
|
-
raise
|
536
|
+
raise FlacInfoReadError, "#{@filename} does not appear to be a valid Flac file"
|
490
537
|
end
|
491
538
|
|
492
539
|
typetable = { 0 => "streaminfo", 1 => "padding", 2 => "application",
|
@@ -501,12 +548,13 @@ class FlacInfo
|
|
501
548
|
# first bit = Last-metadata-block flag
|
502
549
|
# bits 2-8 = BLOCK_TYPE. See typetable above
|
503
550
|
block_header = @fp.read(1).unpack("B*")[0]
|
551
|
+
#puts block_header
|
504
552
|
lastheader = block_header[0].to_i & 1
|
505
553
|
type = sprintf("%u", "0b#{block_header[1..7]}").to_i
|
506
554
|
@metadata_blocks << [typetable[type], type, lastheader]
|
507
555
|
|
508
556
|
if type >= typetable.size
|
509
|
-
raise
|
557
|
+
raise FlacInfoReadError, "Invalid block header type"
|
510
558
|
end
|
511
559
|
|
512
560
|
pos += 1
|
@@ -538,7 +586,7 @@ class FlacInfo
|
|
538
586
|
end
|
539
587
|
|
540
588
|
rescue
|
541
|
-
raise
|
589
|
+
raise FlacInfoReadError, "Could not parse METADATA_BLOCK_SEEKTABLE"
|
542
590
|
end
|
543
591
|
end
|
544
592
|
|
@@ -553,7 +601,7 @@ class FlacInfo
|
|
553
601
|
|
554
602
|
@fp.seek(@cuesheet['block_size'], IO::SEEK_CUR)
|
555
603
|
rescue
|
556
|
-
raise
|
604
|
+
raise FlacInfoReadError, "Could not parse METADATA_BLOCK_CUESHEET"
|
557
605
|
end
|
558
606
|
end
|
559
607
|
|
@@ -591,7 +639,7 @@ class FlacInfo
|
|
591
639
|
|
592
640
|
@fp.seek((@picture[n]['raw_data_length']), IO::SEEK_CUR)
|
593
641
|
rescue
|
594
|
-
raise
|
642
|
+
raise FlacInfoReadError, "Could not parse METADATA_BLOCK_PICTURE"
|
595
643
|
end
|
596
644
|
end
|
597
645
|
|
@@ -620,7 +668,7 @@ class FlacInfo
|
|
620
668
|
@application['raw_data'] = @fp.read(@application['block_size'] - 4)
|
621
669
|
end
|
622
670
|
rescue
|
623
|
-
raise
|
671
|
+
raise FlacInfoReadError, "Could not parse METADATA_BLOCK_APPLICATION"
|
624
672
|
end
|
625
673
|
end
|
626
674
|
|
@@ -664,7 +712,7 @@ class FlacInfo
|
|
664
712
|
end
|
665
713
|
|
666
714
|
rescue
|
667
|
-
raise
|
715
|
+
raise FlacInfoReadError, "Could not parse METADATA_BLOCK_VORBIS_COMMENT"
|
668
716
|
end
|
669
717
|
end
|
670
718
|
|
@@ -679,7 +727,7 @@ class FlacInfo
|
|
679
727
|
|
680
728
|
@fp.seek(@padding['block_size'], IO::SEEK_CUR)
|
681
729
|
rescue
|
682
|
-
raise
|
730
|
+
raise FlacInfoReadError, "Could not parse METADATA_BLOCK_PADDING"
|
683
731
|
end
|
684
732
|
end
|
685
733
|
|
@@ -710,7 +758,7 @@ class FlacInfo
|
|
710
758
|
# 128 bits :: MD5 signature of the unencoded audio data.
|
711
759
|
@streaminfo['md5'] = @fp.read(16).unpack("H32")[0]
|
712
760
|
rescue
|
713
|
-
raise
|
761
|
+
raise FlacInfoReadError, "Could not parse METADATA_BLOCK_STREAMINFO"
|
714
762
|
end
|
715
763
|
end
|
716
764
|
|
@@ -725,7 +773,7 @@ class FlacInfo
|
|
725
773
|
size = size - 2 - desc_length - mime_length
|
726
774
|
@flac_file['raw_data'] = @fp.read(size)
|
727
775
|
rescue
|
728
|
-
raise
|
776
|
+
raise FlacInfoReadError, "Could not parse Flac File data"
|
729
777
|
end
|
730
778
|
end
|
731
779
|
|
@@ -824,6 +872,64 @@ class FlacInfo
|
|
824
872
|
flac.close
|
825
873
|
end
|
826
874
|
|
875
|
+
# remove the padding block
|
876
|
+
def remove_padding_block
|
877
|
+
begin
|
878
|
+
new_last_block = @metadata_blocks[-2]
|
879
|
+
|
880
|
+
flac = File.new(@filename, "r+b")
|
881
|
+
flac.binmode
|
882
|
+
|
883
|
+
flac.seek((@padding['offset'] + @padding['block_size']), IO::SEEK_CUR)
|
884
|
+
rest_of_file = flac.read()
|
885
|
+
|
886
|
+
flac.seek((@padding['offset'] - 4), IO::SEEK_SET)
|
887
|
+
flac.write(rest_of_file)
|
888
|
+
|
889
|
+
nbh = build_block_header(new_last_block[1], new_last_block[4], 1)
|
890
|
+
|
891
|
+
flac.seek((new_last_block[3] - 4), IO::SEEK_SET)
|
892
|
+
flac.write(nbh)
|
893
|
+
flac.close()
|
894
|
+
|
895
|
+
parse_flac_meta_blocks # Parse the file again to update new values
|
896
|
+
true
|
897
|
+
rescue
|
898
|
+
false
|
899
|
+
end
|
900
|
+
end
|
901
|
+
|
902
|
+
def build_padding_block(size)
|
903
|
+
begin
|
904
|
+
old_last_block = @metadata_blocks[-1]
|
905
|
+
|
906
|
+
a = Array.new(size / 2, 0)
|
907
|
+
pbd = a.pack("v*")
|
908
|
+
pbh = build_block_header(1, size, 1)
|
909
|
+
|
910
|
+
flac = File.new(@filename, "r+b")
|
911
|
+
flac.binmode
|
912
|
+
|
913
|
+
flac.seek((old_last_block[4] + old_last_block[3]), IO::SEEK_CUR)
|
914
|
+
co = flac.tell
|
915
|
+
rest_of_file = flac.read()
|
916
|
+
flac.seek(co, IO::SEEK_SET)
|
917
|
+
|
918
|
+
flac.write(pbh)
|
919
|
+
flac.write(pbd)
|
920
|
+
flac.write(rest_of_file)
|
921
|
+
nbh = build_block_header(old_last_block[1], old_last_block[4], 0)
|
922
|
+
|
923
|
+
flac.seek((old_last_block[3] - 4), IO::SEEK_SET)
|
924
|
+
flac.write(nbh)
|
925
|
+
|
926
|
+
flac.close()
|
927
|
+
parse_flac_meta_blocks # Parse the file again to update new values
|
928
|
+
true
|
929
|
+
rescue
|
930
|
+
false
|
931
|
+
end
|
932
|
+
end
|
827
933
|
end
|
828
934
|
|
829
935
|
# If called directly from the command line, run meta_flac on each argument
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
require 'flacinfo'
|
7
|
+
|
8
|
+
class TestFlacInfo < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@flac = FlacInfo.new("test.flac")
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_streaminfo
|
15
|
+
assert_equal(34, @flac.streaminfo['block_size'])
|
16
|
+
assert_equal(8, @flac.streaminfo['offset'])
|
17
|
+
assert_equal(4096, @flac.streaminfo['minimum_block'])
|
18
|
+
assert_equal(4096, @flac.streaminfo['maximum_block'])
|
19
|
+
assert_equal(4454, @flac.streaminfo['minimum_frame'])
|
20
|
+
assert_equal(7491, @flac.streaminfo['maximum_frame'])
|
21
|
+
assert_equal(44100, @flac.streaminfo['samplerate'])
|
22
|
+
assert_equal(2, @flac.streaminfo['channels'])
|
23
|
+
assert_equal(16, @flac.streaminfo['bits_per_sample'])
|
24
|
+
assert_equal(663552, @flac.streaminfo['total_samples'])
|
25
|
+
assert_equal("8cde6ba4d9b7458f1446215941ea5e1b", @flac.streaminfo['md5'])
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_vorbis_comments
|
29
|
+
assert_equal(294, @flac.tags['block_size'])
|
30
|
+
assert_equal(46, @flac.tags['offset'])
|
31
|
+
assert_equal("test.flac", @flac.tags['TITLE'])
|
32
|
+
assert_equal("Darren Kirby", @flac.tags['ARTIST'])
|
33
|
+
assert_equal("No Thanks", @flac.tags['COPYRIGHT'])
|
34
|
+
assert_equal("Badcomputer Org.", @flac.tags['ORGANIZATION'])
|
35
|
+
assert_equal("A simple reference flac for the unit tests", @flac.tags['DESCRIPTION'])
|
36
|
+
assert_equal("Spoken Word", @flac.tags['GENRE'])
|
37
|
+
assert_equal("Wed Aug 15 14:28:07 2007", @flac.tags['DATE'])
|
38
|
+
assert_equal("IN UR COMPUTER READING UR FLACS", @flac.tags['LOCATION'])
|
39
|
+
assert_equal("reference libFLAC 1.2.0 20070715", @flac.tags['vendor_tag'])
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_picture_one
|
43
|
+
assert_equal(2, @flac.picture["n"])
|
44
|
+
assert_equal(15921, @flac.picture[1]['block_size'])
|
45
|
+
assert_equal(344, @flac.picture[1]['offset'])
|
46
|
+
assert_equal("image/png", @flac.picture[1]['mime_type'])
|
47
|
+
assert_equal("Artist/performer", @flac.picture[1]['type_string'])
|
48
|
+
assert_equal(8, @flac.picture[1]['type_int'])
|
49
|
+
assert_equal("The author of flacinfo", @flac.picture[1]['description_string'])
|
50
|
+
assert_equal(0, @flac.picture[1]['n_colours'])
|
51
|
+
assert_equal(32, @flac.picture[1]['colour_depth'])
|
52
|
+
assert_equal(99, @flac.picture[1]['height'])
|
53
|
+
assert_equal(100, @flac.picture[1]['width'])
|
54
|
+
assert_equal(407, @flac.picture[1]['raw_data_offset'])
|
55
|
+
assert_equal(15858, @flac.picture[1]['raw_data_length'])
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_picture_two
|
59
|
+
assert_equal(2, @flac.picture["n"])
|
60
|
+
assert_equal(22183, @flac.picture[2]['block_size'])
|
61
|
+
assert_equal(16269, @flac.picture[2]['offset'])
|
62
|
+
assert_equal("image/jpeg", @flac.picture[2]['mime_type'])
|
63
|
+
assert_equal("Other", @flac.picture[2]['type_string'])
|
64
|
+
assert_equal(0, @flac.picture[2]['type_int'])
|
65
|
+
assert_equal("A silly picture for the unit test", @flac.picture[2]['description_string'])
|
66
|
+
assert_equal(0, @flac.picture[2]['n_colours'])
|
67
|
+
assert_equal(24, @flac.picture[2]['colour_depth'])
|
68
|
+
assert_equal(353, @flac.picture[2]['height'])
|
69
|
+
assert_equal(507, @flac.picture[2]['width'])
|
70
|
+
assert_equal(16344, @flac.picture[2]['raw_data_offset'])
|
71
|
+
assert_equal(22108, @flac.picture[2]['raw_data_length'])
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_padding
|
75
|
+
assert_equal(258, @flac.padding['block_size'])
|
76
|
+
assert_equal(38456, @flac.padding['offset'])
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
data/test/test.flac
ADDED
Binary file
|
metadata
CHANGED
@@ -3,14 +3,14 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: flacinfo-rb
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
8
|
-
summary: Pure Ruby
|
6
|
+
version: "0.4"
|
7
|
+
date: 2007-09-08 00:00:00 -06:00
|
8
|
+
summary: Pure Ruby library for accessing metadata from Flac files
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email: bulliver@badcomputer.org
|
12
12
|
homepage: http://badcomputer.org/unix/code/flacinfo/
|
13
|
-
rubyforge_project:
|
13
|
+
rubyforge_project: flacinfo-rb
|
14
14
|
description:
|
15
15
|
autorequire: flacinfo
|
16
16
|
default_executable:
|
@@ -31,8 +31,9 @@ authors:
|
|
31
31
|
files:
|
32
32
|
- README
|
33
33
|
- lib/flacinfo.rb
|
34
|
-
test_files:
|
35
|
-
|
34
|
+
test_files:
|
35
|
+
- test/test-flacinfo.rb
|
36
|
+
- test/test.flac
|
36
37
|
rdoc_options: []
|
37
38
|
|
38
39
|
extra_rdoc_files:
|