示例#1
0
    def read_data(cls, data):
        if (not data.startswith(u"# xmcd")):
            raise XMCDException("")

        disc_length = re.search(r'# Disc length: (\d+)', data)
        if (disc_length is not None):
            disc_length = int(disc_length.group(1))

        track_lengths = re.compile(r'# Track frame offsets:\s+[#\s\d]+',
                                   re.DOTALL).search(data)
        if (track_lengths is not None):
            track_lengths = map(int, re.findall(r'\d+',
                                                track_lengths.group(0)))

        fields = {}

        for line in re.findall(r'.+=.*[\r\n]', data):
            (field, value) = line.split(u'=', 1)
            field = field.encode('ascii')
            value = value.rstrip('\r\n')
            if (field in fields.keys()):
                fields[field] += value
            else:
                fields[field] = value

        return XMCD(values=fields, offsets=track_lengths, length=disc_length)
    def from_string(cls, string):
        # try:
        #     data = string.decode('latin-1')
        # except UnicodeDecodeError:
        #     data = string.decode('utf-8','replace')
        #FIXME - handle latin-1 files?
        data = string.decode('utf-8', 'replace')

        if (not data.startswith(u"# xmcd")):
            raise XMCDException()

        fields = {}
        comments = []
        field_line = re.compile(r'([A-Z0-9]+?)=(.*)')

        for line in StringIO.StringIO(data):
            if (line.startswith(u'#')):
                comments.append(line.rstrip('\r\n'))
            else:
                match = field_line.match(line.rstrip('\r\n'))
                if (match is not None):
                    key = match.group(1).encode('ascii')
                    value = match.group(2)
                    if (key in fields):
                        fields[key] += value
                    else:
                        fields[key] = value

        return cls(fields, comments)
示例#3
0
    def from_string(cls, string):
        # try:
        #     data = string.decode('latin-1')
        # except UnicodeDecodeError:
        #     data = string.decode('utf-8','replace')
        #FIXME - handle latin-1 files?
        data = string.decode('utf-8', 'replace')

        if (not data.startswith(u"# xmcd")):
            raise XMCDException()

        fields = {}
        comments = []
        field_line = re.compile(r'([A-Z0-9]+?)=(.*)')

        for line in StringIO.StringIO(data):
            if (line.startswith(u'#')):
                comments.append(line.rstrip('\r\n'))
            else:
                match = field_line.match(line.rstrip('\r\n'))
                if (match is not None):
                    key = match.group(1).encode('ascii')
                    value = match.group(2)
                    if (key in fields):
                        fields[key] += value
                    else:
                        fields[key] = value

        return cls(fields, comments)
示例#4
0
    def __len__(self):
        track_field = re.compile(r"(TTITLE|EXTT)(\d+)")

        return (
            max(set([int(m.group(2)) for m in [track_field.match(key) for key in self.fields.keys()] if m is not None]))
            + 1
        )
示例#5
0
    def __len__(self):
        track_field = re.compile(r'(TTITLE|EXTT)(\d+)')

        return max(
            set([
                int(m.group(2)) for m in
                [track_field.match(key)
                 for key in self.fields.keys()] if m is not None
            ])) + 1
示例#6
0
    def stream(self):
        titleset = re.compile("ATS_%2.2d_\\d\\.AOB" % (self.titleset))

        return AOBStream(
            aob_files=sorted([self.dvdaudio.files[key]
                              for key in self.dvdaudio.files.keys()
                              if (titleset.match(key))]),
            first_sector=self[0].first_sector,
            last_sector=self[-1].last_sector,
            unprotector=self.dvdaudio.unprotector)
示例#7
0
class FreeDB:
    LINE = re.compile(r'\d\d\d\s.+')

    def __init__(self, server, port, messenger):
        self.server = server
        self.port = port
        self.socket = None
        self.r = None
        self.w = None
        self.messenger = messenger

    def connect(self):
        import socket

        try:
            self.messenger.info(_(u"Connecting to \"%s\"") % (self.server))

            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.connect((self.server, self.port))

            self.r = self.socket.makefile("rb")
            self.w = self.socket.makefile("wb")

            (code, msg) = self.read()  #the welcome message
            if (code == 201):
                self.messenger.info(_(u"Connected ... attempting to login"))
            else:
                self.r.close()
                self.w.close()
                self.socket.close()
                raise FreeDBException(_(u"Invalid hello message"))

            self.write("cddb hello user %s %s %s" % \
                       (socket.getfqdn(),"audiotools",VERSION))

            (code, msg) = self.read()  #the handshake successful message
            if (code != 200):
                self.r.close()
                self.w.close()
                self.socket.close()
                raise FreeDBException(_(u"Handshake unsuccessful"))

            self.write("proto 6")

            (code, msg) = self.read()  #the protocol successful message
            if ((code != 200) and (code != 201)):
                self.r.close()
                self.w.close()
                self.socket.close()
                raise FreeDBException(_(u"Protocol change unsuccessful"))

        except socket.error, err:
            raise FreeDBException(err[1])
