Package musicbrainz2 :: Module disc
[frames] | no frames]

Source Code for Module musicbrainz2.disc

  1  """Utilities for working with Audio CDs. 
  2   
  3  This module contains utilities for working with Audio CDs. 
  4   
  5  The functions in this module need both a working ctypes package (already 
  6  included in python-2.5) and an installed libdiscid. If you don't have 
  7  libdiscid, it can't be loaded, or your platform isn't supported by either 
  8  ctypes or this module, a C{NotImplementedError} is raised when using the 
  9  L{readDisc()} function. 
 10   
 11  @author: Matthias Friedrich <matt@mafr.de> 
 12  """ 
 13  __revision__ = '$Id: disc.py 11987 2009-08-22 11:57:51Z matt $' 
 14   
 15  import sys 
 16  import urllib 
 17  import urlparse 
 18  import ctypes 
 19  import ctypes.util 
 20  from musicbrainz2.model import Disc 
 21   
 22  __all__ = [ 'DiscError', 'readDisc', 'getSubmissionUrl' ] 
 23   
 24   
25 -class DiscError(IOError):
26 """The Audio CD could not be read. 27 28 This may be simply because no disc was in the drive, the device name 29 was wrong or the disc can't be read. Reading errors can occur in case 30 of a damaged disc or a copy protection mechanism, for example. 31 """ 32 pass
33 34
35 -def _openLibrary():
36 """Tries to open libdiscid. 37 38 @return: a C{ctypes.CDLL} object, representing the opened library 39 40 @raise NotImplementedError: if the library can't be opened 41 """ 42 # This only works for ctypes >= 0.9.9.3. Any libdiscid is found, 43 # no matter how it's called on this platform. 44 try: 45 if hasattr(ctypes.cdll, 'find'): 46 libDiscId = ctypes.cdll.find('discid') 47 _setPrototypes(libDiscId) 48 return libDiscId 49 except OSError, e: 50 raise NotImplementedError('Error opening library: ' + str(e)) 51 52 # Try to find the library using ctypes.util 53 libName = ctypes.util.find_library('discid') 54 if libName != None: 55 try: 56 libDiscId = ctypes.cdll.LoadLibrary(libName) 57 _setPrototypes(libDiscId) 58 return libDiscId 59 except OSError, e: 60 raise NotImplementedError('Error opening library: ' + 61 str(e)) 62 63 # For compatibility with ctypes < 0.9.9.3 try to figure out the library 64 # name without the help of ctypes. We use cdll.LoadLibrary() below, 65 # which isn't available for ctypes == 0.9.9.3. 66 # 67 if sys.platform == 'linux2': 68 libName = 'libdiscid.so.0' 69 elif sys.platform == 'darwin': 70 libName = 'libdiscid.0.dylib' 71 elif sys.platform == 'win32': 72 libName = 'discid.dll' 73 else: 74 # This should at least work for Un*x-style operating systems 75 libName = 'libdiscid.so.0' 76 77 try: 78 libDiscId = ctypes.cdll.LoadLibrary(libName) 79 _setPrototypes(libDiscId) 80 return libDiscId 81 except OSError, e: 82 raise NotImplementedError('Error opening library: ' + str(e)) 83 84 assert False # not reached
85 86
87 -def _setPrototypes(libDiscId):
88 ct = ctypes 89 libDiscId.discid_new.argtypes = ( ) 90 libDiscId.discid_new.restype = ct.c_void_p 91 92 libDiscId.discid_free.argtypes = (ct.c_void_p, ) 93 94 libDiscId.discid_read.argtypes = (ct.c_void_p, ct.c_char_p) 95 96 libDiscId.discid_get_error_msg.argtypes = (ct.c_void_p, ) 97 libDiscId.discid_get_error_msg.restype = ct.c_char_p 98 99 libDiscId.discid_get_id.argtypes = (ct.c_void_p, ) 100 libDiscId.discid_get_id.restype = ct.c_char_p 101 102 libDiscId.discid_get_first_track_num.argtypes = (ct.c_void_p, ) 103 libDiscId.discid_get_first_track_num.restype = ct.c_int 104 105 libDiscId.discid_get_last_track_num.argtypes = (ct.c_void_p, ) 106 libDiscId.discid_get_last_track_num.restype = ct.c_int 107 108 libDiscId.discid_get_sectors.argtypes = (ct.c_void_p, ) 109 libDiscId.discid_get_sectors.restype = ct.c_int 110 111 libDiscId.discid_get_track_offset.argtypes = (ct.c_void_p, ct.c_int) 112 libDiscId.discid_get_track_offset.restype = ct.c_int 113 114 libDiscId.discid_get_track_length.argtypes = (ct.c_void_p, ct.c_int) 115 libDiscId.discid_get_track_length.restype = ct.c_int
116 117
118 -def getSubmissionUrl(disc, host='mm.musicbrainz.org', port=80):
119 """Returns a URL for adding a disc to the MusicBrainz database. 120 121 A fully initialized L{musicbrainz2.model.Disc} object is needed, as 122 returned by L{readDisc}. A disc object returned by the web service 123 doesn't provide the necessary information. 124 125 Note that the created URL is intended for interactive use and points 126 to the MusicBrainz disc submission wizard by default. This method 127 just returns a URL, no network connection is needed. The disc drive 128 isn't used. 129 130 @param disc: a fully initialized L{musicbrainz2.model.Disc} object 131 @param host: a string containing a host name 132 @param port: an integer containing a port number 133 134 @return: a string containing the submission URL 135 136 @see: L{readDisc} 137 """ 138 assert isinstance(disc, Disc), 'musicbrainz2.model.Disc expected' 139 discid = disc.getId() 140 first = disc.getFirstTrackNum() 141 last = disc.getLastTrackNum() 142 sectors = disc.getSectors() 143 assert None not in (discid, first, last, sectors) 144 145 tracks = last - first + 1 146 toc = "%d %d %d " % (first, last, sectors) 147 toc = toc + ' '.join( map(lambda x: str(x[0]), disc.getTracks()) ) 148 149 query = urllib.urlencode({ 'id': discid, 'toc': toc, 'tracks': tracks }) 150 151 if port == 80: 152 netloc = host 153 else: 154 netloc = host + ':' + str(port) 155 156 url = ('http', netloc, '/bare/cdlookup.html', '', query, '') 157 158 return urlparse.urlunparse(url)
159 160
161 -def readDisc(deviceName=None):
162 """Reads an Audio CD in the disc drive. 163 164 This reads a CD's table of contents (TOC) and calculates the MusicBrainz 165 DiscID, which is a 28 character ASCII string. This DiscID can be used 166 to retrieve a list of matching releases from the web service (see 167 L{musicbrainz2.webservice.Query}). 168 169 Note that an Audio CD has to be in drive for this to work. The 170 C{deviceName} argument may be used to set the device. The default 171 depends on the operating system (on linux, it's C{'/dev/cdrom'}). 172 No network connection is needed for this function. 173 174 If the device doesn't exist or there's no valid Audio CD in the drive, 175 a L{DiscError} exception is raised. 176 177 @param deviceName: a string containing the CD drive's device name 178 179 @return: a L{musicbrainz2.model.Disc} object 180 181 @raise DiscError: if there was a problem reading the disc 182 @raise NotImplementedError: if DiscID generation isn't supported 183 """ 184 libDiscId = _openLibrary() 185 186 handle = libDiscId.discid_new() 187 assert handle != 0, "libdiscid: discid_new() returned NULL" 188 189 # Access the CD drive. This also works if deviceName is None because 190 # ctypes passes a NULL pointer in this case. 191 # 192 res = libDiscId.discid_read(handle, deviceName) 193 if res == 0: 194 raise DiscError(libDiscId.discid_get_error_msg(handle)) 195 196 197 # Now extract the data from the result. 198 # 199 disc = Disc() 200 201 disc.setId( libDiscId.discid_get_id(handle) ) 202 203 firstTrackNum = libDiscId.discid_get_first_track_num(handle) 204 lastTrackNum = libDiscId.discid_get_last_track_num(handle) 205 206 disc.setSectors(libDiscId.discid_get_sectors(handle)) 207 208 for i in range(firstTrackNum, lastTrackNum+1): 209 trackOffset = libDiscId.discid_get_track_offset(handle, i) 210 trackSectors = libDiscId.discid_get_track_length(handle, i) 211 212 disc.addTrack( (trackOffset, trackSectors) ) 213 214 disc.setFirstTrackNum(firstTrackNum) 215 disc.setLastTrackNum(lastTrackNum) 216 217 libDiscId.discid_free(handle) 218 219 return disc
220 221 # EOF 222