File: __init__.py

package info (click to toggle)
python-libdiscid 1.0-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 664 kB
  • sloc: python: 576; ansic: 104; sh: 14; makefile: 10
file content (317 lines) | stat: -rw-r--r-- 9,368 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# -*- coding: utf-8 -*-

# Copyright 2013 Sebastian Ramacher <sebastian+dev@ramacher.at>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

""" Python bindings for libdiscid

libdiscid is a library to calculate MusicBrainz Disc IDs.
This module provides Python-bindings for libdiscid.

>>> disc = libdiscid.read()
>>> disc.id is not None
True
"""

from __future__ import division

import libdiscid._discid
from libdiscid.exceptions import DiscError
import re
import warnings

__version__ = '0.4.1'

DEFAULT_DEVICE = libdiscid._discid.default_device()
""" The default device to use for :func:`DiscId.read` on this platform.

.. deprecated:: 0.2.0
   Please use :func:`default_device` instead.
"""

FEATURES = libdiscid._discid.FEATURES
""" List of all available features supported by libdiscid on this platform.
"""
FEATURE_READ = libdiscid._discid.FEATURE_READ
""" Read the TOC of the disc to get the disc ID. This feature is always enabled.
"""
FEATURE_MCN = libdiscid._discid.FEATURE_MCN
"""  Read the Media Catalogue Number of the disc.
"""
FEATURE_ISRC = libdiscid._discid.FEATURE_ISRC
""" Read :musicbrainz:`International Standard Recording Codes <ISRC>` of all the
    tracks.
"""
FEATURES_MAPPING = libdiscid._discid.FEATURES_MAPPING
""" Mapping between the constants representing a feature and their string
    representation.
"""
__discid_version__ = libdiscid._discid.__discid_version__
""" The version of the underlying libdiscid.
"""

class DiscId(object):
  """ Disc information

  Class holding all the information obtained from a disc.
  """

  def __init__(self, cdiscid):
      self._id = cdiscid.id
      self._freedb_id = cdiscid.freedb_id
      self._submission_url = cdiscid.submission_url
      self._webservice_url = cdiscid.webservice_url
      self._first_track = cdiscid.first_track
      self._last_track = cdiscid.last_track
      self._sectors = cdiscid.sectors
      self._track_offsets = cdiscid.track_offsets
      self._track_lengths = cdiscid.track_lengths
      self._mcn = cdiscid.mcn
      self._track_isrcs = cdiscid.track_isrcs
      self._device = cdiscid.device
      self._toc = cdiscid.toc

  @property
  def id(self):
    """ The MusicBrainz :musicbrainz:`Disc ID`.
    """

    return self._id

  @property
  def freedb_id(self):
    """ The :musicbrainz:`FreeDB` Disc ID (without category).
    """

    return self._freedb_id

  @property
  def submission_url(self):
    """ Disc ID / TOC Submission URL for MusicBrainz

    With this url you can submit the current TOC as a new MusicBrainz
    :musicbrainz:`Disc ID`.
    """

    return self._submission_url

  @property
  def webservice_url(self):
    """ The web service URL for info about the CD

    With this url you can retrieve information about the CD in XML from the
    MusicBrainz web service.
    """

    warnings.warn('webservice_url is deprecated since it points to the old '
                  'webservice. Please use python-musicbrainz-ngs to access '
                  'the webservice.', DeprecationWarning)
    return self._webservice_url

  @property
  def first_track(self):
    """ Number of the first audio track.
    """

    return self._first_track

  @property
  def last_track(self):
    """ Number of the last audio track.
    """

    return self._last_track

  @property
  def sectors(self):
    """ Total sector count.
    """

    return self._sectors

  @property
  def leadout_track(self):
    """ Leadout track.
    """

    return self.sectors

  @property
  def track_offsets(self):
    """ Tuple of all track offsets (in sectors).

    The first element corresponds to the offset of the track denoted by
    :attr:`first_track` and so on.
    """

    return self._track_offsets

  @property
  def pregap(self):
    """ Pregap of the first track (in sectors).
    """

    return self.track_offsets[0]


  @property
  def track_lengths(self):
    """ Tuple of all track lengths (in sectors).

    The first element corresponds to the length of the track denoted by
    :attr:`first_track` and so on.
    """

    return self._track_lengths

  @property
  def mcn(self):
    """ Media Catalogue Number of the disc.

    :raises NotImplementedError: reading MCN is not supported on this platform
    """

    if self._mcn is None:
      raise NotImplementedError('MCN is not available with this version '
                                'of libdiscid and/or platform')
    return self._mcn

  @property
  def track_isrcs(self):
    """ Tuple of :musicbrainz:`ISRCs <ISRC>` of all tracks.

    The first element of the list corresponds to the ISRC of the
    :attr:`first_track` and so on.

    :raises NotImplementedError: reading ISRCs is not supported on this platform
    """

    if self._track_isrcs is None:
      raise NotImplementedError('ISRC is not available with this version '
                                'of libdiscid and/or platform')
    return self._track_isrcs

  @property
  def device(self):
    """ The device the data was read from.

    If it is ``None``, :func:`libdiscid.put` was called to create the instance.
    """

    return self._device

  @property
  def toc(self):
    """ String representing the CD's Table of Contents (TOC).

    :raises ValueError: extracting TOC string from the submission URL failed
    """

    if self._toc is None:
      # extract TOC string from submission URL
      match = re.match(r'.*toc=([0-9+]+)$', self.submission_url)
      if match is None:
        raise ValueError('Failed to extract TOC from submission URL')
      self._toc = match.group(1).replace('+', ' ')
    return self._toc

