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