Beispiel #1
0
class ID3v1Comment(MetaData, list):
    """A complete ID3v1 tag."""

    ID3v1 = Con.Struct("id3v1", Con.Const(Con.String("identifier", 3), 'TAG'),
                       Con.String("song_title", 30), Con.String("artist", 30),
                       Con.String("album", 30), Con.String("year", 4),
                       Con.String("comment", 28), Con.Padding(1),
                       Con.Byte("track_number"), Con.Byte("genre"))

    ID3v1_NO_TRACKNUMBER = Con.Struct(
        "id3v1_notracknumber", Con.Const(Con.String("identifier", 3), 'TAG'),
        Con.String("song_title", 30), Con.String("artist", 30),
        Con.String("album", 30), Con.String("year", 4),
        Con.String("comment", 30), Con.Byte("genre"))

    ATTRIBUTES = [
        'track_name', 'artist_name', 'album_name', 'year', 'comment',
        'track_number'
    ]

    @classmethod
    def read_id3v1_comment(cls, mp3filename):
        """Reads a ID3v1Comment data from an MP3 filename.

        Returns a (song title, artist, album, year, comment, track number)
        tuple.
        If no ID3v1 tag is present, returns a tuple with those fields blank.
        All text is in unicode.
        If track number is -1, the id3v1 comment could not be found.
        """

        mp3file = file(mp3filename, "rb")
        try:
            mp3file.seek(-128, 2)
            try:
                id3v1 = ID3v1Comment.ID3v1.parse(mp3file.read())
            except Con.adapters.PaddingError:
                mp3file.seek(-128, 2)
                id3v1 = ID3v1Comment.ID3v1_NO_TRACKNUMBER.parse(mp3file.read())
                id3v1.track_number = 0
            except Con.ConstError:
                return tuple([u""] * 5 + [-1])

            field_list = (id3v1.song_title, id3v1.artist, id3v1.album,
                          id3v1.year, id3v1.comment)

            return tuple(
                map(lambda t: t.rstrip('\x00').decode('ascii', 'replace'),
                    field_list) + [id3v1.track_number])
        finally:
            mp3file.close()

    @classmethod
    def build_id3v1(cls, song_title, artist, album, year, comment,
                    track_number):
        """Turns fields into a complete ID3v1 binary tag string.

        All fields are unicode except for track_number, an int."""
        def __s_pad__(s, length):
            if (len(s) < length):
                return s + chr(0) * (length - len(s))
            else:
                s = s[0:length].rstrip()
                return s + chr(0) * (length - len(s))

        c = Con.Container()
        c.identifier = 'TAG'
        c.song_title = __s_pad__(song_title.encode('ascii', 'replace'), 30)
        c.artist = __s_pad__(artist.encode('ascii', 'replace'), 30)
        c.album = __s_pad__(album.encode('ascii', 'replace'), 30)
        c.year = __s_pad__(year.encode('ascii', 'replace'), 4)
        c.comment = __s_pad__(comment.encode('ascii', 'replace'), 28)
        c.track_number = int(track_number)
        c.genre = 0

        return ID3v1Comment.ID3v1.build(c)

    def __init__(self, metadata):
        """Initialized with a read_id3v1_comment tuple.

        Fields are (title,artist,album,year,comment,tracknum)"""

        list.__init__(self, metadata)

    def supports_images(self):
        """Returns False."""

        return False

    #if an attribute is updated (e.g. self.track_name)
    #make sure to update the corresponding list item
    def __setattr__(self, key, value):
        if (key in self.ATTRIBUTES):
            if (key != 'track_number'):
                self[self.ATTRIBUTES.index(key)] = value
            else:
                self[self.ATTRIBUTES.index(key)] = int(value)
        elif (key in MetaData.__FIELDS__):
            pass
        else:
            self.__dict__[key] = value

    def __delattr__(self, key):
        if (key == 'track_number'):
            setattr(self, key, 0)
        elif (key in self.ATTRIBUTES):
            setattr(self, key, u"")

    def __getattr__(self, key):
        if (key in self.ATTRIBUTES):
            return self[self.ATTRIBUTES.index(key)]
        elif (key in MetaData.__INTEGER_FIELDS__):
            return 0
        elif (key in MetaData.__FIELDS__):
            return u""
        else:
            raise AttributeError(key)

    @classmethod
    def converted(cls, metadata):
        """Converts a MetaData object to an ID3v1Comment object."""

        if ((metadata is None) or (isinstance(metadata, ID3v1Comment))):
            return metadata

        return ID3v1Comment(
            (metadata.track_name, metadata.artist_name, metadata.album_name,
             metadata.year, metadata.comment, int(metadata.track_number)))

    def __comment_name__(self):
        return u'ID3v1'

    def __comment_pairs__(self):
        return zip(('Title', 'Artist', 'Album', 'Year', 'Comment', 'Tracknum'),
                   self)

    def build_tag(self):
        """Returns a binary string of this tag's data."""

        return self.build_id3v1(self.track_name, self.artist_name,
                                self.album_name, self.year, self.comment,
                                self.track_number)

    def images(self):
        """Returns an empty list of Image objects."""

        return []
        stream.write(data)

    def _sizeof(self, context):
        return self.sub_atom.sizeof(context) + 8


ATOM_FTYP = Con.Struct("ftyp", Con.String("major_brand", 4),
                       Con.UBInt32("major_brand_version"),
                       Con.GreedyRepeater(Con.String("compatible_brands", 4)))

ATOM_MVHD = Con.Struct(
    "mvhd", Con.Byte("version"), Con.String("flags", 3),
    VersionLength("created_mac_UTC_date"),
    VersionLength("modified_mac_UTC_date"), Con.UBInt32("time_scale"),
    VersionLength("duration"), Con.UBInt32("playback_speed"),
    Con.UBInt16("user_volume"), Con.Padding(10),
    Con.Struct("windows", Con.UBInt32("geometry_matrix_a"),
               Con.UBInt32("geometry_matrix_b"),
               Con.UBInt32("geometry_matrix_u"),
               Con.UBInt32("geometry_matrix_c"),
               Con.UBInt32("geometry_matrix_d"),
               Con.UBInt32("geometry_matrix_v"),
               Con.UBInt32("geometry_matrix_x"),
               Con.UBInt32("geometry_matrix_y"),
               Con.UBInt32("geometry_matrix_w")),
    Con.UBInt64("quicktime_preview"), Con.UBInt32("quicktime_still_poster"),
    Con.UBInt64("quicktime_selection_time"),
    Con.UBInt32("quicktime_current_time"), Con.UBInt32("next_track_id"))

ATOM_IODS = Con.Struct(
    "iods", Con.Byte("version"), Con.String("flags", 3), Con.Byte("type_tag"),