22 from ctypes
import c_int, c_void_p, c_char_p, c_uint
25 from discid.util import _encode, _decode, _sectors_to_seconds
30 _FEATURE_MAPPING = {
"read": 1 << 0,
"mcn": 1 << 1,
"isrc": 1 << 2}
33 FEATURES_IMPLEMENTED = list(_FEATURE_MAPPING.keys())
35 def read(device=None, features=[]):
36 """Reads the TOC from the device given as string
37 and returns a :class:`Disc` object.
39 That string can be either of:
40 :obj:`str <python:str>`, :obj:`unicode` or :obj:`bytes`.
41 However, it should in no case contain non-ASCII characters.
42 If no device is given, a default, also given by :func:`get_default_device`
45 You can optionally add a subset of the features in
46 :data:`FEATURES` or the whole list to read more than just the TOC.
47 In contrast to libdiscid, :func:`read` won't read any
48 of the additional features by default.
50 A :exc:`DiscError` exception is raised when the reading fails,
51 and :exc:`NotImplementedError` when libdiscid doesn't support
52 reading discs on the current platform.
55 disc.read(device, features)
58 def put(first, last, disc_sectors, track_offsets):
59 """Creates a TOC based on the information given
60 and returns a :class:`Disc` object.
62 Takes the `first` track and `last` **audio** track as :obj:`int`.
63 `disc_sectors` is the end of the last audio track,
64 normally the total sector count of the disc.
65 `track_offsets` is a list of all audio track offsets.
67 Depending on how you get the total sector count,
68 you might have to substract 11400 (2:32 min.) for discs with data tracks.
70 A :exc:`TOCError` exception is raised when illegal parameters
73 .. seealso:: :musicbrainz:`Disc ID Calculation`
76 disc.put(first, last, disc_sectors, track_offsets)
81 """:func:`read` will raise this exception when an error occured.
85 class TOCError(Exception):
86 """:func:`put` will raise this exception when illegal paramaters
93 """The class of the object returned by :func:`read` or :func:`put`.
96 _LIB.discid_new.argtypes = ()
97 _LIB.discid_new.restype = c_void_p
99 """The initialization will reserve some memory
100 for internal data structures.
105 assert self.
_handle.value
is not None
111 _LIB.discid_get_error_msg.argtypes = (c_void_p, )
112 _LIB.discid_get_error_msg.restype = c_char_p
114 """Get the error message for the last error with the object.
116 error = _LIB.discid_get_error_msg(self.
_handle)
120 _LIB.discid_read.argtypes = (c_void_p, c_char_p)
121 _LIB.discid_read.restype = c_int
123 _LIB.discid_read_sparse.argtypes = (c_void_p, c_char_p, c_uint)
124 _LIB.discid_read_sparse.restype = c_int
125 except AttributeError:
127 def read(self, device=None, features=[]):
128 """Reads the TOC from the device given as string
130 The user is supposed to use :func:`discid.read`.
132 if "read" not in FEATURES:
133 raise NotImplementedError(
"discid_read not implemented on platform")
137 & set(FEATURES_IMPLEMENTED))
141 for feature
in features:
142 c_features += _FEATURE_MAPPING.get(feature, 0)
148 except AttributeError:
155 _LIB.discid_put.argtypes = (c_void_p, c_int, c_int, c_void_p)
156 _LIB.discid_put.restype = c_int
157 def put(self, first, last, disc_sectors, track_offsets):
158 """Creates a TOC based on the input given.
160 The user is supposed to use :func:`discid.put`.
163 if len(track_offsets) != last - first + 1:
164 raise TOCError(
"Invalid number of track offsets")
165 elif False in [disc_sectors >= off
for off
in track_offsets]:
166 raise TOCError(
"Disc sector count too low")
171 offsets = [disc_sectors] + track_offsets
172 c_offsets = (c_int * len(offsets))(*tuple(offsets))
173 result = _LIB.discid_put(self.
_handle, first, last, c_offsets) == 1
180 _LIB.discid_get_id.argtypes = (c_void_p, )
181 _LIB.discid_get_id.restype = c_char_p
183 """Gets the current MusicBrainz disc ID
186 result = _LIB.discid_get_id(self.
_handle)
189 _LIB.discid_get_freedb_id.argtypes = (c_void_p, )
190 _LIB.discid_get_freedb_id.restype = c_char_p
192 """Gets the current FreeDB disc ID
195 result = _LIB.discid_get_freedb_id(self.
_handle)
198 _LIB.discid_get_submission_url.argtypes = (c_void_p, )
199 _LIB.discid_get_submission_url.restype = c_char_p
201 """Give an URL to submit the current TOC
202 as a new Disc ID to MusicBrainz.
205 result = _LIB.discid_get_submission_url(self.
_handle)
209 _LIB.discid_get_toc_string.argtypes = (c_void_p, )
210 _LIB.discid_get_toc_string.restype = c_char_p
211 except AttributeError:
214 """The TOC suitable as value of the `toc parameter`
215 when accessing the MusicBrainz Web Service.
219 result = _LIB.discid_get_toc_string(self.
_handle)
220 except AttributeError:
225 _LIB.discid_get_first_track_num.argtypes = (c_void_p, )
226 _LIB.discid_get_first_track_num.restype = c_int
228 """Gets the first track number
231 return _LIB.discid_get_first_track_num(self.
_handle)
233 _LIB.discid_get_last_track_num.argtypes = (c_void_p, )
234 _LIB.discid_get_last_track_num.restype = c_int
236 """Gets the last track number
239 return _LIB.discid_get_last_track_num(self.
_handle)
241 _LIB.discid_get_sectors.argtypes = (c_void_p, )
242 _LIB.discid_get_sectors.restype = c_int
244 """Gets the total number of sectors on the disc
247 return _LIB.discid_get_sectors(self.
_handle)
250 _LIB.discid_get_mcn.argtypes = (c_void_p, )
251 _LIB.discid_get_mcn.restype = c_char_p
252 except AttributeError:
255 """Gets the current Media Catalogue Number (MCN/UPC/EAN)
260 result = _LIB.discid_get_mcn(self.
_handle)
261 except AttributeError:
271 """This is the MusicBrainz :musicbrainz:`Disc ID`,
272 a :obj:`unicode` or :obj:`str <python:str>` object.
278 """This is the :musicbrainz:`FreeDB` Disc ID (without category),
279 a :obj:`unicode` or :obj:`str <python:str>` object.
285 """Disc ID / TOC Submission URL for MusicBrainz
287 With this url you can submit the current TOC
288 as a new MusicBrainz :musicbrainz:`Disc ID`.
289 This is a :obj:`unicode` or :obj:`str <python:str>` object.
296 url = url.replace(
"//mm.",
"//")
297 url = url.replace(
"/bare/cdlookup.html",
"/cdtoc/attach")
302 """The TOC suitable as value of the `toc parameter`
303 when accessing the MusicBrainz Web Service.
305 This is a :obj:`unicode` or :obj:`str <python:str>` object
306 and enables fuzzy searching when the actual Disc ID is not found.
308 Note that this is the unencoded value, which still contains spaces.
310 .. seealso:: `MusicBrainz Web Service <http://musicbrainz.org/doc/Development/XML_Web_Service/Version_2#discid>`_
318 raise ValueError(
"can't get toc string from submission url")
320 return match.group(1).replace(
"+",
" ")
326 """Number of the first track"""
331 """Number of the last **audio** track"""
336 """Total length in sectors"""
340 """This is an alias for :attr:`sectors`"""
344 """Total length in seconds"""
352 """This is the Media Catalogue Number (MCN/UPC/EAN)
354 It is set after the `"mcn"` feature was requested on a read
355 and supported by the platform or :obj:`None`.
356 If set, this is a :obj:`unicode` or :obj:`str <python:str>` object.
362 """A list of :class:`Track` objects for this Disc.
367 tracks.append(
Track(self, number))
371 _LIB.discid_free.argtypes = (c_void_p, )
372 _LIB.discid_free.restype =
None
374 """This will free the internal allocated memory for the object.
380 """deprecated :keyword:`with` usage"""
383 def __exit__(self, exc_type, exc_value, traceback):
384 """deprecated :keyword:`with` usage"""