示例#8
0
    def __init__(self, audio_ts_path, cdrom_device=None):
        """A DVD-A which contains PCMReader-compatible track objects."""

        #an inventory of AUDIO_TS files converted to uppercase keys
        self.files = dict([(name.upper(),
                            os.path.join(audio_ts_path, name))
                           for name in os.listdir(audio_ts_path)])

        titleset_numbers = list(self.__titlesets__())

        #for each titleset, read an ATS_XX_0.IFO file
        #each titleset contains one or more DVDATitle objects
        #and each DVDATitle object contains one or more DVDATrack objects
        self.titlesets = [self.__titles__(titleset) for titleset in
                          titleset_numbers]

        #for each titleset, calculate the lengths of the corresponding AOBs
        #in terms of 2048 byte sectors
        self.aob_sectors = []
        for titleset in titleset_numbers:
            aob_re = re.compile("ATS_%2.2d_\\d\\.AOB" % (titleset))
            titleset_aobs = dict([(key, value) for (key, value) in
                                  self.files.items()
                                  if (aob_re.match(key))])
            for aob_length in [os.path.getsize(titleset_aobs[key]) /
                               DVDAudio.SECTOR_SIZE
                               for key in sorted(titleset_aobs.keys())]:
                if (len(self.aob_sectors) == 0):
                    self.aob_sectors.append(
                        (0, aob_length))
                else:
                    self.aob_sectors.append(
                        (self.aob_sectors[-1][1],
                         self.aob_sectors[-1][1] + aob_length))

        try:
            if ((cdrom_device is not None) and
                ('DVDAUDIO.MKB' in self.files.keys())):

                from audiotools.prot import CPPMDecoder

                self.unprotector = CPPMDecoder(
                    cdrom_device, self.files['DVDAUDIO.MKB']).decode
            else:
                self.unprotector = lambda sector: sector
        except ImportError:
            self.unprotector = lambda sector: sector
示例#9
0
    def info(self):
        """returns a (sample_rate, channels, channel_mask, bps, type) tuple"""

        #find the AOB file of the title's first track
        track_sector = self[0].first_sector
        titleset = re.compile("ATS_%2.2d_\\d\\.AOB" % (self.titleset))
        for aob_path in sorted([self.dvdaudio.files[key] for key in
                           self.dvdaudio.files.keys()
                           if (titleset.match(key))]):
            aob_sectors = os.path.getsize(aob_path) / DVDAudio.SECTOR_SIZE
            if (track_sector > aob_sectors):
                track_sector -= aob_sectors
            else:
                break
        else:
            raise ValueError(_(u"unable to find track sector in AOB files"))

        #open that AOB file and seek to that track's first sector
        aob_file = open(aob_path, 'rb')
        try:
            aob_file.seek(track_sector * DVDAudio.SECTOR_SIZE)

            #read the pack header
            DVDAudio.PACK_HEADER.parse_stream(aob_file)

            #skip packets until the stream ID 0xBD is found
            pes_header = DVDAudio.PES_HEADER.parse_stream(aob_file)
            while (pes_header.stream_id != 0xBD):
                aob_file.read(pes_header.packet_length)
                pes_header = DVDAudio.PES_HEADER.parse_stream(aob_file)

            #parse the PCM/MLP header
            header = DVDAudio.PACKET_HEADER.parse_stream(aob_file)

            #return the values indicated by the header
            return (DVDATrack.SAMPLE_RATE[
                      header.info.group1_sample_rate],
                    DVDATrack.CHANNELS[
                      header.info.channel_assignment],
                    DVDATrack.CHANNEL_MASK[
                      header.info.channel_assignment],
                    DVDATrack.BITS_PER_SAMPLE[
                      header.info.group1_bps],
                    header.stream_id)

        finally:
            aob_file.close()
示例#10
0
    def __parse_info__(self):
        """generates a cache of sample_rate, bits-per-sample, etc."""

        if (len(self.tracks) == 0):
            return

        #Why is this post-init processing necessary?
        #DVDATrack references DVDATitle
        #so a DVDATitle must exist when DVDATrack is initialized.
        #But because reading this info requires knowing the sector
        #of the first track, we wind up with a circular dependency.
        #Doing a "post-process" pass fixes that problem.

        #find the AOB file of the title's first track
        track_sector = self[0].first_sector
        titleset = re.compile("ATS_%2.2d_\\d\\.AOB" % (self.titleset))
        for aob_path in sorted([self.dvdaudio.files[key] for key in
                           self.dvdaudio.files.keys()
                           if (titleset.match(key))]):
            aob_sectors = os.path.getsize(aob_path) / DVDAudio.SECTOR_SIZE
            if (track_sector > aob_sectors):
                track_sector -= aob_sectors
            else:
                break
        else:
            raise ValueError(_(u"unable to find track sector in AOB files"))

        #open that AOB file and seek to that track's first sector
        aob_file = open(aob_path, 'rb')
        try:
            aob_file.seek(track_sector * DVDAudio.SECTOR_SIZE)
            aob_reader = BitstreamReader(aob_file, 0)

            #read and validate the pack header
            #(there's one pack header per sector, at the sector's start)
            (sync_bytes,
             marker1,
             current_pts_high,
             marker2,
             current_pts_mid,
             marker3,
             current_pts_low,
             marker4,
             scr_extension,
             marker5,
             bit_rate,
             marker6,
             stuffing_length) = aob_reader.parse(
                "32u 2u 3u 1u 15u 1u 15u 1u 9u 1u 22u 2u 5p 3u")
            aob_reader.skip_bytes(stuffing_length)
            if (sync_bytes != 0x1BA):
                raise InvalidDVDA(_(u"invalid AOB sync bytes"))
            if ((marker1 != 1) or (marker2 != 1) or (marker3 != 1) or
                (marker4 != 1) or (marker5 != 1) or (marker6 != 3)):
                raise InvalidDVDA(_(u"invalid AOB marker bits"))
            packet_pts = ((current_pts_high << 30) |
                          (current_pts_mid << 15) |
                          current_pts_low)

            #skip packets until one with a stream ID of 0xBD is found
            (start_code,
             stream_id,
             packet_length) = aob_reader.parse("24u 8u 16u")
            if (start_code != 1):
                raise InvalidDVDA(_(u"invalid AOB packet start code"))
            while (stream_id != 0xBD):
                aob_reader.skip_bytes(packet_length)
                (start_code,
                 stream_id,
                 packet_length) = aob_reader.parse("24u 8u 16u")
                if (start_code != 1):
                    raise InvalidDVDA(_(u"invalid AOB packet start code"))

            #parse the PCM/MLP header in the packet data
            (pad1_size,) = aob_reader.parse("16p 8u")
            aob_reader.skip_bytes(pad1_size)
            (stream_id, crc) = aob_reader.parse("8u 8u 8p")
            if (stream_id == 0xA0):  #PCM
                #read a PCM reader
                (pad2_size,
                 first_audio_frame,
                 padding2,
                 group1_bps,
                 group2_bps,
                 group1_sample_rate,
                 group2_sample_rate,
                 padding3,
                 channel_assignment) = aob_reader.parse(
                    "8u 16u 8u 4u 4u 4u 4u 8u 8u")
            else:                    #MLP
                aob_reader.skip_bytes(aob_reader.read(8)) #skip pad2
                #read a total frame size + MLP major sync header
                (total_frame_size,
                 sync_words,
                 stream_type,
                 group1_bps,
                 group2_bps,
                 group1_sample_rate,
                 group2_sample_rate,
                 unknown1,
                 channel_assignment,
                 unknown2) = aob_reader.parse(
                    "4p 12u 16p 24u 8u 4u 4u 4u 4u 11u 5u 48u")

            #return the values indicated by the header
            self.sample_rate = DVDATrack.SAMPLE_RATE[group1_sample_rate]
            self.channels = DVDATrack.CHANNELS[channel_assignment]
            self.channel_mask = DVDATrack.CHANNEL_MASK[channel_assignment]
            self.bits_per_sample = DVDATrack.BITS_PER_SAMPLE[group1_bps]
            self.stream_id = stream_id

        finally:
            aob_file.close()
示例#11
0
class FreeDB:
    """A class for performing queries on a FreeDB or compatible server.

    This operates using the original FreeDB client-server protocol."""

    LINE = re.compile(r'\d\d\d\s.+')

    def __init__(self, server, port, messenger):
        """server is a string, port is an int, messenger is a Messenger.

        Queries are sent to the server, and output to the messenger."""

        self.server = server
        self.port = port
        self.socket = None
        self.r = None
        self.w = None
        self.messenger = messenger

    def connect(self):
        """Performs the initial connection."""

        import socket

        try:
            self.messenger.info(_(u"Connecting to \"%s\"") % (self.server))

            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.connect((self.server, self.port))

            self.r = self.socket.makefile("rb")
            self.w = self.socket.makefile("wb")

            (code, msg) = self.read()  # the welcome message
            if (code == 201):
                self.messenger.info(_(u"Connected ... attempting to login"))
            else:
                self.r.close()
                self.w.close()
                self.socket.close()
                raise FreeDBException(_(u"Invalid hello message"))

            self.write("cddb hello user %s %s %s" % \
                       (socket.getfqdn(), "audiotools", VERSION))

            (code, msg) = self.read()  # the handshake successful message
            if (code != 200):
                self.r.close()
                self.w.close()
                self.socket.close()
                raise FreeDBException(_(u"Handshake unsuccessful"))

            self.write("proto 6")

            (code, msg) = self.read()  # the protocol successful message
            if ((code != 200) and (code != 201)):
                self.r.close()
                self.w.close()
                self.socket.close()
                raise FreeDBException(_(u"Protocol change unsuccessful"))

        except socket.error, err:
            raise FreeDBException(err[1])