Example #1
0
    def undo(self, new_file):
        """updates new_file to its original state,
        if present in the undo database"""

        undo_checksum = OldUndoDB.checksum(new_file)
        if (undo_checksum in self.db.keys()):
            # copy the xdelta to a temporary file
            xdelta_f = tempfile.NamedTemporaryFile(suffix=".delta")
            xdelta_f.write(self.db[undo_checksum])
            xdelta_f.flush()

            # patch the existing track to a temporary track
            old_track = tempfile.NamedTemporaryFile()
            try:
                if (subprocess.call([BIN["xdelta"],
                                     "patch",
                                     xdelta_f.name,
                                     new_file,
                                     old_track.name]) == 0):
                    # copy the temporary track over the existing file
                    f1 = open(old_track.name, 'rb')
                    f2 = open(new_file, 'wb')
                    transfer_data(f1.read, f2.write)
                    f1.close()
                    f2.close()
                    return True
                else:
                    raise IOError("error performing xdelta operation")
            finally:
                old_track.close()
                xdelta_f.close()
        else:
            return False
Example #2
0
    def delete_metadata(self):
        """deletes the track's MetaData

        this removes or unsets tags as necessary in order to remove all data
        raises IOError if unable to write the file"""

        import os
        from audiotools import (TemporaryFile, LimitedFileReader,
                                transfer_data)

        #this works a lot like update_metadata
        #but without any new metadata to set

        if (not os.access(self.filename, os.W_OK)):
            raise IOError(self.filename)

        new_mp3 = TemporaryFile(self.filename)

        #get the original MP3 data
        old_mp3 = open(self.filename, "rb")
        MP3Audio.__find_last_mp3_frame__(old_mp3)
        data_end = old_mp3.tell()
        old_mp3.seek(0, 0)
        MP3Audio.__find_mp3_start__(old_mp3)
        data_start = old_mp3.tell()
        old_mp3 = LimitedFileReader(old_mp3, data_end - data_start)

        #write data to file
        transfer_data(old_mp3.read, new_mp3.write)

        #commit change to disk
        old_mp3.close()
        new_mp3.close()
Example #3
0
    def delete_metadata(self):
        """deletes the track's MetaData

        this removes or unsets tags as necessary in order to remove all data
        raises IOError if unable to write the file"""

        import os
        from audiotools import (transfer_data,
                                LimitedFileReader,
                                TemporaryFile)
        from audiotools.id3 import skip_id3v2_comment

        if (not os.access(self.filename, os.W_OK)):
            raise IOError(self.filename)

        # overwrite original with no tags attached
        old_tta = open(self.filename, "rb")
        skip_id3v2_comment(old_tta)
        old_tta = LimitedFileReader(old_tta, self.data_size())

        new_tta = TemporaryFile(self.filename)
        transfer_data(old_tta.read, new_tta.write)

        old_tta.close()
        new_tta.close()
Example #4
0
    def delete_metadata(self):
        """deletes the track's MetaData

        this removes or unsets tags as necessary in order to remove all data
        raises IOError if unable to write the file"""

        import os
        from audiotools import (transfer_data,
                                LimitedFileReader,
                                TemporaryFile)
        from audiotools.id3 import skip_id3v2_comment

        if (not os.access(self.filename, os.W_OK)):
            raise IOError(self.filename)

        # overwrite original with no tags attached
        old_tta = open(self.filename, "rb")
        skip_id3v2_comment(old_tta)
        old_tta = LimitedFileReader(old_tta, self.data_size())

        new_tta = TemporaryFile(self.filename)
        transfer_data(old_tta.read, new_tta.write)

        old_tta.close()
        new_tta.close()
Example #5
0
    def add(self, old_file, new_file):
        """adds an undo entry for transforming new_file to old_file

        both are filename strings"""

        from io import BytesIO

        # perform xdelta between old and new track to temporary file
        delta_f = tempfile.NamedTemporaryFile(suffix=".delta")

        try:
            if (subprocess.call([BIN["xdelta"],
                                 "delta",
                                 new_file, old_file, delta_f.name]) != 2):
                # store the xdelta in our internal db
                f = open(delta_f.name, 'rb')
                data = BytesIO()
                transfer_data(f.read, data.write)
                f.close()

                self.db[OldUndoDB.checksum(new_file)] = data.getvalue()
            else:
                raise IOError("error performing xdelta operation")
        finally:
            delta_f.close()
Example #6
0
    def add(self, old_file, new_file):
        """adds an undo entry for transforming new_file to old_file

        both are filename strings"""

        from io import BytesIO

        # perform xdelta between old and new track to temporary file
        delta_f = tempfile.NamedTemporaryFile(suffix=".delta")

        try:
            if (subprocess.call([
                    BIN["xdelta"], "delta", new_file, old_file, delta_f.name
            ]) != 2):
                # store the xdelta in our internal db
                f = open(delta_f.name, 'rb')
                data = BytesIO()
                transfer_data(f.read, data.write)
                f.close()

                self.db[OldUndoDB.checksum(new_file)] = data.getvalue()
            else:
                raise IOError("error performing xdelta operation")
        finally:
            delta_f.close()
Example #7
0
    def to_wave(self, wave_filename):
        """writes the contents of this file to the given .wav filename string

        raises EncodingError if some error occurs during decoding"""

        from audiotools import BIN
        from audiotools import transfer_data
        import subprocess
        import os

        if (self.filename.endswith(".ape")):
            devnull = open(os.devnull, "wb")
            sub = subprocess.Popen(
                [BIN['mac'], self.filename, wave_filename, '-d'],
                stdout=devnull,
                stderr=devnull)
            sub.wait()
            devnull.close()
        else:
            devnull = open(os.devnull, 'ab')
            import tempfile
            ape = tempfile.NamedTemporaryFile(suffix='.ape')
            f = open(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()
    def delete_metadata(self):
        """Deletes the track's MetaData.

        This removes or unsets tags as necessary in order to remove all data.
        Raises IOError if unable to write the file."""

        import tempfile

        new_aiff = tempfile.TemporaryFile()
        new_aiff.seek(12, 0)

        for (chunk_id, chunk_length, chunk_file) in self.chunk_files():
            if (chunk_id != 'ID3 '):
                new_aiff.write(
                    self.CHUNK_HEADER.build(
                        Con.Container(chunk_id=chunk_id,
                                      chunk_length=chunk_length)))
                transfer_data(chunk_file.read, new_aiff.write)

        header = Con.Container(aiff_id='FORM',
                               aiff_size=new_aiff.tell() - 8,
                               aiff_type='AIFF')
        new_aiff.seek(0, 0)
        new_aiff.write(self.AIFF_HEADER.build(header))
        new_aiff.seek(0, 0)
        f = open(self.filename, 'wb')
        transfer_data(new_aiff.read, f.write)
        new_aiff.close()
        f.close()
Example #9
0
def undo_from_backup(track, undo_db, messenger):
    undo_checksum = checksum(track.filename)

    if (undo_db.has_key(undo_checksum)):
        #copy the xdelta to a temporary file
        xdelta_f = tempfile.NamedTemporaryFile(suffix=".delta")
        xdelta_f.write(undo_db[undo_checksum])
        xdelta_f.flush()

        #patch the existing track to a temporary track
        old_track = tempfile.NamedTemporaryFile(suffix="." + track.SUFFIX)
        if (subprocess.call([
                audiotools.BIN["xdelta"], "patch", xdelta_f.name,
                track.filename, old_track.name
        ]) == 0):
            #copy the temporary track over the existing file
            f1 = open(old_track.name, 'rb')
            f2 = open(track.filename, 'wb')
            audiotools.transfer_data(f1.read, f2.write)
            f1.close()
            f2.close()
            old_track.close()
            xdelta_f.close()

            messenger.info(_(u"Restored: %s") % \
                               (messenger.filename(track.filename)))
    def delete_metadata(self):
        """Deletes the track's MetaData.

        This removes or unsets tags as necessary in order to remove all data.
        Raises IOError if unable to write the file."""

        def chunk_filter(chunks):
            for (chunk_id, chunk_size, chunk_data) in chunks:
                if (chunk_id == 'ID3 '):
                    continue
                else:
                    yield (chunk_id, chunk_size, chunk_data)

        import tempfile

        import tempfile

        new_aiff = tempfile.NamedTemporaryFile(suffix=self.SUFFIX)
        self.__class__.aiff_from_chunks(new_aiff.name,
                                        chunk_filter(self.chunks()))

        new_file = open(new_aiff.name, 'rb')
        old_file = open(self.filename, 'wb')
        transfer_data(new_file.read, old_file.write)
        old_file.close()
        new_file.close()
Example #11
0
    def fix_id3_preserve_originals(self, tempFilePath):
        f = file(self.filename, 'rb')

        #figure out where the start and end points of the FLAC file are
        audiotools.ID3v2Comment.skip(f)
        flac_start = f.tell()
        f.seek(-128, 2)
        if (f.read(3) == 'TAG'):
            f.seek(-3, 1)
            flac_end = f.tell()
        else:
            f.seek(0, 2)
            flac_end = f.tell()

        #copy the FLAC data to a temporary location
        temp = tempfile.TemporaryFile()
        f.seek(flac_start, 0)
        reader = audiotools.__capped_stream_reader__(f, flac_end - flac_start)
        audiotools.transfer_data(reader.read, temp.write)

        #rewrite the original FLAC with our temporary data
        temp.seek(0, 0)
        f.close()
        f = file(tempFilePath, 'wb')
        audiotools.transfer_data(temp.read, f.write)
        temp.close()
        f.close()
        returnValue = audiotools.open(tempFilePath)
        return returnValue
Example #12
0
    def delete_metadata(self):
        """Deletes the track's MetaData.

        This removes or unsets tags as necessary in order to remove all data.
        Raises IOError if unable to write the file."""

        import tempfile

        new_aiff = tempfile.TemporaryFile()
        new_aiff.seek(12, 0)

        for (chunk_id, chunk_length, chunk_file) in self.chunk_files():
            if (chunk_id != 'ID3 '):
                new_aiff.write(self.CHUNK_HEADER.build(
                        Con.Container(chunk_id=chunk_id,
                                      chunk_length=chunk_length)))
                transfer_data(chunk_file.read, new_aiff.write)

        header = Con.Container(
            aiff_id='FORM',
            aiff_size=new_aiff.tell() - 8,
            aiff_type='AIFF')
        new_aiff.seek(0, 0)
        new_aiff.write(self.AIFF_HEADER.build(header))
        new_aiff.seek(0, 0)
        f = open(self.filename, 'wb')
        transfer_data(new_aiff.read, f.write)
        new_aiff.close()
        f.close()
Example #13
0
    def delete_metadata(self):
        """deletes the track's MetaData

        this removes or unsets tags as necessary in order to remove all data
        raises IOError if unable to write the file"""

        import os
        from audiotools import (TemporaryFile,
                                LimitedFileReader,
                                transfer_data)

        #this works a lot like update_metadata
        #but without any new metadata to set

        if (not os.access(self.filename, os.W_OK)):
            raise IOError(self.filename)

        new_mp3 = TemporaryFile(self.filename)

        #get the original MP3 data
        old_mp3 = open(self.filename, "rb")
        MP3Audio.__find_last_mp3_frame__(old_mp3)
        data_end = old_mp3.tell()
        old_mp3.seek(0, 0)
        MP3Audio.__find_mp3_start__(old_mp3)
        data_start = old_mp3.tell()
        old_mp3 = LimitedFileReader(old_mp3, data_end - data_start)

        #write data to file
        transfer_data(old_mp3.read, new_mp3.write)

        #commit change to disk
        old_mp3.close()
        new_mp3.close()
Example #14
0
    def undo(self, new_file):
        """Updates new_file to its original state,
        if present in the undo database."""

        undo_checksum = OldUndoDB.checksum(new_file)
        if (undo_checksum in self.db.keys()):
            #copy the xdelta to a temporary file
            xdelta_f = tempfile.NamedTemporaryFile(suffix=".delta")
            xdelta_f.write(self.db[undo_checksum])
            xdelta_f.flush()

            #patch the existing track to a temporary track
            old_track = tempfile.NamedTemporaryFile()
            try:
                if (subprocess.call([
                        BIN["xdelta"], "patch", xdelta_f.name, new_file,
                        old_track.name
                ]) == 0):
                    #copy the temporary track over the existing file
                    f1 = open(old_track.name, 'rb')
                    f2 = open(new_file, 'wb')
                    transfer_data(f1.read, f2.write)
                    f1.close()
                    f2.close()
                    return True
                else:
                    raise IOError("error performing xdelta operation")
            finally:
                old_track.close()
                xdelta_f.close()
        else:
            return False
Example #15
0
    def to_wave(self, wave_filename):
        """Writes the contents of this file to the given .wav filename string.

        Raises EncodingError if some error occurs during decoding."""

        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()
Example #16
0
 def __help_output__(cls):
     import cStringIO
     help_data = cStringIO.StringIO()
     sub = subprocess.Popen([BIN['lame'],'--help'],
                            stdout=subprocess.PIPE)
     transfer_data(sub.stdout.read,help_data.write)
     sub.wait()
     return help_data.getvalue()
Example #17
0
def checksum(path):
    f = open(path, "rb")
    c = sha1("")
    try:
        audiotools.transfer_data(f.read, c.update)
        return c.hexdigest()
    finally:
        f.close()
 def __help_output__(cls):
     import cStringIO
     help_data = cStringIO.StringIO()
     sub = subprocess.Popen([BIN['lame'], '--help'],
                            stdout=subprocess.PIPE)
     transfer_data(sub.stdout.read, help_data.write)
     sub.wait()
     return help_data.getvalue()
Example #19
0
    def to_pcm(self):
        #if mpg123 is available, use that for decoding
        if (BIN.can_execute(BIN["mpg123"])):
            sub = subprocess.Popen([BIN["mpg123"],"-qs",self.filename],
                                   stdout=subprocess.PIPE,
                                   stderr=file(os.devnull,"a"))
            return PCMReader(sub.stdout,
                             sample_rate=self.sample_rate(),
                             channels=self.channels(),
                             bits_per_sample=16,
                             channel_mask=int(ChannelMask.from_channels(self.channels())),
                             process=sub,
                             big_endian=BIG_ENDIAN)
        else:
            #if not, use LAME for decoding
            if (self.filename.endswith("." + self.SUFFIX)):
                if (BIG_ENDIAN):
                    endian = ['-x']
                else:
                    endian = []

                sub = subprocess.Popen([BIN['lame']] + endian + \
                                           ["--decode","-t","--quiet",
                                            self.filename,"-"],
                                       stdout=subprocess.PIPE)
                return PCMReader(sub.stdout,
                                 sample_rate=self.sample_rate(),
                                 channels=self.channels(),
                                 bits_per_sample=16,
                                 channel_mask=int(ChannelMask.from_channels(self.channels())),
                                 process=sub)
            else:
                import tempfile
                from audiotools import TempWaveReader
                #copy our file to one that ends with .mp3
                tempmp3 = tempfile.NamedTemporaryFile(suffix='.' + self.SUFFIX)
                f = open(self.filename,'rb')
                transfer_data(f.read,tempmp3.write)
                f.close()
                tempmp3.flush()

                #decode the mp3 file to a WAVE file
                wave = tempfile.NamedTemporaryFile(suffix='.wav')
                returnval = subprocess.call([BIN['lame'],"--decode","--quiet",
                                             tempmp3.name,wave.name])
                tempmp3.close()

                if (returnval == 0):
                    #return WAVE file as a stream
                    wave.seek(0,0)
                    return TempWaveReader(wave)
                else:
                    return PCMReaderError(None,
                                          sample_rate=self.sample_rate(),
                                          channels=self.channels(),
                                          bits_per_sample=16)
Example #20
0
    def checksum(cls, filename):
        """Returns the SHA1 checksum of the filename's contents."""

        f = open(filename, "rb")
        c = sha1("")
        try:
            transfer_data(f.read, c.update)
            return c.hexdigest()
        finally:
            f.close()
Example #21
0
    def checksum(cls, filename):
        """returns the SHA1 checksum of the filename's contents"""

        f = open(filename, "rb")
        c = sha1("")
        try:
            transfer_data(f.read, c.update)
            return c.hexdigest()
        finally:
            f.close()
Example #22
0
    def clean(self, output_filename=None):
        """cleans the file of known data and metadata problems

        output_filename is an optional filename of the fixed file
        if present, a new AudioFile is written to that path
        otherwise, only a dry-run is performed and no new file is written

        return list of fixes performed as Unicode strings

        raises IOError if unable to write the file or its metadata
        raises ValueError if the file has errors of some sort
        """

        from audiotools.id3 import total_id3v2_comments
        from audiotools import transfer_data
        from audiotools import open as open_audiofile
        from audiotools.text import CLEAN_REMOVE_DUPLICATE_ID3V2

        with open(self.filename, "rb") as f:
            if total_id3v2_comments(f) > 1:
                file_fixes = [CLEAN_REMOVE_DUPLICATE_ID3V2]
            else:
                file_fixes = []

        if output_filename is None:
            # dry run only
            metadata = self.get_metadata()
            if metadata is not None:
                (metadata, fixes) = metadata.clean()
                return file_fixes + fixes
            else:
                return file_fixes
        else:
            # perform complete fix
            input_f = open(self.filename, "rb")
            output_f = open(output_filename, "wb")
            try:
                transfer_data(input_f.read, output_f.write)
            finally:
                input_f.close()
                output_f.close()

            new_track = open_audiofile(output_filename)
            metadata = self.get_metadata()
            if metadata is not None:
                (metadata, fixes) = metadata.clean()
                if len(file_fixes + fixes) > 0:
                    # only update metadata if fixes are actually performed
                    new_track.update_metadata(metadata)
                return file_fixes + fixes
            else:
                return file_fixes
Example #23
0
    def clean(self, output_filename=None):
        """cleans the file of known data and metadata problems

        output_filename is an optional filename of the fixed file
        if present, a new AudioFile is written to that path
        otherwise, only a dry-run is performed and no new file is written

        return list of fixes performed as Unicode strings

        raises IOError if unable to write the file or its metadata
        raises ValueError if the file has errors of some sort
        """

        from audiotools.id3 import total_id3v2_comments
        from audiotools import open as open_audiofile
        from audiotools import transfer_data
        from audiotools.text import CLEAN_REMOVE_DUPLICATE_ID3V2

        with open(self.filename, "rb") as f:
            if (total_id3v2_comments(f) > 1):
                file_fixes = [CLEAN_REMOVE_DUPLICATE_ID3V2]
            else:
                file_fixes = []

        if (output_filename is None):
            # dry run only
            metadata = self.get_metadata()
            if (metadata is not None):
                (metadata, fixes) = metadata.clean()
                return file_fixes + fixes
            else:
                return []
        else:
            # perform full fix
            input_f = open(self.filename, "rb")
            output_f = open(output_filename, "wb")
            try:
                transfer_data(input_f.read, output_f.write)
            finally:
                input_f.close()
                output_f.close()

            new_track = open_audiofile(output_filename)
            metadata = self.get_metadata()
            if (metadata is not None):
                (metadata, fixes) = metadata.clean()
                if (len(file_fixes + fixes) > 0):
                    # only update metadata if fixes are actually performed
                    new_track.update_metadata(metadata)
                return file_fixes + fixes
            else:
                return []
Example #24
0
    def set_metadata(self, metadata):
        """Takes a MetaData object and sets this track's metadata.

        This metadata includes track name, album name, and so on.
        Raises IOError if unable to write the file."""

        if (metadata is None):
            return

        import tempfile

        id3_chunk = ID3v22Comment.converted(metadata).build()

        new_aiff = tempfile.TemporaryFile()
        new_aiff.seek(12, 0)

        id3_found = False
        for (chunk_id, chunk_length, chunk_file) in self.chunk_files():
            if (chunk_id != 'ID3 '):
                new_aiff.write(
                    self.CHUNK_HEADER.build(
                        Con.Container(chunk_id=chunk_id,
                                      chunk_length=chunk_length)))
                transfer_data(chunk_file.read, new_aiff.write)
            else:
                new_aiff.write(
                    self.CHUNK_HEADER.build(
                        Con.Container(chunk_id='ID3 ',
                                      chunk_length=len(id3_chunk))))
                new_aiff.write(id3_chunk)
                id3_found = True

        if (not id3_found):
            new_aiff.write(
                self.CHUNK_HEADER.build(
                    Con.Container(chunk_id='ID3 ',
                                  chunk_length=len(id3_chunk))))
            new_aiff.write(id3_chunk)

        header = Con.Container(aiff_id='FORM',
                               aiff_size=new_aiff.tell() - 8,
                               aiff_type='AIFF')
        new_aiff.seek(0, 0)
        new_aiff.write(self.AIFF_HEADER.build(header))
        new_aiff.seek(0, 0)
        f = open(self.filename, 'wb')
        transfer_data(new_aiff.read, f.write)
        new_aiff.close()
        f.close()
Example #25
0
    def set_metadata(self, metadata):
        """Takes a MetaData object and sets this track's metadata.

        This metadata includes track name, album name, and so on.
        Raises IOError if unable to write the file."""

        if (metadata is None):
            return

        import tempfile

        id3_chunk = ID3v22Comment.converted(metadata).build()

        new_aiff = tempfile.TemporaryFile()
        new_aiff.seek(12, 0)

        id3_found = False
        for (chunk_id, chunk_length, chunk_file) in self.chunk_files():
            if (chunk_id != 'ID3 '):
                new_aiff.write(self.CHUNK_HEADER.build(
                        Con.Container(chunk_id=chunk_id,
                                      chunk_length=chunk_length)))
                transfer_data(chunk_file.read, new_aiff.write)
            else:
                new_aiff.write(self.CHUNK_HEADER.build(
                        Con.Container(chunk_id='ID3 ',
                                      chunk_length=len(id3_chunk))))
                new_aiff.write(id3_chunk)
                id3_found = True

        if (not id3_found):
            new_aiff.write(self.CHUNK_HEADER.build(
                    Con.Container(chunk_id='ID3 ',
                                  chunk_length=len(id3_chunk))))
            new_aiff.write(id3_chunk)

        header = Con.Container(
            aiff_id='FORM',
            aiff_size=new_aiff.tell() - 8,
            aiff_type='AIFF')
        new_aiff.seek(0, 0)
        new_aiff.write(self.AIFF_HEADER.build(header))
        new_aiff.seek(0, 0)
        f = open(self.filename, 'wb')
        transfer_data(new_aiff.read, f.write)
        new_aiff.close()
        f.close()
Example #26
0
    def _checksum_track_audiotools(
            self, track: cd.Track) -> Dict[int, AccurateRipTrackID1]:
        file = audiotools.open(self._wav)
        # just doublechecking
        if not isinstance(file, audiotools.WaveAudio) \
           or not file.supports_to_pcm() \
           or file.channels() != 2 \
           or file.sample_rate() != cd.CDA_SAMLES_PER_SEC \
           or file.bits_per_sample() != cd.CDA_BITS_PER_SAMPLE:
            raise AccurateRipException(
                "Input file doesn't look like a CDA rip")

        # most of this is taken from https://github.com/tuffy/python-audio-tools/blob/master/trackverify#L244
        reader = file.to_pcm()
        if not hasattr(reader, "seek") or not callable(reader.seek):
            raise AccurateRipException("Can't seek in file")

        # we start reading a bit before the track, in order to try out different offsets for the accuraterip checksums
        # the reader below will take care of padding if this is negative
        offset = track.first_sample - self.PREVIOUS_TRACK_FRAMES

        if offset > 0:
            offset -= reader.seek(offset)

        checksummer = audiotools.accuraterip.Checksum(
            total_pcm_frames=track.length_samples,
            sample_rate=cd.CDA_SAMLES_PER_SEC,
            is_first=track.is_first,
            is_last=track.is_last,
            pcm_frame_range=self.PREVIOUS_TRACK_FRAMES + 1 +
            self.NEXT_TRACK_FRAMES,
            accurateripv2_offset=self.PREVIOUS_TRACK_FRAMES)

        window_reader = audiotools.PCMReaderWindow(
            reader, offset, self.PREVIOUS_TRACK_FRAMES + track.length_samples +
            self.NEXT_TRACK_FRAMES)

        audiotools.transfer_data(window_reader.read, checksummer.update)

        checksums_v1 = checksummer.checksums_v1()

        crc1_by_offset: Dict[int, AccurateRipTrackID1] = {
            i: AccurateRipTrackID1(c)
            for i, c in enumerate(checksums_v1, -self.PREVIOUS_TRACK_FRAMES)
        }

        return crc1_by_offset
Example #27
0
    def delete_metadata(self):
        """Deletes the track's MetaData.

        Raises IOError if unable to write the file."""

        from .bitstream import BitstreamReader, BitstreamWriter

        f = file(self.filename, "r+b")
        f.seek(-32, 2)

        (preamble,
         version,
         tag_size,
         item_count,
         read_only,
         item_encoding,
         is_header,
         no_footer,
         has_header) = BitstreamReader(f, 1).parse(ApeTag.HEADER_FORMAT)

        if ((preamble == 'APETAGEX') and (version == 2000)):
            #there's existing metadata to delete
            #so rewrite file without trailing metadata tag
            if (has_header):
                old_tag_size = 32 + tag_size
            else:
                old_tag_size = tag_size

            import tempfile
            from os.path import getsize
            rewritten = tempfile.TemporaryFile()

            #copy everything but the last "old_tag_size" bytes
            #from existing file to rewritten file
            f = open(self.filename, "rb")
            limited_transfer_data(f.read, rewritten.write,
                                  os.path.getsize(self.filename) -
                                  old_tag_size)
            f.close()

            #finally, overwrite current file with rewritten file
            rewritten.seek(0, 0)
            f = open(self.filename, "wb")
            transfer_data(rewritten.read, f.write)
            f.close()
            rewritten.close()
    def __from_wave__(cls, filename, wave_filename, compression=None):
        if (str(compression) not in cls.COMPRESSION_MODES):
            compression = cls.DEFAULT_COMPRESSION

        #mppenc requires files to end with .mpc for some reason
        if (not filename.endswith(".mpc")):
            import tempfile
            actual_filename = filename
            tempfile = tempfile.NamedTemporaryFile(suffix=".mpc")
            filename = tempfile.name
        else:
            actual_filename = tempfile = None

        ###Musepack SV7###
        #sub = subprocess.Popen([BIN['mppenc'],
        #                        "--silent",
        #                        "--overwrite",
        #                        "--%s" % (compression),
        #                        wave_filename,
        #                        filename],
        #                       preexec_fn=ignore_sigint)

        ###Musepack SV8###
        sub = subprocess.Popen([BIN['mpcenc'],
                                "--silent",
                                "--overwrite",
                                "--%s" % (compression),
                                wave_filename,
                                filename])

        if (sub.wait() == 0):
            if (tempfile is not None):
                filename = actual_filename
                f = file(filename, 'wb')
                tempfile.seek(0, 0)
                transfer_data(tempfile.read, f.write)
                f.close()
                tempfile.close()

            return MusepackAudio(filename)
        else:
            if (tempfile is not None):
                tempfile.close()
            raise EncodingError(u"error encoding file with mpcenc")
Example #29
0
    def set_metadata(self, metadata):
        if (metadata is None):
            return

        import tempfile

        id3_chunk = ID3v22Comment.converted(metadata).build()

        new_aiff = tempfile.TemporaryFile()
        new_aiff.seek(12, 0)

        id3_found = False
        for (chunk_id, chunk_length, chunk_file) in self.chunk_files():
            if (chunk_id != 'ID3 '):
                new_aiff.write(
                    self.CHUNK_HEADER.build(
                        construct.Container(chunk_id=chunk_id,
                                            chunk_length=chunk_length)))
                transfer_data(chunk_file.read, new_aiff.write)
            else:
                new_aiff.write(
                    self.CHUNK_HEADER.build(
                        construct.Container(chunk_id='ID3 ',
                                            chunk_length=len(id3_chunk))))
                new_aiff.write(id3_chunk)
                id3_found = True

        if (not id3_found):
            new_aiff.write(
                self.CHUNK_HEADER.build(
                    construct.Container(chunk_id='ID3 ',
                                        chunk_length=len(id3_chunk))))
            new_aiff.write(id3_chunk)

        header = construct.Container(aiff_id='FORM',
                                     aiff_size=new_aiff.tell() - 8,
                                     aiff_type='AIFF')
        new_aiff.seek(0, 0)
        new_aiff.write(self.AIFF_HEADER.build(header))
        new_aiff.seek(0, 0)
        f = open(self.filename, 'wb')
        transfer_data(new_aiff.read, f.write)
        new_aiff.close()
        f.close()
Example #30
0
    def __from_wave__(cls, filename, wave_filename, compression=None):
        if (str(compression) not in cls.COMPRESSION_MODES):
            compression = cls.DEFAULT_COMPRESSION

        #mppenc requires files to end with .mpc for some reason
        if (not filename.endswith(".mpc")):
            import tempfile
            actual_filename = filename
            tempfile = tempfile.NamedTemporaryFile(suffix=".mpc")
            filename = tempfile.name
        else:
            actual_filename = tempfile = None

        ###Musepack SV7###
        #sub = subprocess.Popen([BIN['mppenc'],
        #                        "--silent",
        #                        "--overwrite",
        #                        "--%s" % (compression),
        #                        wave_filename,
        #                        filename],
        #                       preexec_fn=ignore_sigint)

        ###Musepack SV8###
        sub = subprocess.Popen([BIN['mpcenc'],
                                "--silent",
                                "--overwrite",
                                "--%s" % (compression),
                                wave_filename,
                                filename])

        if (sub.wait() == 0):
            if (tempfile is not None):
                filename = actual_filename
                f = file(filename, 'wb')
                tempfile.seek(0, 0)
                transfer_data(tempfile.read, f.write)
                f.close()
                tempfile.close()

            return MusepackAudio(filename)
        else:
            if (tempfile is not None):
                tempfile.close()
            raise EncodingError(u"error encoding file with mpcenc")
    def update_metadata(self, metadata):
        """takes this track's current MetaData object
        as returned by get_metadata() and sets this track's metadata
        with any fields updated in that object

        raises IOError if unable to write the file
        """

        import tempfile
        from .bitstream import BitstreamRecorder

        if (metadata is None):
            return
        elif (not isinstance(metadata, ID3v22Comment)):
            raise ValueError(_(u"metadata not from audio file"))

        def chunk_filter(chunks, id3_chunk_data):
            for chunk in chunks:
                if (chunk.id == "ID3 "):
                    yield AIFF_Chunk("ID3 ",
                                     len(id3_chunk_data),
                                     id3_chunk_data)
                else:
                    yield chunk

        #turn our ID3v2.2 tag into a raw binary chunk
        id3_chunk = BitstreamRecorder(0)
        metadata.build(id3_chunk)
        id3_chunk = id3_chunk.data()

        #generate a temporary AIFF file in which our new ID3v2.2 chunk
        #replaces the existing ID3v2.2 chunk
        new_aiff = tempfile.NamedTemporaryFile(suffix=self.SUFFIX)
        self.__class__.aiff_from_chunks(new_aiff.name,
                                        chunk_filter(self.chunks(),
                                                     id3_chunk))

        #replace the existing file with data from the temporary file
        new_file = open(new_aiff.name, 'rb')
        old_file = open(self.filename, 'wb')
        transfer_data(new_file.read, old_file.write)
        old_file.close()
        new_file.close()
    def set_metadata(self, metadata):
        """takes a MetaData object and sets this track's metadata

        this metadata includes track name, album name, and so on
        raises IOError if unable to write the file"""

        if (metadata is None):
            return
        elif (self.get_metadata() is not None):
            #current file has metadata, so replace it with new metadata
            self.update_metadata(ID3v22Comment.converted(metadata))
        else:
            #current file has no metadata, so append new ID3 block

            import tempfile
            from .bitstream import BitstreamRecorder

            def chunk_filter(chunks, id3_chunk_data):
                for chunk in chunks:
                    yield chunk

                yield AIFF_Chunk("ID3 ",
                                 len(id3_chunk_data),
                                 id3_chunk_data)

            #turn our ID3v2.2 tag into a raw binary chunk
            id3_chunk = BitstreamRecorder(0)
            ID3v22Comment.converted(metadata).build(id3_chunk)
            id3_chunk = id3_chunk.data()

            #generate a temporary AIFF file in which our new ID3v2.2 chunk
            #is appended to the file's set of chunks
            new_aiff = tempfile.NamedTemporaryFile(suffix=self.SUFFIX)
            self.__class__.aiff_from_chunks(new_aiff.name,
                                            chunk_filter(self.chunks(),
                                                         id3_chunk))

            #replace the existing file with data from the temporary file
            new_file = open(new_aiff.name, 'rb')
            old_file = open(self.filename, 'wb')
            transfer_data(new_file.read, old_file.write)
            old_file.close()
            new_file.close()
Example #33
0
 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()
Example #34
0
    def delete_metadata(self):
        import tempfile

        new_aiff = tempfile.TemporaryFile()
        new_aiff.seek(12, 0)

        for (chunk_id, chunk_length, chunk_file) in self.chunk_files():
            if (chunk_id != 'ID3 '):
                new_aiff.write(
                    self.CHUNK_HEADER.build(
                        construct.Container(chunk_id=chunk_id,
                                            chunk_length=chunk_length)))
                transfer_data(chunk_file.read, new_aiff.write)

        header = construct.Container(aiff_id='FORM',
                                     aiff_size=new_aiff.tell() - 8,
                                     aiff_type='AIFF')
        new_aiff.seek(0, 0)
        new_aiff.write(self.AIFF_HEADER.build(header))
        new_aiff.seek(0, 0)
        f = open(self.filename, 'wb')
        transfer_data(new_aiff.read, f.write)
        new_aiff.close()
        f.close()
    def update_metadata(self, metadata):
        """takes this track's current MetaData object
        as returned by get_metadata() and sets this track's metadata
        with any fields updated in that object

        raises IOError if unable to write the file
        """

        import os
        from audiotools import (TemporaryFile,
                                LimitedFileReader,
                                transfer_data)
        from audiotools.id3 import (ID3v2Comment, ID3CommentPair)
        from audiotools.id3v1 import ID3v1Comment
        from audiotools.bitstream import BitstreamWriter

        if metadata is None:
            return
        elif (not (isinstance(metadata, ID3v2Comment) or
                   isinstance(metadata, ID3CommentPair) or
                   isinstance(metadata, ID3v1Comment))):
            from audiotools.text import ERR_FOREIGN_METADATA
            raise ValueError(ERR_FOREIGN_METADATA)
        elif not os.access(self.filename, os.W_OK):
            raise IOError(self.filename)

        new_mp3 = TemporaryFile(self.filename)

        # get the original MP3 data
        old_mp3 = open(self.filename, "rb")
        MP3Audio.__find_last_mp3_frame__(old_mp3)
        data_end = old_mp3.tell()
        old_mp3.seek(0, 0)
        MP3Audio.__find_mp3_start__(old_mp3)
        data_start = old_mp3.tell()
        old_mp3 = LimitedFileReader(old_mp3, data_end - data_start)

        # write id3v2 + data + id3v1 to file
        if isinstance(metadata, ID3CommentPair):
            metadata.id3v2.build(BitstreamWriter(new_mp3, False))
            transfer_data(old_mp3.read, new_mp3.write)
            metadata.id3v1.build(new_mp3)
        elif isinstance(metadata, ID3v2Comment):
            metadata.build(BitstreamWriter(new_mp3, False))
            transfer_data(old_mp3.read, new_mp3.write)
        elif isinstance(metadata, ID3v1Comment):
            transfer_data(old_mp3.read, new_mp3.write)
            metadata.build(new_mp3)

        # commit change to disk
        old_mp3.close()
        new_mp3.close()
Example #36
0
    def update_metadata(self, metadata):
        """takes this track's current MetaData object
        as returned by get_metadata() and sets this track's metadata
        with any fields updated in that object

        raises IOError if unable to write the file
        """

        import os
        from audiotools import (TemporaryFile,
                                LimitedFileReader,
                                transfer_data)
        from audiotools.id3 import (ID3v2Comment, ID3CommentPair)
        from audiotools.id3v1 import ID3v1Comment
        from audiotools.bitstream import BitstreamWriter

        if (metadata is None):
            return
        elif (not (isinstance(metadata, ID3v2Comment) or
                   isinstance(metadata, ID3CommentPair) or
                   isinstance(metadata, ID3v1Comment))):
            from .text import ERR_FOREIGN_METADATA
            raise ValueError(ERR_FOREIGN_METADATA)
        elif (not os.access(self.filename, os.W_OK)):
            raise IOError(self.filename)

        new_mp3 = TemporaryFile(self.filename)

        #get the original MP3 data
        old_mp3 = open(self.filename, "rb")
        MP3Audio.__find_last_mp3_frame__(old_mp3)
        data_end = old_mp3.tell()
        old_mp3.seek(0, 0)
        MP3Audio.__find_mp3_start__(old_mp3)
        data_start = old_mp3.tell()
        old_mp3 = LimitedFileReader(old_mp3, data_end - data_start)

        #write id3v2 + data + id3v1 to file
        if (isinstance(metadata, ID3CommentPair)):
            metadata.id3v2.build(BitstreamWriter(new_mp3, 0))
            transfer_data(old_mp3.read, new_mp3.write)
            metadata.id3v1.build(new_mp3)
        elif (isinstance(metadata, ID3v2Comment)):
            metadata.build(BitstreamWriter(new_mp3, 0))
            transfer_data(old_mp3.read, new_mp3.write)
        elif (isinstance(metadata, ID3v1Comment)):
            transfer_data(old_mp3.read, new_mp3.write)
            metadata.build(new_mp3)

        #commit change to disk
        old_mp3.close()
        new_mp3.close()
Example #37
0
def update_and_backup(track, undo_db, messenger):
    changes_made = False

    #copy the track to a temporary location
    temp_track_f = tempfile.NamedTemporaryFile(suffix="." + track.SUFFIX)
    f = open(track.filename, 'rb')
    audiotools.transfer_data(f.read, temp_track_f.write)
    f.close()
    temp_track_f.flush()
    temp_track = audiotools.open(temp_track_f.name)

    #perform any Track fixes on the temporary Track
    (temp_track, messages) = fix_track(temp_track, dry_run=False)
    changes_made = changes_made or (len(messages) > 0)
    display_messages(messenger, track, messages)

    #perform any MetaData fixes on the temporary Track
    (metadata, messages) = fix_metadata(temp_track.get_metadata())
    changes_made = changes_made or (len(messages) > 0)
    display_messages(messenger, track, messages)
    if (len(messages) > 0):
        temp_track.set_metadata(metadata)

    if (changes_made):
        #perform xdelta between the old and new track
        delta_f = tempfile.NamedTemporaryFile(suffix=".delta")
        if (subprocess.call([
                audiotools.BIN["xdelta"], "delta", temp_track.filename,
                track.filename, delta_f.name
        ]) != 2):
            #store the xdelta in our undo DB
            f = open(delta_f.name, 'rb')
            data = cStringIO.StringIO()
            audiotools.transfer_data(f.read, data.write)
            f.close()
            delta_f.close()

            undo_db[checksum(temp_track_f.name)] = data.getvalue()

            #copy the temporary track over the original
            f1 = open(temp_track_f.name, 'rb')
            f2 = open(track.filename, 'wb')
            audiotools.transfer_data(f1.read, f2.write)
            f1.close()
            f2.close()

    temp_track_f.close()
                cls.__unlink__(filename)
                raise EncodingError(str(err))

            #write header to disk
            write_header(writer,
                         pcmreader.channels,
                         pcmreader.bits_per_sample,
                         pcmreader.sample_rate,
                         counter.frames_written)

            #write seektable to disk
            write_seektable(writer, frame_sizes)

            #transfer TTA frames from temporary space to disk
            frames.seek(0, 0)
            transfer_data(frames.read, file.write)
            frames.close()

        file.close()

        return cls(filename)

    def data_size(self):
        """returns the size of the file's data, in bytes,
        calculated from its header and seektable"""

        return (22 +                                     # header size
                (len(self.__frame_lengths__) * 4) + 4 +  # seektable size
                sum(self.__frame_lengths__))             # frames size

    @classmethod
Example #39
0
def accuraterip_image_checksum(progress, track, is_first, is_last, ar_matches,
                               displayed_filename, pcm_frames_offset,
                               total_pcm_frames):
    from audiotools import (transfer_data, PCMReaderProgress, PCMReaderWindow)
    from audiotools.accuraterip import Checksum, match_offset

    reader = track.to_pcm()

    pcm_frames_offset -= PREVIOUS_TRACK_FRAMES

    # if PCMReader has seek(), use it to reduce the amount of frames to skip
    if (hasattr(reader, "seek") and callable(reader.seek)
            and (pcm_frames_offset > 0)):
        pcm_frames_offset -= reader.seek(pcm_frames_offset)

    # feed stream to checksummers
    checksummer = Checksum(total_pcm_frames=total_pcm_frames,
                           sample_rate=track.sample_rate(),
                           is_first=is_first,
                           is_last=is_last,
                           pcm_frame_range=PREVIOUS_TRACK_FRAMES + 1 +
                           NEXT_TRACK_FRAMES,
                           accurateripv2_offset=PREVIOUS_TRACK_FRAMES)

    try:
        pcmreader = audiotools.PCMReaderProgress(
            audiotools.PCMReaderWindow(
                reader, pcm_frames_offset,
                PREVIOUS_TRACK_FRAMES + total_pcm_frames + NEXT_TRACK_FRAMES),
            PREVIOUS_TRACK_FRAMES + total_pcm_frames + NEXT_TRACK_FRAMES,
            progress)

        audiotools.transfer_data(pcmreader.read, checksummer.update)
    except (IOError, ValueError) as err:
        return {
            "filename": displayed_filename,
            "error": str(err),
            "v1": {
                "checksum": None,
                "offset": None,
                "confidence": None
            },
            "v2": {
                "checksum": None,
                "offset": None,
                "confidence": None
            }
        }

    # determine checksum, confidence and offset from
    # the calculated checksums and possible AccurateRip matches
    (checksum_v2, confidence_v2,
     offset_v2) = match_offset(ar_matches=ar_matches,
                               checksums=[checksummer.checksum_v2()],
                               initial_offset=0)

    (checksum_v1, confidence_v1,
     offset_v1) = match_offset(ar_matches=ar_matches,
                               checksums=checksummer.checksums_v1(),
                               initial_offset=-PREVIOUS_TRACK_FRAMES)

    if len(ar_matches) == 0:
        return {
            "filename": displayed_filename,
            "error": None,
            "v1": {
                "checksum": checksum_v1,
                "offset": offset_v1,
                "confidence": AR_NOT_FOUND
            },
            "v2": {
                "checksum": checksum_v2,
                "offset": offset_v2,
                "confidence": AR_NOT_FOUND
            }
        }
    else:
        return {
            "filename": displayed_filename,
            "error": None,
            "v1": {
                "checksum":
                checksum_v1,
                "offset":
                offset_v1,
                "confidence": (confidence_v1 if
                               (confidence_v1 is not None) else AR_MISMATCH)
            },
            "v2": {
                "checksum":
                checksum_v2,
                "offset":
                offset_v2,
                "confidence": (confidence_v2 if
                               (confidence_v2 is not None) else AR_MISMATCH)
            }
        }
    def from_pcm(cls, filename, pcmreader,
                 compression=None, total_pcm_frames=None):
        """encodes a new file from PCM data

        takes a filename string, PCMReader object,
        optional compression level string and optional
        total_pcm_frames integer
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new M4AAudio object"""

        import subprocess
        import os
        from audiotools import PCMConverter
        from audiotools import transfer_data
        from audiotools import transfer_framelist_data
        from audiotools import ignore_sigint
        from audiotools import EncodingError
        from audiotools import DecodingError
        from audiotools import ChannelMask
        from audiotools import __default_quality__

        if ((compression is None) or (compression not in
                                      cls.COMPRESSION_MODES)):
            compression = __default_quality__(cls.NAME)

        if pcmreader.bits_per_sample not in {8, 16, 24}:
            from audiotools import UnsupportedBitsPerSample
            pcmreader.close()
            raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample)

        if pcmreader.channels > 2:
            pcmreader = PCMConverter(pcmreader,
                                     sample_rate=pcmreader.sample_rate,
                                     channels=2,
                                     channel_mask=ChannelMask.from_channels(2),
                                     bits_per_sample=pcmreader.bits_per_sample)

        # faac requires files to end with .m4a for some reason
        if not filename.endswith(".m4a"):
            import tempfile
            actual_filename = filename
            tempfile = tempfile.NamedTemporaryFile(suffix=".m4a")
            filename = tempfile.name
        else:
            actual_filename = tempfile = None

        sub = subprocess.Popen(
            [BIN['faac'],
             "-q", compression,
             "-P",
             "-R", str(pcmreader.sample_rate),
             "-B", str(pcmreader.bits_per_sample),
             "-C", str(pcmreader.channels),
             "-X",
             "-o", filename,
             "-"],
            stdin=subprocess.PIPE,
            stderr=subprocess.DEVNULL if hasattr(subprocess, "DEVNULL") else
            open(os.devnull, "wb"),
            stdout=subprocess.DEVNULL if hasattr(subprocess, "DEVNULL") else
            open(os.devnull, "wb"),
            preexec_fn=ignore_sigint)
        # Note: faac handles SIGINT on its own,
        # so trying to ignore it doesn't work like on most other encoders.

        try:
            if total_pcm_frames is not None:
                from audiotools import CounterPCMReader
                pcmreader = CounterPCMReader(pcmreader)

            transfer_framelist_data(pcmreader, sub.stdin.write)

            if ((total_pcm_frames is not None) and
                (total_pcm_frames != pcmreader.frames_written)):
                from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH
                raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH)

        except (ValueError, IOError) as err:
            sub.stdin.close()
            sub.wait()
            cls.__unlink__(filename)
            raise EncodingError(str(err))
        except Exception:
            sub.stdin.close()
            sub.wait()
            cls.__unlink__(filename)
            raise

        sub.stdin.close()

        if sub.wait() == 0:
            if tempfile is not None:
                filename = actual_filename
                f = open(filename, 'wb')
                tempfile.seek(0, 0)
                transfer_data(tempfile.read, f.write)
                f.close()
                tempfile.close()

            return M4AAudio(filename)
        else:
            if tempfile is not None:
                tempfile.close()
            raise EncodingError(u"unable to write file with faac")
        """Writes the contents of this file to the given .wav filename string.

        Raises EncodingError if some error occurs during decoding."""

        try:
            self.verify()
        except InvalidWave, err:
            raise EncodingError(str(err))

        try:
            output = file(wave_filename, 'wb')
            input = file(self.filename, 'rb')
        except IOError, msg:
            raise EncodingError(str(msg))
        try:
            transfer_data(input.read, output.write)
        finally:
            input.close()
            output.close()

    @classmethod
    def from_wave(cls, filename, wave_filename, compression=None,
                  progress=None):
        """Encodes a new AudioFile from an existing .wav file.

        Takes a filename string, wave_filename string
        of an existing WaveAudio file
        and an optional compression level string.
        Encodes a new audio file from the wave's data
        at the given filename with the specified compression level
        and returns a new WaveAudio object."""
Example #42
0
def rip_disk(self):
    """Most of this is just copied from cdda2track, a part of python-audio-tools"""
    try:
        cddareader = CDDAReader(app.config['ripper']['cdrom'], True)
        track_offsets = cddareader.track_offsets
        track_lengths = cddareader.track_lengths
    except (IOError, ValueError, OSError) as err:
        self.update_state(state='FAILURE', meta={'error': str(err)})
        raise Ignore()

    if "offset" in app.config['ripper']:
        read_offset = app.config['ripper']['offset']
    else:
        read_offset = 0
    if "speed" in app.config['ripper']:
        cddareader.set_speed(app.config['ripper']['speed'])

    pre_gap_length = cddareader.track_offsets[1]
    if pre_gap_length > 0:
        with audiotools.BufferedPCMReader(audiotools.PCMReaderWindow(cddareader,
                                                                     read_offset,
                                                                     pre_gap_length,
                                                                     forward_close=False)) as r:
            preserve_pre_gap = set(r.read(pre_gap_length)) != {0}
            if preserve_pre_gap:
                track_offsets[0] = 0
                track_lengths[0] = pre_gap_length
    else:
        preserve_pre_gap = False

    self.update_state(state='PROGRESS', meta={'current': None,
                                              'total': None,
                                              'album': None,
                                              'artist': None,
                                              'metadata': [],
                                              'status': 'Reading metadata'})
    metadata_choices = audiotools.cddareader_metadata_lookup(cddareader)

    if preserve_pre_gap:
        # prepend "track 0" track to start of list for each choice
        for choice in metadata_choices:
            track_0 = merge_metadatas(choice)
            track_0.track_number = 0
            choice.insert(0, track_0)

    album = metadata_choices[0][0].album_name
    artist = metadata_choices[0][0].artist_name
    self.update_state(state='PROGRESS', meta={'current': None,
                                              'total': len(track_offsets),
                                              'album': album,
                                              'artist': artist,
                                              'metadata': jsonify_metadata(metadata_choices[0]),
                                              'status': 'Got metadata, preparing to rip'})

    tracks_to_rip = list(sorted(track_offsets.keys()))
    try:
        output_tracks = list(
            process_output_options(
                metadata_choices=[
                    [c for i, c in
                     enumerate(choices, 0 if preserve_pre_gap else 1)
                     if i in tracks_to_rip]
                    for choices in metadata_choices],
                input_filenames=[
                    audiotools.Filename("track{:02d}.cdda.wav".format(i))
                    for i in tracks_to_rip],
                output_directory=app.config['ripper']['output_directory'],
                format_string=None,
                output_class=audiotools.wav.WaveAudio,
                quality='',
                msg=msg,
                use_default=True))
    except audiotools.UnsupportedTracknameField as err:
        self.update_state(state='FAILURE', meta={'error': str(err)})
        raise Ignore()

    encoded = []
    rip_log = {}
    accuraterip_log_v1 = {}
    accuraterip_log_v2 = {}
    replay_gain = audiotools.ReplayGainCalculator(cddareader.sample_rate)

    for (track_number,
         index,
         (output_class,
          output_filename,
          output_quality,
          output_metadata)) in zip(tracks_to_rip,
                                   range(1, len(tracks_to_rip) + 1),
                                   output_tracks):
        self.update_state(state='PROGRESS', meta={'current': track_number,
                                                  'total': len(tracks_to_rip),
                                                  'album': album,
                                                  'artist': artist,
                                                  'metadata': jsonify_metadata(metadata_choices[0]),
                                                  'status': 'Preparing to rip'})
        cddareader.reset_log()
        track_offset = (track_offsets[track_number] +
                        read_offset -
                        PREVIOUS_TRACK_FRAMES)
        track_length = track_lengths[track_number]

        # seek to indicated starting offset
        if track_offset > 0:
            seeked_offset = cddareader.seek(track_offset)
        else:
            seeked_offset = cddareader.seek(0)

        # make leading directories, if necessary
        try:
            audiotools.make_dirs(str(output_filename))
        except OSError as err:
            self.update_state(state='FAILURE', meta={'error': str(err)})
            raise Ignore()

        # setup individual progress bar per track
        state = {'current': track_number,
                 'total': len(tracks_to_rip),
                 'album': album,
                 'artist': artist,
                 'metadata': jsonify_metadata(metadata_choices[0]),
                 'status': 'Ripping...'}
        progress = CeleryProgressDisplay(msg, state, self)

        # perform extraction over an AccurateRip window
        track_data = audiotools.PCMReaderWindow(
            cddareader,
            track_offset - seeked_offset,
            PREVIOUS_TRACK_FRAMES + track_length + NEXT_TRACK_FRAMES)

        # with AccurateRip calculated during extraction
        accuraterip = AccurateRipReader(
            track_data,
            track_length,
            track_number == min(track_offsets.keys()),
            track_number == max(track_offsets.keys()))

        try:
            # encode output file itself
            track = output_class.from_pcm(
                str(output_filename),
                replay_gain.to_pcm(
                    audiotools.PCMReaderProgress(
                        audiotools.PCMReaderWindow(
                            accuraterip,
                            PREVIOUS_TRACK_FRAMES,
                            track_length,
                            forward_close=False),
                        track_length,
                        progress.update)),
                output_quality,
                total_pcm_frames=track_length)
            encoded.append(track)

            # since the inner PCMReaderWindow only outputs part
            # of the accuraterip reader, we need to ensure
            # anything left over in accuraterip gets processed also
            audiotools.transfer_data(accuraterip.read, lambda f: None)
        except audiotools.EncodingError as err:
            self.update_state(state='FAILURE', meta={'error': str(err)})
            raise Ignore()

        track.set_metadata(output_metadata)

        rip_log[track_number] = cddareader.log()
        accuraterip_log_v1[track_number] = accuraterip.checksums_v1()
        accuraterip_log_v2[track_number] = accuraterip.checksums_v2()

    self.update_state(state='SUCCESS', meta={'total': len(tracks_to_rip),
                                             'album': album,
                                             'artist': artist,
                                             'metadata': jsonify_metadata(metadata_choices[0]),
                                             'status': 'Fully ripped'})

    return {'status': 'done', 'tracks': len(tracks_to_rip)}
Example #43
0
    def from_pcm(cls,
                 filename,
                 pcmreader,
                 compression=None,
                 total_pcm_frames=None,
                 encoding_function=None):
        """encodes a new file from PCM data

        takes a filename string, PCMReader object,
        optional compression level string and
        optional total_pcm_frames integer
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new AudioFile-compatible object

        may raise EncodingError if some problem occurs when
        encoding the input file.  This includes an error
        in the input stream, a problem writing the output file,
        or even an EncodingError subclass such as
        "UnsupportedBitsPerSample" if the input stream
        is formatted in a way this class is unable to support
        """

        from audiotools import (BufferedPCMReader, CounterPCMReader,
                                transfer_data, EncodingError)
        # from audiotools.py_encoders import encode_tta
        from audiotools.encoders import encode_tta
        from audiotools.bitstream import BitstreamWriter

        # open output file right away
        # so we can fail as soon as possible
        try:
            file = open(filename, "wb")
        except IOError as err:
            pcmreader.close()
            raise EncodingError(str(err))

        writer = BitstreamWriter(file, True)
        counter = CounterPCMReader(pcmreader)
        try:
            if (total_pcm_frames is not None):
                # write header to disk
                write_header(writer, pcmreader.channels,
                             pcmreader.bits_per_sample, pcmreader.sample_rate,
                             total_pcm_frames)

                block_size = (pcmreader.sample_rate * 256) // 245
                total_tta_frames = div_ceil(total_pcm_frames, block_size)

                # write temporary seektable to disk
                writer.mark()
                write_seektable(writer, [0] * total_tta_frames)
                writer.flush()

                # write frames to disk
                try:
                    frame_sizes = \
                        (encode_tta if encoding_function is None
                         else encoding_function)(file,
                                                 BufferedPCMReader(counter))
                except (IOError, ValueError) as err:
                    cls.__unlink__(filename)
                    raise EncodingError(str(err))

                # ensure written number of PCM frames
                # matches total_pcm_frames
                if (counter.frames_written != total_pcm_frames):
                    from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH
                    cls.__unlink__(filename)
                    raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH)

                assert (len(frame_sizes) == total_tta_frames)

                # go back and rewrite seektable with completed one
                writer.rewind()
                write_seektable(writer, frame_sizes)
                writer.unmark()
            else:
                import tempfile

                frames = tempfile.TemporaryFile()

                # encode TTA frames to temporary file
                try:
                    frame_sizes = \
                        (encode_tta if encoding_function is None
                         else encoding_function)(frames,
                                                 BufferedPCMReader(counter))
                except (IOError, ValueError) as err:
                    frames.close()
                    cls.__unlink__(filename)
                    raise EncodingError(str(err))

                # write header to disk
                write_header(writer, pcmreader.channels,
                             pcmreader.bits_per_sample, pcmreader.sample_rate,
                             counter.frames_written)

                # write seektable to disk
                write_seektable(writer, frame_sizes)

                # transfer TTA frames from temporary space to disk
                frames.seek(0, 0)
                transfer_data(frames.read, writer.write_bytes)
                frames.close()
        finally:
            counter.close()
            if (writer.has_mark()):
                writer.unmark()
            writer.close()

        return cls(filename)
Example #44
0
            sub.wait()
            cls.__unlink__(filename)
            raise err

        try:
            pcmreader.close()
        except DecodingError, err:
            raise EncodingError(err.error_message)
        sub.stdin.close()

        if sub.wait() == 0:
            if tempfile is not None:
                filename = actual_filename
                f = file(filename, "wb")
                tempfile.seek(0, 0)
                transfer_data(tempfile.read, f.write)
                f.close()
                tempfile.close()

            return M4AAudio(filename)
        else:
            if tempfile is not None:
                tempfile.close()
            raise EncodingError(u"unable to write file with faac")

    @classmethod
    def can_add_replay_gain(cls):
        """Returns False."""

        return False
Example #45
0
    def update_metadata(self, metadata):
        """takes this track's current MetaData object
        as returned by get_metadata() and sets this track's metadata
        with any fields updated in that object

        raises IOError if unable to write the file
        """

        import os
        from audiotools.ape import ApeTag
        from audiotools.id3 import ID3v2Comment
        from audiotools.id3 import ID3CommentPair
        from audiotools.id3v1 import ID3v1Comment

        if (metadata is None):
            return
        elif (not os.access(self.filename, os.W_OK)):
            raise IOError(self.filename)

        # ensure metadata is APEv2, ID3v2, ID3v1, or ID3CommentPair
        if (((not isinstance(metadata, ApeTag))
             and (not isinstance(metadata, ID3v2Comment))
             and (not isinstance(metadata, ID3CommentPair))
             and (not isinstance(metadata, ID3v1Comment)))):
            from audiotools.text import ERR_FOREIGN_METADATA
            raise ValueError(ERR_FOREIGN_METADATA)

        current_metadata = self.get_metadata()

        if (isinstance(metadata, ApeTag) and (current_metadata is None)):
            # if new metadata is APEv2 and no current metadata,
            # simply append APEv2 tag
            from audiotools.bitstream import BitstreamWriter
            with BitstreamWriter(open(self.filename, "ab"), True) as writer:
                metadata.build(writer)
        elif (isinstance(metadata, ApeTag)
              and isinstance(current_metadata, ApeTag)
              and (metadata.total_size() > current_metadata.total_size())):
            # if new metadata is APEv2, current metadata is APEv2
            # and new metadata is larger,
            # overwrite old tag with new tag
            from audiotools.bitstream import BitstreamWriter
            with open(self.filename, "r+b") as f:
                f.seek(-current_metadata.total_size(), 2)
                metadata.build(BitstreamWriter(f, True))
        else:
            from audiotools.bitstream import BitstreamWriter
            from audiotools import (transfer_data, LimitedFileReader,
                                    TemporaryFile)
            from audiotools.id3 import skip_id3v2_comment

            # otherwise, rebuild TTA with APEv2/ID3 tags in place
            old_tta = open(self.filename, "rb")
            skip_id3v2_comment(old_tta)
            old_tta = LimitedFileReader(old_tta, self.data_size())

            new_tta = TemporaryFile(self.filename)

            if (isinstance(metadata, ApeTag)):
                transfer_data(old_tta.read, new_tta.write)
                metadata.build(BitstreamWriter(new_tta, True))
            elif (isinstance(metadata, ID3CommentPair)):
                metadata.id3v2.build(BitstreamWriter(new_tta, False))
                transfer_data(old_tta.read, new_tta.write)
                metadata.id3v1.build(new_tta)
            elif (isinstance(metadata, ID3v2Comment)):
                metadata.build(BitstreamWriter(new_tta, False))
                transfer_data(old_tta.read, new_tta.write)
            else:
                # ID3v1Comment
                transfer_data(old_tta.read, new_tta.write)
                metadata.build(new_tta)

            old_tta.close()
            new_tta.close()
Example #46
0
def accuraterip_checksum(progress, track, previous_track, next_track,
                         ar_matches):
    from audiotools import (transfer_data, PCMCat, PCMReaderHead,
                            PCMReaderDeHead, PCMReaderProgress)
    from audiotools.decoders import SameSample
    from audiotools.accuraterip import Checksum, match_offset

    # unify previous track, current track and next track into a single stream

    pcmreaders = []
    if previous_track is not None:
        frames_to_skip = previous_track.total_frames() - PREVIOUS_TRACK_FRAMES
        prev_pcmreader = previous_track.to_pcm()
        if hasattr(prev_pcmreader, "seek") and callable(prev_pcmreader.seek):
            frames_to_skip -= prev_pcmreader.seek(frames_to_skip)
        pcmreaders.append(PCMReaderDeHead(prev_pcmreader, frames_to_skip))
    else:
        pcmreaders.append(
            SameSample(sample=0,
                       total_pcm_frames=PREVIOUS_TRACK_FRAMES,
                       sample_rate=track.sample_rate(),
                       channels=track.channels(),
                       channel_mask=int(track.channel_mask()),
                       bits_per_sample=track.bits_per_sample()))

    pcmreaders.append(track.to_pcm())

    if next_track is not None:
        pcmreaders.append(PCMReaderHead(next_track.to_pcm(),
                                        NEXT_TRACK_FRAMES))
    else:
        pcmreaders.append(
            SameSample(sample=0,
                       total_pcm_frames=NEXT_TRACK_FRAMES,
                       sample_rate=track.sample_rate(),
                       channels=track.channels(),
                       channel_mask=int(track.channel_mask()),
                       bits_per_sample=track.bits_per_sample()))

    # feed stream to checksummer
    checksummer = Checksum(total_pcm_frames=track.total_frames(),
                           sample_rate=track.sample_rate(),
                           is_first=(previous_track is None),
                           is_last=(next_track is None),
                           pcm_frame_range=PREVIOUS_TRACK_FRAMES + 1 +
                           NEXT_TRACK_FRAMES,
                           accurateripv2_offset=PREVIOUS_TRACK_FRAMES)

    try:
        pcmreader = PCMReaderProgress(
            PCMCat(pcmreaders),
            PREVIOUS_TRACK_FRAMES + track.total_frames() + NEXT_TRACK_FRAMES,
            progress)
        audiotools.transfer_data(pcmreader.read, checksummer.update)
    except (IOError, ValueError) as err:
        return {
            "filename": audiotools.Filename(track.filename).__unicode__(),
            "error": str(err),
            "v1": {
                "checksum": None,
                "offset": None,
                "confidence": None
            },
            "v2": {
                "checksum": None,
                "offset": None,
                "confidence": None
            }
        }

    # determine checksum, confidence and offset from
    # the calculated checksums and possible AccurateRip matches
    (checksum_v2, confidence_v2,
     offset_v2) = match_offset(ar_matches,
                               checksums=[checksummer.checksum_v2()],
                               initial_offset=0)

    (checksum_v1, confidence_v1,
     offset_v1) = match_offset(ar_matches=ar_matches,
                               checksums=checksummer.checksums_v1(),
                               initial_offset=-PREVIOUS_TRACK_FRAMES)

    if len(ar_matches) == 0:
        return {
            "filename": audiotools.Filename(track.filename).__unicode__(),
            "error": None,
            "v1": {
                "checksum": checksum_v1,
                "offset": offset_v1,
                "confidence": AR_NOT_FOUND
            },
            "v2": {
                "checksum": checksum_v2,
                "offset": offset_v2,
                "confidence": AR_NOT_FOUND
            }
        }
    else:
        return {
            "filename": audiotools.Filename(track.filename).__unicode__(),
            "error": None,
            "v1": {
                "checksum":
                checksum_v1,
                "offset":
                offset_v1,
                "confidence": (confidence_v1 if
                               (confidence_v1 is not None) else AR_MISMATCH)
            },
            "v2": {
                "checksum":
                checksum_v2,
                "offset":
                offset_v2,
                "confidence": (confidence_v2 if
                               (confidence_v2 is not None) else AR_MISMATCH)
            }
        }
Example #47
0
    def from_pcm(cls, filename, pcmreader,
                 compression=None,
                 total_pcm_frames=None,
                 encoding_function=None):
        """encodes a new file from PCM data

        takes a filename string, PCMReader object,
        optional compression level string and
        optional total_pcm_frames integer
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new AudioFile-compatible object

        may raise EncodingError if some problem occurs when
        encoding the input file.  This includes an error
        in the input stream, a problem writing the output file,
        or even an EncodingError subclass such as
        "UnsupportedBitsPerSample" if the input stream
        is formatted in a way this class is unable to support
        """

        from audiotools import (BufferedPCMReader,
                                CounterPCMReader,
                                transfer_data,
                                EncodingError)
        # from audiotools.py_encoders import encode_tta
        from audiotools.encoders import encode_tta
        from audiotools.bitstream import BitstreamWriter

        # open output file right away
        # so we can fail as soon as possible
        try:
            file = open(filename, "wb")
        except IOError as err:
            pcmreader.close()
            raise EncodingError(str(err))

        writer = BitstreamWriter(file, True)
        counter = CounterPCMReader(pcmreader)
        try:
            if (total_pcm_frames is not None):
                # write header to disk
                write_header(writer,
                             pcmreader.channels,
                             pcmreader.bits_per_sample,
                             pcmreader.sample_rate,
                             total_pcm_frames)

                block_size = (pcmreader.sample_rate * 256) // 245
                total_tta_frames = div_ceil(total_pcm_frames, block_size)

                # write temporary seektable to disk
                writer.mark()
                write_seektable(writer, [0] * total_tta_frames)
                writer.flush()

                # write frames to disk
                try:
                    frame_sizes = \
                        (encode_tta if encoding_function is None
                         else encoding_function)(file,
                                                 BufferedPCMReader(counter))
                except (IOError, ValueError) as err:
                    cls.__unlink__(filename)
                    raise EncodingError(str(err))

                # ensure written number of PCM frames
                # matches total_pcm_frames
                if (counter.frames_written != total_pcm_frames):
                    from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH
                    cls.__unlink__(filename)
                    raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH)

                assert(len(frame_sizes) == total_tta_frames)

                # go back and rewrite seektable with completed one
                writer.rewind()
                write_seektable(writer, frame_sizes)
                writer.unmark()
            else:
                import tempfile

                frames = tempfile.TemporaryFile()

                # encode TTA frames to temporary file
                try:
                    frame_sizes = \
                        (encode_tta if encoding_function is None
                         else encoding_function)(frames,
                                                 BufferedPCMReader(counter))
                except (IOError, ValueError) as err:
                    frames.close()
                    cls.__unlink__(filename)
                    raise EncodingError(str(err))

                # write header to disk
                write_header(writer,
                             pcmreader.channels,
                             pcmreader.bits_per_sample,
                             pcmreader.sample_rate,
                             counter.frames_written)

                # write seektable to disk
                write_seektable(writer, frame_sizes)

                # transfer TTA frames from temporary space to disk
                frames.seek(0, 0)
                transfer_data(frames.read, writer.write_bytes)
                frames.close()
        finally:
            counter.close()
            if (writer.has_mark()):
                writer.unmark()
            writer.close()

        return cls(filename)
    def add_replay_gain(cls, filenames, progress=None):
        """Adds ReplayGain values to a list of filename strings.

        All the filenames must be of this AudioFile type.
        Raises ValueError if some problem occurs during ReplayGain application.
        """

        from audiotools.replaygain import ReplayGain, ReplayGainReader
        import tempfile

        wave_files = [track for track in open_files(filenames) if
                      isinstance(track, cls)]

        track_gains = []
        total_frames = sum([track.total_frames() for track in wave_files]) * 2
        processed_frames = 0

        #first, calculate the Gain and Peak values from our files
        for original_wave in wave_files:
            try:
                rg = ReplayGain(original_wave.sample_rate())
            except ValueError:
                track_gains.append((None, None))
            pcm = original_wave.to_pcm()
            try:
                try:
                    frame = pcm.read(BUFFER_SIZE)
                    while (len(frame) > 0):
                        processed_frames += frame.frames
                        if (progress is not None):
                            progress(processed_frames, total_frames)
                        rg.update(frame)
                        frame = pcm.read(BUFFER_SIZE)
                    track_gains.append(rg.title_gain())
                except ValueError:
                    track_gains.append((None, None))
            finally:
                pcm.close()

        #then, apply those Gain and Peak values to our files
        #rewriting the originals in the process
        for (original_wave, (gain, peak)) in zip(wave_files, track_gains):
            if (gain is None):
                continue

            temp_wav_file = tempfile.NamedTemporaryFile(suffix=".wav")
            try:
                (header, footer) = original_wave.pcm_split()
                temp_wav_file.write(header)
                replaygain_pcm = ReplayGainReader(original_wave.to_pcm(),
                                                  gain, peak)
                frame = replaygain_pcm.read(BUFFER_SIZE)
                while (len(frame) > 0):
                    processed_frames += frame.frames
                    if (progress is not None):
                        progress(processed_frames, total_frames)
                    temp_wav_file.write(frame.to_bytes(
                            False,
                            original_wave.bits_per_sample() > 8))
                    frame = replaygain_pcm.read(BUFFER_SIZE)

                temp_wav_file.write(footer)
                temp_wav_file.seek(0, 0)
                new_wave = open(original_wave.filename, 'wb')
                transfer_data(temp_wav_file.read, new_wave.write)
                new_wave.close()
            finally:
                temp_wav_file.close()
    def from_pcm(cls,
                 filename,
                 pcmreader,
                 compression=None,
                 total_pcm_frames=None):
        """encodes a new file from PCM data

        takes a filename string, PCMReader object,
        optional compression level string and optional
        total_pcm_frames integer
        encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new M4AAudio object"""

        import subprocess
        import os
        from audiotools import PCMConverter
        from audiotools import transfer_data
        from audiotools import transfer_framelist_data
        from audiotools import ignore_sigint
        from audiotools import EncodingError
        from audiotools import DecodingError
        from audiotools import ChannelMask
        from audiotools import __default_quality__

        if ((compression is None)
                or (compression not in cls.COMPRESSION_MODES)):
            compression = __default_quality__(cls.NAME)

        if pcmreader.channels > 2:
            pcmreader = PCMConverter(pcmreader,
                                     sample_rate=pcmreader.sample_rate,
                                     channels=2,
                                     channel_mask=ChannelMask.from_channels(2),
                                     bits_per_sample=pcmreader.bits_per_sample)

        # faac requires files to end with .m4a for some reason
        if not filename.endswith(".m4a"):
            import tempfile
            actual_filename = filename
            tempfile = tempfile.NamedTemporaryFile(suffix=".m4a")
            filename = tempfile.name
        else:
            actual_filename = tempfile = None

        sub = subprocess.Popen(
            [
                BIN['faac'], "-q", compression, "-P", "-R",
                str(pcmreader.sample_rate), "-B",
                str(pcmreader.bits_per_sample), "-C",
                str(pcmreader.channels), "-X", "-o", filename, "-"
            ],
            stdin=subprocess.PIPE,
            stderr=subprocess.DEVNULL
            if hasattr(subprocess, "DEVNULL") else open(os.devnull, "wb"),
            stdout=subprocess.DEVNULL
            if hasattr(subprocess, "DEVNULL") else open(os.devnull, "wb"),
            preexec_fn=ignore_sigint)
        # Note: faac handles SIGINT on its own,
        # so trying to ignore it doesn't work like on most other encoders.

        try:
            if total_pcm_frames is not None:
                from audiotools import CounterPCMReader
                pcmreader = CounterPCMReader(pcmreader)

            transfer_framelist_data(pcmreader, sub.stdin.write)

            if ((total_pcm_frames is not None)
                    and (total_pcm_frames != pcmreader.frames_written)):
                from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH
                raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH)

        except (ValueError, IOError) as err:
            sub.stdin.close()
            sub.wait()
            cls.__unlink__(filename)
            raise EncodingError(str(err))
        except Exception:
            sub.stdin.close()
            sub.wait()
            cls.__unlink__(filename)
            raise

        sub.stdin.close()

        if sub.wait() == 0:
            if tempfile is not None:
                filename = actual_filename
                f = open(filename, 'wb')
                tempfile.seek(0, 0)
                transfer_data(tempfile.read, f.write)
                f.close()
                tempfile.close()

            return M4AAudio(filename)
        else:
            if tempfile is not None:
                tempfile.close()
            raise EncodingError(u"unable to write file with faac")
Example #50
0
    def update_metadata(self, metadata):
        """takes this track's current MetaData object
        as returned by get_metadata() and sets this track's metadata
        with any fields updated in that object

        raises IOError if unable to write the file
        """

        import os
        from audiotools.ape import ApeTag
        from audiotools.id3 import ID3v2Comment
        from audiotools.id3 import ID3CommentPair
        from audiotools.id3v1 import ID3v1Comment

        if (metadata is None):
            return
        elif (not os.access(self.filename, os.W_OK)):
            raise IOError(self.filename)

        # ensure metadata is APEv2, ID3v2, ID3v1, or ID3CommentPair
        if (((not isinstance(metadata, ApeTag)) and
             (not isinstance(metadata, ID3v2Comment)) and
             (not isinstance(metadata, ID3CommentPair)) and
             (not isinstance(metadata, ID3v1Comment)))):
            from audiotools.text import ERR_FOREIGN_METADATA
            raise ValueError(ERR_FOREIGN_METADATA)

        current_metadata = self.get_metadata()

        if (isinstance(metadata, ApeTag) and (current_metadata is None)):
            # if new metadata is APEv2 and no current metadata,
            # simply append APEv2 tag
            from audiotools.bitstream import BitstreamWriter
            with BitstreamWriter(open(self.filename, "ab"), True) as writer:
                metadata.build(writer)
        elif (isinstance(metadata, ApeTag) and
              isinstance(current_metadata, ApeTag) and
              (metadata.total_size() > current_metadata.total_size())):
            # if new metadata is APEv2, current metadata is APEv2
            # and new metadata is larger,
            # overwrite old tag with new tag
            from audiotools.bitstream import BitstreamWriter
            with open(self.filename, "r+b") as f:
                f.seek(-current_metadata.total_size(), 2)
                metadata.build(BitstreamWriter(f, True))
        else:
            from audiotools.bitstream import BitstreamWriter
            from audiotools import (transfer_data,
                                    LimitedFileReader,
                                    TemporaryFile)
            from audiotools.id3 import skip_id3v2_comment

            # otherwise, rebuild TTA with APEv2/ID3 tags in place
            old_tta = open(self.filename, "rb")
            skip_id3v2_comment(old_tta)
            old_tta = LimitedFileReader(old_tta, self.data_size())

            new_tta = TemporaryFile(self.filename)

            if (isinstance(metadata, ApeTag)):
                transfer_data(old_tta.read, new_tta.write)
                metadata.build(BitstreamWriter(new_tta, True))
            elif (isinstance(metadata, ID3CommentPair)):
                metadata.id3v2.build(BitstreamWriter(new_tta, False))
                transfer_data(old_tta.read, new_tta.write)
                metadata.id3v1.build(new_tta)
            elif (isinstance(metadata, ID3v2Comment)):
                metadata.build(BitstreamWriter(new_tta, False))
                transfer_data(old_tta.read, new_tta.write)
            else:
                # ID3v1Comment
                transfer_data(old_tta.read, new_tta.write)
                metadata.build(new_tta)

            old_tta.close()
            new_tta.close()
Example #51
0
    def update_metadata(self, metadata):
        if (metadata is None):
            return
        elif (not isinstance(metadata, ApeTag)):
            raise ValueError(_(u"metadata not from audio file"))

        from .bitstream import BitstreamReader, BitstreamWriter

        f = file(self.filename, "r+b")
        f.seek(-32, 2)

        (preamble,
         version,
         tag_size,
         item_count,
         read_only,
         item_encoding,
         is_header,
         no_footer,
         has_header) = BitstreamReader(f, 1).parse(ApeTag.HEADER_FORMAT)

        if ((preamble == 'APETAGEX') and (version == 2000)):
            if (has_header):
                old_tag_size = 32 + tag_size
            else:
                old_tag_size = tag_size

            if (metadata.total_size() >= old_tag_size):
                #metadata has grown
                #so append it to existing file
                f.seek(-old_tag_size, 2)
                metadata.build(BitstreamWriter(f, 1))
            else:
                #metadata has shrunk
                #so rewrite file with smaller metadata
                import tempfile
                from os.path import getsize
                rewritten = tempfile.TemporaryFile()

                #copy everything but the last "old_tag_size" bytes
                #from existing file to rewritten file
                f = open(self.filename, "rb")
                limited_transfer_data(f.read, rewritten.write,
                                      os.path.getsize(self.filename) -
                                      old_tag_size)
                f.close()

                #append new tag to rewritten file
                metadata.build(BitstreamWriter(rewritten, 1))

                #finally, overwrite current file with rewritten file
                rewritten.seek(0, 0)
                f = open(self.filename, "wb")
                transfer_data(rewritten.read, f.write)
                f.close()
                rewritten.close()
        else:
            #no existing metadata, so simply append a fresh tag
            f = file(self.filename, "ab")
            metadata.build(BitstreamWriter(f, 1))
            f.close()
    def update_metadata(self, metadata):
        """Takes this track's current MetaData object
        as returned by get_metadata() and sets this track's metadata
        with any fields updated in that object.

        Raises IOError if unable to write the file.
        """

        from struct import pack

        if (metadata is None):
            return
        elif (not isinstance(metadata, ID3v22Comment)):
            raise _(u"metadata not from audio file")

        def chunk_filter(chunks, id3_chunk):
            id3_found = False

            for (chunk_id, chunk_size, chunk_data) in chunks:
                if (chunk_id == 'ID3 '):
                    if (len(id3_chunk) % 2):
                        yield (chunk_id,
                               pack(">I", len(id3_chunk)),
                               id3_chunk + chr(0))
                    else:
                        yield (chunk_id,
                               pack(">I", len(id3_chunk)),
                               id3_chunk)
                    id3_found = True
                else:
                    yield (chunk_id, chunk_size, chunk_data)
            else:
                if (not id3_found):
                    if (len(id3_chunk) % 2):
                        yield ('ID3 ',
                               pack(">I", len(id3_chunk)),
                               id3_chunk + chr(0))
                    else:
                        yield ('ID3 ',
                               pack(">I", len(id3_chunk)),
                               id3_chunk)

        import tempfile
        from .bitstream import BitstreamRecorder

        #turn our ID3v2.2 tag into a raw binary chunk
        id3_chunk = BitstreamRecorder(0)
        metadata.build(id3_chunk)
        id3_chunk = id3_chunk.data()

        #generate a temporary AIFF file in which our new ID3v2.2 chunk
        #replaces the existing ID3v2.2 chunk
        new_aiff = tempfile.NamedTemporaryFile(suffix=self.SUFFIX)
        self.__class__.aiff_from_chunks(new_aiff.name,
                                        chunk_filter(self.chunks(),
                                                     id3_chunk))

        #replace the existing file with data from the temporary file
        new_file = open(new_aiff.name, 'rb')
        old_file = open(self.filename, 'wb')
        transfer_data(new_file.read, old_file.write)
        old_file.close()
        new_file.close()
    def to_pcm(self):
        """Returns a PCMReader object containing the track's PCM data."""

        #if mpg123 is available, use that for decoding
        if (BIN.can_execute(BIN["mpg123"])):
            sub = subprocess.Popen([BIN["mpg123"], "-qs", self.filename],
                                   stdout=subprocess.PIPE,
                                   stderr=file(os.devnull, "a"))
            return PCMReader(sub.stdout,
                             sample_rate=self.sample_rate(),
                             channels=self.channels(),
                             bits_per_sample=16,
                             channel_mask=int(ChannelMask.from_channels(
                        self.channels())),
                             process=sub,
                             big_endian=BIG_ENDIAN)
        else:
            #if not, use LAME for decoding
            if (self.filename.endswith("." + self.SUFFIX)):
                if (BIG_ENDIAN):
                    endian = ['-x']
                else:
                    endian = []

                sub = subprocess.Popen([BIN['lame']] + endian + \
                                           ["--decode", "-t", "--quiet",
                                            self.filename, "-"],
                                       stdout=subprocess.PIPE)
                return PCMReader(
                    sub.stdout,
                    sample_rate=self.sample_rate(),
                    channels=self.channels(),
                    bits_per_sample=16,
                    channel_mask=int(self.channel_mask()),
                    process=sub)
            else:
                import tempfile
                from audiotools import TempWaveReader
                #copy our file to one that ends with .mp3
                tempmp3 = tempfile.NamedTemporaryFile(suffix='.' + self.SUFFIX)
                f = open(self.filename, 'rb')
                transfer_data(f.read, tempmp3.write)
                f.close()
                tempmp3.flush()

                #decode the mp3 file to a WAVE file
                wave = tempfile.NamedTemporaryFile(suffix='.wav')
                returnval = subprocess.call([BIN['lame'], "--decode",
                                             "--quiet",
                                             tempmp3.name, wave.name])
                tempmp3.close()

                if (returnval == 0):
                    #return WAVE file as a stream
                    wave.seek(0, 0)
                    return TempWaveReader(wave)
                else:
                    return PCMReaderError(
                        error_message=u"lame exited with error",
                        sample_rate=self.sample_rate(),
                        channels=self.channels(),
                        channel_mask=int(self.channel_mask()),
                        bits_per_sample=16)