loading
Generated 2025-05-09T13:24:24+00:00

All Files ( 91.26% covered at 6.27 hits/line )

5 files in total.
183 relevant lines, 167 lines covered and 16 lines missed. ( 91.26% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
lib/discid.rb 96.77 % 200 31 30 1 9.16
lib/discid/disc.rb 93.67 % 231 79 74 5 6.22
lib/discid/disc_error.rb 100.00 % 21 2 2 0 1.00
lib/discid/lib.rb 78.26 % 133 46 36 10 1.43
lib/discid/track.rb 100.00 % 135 25 25 0 12.16

lib/discid.rb

96.77% lines covered

31 relevant lines. 30 lines covered and 1 lines missed.
    
  1. # Copyright (C) 2013-2017, 2019 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. 1 require 'discid/lib'
  16. 1 require 'discid/disc'
  17. 1 require 'discid/version'
  18. # The DiscId module allows calculating DiscIDs (MusicBrainz and freedb)
  19. # for Audio CDs. Additionally the library can extract the MCN/UPC/EAN and
  20. # the ISRCs from disc.
  21. #
  22. # The main interface for using this module are the {read} and {put}
  23. # methods which both return an instance of {Disc}. {read} allows you to
  24. # read the data from an actual CD drive while {put} allows you to
  25. # calculate the DiscID for a previously read CD TOC.
  26. #
  27. # Depending on the version of libdiscid and the operating system used
  28. # additional features like reading the MCN or ISRCs from the disc
  29. # might be available. You can check for supported features with {has_feature?}.
  30. #
  31. # @see http://musicbrainz.org/doc/libdiscid#Feature_Matrix
  32. #
  33. # @example Read the TOC, MCN and ISRCs
  34. # require 'discid'
  35. #
  36. # device = "/dev/cdrom"
  37. # disc = DiscId.read(device, :mcn, :isrc)
  38. #
  39. # # Print information about the disc:
  40. # puts "DiscID : #{disc.id}"
  41. # puts "FreeDB ID : #{disc.freedb_id}"
  42. # puts "Total length: #{disc.seconds} seconds"
  43. # puts "MCN : #{disc.mcn}"
  44. #
  45. # # Print information about individual tracks:
  46. # disc.tracks do |track|
  47. # puts "Track ##{track.number}"
  48. # puts " Length: %02d:%02d (%i sectors)" %
  49. # [track.seconds / 60, track.seconds % 60, track.sectors]
  50. # puts " ISRC : %s" % track.isrc
  51. # end
  52. #
  53. # @example Get the DiscID for an existing TOC
  54. # require 'discid'
  55. #
  56. # first_track = 1
  57. # sectors = 82255
  58. # offsets = [150, 16157, 35932, 57527]
  59. # disc = DiscId.put(first_track, sectors, offsets)
  60. # puts disc.id # Output: E5VLOkhodzhvsMlK8LSNVioYOgY-
  61. #
  62. # @example Check for supported MCN feature
  63. # disc = DiscId.read(nil, :mcn)
  64. # puts "MCN: #{disc.mcn}" if DiscId.has_feature?(:mcn)
  65. 1 module DiscId
  66. # Read the disc in the given CD-ROM/DVD-ROM drive extracting only the
  67. # TOC and additionally specified features.
  68. #
  69. # This function reads the disc in the drive specified by the given device
  70. # identifier. If the device is `nil`, the default device, as returned by
  71. # {default_device}, is used.
  72. #
  73. # This function will always read the TOC, but additional features like `:mcn`
  74. # and `:isrc` can be set using the features parameter. You can set multiple
  75. # features.
  76. #
  77. # @example Read only the TOC:
  78. # disc = DiscId.read(device)
  79. #
  80. # @example Read the TOC, MCN and ISRCs:
  81. # disc = DiscId.read(device, :mcn, :isrc)
  82. #
  83. # @note libdiscid >= 0.5.0 is required for the feature selection to work.
  84. # Older versions will allways read MCN and ISRCs when supported. See
  85. # {http://musicbrainz.org/doc/libdiscid#Feature_Matrix} for a list of
  86. # supported features by version and platform.
  87. #
  88. # @raise [TypeError] `device` can not be converted to a String.
  89. # @raise [DiscError] Error reading from `device`. `Exception#message` contains
  90. # error details.
  91. # @param device [String] The device identifier. If set to `nil` {default_device}
  92. # will be used.
  93. # @param features [:mcn, :isrc] List of features to use.
  94. # `:read` is always implied.
  95. # @return [Disc]
  96. 1 def self.read(device = nil, *features)
  97. 3 disc = Disc.new
  98. 3 disc.read device, *features
  99. return disc
  100. end
  101. # Provides the TOC of a known CD.
  102. #
  103. # This function may be used if the TOC has been read earlier and you want to
  104. # calculate the disc ID afterwards, without accessing the disc drive.
  105. #
  106. # @raise [DiscError] The TOC could not be set. `Exception#message`contains
  107. # error details.
  108. # @param first_track [Integer] The number of the first audio track on the
  109. # disc (usually one).
  110. # @param sectors [Integer] The total number of sectors on the disc.
  111. # @param offsets [Array] An array with track offsets (sectors) for each track.
  112. # @return [Disc]
  113. 1 def self.put(first_track, sectors, offsets)
  114. 10 disc = Disc.new
  115. 10 disc.put first_track, sectors, offsets
  116. 7 return disc
  117. end
  118. # Parses a TOC string and returns a [Disc] instance for it.
  119. #
  120. # This function can be used if you already have a TOC string like e.g.
  121. # `1 11 242457 150 44942 61305 72755 96360 130485 147315 164275 190702 205412 220437`
  122. #
  123. # @raise [DiscError] The TOC string was invalid and could not be parsed.
  124. # @param toc [String] A TOC string in the format `1 11 242457 150 44942 61305 72755 96360 130485 147315 164275 190702 205412 220437`.
  125. # @return [Disc]
  126. 1 def self.parse(toc)
  127. 8 if toc.nil? || toc.empty?
  128. 2 raise DiscError, "Invalid TOC #{toc.inspect}"
  129. end
  130. begin
  131. 6 parts = toc.split(' ')
  132. 6 first_track = Integer(parts[0])
  133. 6 sectors = Integer(parts[2])
  134. 29 offsets = parts[3..-1].map{|i| Integer(i)}
  135. rescue ArgumentError, TypeError => e
  136. 2 raise DiscError, e
  137. end
  138. 4 return self.put(first_track, sectors, offsets)
  139. end
  140. # Return the name of the default disc drive for this operating system.
  141. #
  142. # @return [String] An operating system dependent device identifier
  143. 1 def self.default_device
  144. 1 Lib.default_device
  145. end
  146. # Check if a certain feature is implemented on the current platform.
  147. #
  148. # You can obtain a list of supported features with {feature_list}.
  149. #
  150. # @note libdiscid >= 0.5.0 required. Older versions will return `true`
  151. # for `:read` and `false` for anything else.
  152. #
  153. # @param feature [:read, :mcn, :isrc]
  154. # @return [Boolean] True if the feature is implemented and false if not.
  155. 1 def self.has_feature?(feature)
  156. 4 feature = feature.to_sym if feature.respond_to? :to_sym
  157. 4 return self.feature_list.include? feature
  158. end
  159. # A list of features supported by the current platform.
  160. #
  161. # Currently the following features are available:
  162. #
  163. # * :read
  164. # * :mcn
  165. # * :isrc
  166. #
  167. # @note libdiscid >= 0.5.0 required. Older versions will return only [:read].
  168. #
  169. # @return [Array<Symbol>]
  170. 1 def self.feature_list
  171. 20 return Lib::Features.symbols.select {|f| Lib.has_feature(f) == 1}
  172. end
  173. # Converts sectors to seconds.
  174. #
  175. # According to the red book standard 75 sectors are one second.
  176. #
  177. # @private
  178. # @param sectors [Integer] Number of sectors
  179. # @return [Integer] The seconds
  180. 1 def self.sectors_to_seconds(sectors)
  181. 147 return (sectors.to_f / 75).truncate
  182. end
  183. # The libdiscid version.
  184. #
  185. # @note This will only give meaningful results for libdiscid 0.4.0
  186. # and higher. For lower versions this constant will always have
  187. # the value "libdiscid < 0.4.0".
  188. 1 LIBDISCID_VERSION = Lib.get_version_string
  189. end

lib/discid/disc.rb

93.67% lines covered

79 relevant lines. 74 lines covered and 5 lines missed.
    
  1. # Copyright (C) 2013-2014, 2021 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. 1 require 'ffi'
  16. 1 require 'discid/disc_error'
  17. 1 require 'discid/lib'
  18. 1 require 'discid/track'
  19. #
  20. 1 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. 1 class Disc
  25. # The device from which this disc object was read.
  26. 1 attr_reader :device
  27. # @private
  28. 1 def initialize
  29. 15 pointer = Lib.new
  30. 15 @handle = FFI::AutoPointer.new(pointer, Lib.method(:free))
  31. 15 @read = false
  32. 15 @tracks = nil
  33. end
  34. # @private
  35. 1 def read(device, *features)
  36. 3 @read = false
  37. 3 device = DiscId.default_device if device.nil?
  38. 3 if not device.respond_to? :to_s
  39. 1 raise TypeError, 'wrong argument type (expected String)'
  40. end
  41. 2 @device = device.to_s
  42. 2 flags = Lib.features_to_int features
  43. 2 result = Lib.read @handle, @device, flags
  44. 2 if result == 0
  45. 2 raise DiscError, Lib.get_error_msg(@handle)
  46. else
  47. @read = true
  48. end
  49. end
  50. # @private
  51. 1 def put(first_track, sectors, offsets)
  52. 10 @read = false
  53. 10 @device = nil
  54. 10 last_track = first_track - 1 + offsets.length
  55. # discid_put expects always an offsets array with exactly 100 elements.
  56. 10 FFI::MemoryPointer.new(:int, 100) do |p|
  57. 10 if last_track > offsets.length
  58. 2 offsets = Array.new(last_track - offsets.length, 0) + offsets
  59. end
  60. 10 p.write_array_of_int(([sectors] + offsets)[0, 100])
  61. 10 result = Lib.put @handle, first_track, last_track, p
  62. 10 if result == 0
  63. 3 raise DiscError, Lib.get_error_msg(@handle)
  64. else
  65. 7 @read = true
  66. end
  67. end
  68. end
  69. # The MusicBrainz DiscID.
  70. #
  71. # @return [String] The DiscID or `nil` if no ID was yet read.
  72. 1 def id
  73. 10 return Lib.get_id @handle if @read
  74. end
  75. # The FreeDB DiscID.
  76. #
  77. # @return [String] The DiscID or `nil` if no ID was yet read.
  78. 1 def freedb_id
  79. 1 return Lib.get_freedb_id @handle if @read
  80. end
  81. # The number of the first track on this disc.
  82. #
  83. # @return [Integer] The number of the first track or `nil` if no ID was yet read.
  84. 1 def first_track_number
  85. 7 return Lib.get_first_track_num @handle if @read
  86. end
  87. # The number of the last track on this disc.
  88. #
  89. # @return [Integer] The number of the last track or `nil` if no ID was yet read.
  90. 1 def last_track_number
  91. 39 return Lib.get_last_track_num @handle if @read
  92. end
  93. # The length of the disc in sectors.
  94. #
  95. # @return [Integer] Sectors or `nil` if no ID was yet read.
  96. 1 def sectors
  97. 4 return Lib.get_sectors @handle if @read
  98. end
  99. # The length of the disc in seconds.
  100. #
  101. # @return [Integer] Seconds or `nil` if no ID was yet read.
  102. 1 def seconds
  103. 3 DiscId.sectors_to_seconds(sectors) if @read
  104. end
  105. # The media catalogue number on the disc, if present.
  106. #
  107. # Requires libdiscid >= 0.5. If not supported this method will always
  108. # return `nil`.
  109. #
  110. # @note libdiscid >= 0.3.0 required. Older versions will always return nil.
  111. # Not available on all platforms, see
  112. # {http://musicbrainz.org/doc/libdiscid#Feature_Matrix}.
  113. #
  114. # @return [String] MCN or `nil` if no ID was yet read.
  115. 1 def mcn
  116. 1 return Lib.get_mcn @handle if @read
  117. end
  118. # Return a string representing CD Table Of Contents (TOC).
  119. #
  120. # The TOC suitable as a value of the toc parameter when accessing the
  121. # MusicBrainz Web Service. This enables fuzzy searching when the actual
  122. # DiscID is not found.
  123. #
  124. # Note that this is the unencoded value, which still contains spaces.
  125. #
  126. # For libdiscid >= 0.6 the native implementation will be used. For older
  127. # versions falls back to extract the TOC from {Disc#submission_url}.
  128. #
  129. # @since 1.3
  130. #
  131. # @raise [RuntimeError] get_toc_string is unsupported by libdiscid and
  132. # could not get extracted from {Disc#submission_url}
  133. # @return [String] The TOC string or `nil` if no ID was yet read.
  134. 1 def toc_string
  135. 4 return nil if not @read
  136. 4 result = Lib.get_toc_string @handle
  137. 4 if not result
  138. # probably an old version of libdiscid (< 0.6.0)
  139. # gather toc string from submission_url
  140. match = /toc=([0-9+]+)/.match self.submission_url
  141. if match
  142. result = match[1].tr("+", " ")
  143. else
  144. raise "can't get toc string from submission url"
  145. end
  146. end
  147. 4 return result
  148. end
  149. # An URL for submitting the DiscID to MusicBrainz.
  150. #
  151. # The URL leads to an interactive disc submission wizard that guides the
  152. # user through the process of associating this disc's DiscID with a release
  153. # in the MusicBrainz database.
  154. #
  155. # @return [String] Submission URL
  156. 1 def submission_url
  157. 1 return Lib.get_submission_url @handle if @read
  158. end
  159. # DiscID to String conversion. Same as calling the method {#id} but guaranteed
  160. # to return a string.
  161. #
  162. # @return [String] The disc ID as a string or an empty string if no ID
  163. # was yet read.
  164. 1 def to_s
  165. 3 id.to_s
  166. end
  167. # Returns an array of {Track} objects. Each Track object contains
  168. # detailed information about the track.
  169. #
  170. # Returns always `nil` if no ID was yet read. The block won't be
  171. # called in this case.
  172. #
  173. # @yield [Track] If a block is given this method returns `nil` and
  174. # instead iterates over the block calling the block with one argument.
  175. # @yieldreturn [nil]
  176. # @return [Array<Track>] Array of {Track} objects.
  177. 1 def tracks(&block)
  178. 7 if @read
  179. 5 read_tracks if @tracks.nil?
  180. 5 if block
  181. 1 @tracks.each(&block)
  182. return nil
  183. else
  184. 4 return @tracks
  185. end
  186. end
  187. end
  188. 1 private
  189. 1 def read_tracks
  190. 3 track_number = self.first_track_number - 1
  191. 3 @tracks = []
  192. 3 while track_number < self.last_track_number do
  193. 30 track_number += 1
  194. 30 isrc = Lib.get_track_isrc(@handle, track_number)
  195. 30 offset = Lib.get_track_offset(@handle, track_number)
  196. 30 length = Lib.get_track_length(@handle, track_number)
  197. 30 track_info = Track.new(track_number, offset, length, isrc)
  198. 30 @tracks << track_info
  199. end
  200. end
  201. end
  202. end

lib/discid/disc_error.rb

100.0% lines covered

2 relevant lines. 2 lines covered and 0 lines missed.
    
  1. # Copyright (C) 2013-2014 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. 1 module DiscId
  17. # This exception is thrown on errors reading the disc or setting the TOC.
  18. 1 class DiscError < StandardError
  19. end
  20. end

lib/discid/lib.rb

78.26% lines covered

46 relevant lines. 36 lines covered and 10 lines missed.
    
  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. 1 require "ffi"
  16. #
  17. 1 module DiscId
  18. # This module encapsulates the C interface for libdiscid using FFI.
  19. # The Lib module is intended for internal use only and should be
  20. # considered private.
  21. #
  22. # @private
  23. 1 module Lib
  24. 1 extend FFI::Library
  25. 1 ffi_lib %w[discid libdiscid.so.0 libdiscid.0.dylib]
  26. 1 attach_function :new, :discid_new, [], :pointer
  27. 1 attach_function :free, :discid_free, [:pointer], :void
  28. begin
  29. 1 attach_function :read, :discid_read_sparse, [:pointer, :string, :uint], :int
  30. rescue FFI::NotFoundError
  31. attach_function :legacy_read, :discid_read, [:pointer, :string], :int
  32. def self.read(handle, device, _features)
  33. legacy_read(handle, device)
  34. end
  35. end
  36. 1 attach_function :put, :discid_put, [:pointer, :int, :int, :pointer], :int
  37. 1 attach_function :get_error_msg, :discid_get_error_msg, [:pointer], :string
  38. 1 attach_function :get_id, :discid_get_id, [:pointer], :string
  39. 1 attach_function :get_freedb_id, :discid_get_freedb_id, [:pointer], :string
  40. 1 attach_function :get_submission_url, :discid_get_submission_url, [:pointer], :string
  41. 1 attach_function :default_device, :discid_get_default_device, [], :string
  42. 1 attach_function :get_first_track_num, :discid_get_first_track_num, [:pointer], :int
  43. 1 attach_function :get_last_track_num, :discid_get_last_track_num, [:pointer], :int
  44. 1 attach_function :get_sectors, :discid_get_sectors, [:pointer], :int
  45. 1 attach_function :get_track_offset, :discid_get_track_offset, [:pointer, :int], :int
  46. 1 attach_function :get_track_length, :discid_get_track_length, [:pointer, :int], :int
  47. begin
  48. 1 attach_function :get_toc_string, :discid_get_toc_string, [:pointer], :string
  49. rescue FFI::NotFoundError
  50. def self.get_toc_string
  51. return nil
  52. end
  53. end
  54. begin
  55. 1 attach_function :get_mcn, :discid_get_mcn, [:pointer], :string
  56. rescue FFI::NotFoundError
  57. def self.get_mcn(_handle)
  58. return nil
  59. end
  60. end
  61. begin
  62. 1 attach_function :get_track_isrc, :discid_get_track_isrc, [:pointer, :int], :string
  63. rescue FFI::NotFoundError
  64. def self.get_track_isrc(_handle, _track)
  65. return nil
  66. end
  67. end
  68. 1 Features = enum(:feature, [:read, 1 << 0,
  69. :mcn, 1 << 1,
  70. :isrc, 1 << 2])
  71. begin
  72. 1 attach_function :has_feature, :discid_has_feature, [:feature], :int
  73. rescue FFI::NotFoundError
  74. def self.has_feature(feature)
  75. return feature.to_sym == :read ? 1 : 0
  76. end
  77. end
  78. #attach_function :get_feature_list, :discid_get_feature_list, [:pointer], :void
  79. begin
  80. 1 attach_function :get_version_string, :discid_get_version_string, [], :string
  81. rescue FFI::NotFoundError
  82. def self.get_version_string
  83. return "libdiscid < 0.4.0"
  84. end
  85. end
  86. 1 def self.features_to_int(features)
  87. 6 feature_flags = 0
  88. 6 features.each do |feature|
  89. 4 if feature.respond_to? :to_sym
  90. 4 feature = feature.to_sym
  91. 4 feature_flags = self.add_feature_to_flags(feature_flags, feature)
  92. end
  93. end
  94. 6 return feature_flags
  95. end
  96. 1 private
  97. 1 def self.add_feature_to_flags(flags, feature)
  98. flags |= self::Features[feature] if
  99. 4 self::Features.symbols.include?(feature) and
  100. self.has_feature(feature)
  101. 4 return flags
  102. end
  103. end
  104. end

lib/discid/track.rb

100.0% lines covered

25 relevant lines. 25 lines covered and 0 lines missed.
    
  1. # Copyright (C) 2008-2023 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. 1 module DiscId
  17. # This class holds information about a single track.
  18. #
  19. # Currently this includes the following fields:
  20. #
  21. # * number: The number of the track on the disc.
  22. # * sectors: Length of the track in sectors.
  23. # * offset: Start position of the track on the disc in sectors.
  24. # * end_sector: End position of the track on the disc in sectors.
  25. # * seconds: Length of the track in seconds.
  26. # * start_time: Start position of the track on the disc in seconds.
  27. # * end_time: End position of the track on the disc in seconds.
  28. # * isrc: The track's ISRC (International Standard Recordings Code)
  29. # if available.
  30. #
  31. # You can access all fields either directly or with the square bracket
  32. # notation:
  33. #
  34. # track = Track.new(1, 150, 16007)
  35. # puts track.sectors # 16007
  36. # puts track[:sectors] # 16007
  37. #
  38. # @see DiscId::Disc#tracks
  39. 1 class Track
  40. # The number of the track on the disc.
  41. #
  42. # @return [Integer]
  43. 1 attr_reader :number
  44. # Length of the track in sectors.
  45. #
  46. # @return [Integer]
  47. 1 attr_reader :sectors
  48. # Start position of the track on the disc in sectors.
  49. #
  50. # @return [Integer]
  51. 1 attr_reader :offset
  52. # ISRC number of the track.
  53. #
  54. # @note libdiscid >= 0.3.0 required. Older versions will always return nil.
  55. # Not available on all platforms, see
  56. # {http://musicbrainz.org/doc/libdiscid#Feature_Matrix}.
  57. #
  58. # @return [String]
  59. 1 attr_reader :isrc
  60. # Initializes a new Track object.
  61. 1 def initialize(number, offset, sectors, isrc)
  62. 34 @number = number
  63. 34 @offset = offset
  64. 34 @sectors = sectors
  65. 34 @isrc = isrc
  66. end
  67. # End position of the track on the disc in sectors.
  68. #
  69. # @return [Integer]
  70. 1 def end_sector
  71. 54 offset + sectors
  72. end
  73. # Length of the track in seconds.
  74. #
  75. # @return [Integer]
  76. 1 def seconds
  77. 27 DiscId.sectors_to_seconds(sectors)
  78. end
  79. # Start position of the track on the disc in seconds.
  80. #
  81. # @return [Integer]
  82. 1 def start_time
  83. 27 DiscId.sectors_to_seconds(offset)
  84. end
  85. # End position of the track on the disc in seconds.
  86. #
  87. # @return [Integer]
  88. 1 def end_time
  89. 27 DiscId.sectors_to_seconds(end_sector)
  90. end
  91. # Allows access to all fields similar to accessing values in a hash.
  92. #
  93. # Example:
  94. #
  95. # track = Track.new(1, 150, 16007)
  96. # puts track.sectors # 16007
  97. # puts track[:sectors] # 16007
  98. 1 def [](key)
  99. 9 if [:number, :sectors, :offset, :end_sector,
  100. :seconds, :start_time, :end_time, :isrc].include?(key.to_sym)
  101. 8 method(key).call
  102. end
  103. end
  104. # Converts the Track into a Hash.
  105. #
  106. # @return [Hash]
  107. 1 def to_h
  108. {
  109. 2 :number => number,
  110. :sectors => sectors,
  111. :offset => offset,
  112. :end_sector => end_sector,
  113. :seconds => seconds,
  114. :start_time => start_time,
  115. :end_time => end_time,
  116. :isrc => isrc,
  117. }
  118. end
  119. 1 alias_method :to_hash, :to_h
  120. end
  121. end