discid 1.0.0.a1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ *~
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.8.7"
4
+ - "1.9.3"
5
+ - jruby-18mode # JRuby in 1.8 mode
6
+ - jruby-19mode # JRuby in 1.9 mode
7
+ - rbx-18mode
8
+ - rbx-19mode
9
+ before_install:
10
+ - sudo apt-get update -qq
11
+ - sudo apt-get install -qq libdiscid0
12
+ script: bundle exec rake test
@@ -0,0 +1 @@
1
+ --no-private --markup markdown
data/CHANGES ADDED
@@ -0,0 +1,42 @@
1
+ = Changelog
2
+ == 1.0.0.a1 (2013-04-22)
3
+ * Use FFI instead of C module
4
+ * Completely overhauled API
5
+ * Full support for MCN, ISRC and libdiscid feature detection
6
+ * Renamed from mb-discid to just discid
7
+
8
+ == 0.2.0 (2013-02-17)
9
+ * Support for MCN and ISRC (requires libdiscid >= 0.3)
10
+
11
+ == 0.1.5 (2011-07-02)
12
+ * Added binding for get_webservice_url()
13
+ * Add lib path detection, allows out-of-the-box install when your
14
+ libdiscid is in /usr/local (Matt Patterson)
15
+
16
+ == 0.1.4 (2009-11-19)
17
+ * Fixed calling +read+ method without argument
18
+
19
+ == 0.1.3 (2009-11-19)
20
+ * Added singleton method +sectors_to_seconds+ to convert sectors into seconds
21
+ * Added method +seconds+ to retrieve disc length in seconds
22
+ * Added method +track_info+ for accessing more detailed information about tracks
23
+ * Fixed building with Ruby 1.9 (Mihaly Csomay)
24
+
25
+ == 0.1.2 (2007-07-04)
26
+ * Support the method +put+ to set the TOC information directly instead of
27
+ reading it from a device.
28
+ * Fixed possible core dump if +read+ was called twice and failed the
29
+ second time.
30
+ * New to_s method (returns string representation of the ID itself).
31
+ * Complete RDoc documentation.
32
+
33
+ == 0.1.1 (2007-06-03)
34
+ * Minor changes to source to support MS compiler
35
+ * Provide Win32 binary gem
36
+ * Changed require of library to "require 'mb-discid'" (was "require 'DiscID'"
37
+ before, which was likely to cause problems)
38
+
39
+ == 0.1.0 (2007-06-02)
40
+ * Initial release
41
+
42
+ $Id$
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in discid.gemspec
4
+ gemspec
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
@@ -0,0 +1,56 @@
1
+ # Ruby bindings for MusicBrainz libdiscid
2
+
3
+ ## About
4
+ ruby-discid provides Ruby bindings for the MusicBrainz DiscID library libdiscid.
5
+ It allows calculating DiscIDs (MusicBrainz and freedb) for Audio CDs. Additionally
6
+ the library can extract the MCN/UPC/EAN and the ISRCs from disc.
7
+
8
+ ## Requirements
9
+ * Ruby >= 1.8.6
10
+ * Ruby-FFI
11
+ * libdiscid >= 0.2.2
12
+
13
+ ## Installation
14
+ Before installing ruby-discid make sure you have libdiscid installed. See
15
+ http://musicbrainz.org/doc/libdiscid for more information on how to do this.
16
+
17
+ Installing ruby-discid is best done using RubyGems:
18
+
19
+ gem install discid
20
+
21
+ Or install it from the source tarball:
22
+
23
+ rake install
24
+
25
+ ## Usage
26
+ ### Read only the TOC
27
+
28
+ require 'discid'
29
+
30
+ device = "/dev/cdrom"
31
+ disc = DiscId.read(device)
32
+ puts disc.id
33
+
34
+ ### Read the TOC, MCN and ISRCs
35
+
36
+ require 'discid'
37
+
38
+ device = "/dev/cdrom"
39
+ disc = DiscId.read(device, :mcn, :isrc)
40
+
41
+ # Print information about the disc:
42
+ puts "DiscID : #{disc.id}"
43
+ puts "FreeDB ID : #{disc.freedb_id}"
44
+ puts "Total length: #{disc.seconds} seconds"
45
+ puts "MCN : #{disc.mcn}"
46
+
47
+ # Print information about individual tracks:
48
+ disc.tracks do |track|
49
+ puts "Track ##{track.number}"
50
+ puts " Length: %02d:%02d (%i sectors)" %
51
+ [track.seconds / 60, track.seconds % 60, track.sectors]
52
+ puts " ISRC : %s" % track.isrc
53
+ end
54
+
55
+ See the documentation of {DiscId} or the files in the `examples` directory for
56
+ more usage information.
@@ -0,0 +1,13 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ require 'yard'
4
+
5
+ desc "Run just the unit tests"
6
+ Rake::TestTask.new(:test) do |test|
7
+ test.test_files = FileList['test/test*.rb']
8
+ test.libs = ['lib', 'ext']
9
+ test.warning = true
10
+ end
11
+
12
+ YARD::Rake::YardocTask.new do |t|
13
+ end
@@ -0,0 +1,35 @@
1
+ mb-discid (0.2.0-1) quantal; urgency=low
2
+
3
+ * Fixed build dependencies
4
+
5
+ -- Philipp Wolfer <ph.wolfer@gmail.com> Mon, 18 Feb 2013 08:59:35 +0100
6
+
7
+ mb-discid (0.2.0-0) quantal; urgency=low
8
+
9
+ * New upstream release
10
+
11
+ -- Philipp Wolfer <ph.wolfer@gmail.com> Sun, 17 Feb 2013 15:56:01 +0100
12
+
13
+ mb-discid (0.1.5-0) lucid; urgency=low
14
+
15
+ * New upstream release
16
+
17
+ -- Philipp Wolfer <ph.wolfer@googlemail.com> Sat, 02 Jul 2011 16:14:51 +0200
18
+
19
+ mb-discid (0.1.4-2) lucid; urgency=low
20
+
21
+ * Fixed building on Maverick and Natty
22
+
23
+ -- Philipp Wolfer <ph.wolfer@googlemail.com> Mon, 16 May 2011 00:05:40 +0200
24
+
25
+ mb-discid (0.1.4-1) karmic; urgency=low
26
+
27
+ * New upstream release
28
+
29
+ -- Philipp Wolfer <ph.wolfer@googlemail.com> Mon, 14 Jun 2010 23:10:51 +0200
30
+
31
+ mb-discid (0.1.2-1) gutsy; urgency=low
32
+
33
+ * Initial packaging
34
+
35
+ -- Philipp Wolfer <phw@rubyforge.org> Mon, 17 Dec 2007 22:56:57 +0100
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'discid/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "discid"
8
+ spec.version = DiscId::VERSION
9
+ spec.authors = ["Philipp Wolfer"]
10
+ spec.email = ["ph.wolfer@gmail.com"]
11
+ spec.description = %q{ruby-discid provides Ruby bindings for the MusicBrainz DiscID library libdiscid. It allows calculating DiscIDs (MusicBrainz and freedb) for Audio CDs. Additionally the library can extract the MCN/UPC/EAN and the ISRCs from disc.}
12
+ spec.summary = %q{Ruby bindings for libdiscid}
13
+ spec.homepage = "https://github.com/phw/ruby-discid"
14
+ spec.license = "LGPL3"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "ffi", "~> 1.6.0"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "yard"
26
+ spec.add_development_dependency "redcarpet"
27
+ end
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Example script for DiscId.
4
+ #
5
+ # This script will read the disc ID from the default device and print
6
+ # the results. You can specify an alternate device to use by giving the
7
+ # device's name as the first command line argument.
8
+ #
9
+ # Example:
10
+ # ./discid.rb /dev/dvd
11
+
12
+ # Just make sure we can run this example from the command
13
+ # line even if DiscId is not yet installed properly.
14
+ $: << 'lib/' << 'ext/' << '../ext/' << '../lib/'
15
+
16
+ require 'discid'
17
+
18
+ # Read the device name from the command line or use the default.
19
+ device = $*[0] ? $*[0] : DiscId.default_device
20
+
21
+ # Create a new DiscID object and read the disc information.
22
+ # In case of errors exit the application.
23
+ puts "Reading TOC from device '#{device}'."
24
+ begin
25
+ disc = DiscId.read(device, :isrc, :mcn)
26
+
27
+ # Instead of reading from a device we could set the TOC directly:
28
+ #disc = DiscId.put(1, 82255, [150, 16157, 35932, 57527])
29
+ rescue Exception => e
30
+ puts e
31
+ exit(1)
32
+ end
33
+
34
+ # Print information about the disc:
35
+ print <<EOF
36
+
37
+ Device : #{disc.device}
38
+ DiscID : #{disc.id}
39
+ FreeDB ID : #{disc.freedb_id}
40
+ First track : #{disc.first_track_num}
41
+ Last track : #{disc.last_track_num}
42
+ Total length: #{disc.seconds} seconds
43
+ Sectors : #{disc.sectors}
44
+ MCN : #{disc.mcn}
45
+
46
+ EOF
47
+
48
+ # Print information about individual tracks:
49
+ disc.tracks do |track|
50
+ puts "Track ##{track.number}"
51
+ puts " Length: %02d:%02d (%i sectors)" %
52
+ [track.seconds / 60, track.seconds % 60, track.sectors]
53
+ puts " Start : %02d:%02d (sector %i)" %
54
+ [track.start_time / 60, track.start_time % 60, track.start_sector]
55
+ puts " End : %02d:%02d (sector %i)" %
56
+ [track.end_time / 60, track.end_time % 60, track.end_sector]
57
+ puts " ISRC : %s" % track.isrc
58
+ end
59
+
60
+ # Print a submission URL that can be used to submit
61
+ # the disc ID to MusicBrainz.org.
62
+ puts "\nSubmit via #{disc.submission_url}"
63
+ #puts "\nWebservice #{disc.webservice_url}"
@@ -0,0 +1,101 @@
1
+ # Copyright (C) 2013 Philipp Wolfer
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'discid/lib'
17
+ require 'discid/disc'
18
+ require 'discid/version'
19
+
20
+ # The DiscId module allows calculating DiscIDs (MusicBrainz and freedb)
21
+ # for Audio CDs. Additionally the library can extract the MCN/UPC/EAN and
22
+ # the ISRCs from disc.
23
+ module DiscId
24
+
25
+ # Read the disc in the given CD-ROM/DVD-ROM drive extracting only the
26
+ # TOC and additionally specified features.
27
+ #
28
+ # This function reads the disc in the drive specified by the given device
29
+ # identifier. If the device is `nil`, the default device, as returned by
30
+ # {default_device}, is used.
31
+ #
32
+ # This function will always read the TOC, but additional features like `:mcn`
33
+ # and `:isrc` can be set using the features parameter. You can set multiple
34
+ # features.
35
+ #
36
+ # @example Read only the TOC:
37
+ # disc = DiscId.read(device)
38
+ #
39
+ # @example Read the TOC, MCN and ISRCs:
40
+ # disc = DiscId.read(device, :mcn, :isrc)
41
+ #
42
+ # @raise [TypeError] `device` can not be converted to a String.
43
+ # @raise [Exception] Error reading from `device`. `Exception#message` contains
44
+ # error details.
45
+ # @param device [String] The device identifier.
46
+ # @param *features [:mcn, :isrc] List of features to use.
47
+ # `:read` is always implied.
48
+ # @return [Disc]
49
+ def self.read(device, *features)
50
+ disc = Disc.new
51
+ disc.read device, *features
52
+ return disc
53
+ end
54
+
55
+ # Provides the TOC of a known CD.
56
+ #
57
+ # This function may be used if the TOC has been read earlier and you want to
58
+ # calculate the disc ID afterwards, without accessing the disc drive.
59
+ #
60
+ # @raise [Exception] The TOC could not be set. `Exception#message`contains
61
+ # error details.
62
+ # @param first_track [Integer] The number of the first audio track on the
63
+ # disc (usually one).
64
+ # @param sectors [Integer] The total number of sectors on the disc.
65
+ # @param offsets [Array] An array with track offsets (sectors) for each track.
66
+ # @return [Disc]
67
+ def self.put(first_track, sectors, offsets)
68
+ disc = Disc.new
69
+ disc.put first_track, sectors, offsets
70
+ return disc
71
+ end
72
+
73
+ # Return the name of the default disc drive for this operating system.
74
+ #
75
+ # @return [String] An operating system dependent device identifier
76
+ def self.default_device
77
+ Lib.default_device
78
+ end
79
+
80
+ # Check if a certain feature is implemented on the current platform.
81
+ #
82
+ # @param feature [:read, :mcn, :isrc]
83
+ # @return [Boolean] True if the feature is implemented and false if not.
84
+ def self.has_feature?(feature)
85
+ feature = feature.to_sym if feature.respond_to? :to_sym
86
+ result = Lib.has_feature feature if Lib::Features.symbols.include? feature
87
+ return result == 1
88
+ end
89
+
90
+ # Converts sectors to seconds.
91
+ #
92
+ # According to the red book standard 75 sectors are one second.
93
+ #
94
+ # @private
95
+ # @param sectors [Integer] Number of sectors
96
+ # @return [Integer] The seconds
97
+ def self.sectors_to_seconds(sectors)
98
+ return (sectors.to_f / 75).round
99
+ end
100
+
101
+ end
@@ -0,0 +1,188 @@
1
+ # Copyright (C) 2013 Philipp Wolfer
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'ffi'
17
+ require 'discid/lib'
18
+ require 'discid/track_info'
19
+
20
+ module DiscId
21
+ # This class holds information about a disc (TOC, MCN, ISRCs).
22
+ #
23
+ # Use {DiscId#read} or {DiscId#put} to initialize an instance of {Disc}.
24
+ class Disc
25
+
26
+ # The device from which this disc object was read.
27
+ attr_reader :device
28
+
29
+ # @private
30
+ def initialize
31
+ pointer = Lib.new
32
+ @handle = FFI::AutoPointer.new(pointer, Lib.method(:free))
33
+ @read = false
34
+ end
35
+
36
+ # @private
37
+ def read(device, *features)
38
+ @read = false
39
+ device = self.class.default_device if device.nil?
40
+
41
+ if not device.respond_to? :to_s
42
+ raise TypeError, 'wrong argument type (expected String)'
43
+ end
44
+
45
+ @device = device.to_s
46
+ flags = Lib.features_to_int features
47
+ result = Lib.read @handle, @device, flags
48
+
49
+ if result == 0
50
+ raise Exception, Lib.get_error_msg(@handle)
51
+ else
52
+ @read = true
53
+ end
54
+ end
55
+
56
+ # @private
57
+ def put(first_track, sectors, offsets)
58
+ @read = false
59
+ @device = nil
60
+ last_track = offsets.length - 1 + first_track
61
+
62
+ # discid_put expects always an offsets array with exactly 100 elements.
63
+ FFI::MemoryPointer.new(:int, 100) do |p|
64
+ p.write_array_of_int([sectors] + offsets)
65
+ result = Lib.put @handle, first_track, last_track, p
66
+
67
+ if result == 0
68
+ raise Exception, Lib.get_error_msg(@handle)
69
+ else
70
+ @read = true
71
+ end
72
+ end
73
+ end
74
+
75
+ # The MusicBrainz DiscID.
76
+ #
77
+ # @return [String] The DiscID or `nil` if no ID was yet read.
78
+ def id
79
+ return nil unless @read
80
+ return Lib.get_id @handle
81
+ end
82
+
83
+ # The FreeDB DiscID.
84
+ #
85
+ # @return [String] The DiscID or `nil` if no ID was yet read.
86
+ def freedb_id
87
+ return nil unless @read
88
+ return Lib.get_freedb_id @handle
89
+ end
90
+
91
+ # The number of the first track on this disc.
92
+ #
93
+ # @return [Integer] The number of the first track or `nil` if no ID was yet read.
94
+ def first_track_num
95
+ return nil unless @read
96
+ return Lib.get_first_track_num @handle
97
+ end
98
+
99
+ # The number of the last track on this disc.
100
+ #
101
+ # @return [Integer] The number of the last track or `nil` if no ID was yet read.
102
+ def last_track_num
103
+ return nil unless @read
104
+ return Lib.get_last_track_num @handle
105
+ end
106
+
107
+ # The length of the disc in sectors.
108
+ #
109
+ # @return [Integer] Sectors or `nil` if no ID was yet read.
110
+ def sectors
111
+ return nil unless @read
112
+ return Lib.get_sectors @handle
113
+ end
114
+
115
+ # The length of the disc in seconds.
116
+ #
117
+ # @return [Integer] Seconds or `nil` if no ID was yet read.
118
+ def seconds
119
+ DiscId.sectors_to_seconds(sectors) if @read
120
+ end
121
+
122
+ # The media catalogue number on the disc, if present.
123
+ #
124
+ # Requires libdiscid >= 0.5. If not supported this method will always
125
+ # return `nil`.
126
+ #
127
+ # @return [String] MCN or `nil` if no ID was yet read.
128
+ def mcn
129
+ return nil unless @read
130
+ return Lib.get_mcn @handle
131
+ end
132
+
133
+ # An URL for submitting the DiscID to MusicBrainz.
134
+ #
135
+ # The URL leads to an interactive disc submission wizard that guides the
136
+ # user through the process of associating this disc's DiscID with a release
137
+ # in the MusicBrainz database.
138
+ #
139
+ # @return [String] Submission URL
140
+ def submission_url
141
+ return nil unless @read
142
+ return Lib.get_submission_url @handle
143
+ end
144
+
145
+ # DiscID to String conversion. Same as calling the method {#id} but guaranteed
146
+ # to return a string.
147
+ #
148
+ # @return [String] The disc ID as a string or an empty string if no ID
149
+ # was yet read.
150
+ def to_s
151
+ id.to_s
152
+ end
153
+
154
+ # Returns an array of {TrackInfo} objects. Each TrackInfo object contains
155
+ # detailed information about the track.
156
+ #
157
+ # Returns always `nil` if no ID was yet read. The block won't be
158
+ # called in this case.
159
+ #
160
+ # @yield [track_info] If a block is given this method returns `nil` and
161
+ # instead iterates over the block calling the block with one argument.
162
+ # @yieldreturn [nil]
163
+ # @return [Array<TrackInfo>] Array of {TrackInfo} objects.
164
+ def tracks
165
+ if @read
166
+ track_number = self.first_track_num - 1
167
+ tracks = []
168
+
169
+ while track_number < self.last_track_num do
170
+ track_number += 1
171
+ isrc = Lib.get_track_isrc(@handle, track_number)
172
+ offset = Lib.get_track_offset(@handle, track_number)
173
+ length = Lib.get_track_length(@handle, track_number)
174
+ track_info = TrackInfo.new(track_number, offset, length, isrc)
175
+
176
+ if block_given?
177
+ yield track_info
178
+ else
179
+ tracks << track_info
180
+ end
181
+ end
182
+
183
+ return tracks unless block_given?
184
+ end
185
+ end
186
+
187
+ end
188
+ end
@@ -0,0 +1,84 @@
1
+ # Copyright (C) 2013 Philipp Wolfer
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require "ffi"
17
+
18
+ module DiscId
19
+
20
+ # This module encapsulates the C interface for libdiscid using FFI.
21
+ # The Lib module is intended for internal use only and should be considered private.
22
+ #
23
+ # @private
24
+ module Lib
25
+ extend FFI::Library
26
+ ffi_lib %w[discid, libdiscid.so.0]
27
+
28
+ attach_function :new, :discid_new, [], :pointer
29
+
30
+ attach_function :free, :discid_free, [:pointer], :void
31
+
32
+ # TODO: Handle old discid_read
33
+ attach_function :read, :discid_read_sparse, [:pointer, :string, :uint], :int
34
+
35
+ attach_function :put, :discid_put, [:pointer, :int, :int, :pointer], :int
36
+
37
+ attach_function :get_error_msg, :discid_get_error_msg, [:pointer], :string
38
+
39
+ attach_function :get_id, :discid_get_id, [:pointer], :string
40
+
41
+ attach_function :get_freedb_id, :discid_get_freedb_id, [:pointer], :string
42
+
43
+ attach_function :get_submission_url, :discid_get_submission_url, [:pointer], :string
44
+
45
+ attach_function :default_device, :discid_get_default_device, [], :string
46
+
47
+ attach_function :get_first_track_num, :discid_get_first_track_num, [:pointer], :int
48
+
49
+ attach_function :get_last_track_num, :discid_get_last_track_num, [:pointer], :int
50
+
51
+ attach_function :get_sectors, :discid_get_sectors, [:pointer], :int
52
+
53
+ attach_function :get_track_offset, :discid_get_track_offset, [:pointer, :int], :int
54
+
55
+ attach_function :get_track_length, :discid_get_track_length, [:pointer, :int], :int
56
+
57
+ attach_function :get_mcn, :discid_get_mcn, [:pointer], :string
58
+
59
+ attach_function :get_track_isrc, :discid_get_track_isrc, [:pointer, :int], :string
60
+
61
+ Features = enum(:feature, [:read, 1 << 0,
62
+ :mcn, 1 << 1,
63
+ :isrc, 1 << 2])
64
+
65
+ attach_function :has_feature, :discid_has_feature, [:feature], :int
66
+
67
+ #attach_function :get_feature_list, :discid_get_feature_list, [:pointer], :void
68
+
69
+ attach_function :get_version_string, :discid_get_version_string, [], :string
70
+
71
+ def self.features_to_int(features)
72
+ feature_flag = 0
73
+ features.each do |feature|
74
+ if feature.respond_to? :to_sym
75
+ feature = feature.to_sym
76
+ feature_flag |= self::Features[feature] if
77
+ self::Features.symbols.include?(feature)
78
+ end
79
+ end
80
+
81
+ return feature_flag
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,129 @@
1
+ # Copyright (C) 2008 - 2013 Philipp Wolfer
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+
17
+ module DiscId
18
+
19
+ # This class holds information about a single track.
20
+ #
21
+ # Currently this includes the following fields:
22
+ #
23
+ # * number: The number of the track on the disc.
24
+ # * sectors: Length of the track in sectors.
25
+ # * start_sector: Start position of the track on the disc in sectors.
26
+ # * end_sector: End position of the track on the disc in sectors.
27
+ # * seconds: Length of the track in seconds.
28
+ # * start_time: Start position of the track on the disc in seconds.
29
+ # * end_time: End position of the track on the disc in seconds.
30
+ # * isrc: The track's ISRC (International Standard Recordings Code)
31
+ # if available.
32
+ #
33
+ # You can access all fields either directly or with the square bracket
34
+ # notation:
35
+ #
36
+ # track = TrackInfo.new(1, 150, 16007)
37
+ # puts track.sectors # 16007
38
+ # puts track[:sectors] # 16007
39
+ #
40
+ # @see DiscId::Disc#tracks
41
+ class TrackInfo
42
+
43
+ # The number of the track on the disc.
44
+ #
45
+ # @return [Integer]
46
+ attr_reader :number
47
+
48
+ # Length of the track in sectors.
49
+ #
50
+ # @return [Integer]
51
+ attr_reader :sectors
52
+
53
+ # Start position of the track on the disc in sectors.
54
+ #
55
+ # @return [Integer]
56
+ attr_reader :start_sector
57
+
58
+ # ISRC number of the trac
59
+ #
60
+ # @return [String]
61
+ attr_reader :isrc
62
+
63
+ # Returns a new TrackInfo.
64
+ def initialize(number, offset, length, isrc)
65
+ @number = number
66
+ @start_sector = offset
67
+ @sectors = length
68
+ @isrc = isrc
69
+ end
70
+
71
+ # End position of the track on the disc in sectors.
72
+ #
73
+ # @return [Integer]
74
+ def end_sector
75
+ start_sector + sectors
76
+ end
77
+
78
+ # Length of the track in seconds.
79
+ #
80
+ # @return [Integer]
81
+ def seconds
82
+ DiscId.sectors_to_seconds(sectors)
83
+ end
84
+
85
+ # Start position of the track on the disc in seconds.
86
+ #
87
+ # @return [Integer]
88
+ def start_time
89
+ DiscId.sectors_to_seconds(start_sector)
90
+ end
91
+
92
+ # End position of the track on the disc in seconds.
93
+ #
94
+ # @return [Integer]
95
+ def end_time
96
+ DiscId.sectors_to_seconds(end_sector)
97
+ end
98
+
99
+ # Allows access to all fields similar to accessing values in a hash.
100
+ #
101
+ # Example:
102
+ #
103
+ # track = TrackInfo.new(1, 150, 16007)
104
+ # puts track.sectors # 16007
105
+ # puts track[:sectors] # 16007
106
+ def [](key)
107
+ if [:number, :sectors, :start_sector, :end_sector,
108
+ :seconds, :start_time, :end_time].include?(key.to_sym)
109
+ method(key).call
110
+ end
111
+ end
112
+
113
+ # Converts the TrackInfo into a Hash.
114
+ #
115
+ # @return [Hash]
116
+ def to_hash
117
+ {
118
+ :sectors => sectors,
119
+ :start_sector => start_sector,
120
+ :end_sector => end_sector,
121
+ :seconds => seconds,
122
+ :start_time => start_time,
123
+ :end_time => end_time,
124
+ :isrc => isrc,
125
+ }
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,19 @@
1
+ # Copyright (C) 2013 Philipp Wolfer
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+
17
+ module DiscId
18
+ VERSION = "1.0.0.a1"
19
+ end
@@ -0,0 +1,162 @@
1
+ # Author:: Philipp Wolfer (mailto:ph.wolfer@gmail.com)
2
+ # Copyright:: Copyright (c) 2007-2013, Philipp Wolfer
3
+ # License:: RBrainz is free software distributed under LGPLv3.
4
+ # See LICENSE[file:../LICENSE.txt] for permissions.
5
+
6
+ require 'test/unit'
7
+ require 'discid'
8
+
9
+ # Helper class which can't be converted into a string.
10
+ class NotAString
11
+
12
+ private
13
+
14
+ def to_s
15
+ end
16
+
17
+ end
18
+
19
+ # Unit test for the MusicBrainz::DiscID class.
20
+ class TestDiscID < Test::Unit::TestCase
21
+
22
+ def setup
23
+ @fiction_disc_id = 'Wn8eRBtfLDfM0qjYPdxrz.Zjs_U-'
24
+ @fiction_first_track = 1
25
+ @fiction_last_track = 10
26
+ @fiction_sectors = 206535
27
+ @fiction_seconds = 2754
28
+ @fiction_offsets = [150, 18901, 39738, 59557, 79152, 100126,
29
+ 124833, 147278, 166336, 182560]
30
+ @fiction_lengths = [18751, 20837, 19819, 19595, 20974,
31
+ 24707, 22445, 19058, 16224, 23975]
32
+ end
33
+
34
+ def teardown
35
+ end
36
+
37
+ # Test reading the disc id from a device.
38
+ # We would need some kind of small test data to do this.
39
+ #def test_read
40
+ # assert false, "Not implemented yet"
41
+ #end
42
+
43
+ # Test how read reacts on different arguments.
44
+ # Those reads should all fail, but they must never cause a segmentation fault.
45
+ def test_read_invalid_arguments
46
+ assert_raise(TypeError) {DiscId.read(NotAString.new)}
47
+ assert_raise(Exception) {DiscId.read(1)}
48
+ assert_raise(Exception) {DiscId.read('invalid_device')}
49
+ assert_raise(Exception) {DiscId.read(:invalid_device)}
50
+ # assert_raise(ArgumentError) {disc.read(DiscId::DiscId.default_device,
51
+ # 'second argument')}
52
+ end
53
+
54
+ # Test calculation of the disc id if the TOC information
55
+ # gets set by the put method.
56
+ # All attributes should be nil after a failure, even if there was a
57
+ # successfull put before.
58
+ def test_put
59
+ disc = DiscId::Disc.new
60
+ assert_equal nil, disc.id
61
+ assert_equal '', disc.to_s
62
+ assert_equal nil, disc.first_track_num
63
+ assert_equal nil, disc.last_track_num
64
+ assert_equal nil, disc.sectors
65
+ assert_equal nil, disc.seconds
66
+ assert_equal nil, disc.tracks
67
+ assert_equal nil, disc.device
68
+
69
+ # Erroneous put
70
+ assert_raise(Exception) {disc = DiscId.put(-1, @fiction_sectors, @fiction_offsets)}
71
+ assert_equal nil, disc.id
72
+ assert_equal '', disc.to_s
73
+ assert_equal nil, disc.first_track_num
74
+ assert_equal nil, disc.last_track_num
75
+ assert_equal nil, disc.sectors
76
+ assert_equal nil, disc.seconds
77
+ assert_equal nil, disc.tracks
78
+ assert_equal nil, disc.device
79
+
80
+ # Second successfull put
81
+ assert_nothing_raised {disc = DiscId.put(@fiction_first_track, @fiction_sectors,
82
+ @fiction_offsets)}
83
+ assert_equal @fiction_disc_id, disc.id
84
+ assert_equal @fiction_disc_id, disc.to_s
85
+ assert_equal @fiction_first_track, disc.first_track_num
86
+ assert_equal @fiction_last_track, disc.last_track_num
87
+ assert_equal @fiction_sectors, disc.sectors
88
+ assert_equal @fiction_seconds, disc.seconds
89
+ assert_equal @fiction_offsets, disc.tracks.map{|t| t.start_sector}
90
+ assert_equal @fiction_lengths, disc.tracks.map{|t| t.sectors}
91
+ assert_equal nil, disc.device
92
+ end
93
+
94
+ # Test the tracks method and TrackInfo objects
95
+ def test_tracks
96
+ disc = nil
97
+
98
+ assert_nothing_raised {disc = DiscId.put(@fiction_first_track, @fiction_sectors,
99
+ @fiction_offsets)}
100
+
101
+
102
+ # Save a block for testing each track
103
+ number = 0
104
+ proc_test_track = lambda do |track|
105
+ assert_equal number + 1, track.number
106
+
107
+ assert_equal @fiction_offsets[number], track.start_sector
108
+ assert_equal @fiction_lengths[number], track.sectors
109
+ assert_equal @fiction_offsets[number]+ @fiction_lengths[number],
110
+ track.end_sector
111
+
112
+ assert_equal DiscId.sectors_to_seconds(@fiction_offsets[number]), track.start_time
113
+ assert_equal DiscId.sectors_to_seconds(@fiction_lengths[number]), track.seconds
114
+ assert_equal DiscId.sectors_to_seconds(
115
+ @fiction_offsets[number]+ @fiction_lengths[number]),
116
+ track.end_time
117
+
118
+ assert_equal track.number, track[:number]
119
+ assert_equal track.sectors, track[:sectors]
120
+ assert_equal track.start_sector, track[:start_sector]
121
+ assert_equal track.end_sector, track[:end_sector]
122
+ assert_equal track.seconds, track[:seconds]
123
+ assert_equal track.start_time, track[:start_time]
124
+ assert_equal track.end_time, track[:end_time]
125
+
126
+ assert_equal nil, track[:invalid_value]
127
+
128
+ number += 1
129
+ end
130
+
131
+ # Call track_info and retrieve an Array
132
+ track_info = []
133
+ assert_nothing_raised {track_info = disc.tracks}
134
+ assert track_info.is_a?(Array)
135
+ track_info.each(&proc_test_track)
136
+ assert_equal disc.last_track_num, number
137
+
138
+ # Calling track_info directly with a given block
139
+ number = 0 # Reset the number of tracks (the above block is a closure, so this works)
140
+ assert_equal nil, disc.tracks(&proc_test_track)
141
+ assert_equal disc.last_track_num, number
142
+ end
143
+
144
+ # Test the conversion from sectors to seconds
145
+ def test_sectors_to_seconds
146
+ assert_equal 0, DiscId.sectors_to_seconds(0)
147
+ assert_equal @fiction_seconds,
148
+ DiscId.sectors_to_seconds(@fiction_sectors)
149
+ end
150
+
151
+ def test_has_feature
152
+ assert(DiscId.has_feature?(:read),
153
+ "Feature :read should be supported")
154
+ assert(DiscId.has_feature?("read"),
155
+ "Feature 'read' should be supported")
156
+ assert(!DiscId.has_feature?(:notafeature),
157
+ "Feature :notafeature should not be supported")
158
+ assert(!DiscId.has_feature?("notafeature"),
159
+ "Feature 'notafeature' should not be supported")
160
+ end
161
+
162
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: discid
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.a1
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Philipp Wolfer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ffi
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.6.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.6.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.3'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.3'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: yard
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: redcarpet
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: ruby-discid provides Ruby bindings for the MusicBrainz DiscID library
95
+ libdiscid. It allows calculating DiscIDs (MusicBrainz and freedb) for Audio CDs.
96
+ Additionally the library can extract the MCN/UPC/EAN and the ISRCs from disc.
97
+ email:
98
+ - ph.wolfer@gmail.com
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - .gitignore
104
+ - .travis.yml
105
+ - .yardopts
106
+ - CHANGES
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - changelog
112
+ - discid.gemspec
113
+ - examples/discid.rb
114
+ - lib/discid.rb
115
+ - lib/discid/disc.rb
116
+ - lib/discid/lib.rb
117
+ - lib/discid/track_info.rb
118
+ - lib/discid/version.rb
119
+ - test/test_discid.rb
120
+ homepage: https://github.com/phw/ruby-discid
121
+ licenses:
122
+ - LGPL3
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ! '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ! '>'
137
+ - !ruby/object:Gem::Version
138
+ version: 1.3.1
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 1.8.23
142
+ signing_key:
143
+ specification_version: 3
144
+ summary: Ruby bindings for libdiscid
145
+ test_files:
146
+ - test/test_discid.rb
147
+ has_rdoc: