Beispiel #1
0
class __GIF__(ImageMetrics):
    HEADER = construct.Struct('header',
                        construct.Const(construct.String('gif',3),'GIF'),
                        construct.String('version',3))

    SCREEN_DESCRIPTOR = construct.Struct('logical_screen_descriptor',
                                   construct.ULInt16('width'),
                                   construct.ULInt16('height'),
                                   construct.Embed(
        construct.BitStruct('packed_fields',
                      construct.Flag('global_color_table'),
                      construct.Bits('color_resolution',3),
                      construct.Flag('sort'),
                      construct.Bits('global_color_table_size',3))),
                                   construct.Byte('background_color_index'),
                                   construct.Byte('pixel_aspect_ratio'))

    def __init__(self, width, height, color_count):
        ImageMetrics.__init__(self, width, height, 8, color_count, u'image/gif')

    @classmethod
    def parse(cls, file):
        try:
            header = cls.HEADER.parse_stream(file)
            descriptor = cls.SCREEN_DESCRIPTOR.parse_stream(file)

            return __GIF__(descriptor.width, descriptor.height,
                           2 ** (descriptor.global_color_table_size + 1))
        except construct.ConstError:
            raise InvalidGIF(_(u'Invalid GIF'))
Beispiel #2
0
class __BMP__(ImageMetrics):
    HEADER = construct.Struct('bmp_header',
                        construct.Const(construct.String('magic_number',2),'BM'),
                        construct.ULInt32('file_size'),
                        construct.ULInt16('reserved1'),
                        construct.ULInt16('reserved2'),
                        construct.ULInt32('bitmap_data_offset'))

    INFORMATION = construct.Struct('bmp_information',
                             construct.ULInt32('header_size'),
                             construct.ULInt32('width'),
                             construct.ULInt32('height'),
                             construct.ULInt16('color_planes'),
                             construct.ULInt16('bits_per_pixel'),
                             construct.ULInt32('compression_method'),
                             construct.ULInt32('image_size'),
                             construct.ULInt32('horizontal_resolution'),
                             construct.ULInt32('vertical_resolution'),
                             construct.ULInt32('colors_used'),
                             construct.ULInt32('important_colors_used'))

    def __init__(self, width, height, bits_per_pixel, color_count):
        ImageMetrics.__init__(self, width, height, bits_per_pixel, color_count,
                              u'image/x-ms-bmp')

    @classmethod
    def parse(cls, file):
        try:
            header = cls.HEADER.parse_stream(file)
            information = cls.INFORMATION.parse_stream(file)

            return __BMP__(information.width, information.height,
                           information.bits_per_pixel,
                           information.colors_used)

        except construct.ConstError:
            raise InvalidBMP(_(u'Invalid BMP'))
Beispiel #3
0
    def l_tag_value(cls, file, tag):
        subtype = {1:construct.Byte("data"),
                   2:construct.CString("data"),
                   3:construct.ULInt16("data"),
                   4:construct.ULInt32("data"),
                   5:construct.Struct("data",
                                construct.ULInt32("high"),
                                construct.ULInt32("low"))}[tag.type]


        data = construct.StrictRepeater(tag.count,
                                  subtype)
        if ((tag.type != 2) and (data.sizeof() <= 4)):
            return tag.offset
        else:
            file.seek(tag.offset,0)
            return data.parse_stream(file)
Beispiel #4
0
class ApeAudio(ApeTaggedAudio, AudioFile):
    SUFFIX = "ape"
    NAME = SUFFIX
    DEFAULT_COMPRESSION = "5000"
    COMPRESSION_MODES = tuple([str(x * 1000) for x in range(1, 6)])
    del (x)
    BINARIES = ("mac", )

    FILE_HEAD = construct.Struct("ape_head", construct.String('id', 4),
                                 construct.ULInt16('version'))

    #version >= 3.98
    APE_DESCRIPTOR = construct.Struct(
        "ape_descriptor", construct.ULInt16('padding'),
        construct.ULInt32('descriptor_bytes'),
        construct.ULInt32('header_bytes'),
        construct.ULInt32('seektable_bytes'),
        construct.ULInt32('header_data_bytes'),
        construct.ULInt32('frame_data_bytes'),
        construct.ULInt32('frame_data_bytes_high'),
        construct.ULInt32('terminating_data_bytes'),
        construct.String('md5', 16))

    APE_HEADER = construct.Struct("ape_header",
                                  construct.ULInt16('compression_level'),
                                  construct.ULInt16('format_flags'),
                                  construct.ULInt32('blocks_per_frame'),
                                  construct.ULInt32('final_frame_blocks'),
                                  construct.ULInt32('total_frames'),
                                  construct.ULInt16('bits_per_sample'),
                                  construct.ULInt16('number_of_channels'),
                                  construct.ULInt32('sample_rate'))

    #version <= 3.97
    APE_HEADER_OLD = construct.Struct("ape_header_old",
                                      construct.ULInt16('compression_level'),
                                      construct.ULInt16('format_flags'),
                                      construct.ULInt16('number_of_channels'),
                                      construct.ULInt32('sample_rate'),
                                      construct.ULInt32('header_bytes'),
                                      construct.ULInt32('terminating_bytes'),
                                      construct.ULInt32('total_frames'),
                                      construct.ULInt32('final_frame_blocks'))

    def __init__(self, filename):
        AudioFile.__init__(self, filename)

        (self.__samplespersec__, self.__channels__, self.__bitspersample__,
         self.__totalsamples__) = ApeAudio.__ape_info__(filename)

    @classmethod
    def is_type(cls, file):
        return file.read(4) == "MAC "

    def lossless(self):
        return True

    @classmethod
    def supports_foreign_riff_chunks(cls):
        return True

    def has_foreign_riff_chunks(self):
        #FIXME - this isn't strictly true
        #I'll need a way to detect foreign chunks in APE's stream
        #without decoding it first,
        #but since I'm not supporting APE anyway, I'll take the lazy way out
        return True

    def bits_per_sample(self):
        return self.__bitspersample__

    def channels(self):
        return self.__channels__

    def total_frames(self):
        return self.__totalsamples__

    def sample_rate(self):
        return self.__samplespersec__

    @classmethod
    def __ape_info__(cls, filename):
        f = file(filename, 'rb')
        try:
            file_head = cls.FILE_HEAD.parse_stream(f)

            if (file_head.id != 'MAC '):
                raise InvalidFile(_(u"Invalid Monkey's Audio header"))

            if (file_head.version >= 3980):  #the latest APE file type
                descriptor = cls.APE_DESCRIPTOR.parse_stream(f)
                header = cls.APE_HEADER.parse_stream(f)

                return (header.sample_rate,
                        header.number_of_channels,
                        header.bits_per_sample,
                        ((header.total_frames - 1) * \
                         header.blocks_per_frame) + \
                         header.final_frame_blocks)
            else:  #old-style APE file (obsolete)
                header = cls.APE_HEADER_OLD.parse_stream(f)

                if (file_head.version >= 3950):
                    blocks_per_frame = 0x48000
                elif ((file_head.version >= 3900)
                      or ((file_head.version >= 3800) and
                          (header.compression_level == 4000))):
                    blocks_per_frame = 0x12000
                else:
                    blocks_per_frame = 0x2400

                if (header.format_flags & 0x01):
                    bits_per_sample = 8
                elif (header.format_flags & 0x08):
                    bits_per_sample = 24
                else:
                    bits_per_sample = 16

                return (header.sample_rate,
                        header.number_of_channels,
                        bits_per_sample,
                        ((header.total_frames - 1) * \
                         blocks_per_frame) + \
                         header.final_frame_blocks)

        finally:
            f.close()

    def to_wave(self, wave_filename):
        if (self.filename.endswith(".ape")):
            devnull = file(os.devnull, "wb")
            sub = subprocess.Popen(
                [BIN['mac'], self.filename, wave_filename, '-d'],
                stdout=devnull,
                stderr=devnull)
            sub.wait()
            devnull.close()
        else:
            devnull = file(os.devnull, 'ab')
            import tempfile
            ape = tempfile.NamedTemporaryFile(suffix='.ape')
            f = file(self.filename, 'rb')
            transfer_data(f.read, ape.write)
            f.close()
            ape.flush()
            sub = subprocess.Popen([BIN['mac'], ape.name, wave_filename, '-d'],
                                   stdout=devnull,
                                   stderr=devnull)
            sub.wait()
            ape.close()
            devnull.close()

    @classmethod
    def from_wave(cls, filename, wave_filename, compression=None):
        if (str(compression) not in cls.COMPRESSION_MODES):
            compression = cls.DEFAULT_COMPRESSION

        devnull = file(os.devnull, "wb")
        sub = subprocess.Popen(
            [BIN['mac'], wave_filename, filename,
             "-c%s" % (compression)],
            stdout=devnull,
            stderr=devnull)
        sub.wait()
        devnull.close()
        return ApeAudio(filename)
Beispiel #5
0
class WavPackAudio(ApeTaggedAudio, AudioFile):
    SUFFIX = "wv"
    NAME = SUFFIX
    DEFAULT_COMPRESSION = "veryhigh"
    COMPRESSION_MODES = ("fast", "standard", "high", "veryhigh")
    BINARIES = ("wavpack", "wvunpack")

    APE_TAG_CLASS = WavePackAPEv2

    HEADER = construct.Struct(
        "wavpackheader", construct.Const(construct.String("id", 4), 'wvpk'),
        construct.ULInt32("block_size"), construct.ULInt16("version"),
        construct.ULInt8("track_number"), construct.ULInt8("index_number"),
        construct.ULInt32("total_samples"), construct.ULInt32("block_index"),
        construct.ULInt32("block_samples"),
        construct.Embed(
            construct.BitStruct("flags", construct.Flag("floating_point_data"),
                                construct.Flag("hybrid_noise_shaping"),
                                construct.Flag("cross_channel_decorrelation"),
                                construct.Flag("joint_stereo"),
                                construct.Flag("hybrid_mode"),
                                construct.Flag("mono_output"),
                                construct.Bits("bits_per_sample", 2),
                                construct.Bits("left_shift_data_low", 3),
                                construct.Flag("final_block_in_sequence"),
                                construct.Flag("initial_block_in_sequence"),
                                construct.Flag("hybrid_noise_balanced"),
                                construct.Flag("hybrid_mode_control_bitrate"),
                                construct.Flag("extended_size_integers"),
                                construct.Bit("sampling_rate_low"),
                                construct.Bits("maximum_magnitude", 5),
                                construct.Bits("left_shift_data_high", 2),
                                construct.Flag("reserved2"),
                                construct.Flag("false_stereo"),
                                construct.Flag("use_IIR"),
                                construct.Bits("reserved1", 2),
                                construct.Bits("sampling_rate_high", 3))),
        construct.ULInt32("crc"))

    SUB_HEADER = construct.Struct(
        "wavpacksubheader",
        construct.Embed(
            construct.BitStruct("flags", construct.Flag("large_block"),
                                construct.Flag("actual_size_1_less"),
                                construct.Flag("nondecoder_data"),
                                construct.Bits("metadata_function", 5))),
        construct.IfThenElse('size', lambda ctx: ctx['large_block'],
                             ULInt24('s'), construct.Byte('s')))

    BITS_PER_SAMPLE = (8, 16, 24, 32)
    SAMPLING_RATE = (6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000,
                     32000, 44100, 48000, 64000, 88200, 96000, 192000, 0)

    def __init__(self, filename):
        self.filename = filename
        self.__samplerate__ = 0
        self.__channels__ = 0
        self.__bitspersample__ = 0
        self.__total_frames__ = 0

        self.__read_info__()

    @classmethod
    def is_type(cls, file):
        return file.read(4) == 'wvpk'

    def lossless(self):
        return True

    @classmethod
    def supports_foreign_riff_chunks(cls):
        return True

    def channel_mask(self):
        fmt_chunk = WaveAudio.FMT_CHUNK.parse(self.__fmt_chunk__())
        if (fmt_chunk.compression != 0xFFFE):
            if (self.__channels__ == 1):
                return ChannelMask.from_fields(front_center=True)
            elif (self.__channels__ == 2):
                return ChannelMask.from_fields(front_left=True,
                                               front_right=True)
            #if we have a multi-channel WavPack file
            #that's not WAVEFORMATEXTENSIBLE,
            #assume the channels follow SMPTE/ITU-R recommendations
            #and hope for the best
            elif (self.__channels__ == 3):
                return ChannelMask.from_fields(front_left=True,
                                               front_right=True,
                                               front_center=True)
            elif (self.__channels__ == 4):
                return ChannelMask.from_fields(front_left=True,
                                               front_right=True,
                                               back_left=True,
                                               back_right=True)
            elif (self.__channels__ == 5):
                return ChannelMask.from_fields(front_left=True,
                                               front_right=True,
                                               back_left=True,
                                               back_right=True,
                                               front_center=True)
            elif (self.__channels__ == 6):
                return ChannelMask.from_fields(front_left=True,
                                               front_right=True,
                                               back_left=True,
                                               back_right=True,
                                               front_center=True,
                                               low_frequency=True)
            else:
                return ChannelMask(0)
        else:
            return WaveAudio.fmt_chunk_to_channel_mask(fmt_chunk.channel_mask)

    def get_metadata(self):
        metadata = ApeTaggedAudio.get_metadata(self)
        if (metadata is not None):
            metadata.frame_count = self.total_frames()
        return metadata

    def has_foreign_riff_chunks(self):
        for (sub_header, nondecoder, data) in self.sub_frames():
            if ((sub_header == 1) and nondecoder):
                return set(__riff_chunk_ids__(data)) != set(['fmt ', 'data'])
        else:
            return False

    def __fmt_chunk__(self):
        for (sub_header, nondecoder, data) in self.sub_frames():
            if ((sub_header == 1) and nondecoder):
                for (chunk_id, chunk_data) in __riff_chunks__(data):
                    if (chunk_id == 'fmt '):
                        return chunk_data
        else:
            return None

    def frames(self):
        f = file(self.filename)
        remaining_samples = None
        try:
            while ((remaining_samples is None) or (remaining_samples > 0)):
                try:
                    header = WavPackAudio.HEADER.parse(
                        f.read(WavPackAudio.HEADER.sizeof()))
                except construct.ConstError:
                    raise InvalidFile(_(u'WavPack header ID invalid'))

                if (remaining_samples is None):
                    remaining_samples = (header.total_samples - \
                                         header.block_samples)
                else:
                    remaining_samples -= header.block_samples

                data = f.read(header.block_size - 24)

                yield (header, data)
        finally:
            f.close()

    def sub_frames(self):
        import cStringIO

        for (header, data) in self.frames():
            total_size = len(data)
            data = cStringIO.StringIO(data)
            while (data.tell() < total_size):
                sub_header = WavPackAudio.SUB_HEADER.parse_stream(data)
                if (sub_header.actual_size_1_less):
                    yield (sub_header.metadata_function,
                           sub_header.nondecoder_data,
                           data.read((sub_header.size * 2) - 1))
                    data.read(1)
                else:
                    yield (sub_header.metadata_function,
                           sub_header.nondecoder_data,
                           data.read(sub_header.size * 2))

    def __read_info__(self):
        f = file(self.filename)
        try:
            try:
                header = WavPackAudio.HEADER.parse(
                    f.read(WavPackAudio.HEADER.sizeof()))
            except construct.ConstError:
                raise InvalidFile(_(u'WavPack header ID invalid'))

            self.__samplerate__ = WavPackAudio.SAMPLING_RATE[(
                header.sampling_rate_high << 1) | header.sampling_rate_low]
            self.__bitspersample__ = WavPackAudio.BITS_PER_SAMPLE[
                header.bits_per_sample]
            self.__total_frames__ = header.total_samples

            self.__channels__ = 0

            #go through as many headers as necessary
            #to count the number of channels
            if (header.mono_output):
                self.__channels__ += 1
            else:
                self.__channels__ += 2

            while (not header.final_block_in_sequence):
                f.seek(header.block_size - 24, 1)
                header = WavPackAudio.HEADER.parse(
                    f.read(WavPackAudio.HEADER.sizeof()))
                if (header.mono_output):
                    self.__channels__ += 1
                else:
                    self.__channels__ += 2
        finally:
            f.close()

    def bits_per_sample(self):
        return self.__bitspersample__

    def channels(self):
        return self.__channels__

    def total_frames(self):
        return self.__total_frames__

    def sample_rate(self):
        return self.__samplerate__

    @classmethod
    def from_pcm(cls, filename, pcmreader, compression=None):
        compression_param = {
            "fast": ["-f"],
            "standard": [],
            "high": ["-h"],
            "veryhigh": ["-hh"]
        }

        if (str(compression) not in cls.COMPRESSION_MODES):
            compression = cls.DEFAULT_COMPRESSION

        if ('--raw-pcm' in cls.__wavpack_help__()):
            if (filename.endswith(".wv")):
                devnull = file(os.devnull, 'ab')

                if (pcmreader.channels > 18):
                    raise UnsupportedChannelMask()
                elif (pcmreader.channels > 2):
                    order_map = {
                        "front_left": "FL",
                        "front_right": "FR",
                        "front_center": "FC",
                        "low_frequency": "LFE",
                        "back_left": "BL",
                        "back_right": "BR",
                        "front_left_of_center": "FLC",
                        "front_right_of_center": "FRC",
                        "back_center": "BC",
                        "side_left": "SL",
                        "side_right": "SR",
                        "top_center": "TC",
                        "top_front_left": "TFL",
                        "top_front_center": "TFC",
                        "top_front_right": "TFR",
                        "top_back_left": "TBL",
                        "top_back_center": "TBC",
                        "top_back_right": "TBR"
                    }

                    channel_order = [
                        "--channel-order=%s" % (",".join([
                            order_map[channel] for channel in ChannelMask(
                                pcmreader.channel_mask).channels()
                        ]))
                    ]
                else:
                    channel_order = []

                sub = subprocess.Popen([BIN['wavpack']] + \
                                           compression_param[compression] + \
                                           ['-q','-y',
                                            "--raw-pcm=%(sr)s,%(bps)s,%(ch)s"%\
                                              {"sr":pcmreader.sample_rate,
                                               "bps":pcmreader.bits_per_sample,
                                               "ch":pcmreader.channels}] + \
                                           channel_order + \
                                           ['-','-o',filename],
                                       stdout=devnull,
                                       stderr=devnull,
                                       stdin=subprocess.PIPE,
                                       preexec_fn=ignore_sigint)

                transfer_framelist_data(pcmreader, sub.stdin.write)
                devnull.close()
                sub.stdin.close()

                if (sub.wait() == 0):
                    return WavPackAudio(filename)
                else:
                    raise EncodingError(BIN['wavpack'])
            else:
                import tempfile

                tempdir = tempfile.mkdtemp()
                symlink = os.path.join(tempdir,
                                       os.path.basename(filename) + ".wv")
                try:
                    os.symlink(os.path.abspath(filename), symlink)
                    cls.from_pcm(symlink, pcmreader, compression)
                    return WavPackAudio(filename)
                finally:
                    os.unlink(symlink)
                    os.rmdir(tempdir)
        else:
            import tempfile

            f = tempfile.NamedTemporaryFile(suffix=".wav")
            w = WaveAudio.from_pcm(f.name, pcmreader)

            try:
                return cls.from_wave(filename, w.filename, compression)
            finally:
                del (w)
                f.close()

    def to_wave(self, wave_filename):
        devnull = file(os.devnull, 'ab')

        #WavPack stupidly refuses to run if the filename doesn't end with .wv
        if (self.filename.endswith(".wv")):
            sub = subprocess.Popen([
                BIN['wvunpack'], '-q', '-y', self.filename, '-o', wave_filename
            ],
                                   stdout=devnull,
                                   stderr=devnull)
            if (sub.wait() != 0):
                raise EncodingError()
        else:
            #create a temporary symlink to the current file
            #rather than rewrite the whole thing
            import tempfile

            tempdir = tempfile.mkdtemp()
            symlink = os.path.join(tempdir,
                                   os.path.basename(self.filename) + ".wv")
            try:
                os.symlink(os.path.abspath(self.filename), symlink)
                WavPackAudio(symlink).to_wave(wave_filename)
            finally:
                os.unlink(symlink)
                os.rmdir(tempdir)

    def to_pcm(self):
        if (self.filename.endswith(".wv")):
            if ('-r' in WavPackAudio.__wvunpack_help__()):
                sub = subprocess.Popen([
                    BIN['wvunpack'], '-q', '-y', self.filename, '-r', '-o', '-'
                ],
                                       stdout=subprocess.PIPE,
                                       stderr=file(os.devnull, 'ab'))

                return PCMReader(sub.stdout,
                                 sample_rate=self.sample_rate(),
                                 channels=self.channels(),
                                 channel_mask=int(self.channel_mask()),
                                 bits_per_sample=self.bits_per_sample(),
                                 process=sub)
            else:
                sub = subprocess.Popen(
                    [BIN['wvunpack'], '-q', '-y', self.filename, '-o', '-'],
                    stdout=subprocess.PIPE,
                    stderr=file(os.devnull, 'ab'))

                return WaveReader(sub.stdout,
                                  sample_rate=self.sample_rate(),
                                  channels=self.channels(),
                                  channel_mask=int(self.channel_mask()),
                                  bits_per_sample=self.bits_per_sample(),
                                  process=sub)
        else:
            #create a temporary symlink to the current file
            #rather than rewrite the whole thing
            (tempdir, symlink) = SymlinkPCMReader.new(self.filename, ".wv")
            return SymlinkPCMReader(
                WavPackAudio(symlink).to_pcm(), tempdir, symlink)

    @classmethod
    def from_wave(cls, filename, wave_filename, compression=None):
        if (str(compression) not in cls.COMPRESSION_MODES):
            compression = cls.DEFAULT_COMPRESSION

        compression_param = {
            "fast": ["-f"],
            "standard": [],
            "high": ["-h"],
            "veryhigh": ["-hh"]
        }

        #wavpack will add a .wv suffix if there isn't one
        #this isn't desired behavior
        if (filename.endswith(".wv")):
            devnull = file(os.devnull, 'ab')

            sub = subprocess.Popen([BIN['wavpack'],
                                    wave_filename] + \
                                   compression_param[compression] + \
                                   ['-q','-y','-o',
                                    filename],
                                   stdout=devnull,
                                   stderr=devnull,
                                   preexec_fn=ignore_sigint)

            devnull.close()

            if (sub.wait() == 0):
                return WavPackAudio(filename)
            else:
                raise EncodingError(BIN['wavpack'])
        else:
            import tempfile

            tempdir = tempfile.mkdtemp()
            symlink = os.path.join(tempdir, os.path.basename(filename) + ".wv")
            try:
                os.symlink(os.path.abspath(filename), symlink)
                cls.from_wave(symlink, wave_filename, compression)
                return WavPackAudio(filename)
            finally:
                os.unlink(symlink)
                os.rmdir(tempdir)

    @classmethod
    def __wavpack_help__(cls):
        devnull = open(os.devnull, "wb")
        sub = subprocess.Popen([BIN["wavpack"], "--help"],
                               stdout=subprocess.PIPE,
                               stderr=devnull)
        help_data = sub.stdout.read()
        sub.stdout.close()
        devnull.close()
        sub.wait()
        return help_data

    @classmethod
    def __wvunpack_help__(cls):
        devnull = open(os.devnull, "wb")
        sub = subprocess.Popen([BIN["wvunpack"], "--help"],
                               stdout=subprocess.PIPE,
                               stderr=devnull)
        help_data = sub.stdout.read()
        sub.stdout.close()
        devnull.close()
        sub.wait()
        return help_data

    @classmethod
    def add_replay_gain(cls, filenames):
        track_names = [
            track.filename for track in open_files(filenames)
            if isinstance(track, cls)
        ]

        if ((len(track_names) > 0) and BIN.can_execute(BIN['wvgain'])):
            devnull = file(os.devnull, 'ab')

            sub = subprocess.Popen([BIN['wvgain'], '-q', '-a'] + track_names,
                                   stdout=devnull,
                                   stderr=devnull)
            sub.wait()
            devnull.close()

    @classmethod
    def can_add_replay_gain(cls):
        return BIN.can_execute(BIN['wvgain'])

    @classmethod
    def lossless_replay_gain(cls):
        return True

    def replay_gain(self):
        metadata = self.get_metadata()
        if (metadata is None):
            return None

        if (set([
                'replaygain_track_gain', 'replaygain_track_peak',
                'replaygain_album_gain', 'replaygain_album_peak'
        ]).issubset(metadata.keys())):  #we have ReplayGain data
            try:
                return ReplayGain(
                    unicode(metadata['replaygain_track_gain'])[0:-len(" dB")],
                    unicode(metadata['replaygain_track_peak']),
                    unicode(metadata['replaygain_album_gain'])[0:-len(" dB")],
                    unicode(metadata['replaygain_album_peak']))
            except ValueError:
                return None
        else:
            return None

    def get_cuesheet(self):
        import cue

        metadata = self.get_metadata()

        if ((metadata is not None) and ('Cuesheet' in metadata.keys())):
            try:
                return cue.parse(
                    cue.tokens(
                        unicode(metadata['Cuesheet']).encode(
                            'utf-8', 'replace')))
            except cue.CueException:
                #unlike FLAC, just because a cuesheet is embedded
                #does not mean it is compliant
                return None
        else:
            return None

    def set_cuesheet(self, cuesheet):
        import os.path
        import cue

        if (cuesheet is None):
            return

        metadata = self.get_metadata()
        if (metadata is None):
            metadata = WavePackAPEv2.converted(MetaData())

        metadata['Cuesheet'] = WavePackAPEv2.ITEM.string(
            'Cuesheet',
            cue.Cuesheet.file(cuesheet,
                              os.path.basename(self.filename)).decode(
                                  'ascii', 'replace'))
        self.set_metadata(metadata)
Beispiel #6
0
class __TIFF__(ImageMetrics):
    HEADER = construct.Struct('header',
                        construct.String('byte_order',2),
                        construct.Switch('order',
                                   lambda ctx: ctx['byte_order'],
                                   {"II":construct.Embed(
        construct.Struct('little_endian',
                   construct.Const(construct.ULInt16('version'),42),
                   construct.ULInt32('offset'))),
                                    "MM":construct.Embed(
        construct.Struct('big_endian',
                   construct.Const(construct.UBInt16('version'),42),
                   construct.UBInt32('offset')))}))

    L_IFD = construct.Struct('ifd',
                       construct.PrefixedArray(
        length_field=construct.ULInt16('length'),
        subcon=construct.Struct('tags',
                          construct.ULInt16('id'),
                          construct.ULInt16('type'),
                          construct.ULInt32('count'),
                          construct.ULInt32('offset'))),
                       construct.ULInt32('next'))

    B_IFD = construct.Struct('ifd',
                       construct.PrefixedArray(
        length_field=construct.UBInt16('length'),
        subcon=construct.Struct('tags',
                          construct.UBInt16('id'),
                          construct.UBInt16('type'),
                          construct.UBInt32('count'),
                          construct.UBInt32('offset'))),
                       construct.UBInt32('next'))

    def __init__(self, width, height, bits_per_pixel, color_count):
        ImageMetrics.__init__(self, width, height,
                              bits_per_pixel, color_count,
                              u'image/tiff')

    @classmethod
    def b_tag_value(cls, file, tag):
        subtype = {1:construct.Byte("data"),
                   2:construct.CString("data"),
                   3:construct.UBInt16("data"),
                   4:construct.UBInt32("data"),
                   5:construct.Struct("data",
                                construct.UBInt32("high"),
                                construct.UBInt32("low"))}[tag.type]


        data = construct.StrictRepeater(tag.count,
                                  subtype)
        if ((tag.type != 2) and (data.sizeof() <= 4)):
            return tag.offset
        else:
            file.seek(tag.offset,0)
            return data.parse_stream(file)

    @classmethod
    def l_tag_value(cls, file, tag):
        subtype = {1:construct.Byte("data"),
                   2:construct.CString("data"),
                   3:construct.ULInt16("data"),
                   4:construct.ULInt32("data"),
                   5:construct.Struct("data",
                                construct.ULInt32("high"),
                                construct.ULInt32("low"))}[tag.type]


        data = construct.StrictRepeater(tag.count,
                                  subtype)
        if ((tag.type != 2) and (data.sizeof() <= 4)):
            return tag.offset
        else:
            file.seek(tag.offset,0)
            return data.parse_stream(file)

    @classmethod
    def parse(cls, file):
        width = 0
        height = 0
        bits_per_sample = 0
        color_count = 0

        try:
            header = cls.HEADER.parse_stream(file)
            if (header.byte_order == 'II'):
                IFD = cls.L_IFD
                tag_value = cls.l_tag_value
            elif (header.byte_order == 'MM'):
                IFD = cls.B_IFD
                tag_value = cls.b_tag_value
            else:
                raise InvalidTIFF(_(u'Invalid byte order'))

            file.seek(header.offset,0)

            ifd = IFD.parse_stream(file)

            while (True):
                for tag in ifd.tags:
                    if (tag.id == 0x0100):
                        width = tag_value(file,tag)
                    elif (tag.id == 0x0101):
                        height = tag_value(file,tag)
                    elif (tag.id == 0x0102):
                        try:
                            bits_per_sample = sum(tag_value(file,tag))
                        except TypeError:
                            bits_per_sample = tag_value(file,tag)
                    elif (tag.id == 0x0140):
                        color_count = tag.count / 3
                    else:
                        pass

                if (ifd.next == 0x00):
                    break
                else:
                    file.seek(ifd.next,0)
                    ifd = IFD.parse_stream(file)

            return __TIFF__(width,height,bits_per_sample,color_count)
        except construct.ConstError:
            raise InvalidTIFF(_(u'Invalid TIFF'))