def read(device=None, features=None):
  """ Reads the TOC from the device given as string.

  If *device* is ``None``, :func:`default_device` is used to determine
  the device. *features* can be any combination of :data:`FEATURE_MCN` and
  :data:`FEATURE_ISRC` and :data:`FEATURE_READ`. Note that prior to libdiscid
  version 0.5.0 *features* has no effect and that :data:`FEATURE_READ` is always
  assumed, even if not given.

  :param device: device to read from
  :type device: unicode or None
  :param features: selected features, possible values are :data:`FEATURE_READ` \
    :data:`FEATURE_MCN`, :data:`FEATURE_ISRC` and any of these values combined \
    with bitwise or.
  :type features: integer or None
  :raises libdiscid.DiscError: reading the disc failed
  :raises NotImplementedError: reading discs is not supported
  :raises MemoryError: failed to allocate the internal DiscId object
  :rtype: :class:`DiscId` object
  """

  disc = libdiscid._discid.DiscId()
  if features is None:
    disc.read(device)
  else:
    disc.read(device, features)
  return DiscId(disc)

def put(first, last, sectors, offsets):
  """ Creates a TOC based on the given offsets.

  Takes the *first* and *last* audio track, as well as the number of
  *sectors* and a list of *offsets* as in :attr:`track_offsets`.

  :param first: number of the first audio track
  :type first: integer
  :param last: number of the last audio track
  :type last: integer
  :param sectors: total number of sectors on the disc
  :type sectors: integer
  :param offsets: offsets of each track
  :type offsets: list or tuple of integers
  :raises libdiscid.DiscError: operation failed for some reason
  :raises MemoryError: failed to allocated memory to store the offsets or the \
    internal DiscId object
  :rtype: :class:`DiscId` object
  """

  disc = libdiscid._discid.DiscId()
  disc.put(first, last, sectors, offsets)
  return DiscId(disc)

def default_device():
  """ The default device on this platform.

  The default device can change during the run-time of the program. This can
  happen with removable devices for example.

  :rtype: unicode
  """

  return libdiscid._discid.default_device()

def sectors_to_seconds(sectors):
  """ Convert sectors to seconds rounded to the nearest second.

  :param sectors: number of sectors
  :type sectors: integer
  :rtype: integer
  """

  SECTORS_PER_SECOND = 75
  remainder = sectors % SECTORS_PER_SECOND
  return sectors // SECTORS_PER_SECOND + \
    (1 if remainder > SECTORS_PER_SECOND // 2 else 0)

__all__ = (
  'read', 'put', 'default_device', 'sectors_to_seconds',
  '__version__', '__discid_version__',
  'FEATURES', 'FEATURES_MAPPING', 'FEATURE_READ', 'FEATURE_MCN', 'FEATURE_ISRC',
  'DEFAULT_DEVICE',
  'DiscId', 'DiscError'
)