Exemple #1
0
    def handleFile(self, f):
        parse_version = self.args.tag_version

        super(ClassicPlugin, self).handleFile(f, tag_version=parse_version)

        if not self.audio_file:
            return

        try:
            self.printHeader(f)
            printMsg("-" * 79)

            new_tag = False
            if (not self.audio_file.tag
                    or self.handleRemoves(self.audio_file.tag)):
                # No tag, but there might be edit options coming.
                self.audio_file.tag = id3.Tag()
                self.audio_file.tag.file_info = id3.FileInfo(f)
                self.audio_file.tag.version = parse_version
                new_tag = True

            save_tag = (self.handleEdits(self.audio_file.tag)
                        or self.args.force_update or self.args.convert_version)

            self.printAudioInfo(self.audio_file.info)

            if not save_tag and new_tag:
                printError("No ID3 %s tag found!" %
                           id3.versionToString(self.args.tag_version))
                return

            self.printTag(self.audio_file.tag)

            if save_tag:
                # Use current tag version unless a convert was supplied
                version = (self.args.convert_version
                           or self.audio_file.tag.version)
                printWarning("Writing ID3 version %s" %
                             id3.versionToString(version))

                self.audio_file.tag.save(version=version,
                                         encoding=self.args.text_encoding,
                                         backup=self.args.backup)

            if self.args.rename_pattern:
                # Handle file renaming.
                from eyed3.id3.tag import TagTemplate
                template = TagTemplate(self.args.rename_pattern)
                name = template.substitute(self.audio_file.tag, zeropad=True)
                orig = self.audio_file.path
                self.audio_file.rename(name)
                printWarning("Renamed '%s' to '%s'" %
                             (orig, self.audio_file.path))
            printMsg("-" * 79)
        except exceptions.Exception as ex:
            log.error(traceback.format_exc())
            if self.args.debug_pdb:
                import pdb
                pdb.set_trace()
            raise StopIteration()
Exemple #2
0
    def handleFile(self, f):
        parse_version = self.args.tag_version

        super(ClassicPlugin, self).handleFile(f, tag_version=parse_version)

        if not self.audio_file:
            return

        try:
            self.printHeader(f)
            printMsg("-" * 79)

            new_tag = False
            if (not self.audio_file.tag or
                    self.handleRemoves(self.audio_file.tag)):
                # No tag, but there might be edit options coming.
                self.audio_file.tag = id3.Tag()
                self.audio_file.tag.file_info = id3.FileInfo(f)
                self.audio_file.tag.version = parse_version
                new_tag = True

            save_tag = (self.handleEdits(self.audio_file.tag) or
                        self.args.force_update or self.args.convert_version)

            self.printAudioInfo(self.audio_file.info)

            if not save_tag and new_tag:
                printError("No ID3 %s tag found!" %
                           id3.versionToString(self.args.tag_version))
                return

            self.printTag(self.audio_file.tag)

            if save_tag:
                # Use current tag version unless a convert was supplied
                version = (self.args.convert_version or
                           self.audio_file.tag.version)
                printWarning("Writing ID3 version %s" %
                             id3.versionToString(version))

                self.audio_file.tag.save(version=version,
                                         encoding=self.args.text_encoding,
                                         backup=self.args.backup)

            if self.args.rename_pattern:
                # Handle file renaming.
                from eyed3.id3.tag import TagTemplate
                template = TagTemplate(self.args.rename_pattern)
                name = template.substitute(self.audio_file.tag, zeropad=True)
                orig = self.audio_file.path
                self.audio_file.rename(name)
                printWarning("Renamed '%s' to '%s'" % (orig,
                                                       self.audio_file.path))
            printMsg("-" * 79)
        except exceptions.Exception as ex:
            printError("Error: %s" % ex)
            log.error(traceback.format_exc())
            if self.args.debug_pdb:
                import pdb; pdb.set_trace()
            raise StopIteration()
Exemple #3
0
    def handleRemoves(self, tag):
        remove_version = 0
        status = False
        rm_str = ""
        if self.args.remove_all:
            remove_version = id3.ID3_ANY_VERSION
            rm_str = "v1.x and/or v2.x"
        elif self.args.remove_v1:
            remove_version = id3.ID3_V1
            rm_str = "v1.x"
        elif self.args.remove_v2:
            remove_version = id3.ID3_V2
            rm_str = "v2.x"

        if remove_version:
            status = id3.Tag.remove(tag.file_info.name, remove_version)
            printWarning("Removing ID3 %s tag: %s" %
                         (rm_str, "SUCCESS" if status else "FAIL"))

        return status
Exemple #4
0
    def handleRemoves(self, tag):
        remove_version = 0
        status = False
        rm_str = ""
        if self.args.remove_all:
            remove_version = id3.ID3_ANY_VERSION
            rm_str = "v1.x and/or v2.x"
        elif self.args.remove_v1:
            remove_version = id3.ID3_V1
            rm_str = "v1.x"
        elif self.args.remove_v2:
            remove_version = id3.ID3_V2
            rm_str = "v2.x"

        if remove_version:
            status = id3.Tag.remove(tag.file_info.name, remove_version)
            printWarning("Removing ID3 %s tag: %s" %
                         (rm_str, "SUCCESS" if status else "FAIL"))

        return status
Exemple #5
0
    def handleEdits(self, tag):
        retval = False

        # --remove-all-*, Handling removes first means later options are still
        # applied
        for what, arg, fid in (("comments", self.args.remove_all_comments,
                                id3.frames.COMMENT_FID),
                               ("lyrics", self.args.remove_all_lyrics,
                                id3.frames.LYRICS_FID),
                               ("images", self.args.remove_all_images,
                                id3.frames.IMAGE_FID),
                               ("objects", self.args.remove_all_objects,
                                id3.frames.OBJECT_FID),
                               ):
            if arg and tag.frame_set[fid]:
                printWarning("Removing all %s..." % what)
                del tag.frame_set[fid]
                retval = True

        # --artist, --title, etc. All common/simple text frames.
        for (what, arg, setFunc) in (
                ("artist", self.args.artist, tag._setArtist),
                ("album", self.args.album, tag._setAlbum),
                ("title", self.args.title, tag._setTitle),
                ("genre", self.args.genre, tag._setGenre),
                ("release date", self.args.release_date, tag._setReleaseDate),
                ("original release date", self.args.orig_release_date,
                 tag._setOrigReleaseDate),
                ("recording date", self.args.recording_date,
                 tag._setRecordingDate),
                ("encoding date", self.args.encoding_date,
                 tag._setEncodingDate),
                ("tagging date", self.args.tagging_date,
                 tag._setTaggingDate),
                ("beats per minute", self.args.bpm, tag._setBpm),
                ("publisher", self.args.publisher, tag._setPublisher),
            ):
            if arg is not None:
                printWarning("Setting %s: %s" % (what, arg))
                setFunc(arg or None)
                retval = True

        # --track, --track-total
        track_num = self.args.track
        track_total = self.args.track_total
        if (track_num, track_total) != (None, None):
            track_info = (track_num or tag.track_num[0],
                          track_total or tag.track_num[1])

            printWarning("Setting track info: %s" % str(track_info))
            tag.track_num = track_info
            retval = True

        # -Y, --release-year
        if self.args.release_year is not None:
            # empty string means clean, None means not given
            year = self.args.release_year
            printWarning("Setting release year: %s" % year)
            tag.release_date = int(year) if year else None
            retval = True

        # -c , simple comment
        if self.args.simple_comment:
            # Just add it as if it came in --add-comment
            self.args.comments.append((self.args.simple_comment, u"",
                                       id3.DEFAULT_LANG))

        # --remove-comment, remove-lyrics, --remove-image, --remove-object
        for what, arg, accessor in (("comment", self.args.remove_comment,
                                     tag.comments),
                                    ("lyrics", self.args.remove_lyrics,
                                     tag.lyrics),
                                    ("image", self.args.remove_image,
                                     tag.images),
                                    ("object", self.args.remove_object,
                                     tag.objects),
                                   ):
            for vals in arg:
                frame = accessor.remove(*vals)
                if frame:
                    printWarning("Removed %s %s" % (what, str(vals)))
                    retval = True
                else:
                    printError("Removing %s failed, %s not found" %
                               (what, str(vals)))

        # --add-comment, --add-lyrics
        for what, arg, accessor in (("comment", self.args.comments,
                                     tag.comments),
                                    ("lyrics", self.args.lyrics, tag.lyrics),
                                   ):
            for text, desc, lang in arg:
                printWarning("Setting %s: %s/%s" % (what, desc, lang))
                accessor.set(text, desc, lang)
                retval = True

        # --play-count
        playcount_arg = self.args.play_count
        if playcount_arg:
            increment, pc = playcount_arg
            if increment:
                printWarning("Incrementing play count by %d" % pc)
                tag.play_count += pc
            else:
                printWarning("Setting play count to %d" % pc)
                tag.play_count = pc
            retval = True

        # --add-popularty
        for email, rating, play_count in self.args.popularities:
            tag.popularities.set(email, rating, play_count)
            retval = True

        # --remove-popularity
        for email in self.args.remove_popularity:
            popm = tag.popularities.remove(email)
            if popm:
                retval = True

        # --text-frame, --url-frame
        for what, arg, setter in (
                ("text frame", self.args.text_frames, tag.setTextFrame),
                ("url frame", self.args.url_frames, tag._setUrlFrame),
            ):
            for fid, text in arg:
                if text:
                    printWarning("Setting %s %s to '%s'" % (fid, what, text))
                else:
                    printWarning("Removing %s %s" % (fid, what))
                setter(fid, text)
                retval = True

        # --user-text-frame, --user-url-frame
        for what, arg, accessor in (
                ("user text frame", self.args.user_text_frames,
                 tag.user_text_frames),
                ("user url frame", self.args.user_url_frames,
                 tag.user_url_frames),
            ):
            for desc, text in arg:
                if text:
                    printWarning("Setting '%s' %s to '%s'" % (desc, what, text))
                    accessor.set(text, desc)
                else:
                    printWarning("Removing '%s' %s" % (desc, what))
                    accessor.remove(desc)
                retval = True

        # --add-image
        for img_path, img_type, img_mt, img_desc in self.args.images:
            assert(img_path)
            printWarning("Adding image %s" % img_path)
            if img_mt != ImageFrame.URL_MIME_TYPE:
                with open(img_path, "rb") as img_fp:
                    tag.images.set(img_type, img_fp.read(), img_mt, img_desc)
            else:
                tag.images.set(img_type, None, None, img_desc, img_url=img_path)
            retval = True

        # --add-object
        for obj_path, obj_mt, obj_desc, obj_fname in self.args.objects or []:
            assert(obj_path)
            printWarning("Adding object %s" % obj_path)
            with open(obj_path, "rb") as obj_fp:
                tag.objects.set(obj_fp.read(), obj_mt, obj_desc, obj_fname)
            retval = True

        # --unique-file-id
        for arg in self.args.unique_file_ids:
            owner_id, id = arg
            if not id:
                if tag.unique_file_ids.remove(owner_id):
                    printWarning("Removed unique file ID '%s'" % owner_id)
                    retval = True
                else:
                    printWarning("Unique file ID '%s' not found" % owner_id)
            else:
                tag.unique_file_ids.set(id, owner_id)
                printWarning("Setting unique file ID '%s' to %s" %
                              (owner_id, id))
                retval = True

        # --remove-frame
        for fid in self.args.remove_fids:
            if fid in tag.frame_set:
                del tag.frame_set[fid]
                retval = True

        return retval
Exemple #6
0
    def printTag(self, tag):
        if isinstance(tag, id3.Tag):
            if self.args.quiet:
                printMsg("ID3 %s: %d frames" %
                         (id3.versionToString(tag.version),
                          len(tag.frame_set)))
                return

            printMsg("ID3 %s:" % id3.versionToString(tag.version))
            artist = tag.artist if tag.artist else u""
            title = tag.title if tag.title else u""
            album = tag.album if tag.album else u""
            printMsg("%s: %s" % (boldText("title"), title))
            printMsg("%s: %s" % (boldText("artist"), artist))
            printMsg("%s: %s" % (boldText("album"), album))

            for date, date_label in [
                    (tag.release_date, "release date"),
                    (tag.original_release_date, "original release date"),
                    (tag.recording_date, "recording date"),
                    (tag.encoding_date, "encoding date"),
                    (tag.tagging_date, "tagging date"),
                    ]:
                if date:
                    printMsg("%s: %s" % (boldText(date_label), str(date)))

            track_str = ""
            (track_num, track_total) = tag.track_num
            if track_num != None:
                track_str = str(track_num)
                if track_total:
                    track_str += "/%d" % track_total

            genre = tag.genre
            genre_str = "%s: %s (id %s)" % (boldText("genre"),
                                            genre.name,
                                            str(genre.id)) if genre else u""
            printMsg("%s: %s\t\t%s" % (boldText("track"), track_str, genre_str))

            # PCNT
            play_count = tag.play_count
            if tag.play_count is not None:
                 printMsg("%s %d" % (boldText("Play Count:"), play_count))

            # POPM
            for popm in tag.popularities:
                printMsg("%s [email: %s] [rating: %d] [play count: %d]" %
                         (boldText("Popularity:"), popm.email, popm.rating,
                          popm.count))

            # TBPM
            bpm = tag.bpm
            if bpm is not None:
                 printMsg("%s %d" % (boldText("BPM:"), bpm))

            # TPUB
            pub = tag.publisher
            if pub is not None:
                 printMsg("%s %s" % (boldText("Publisher/label:"), pub))

            # UFID
            for ufid in tag.unique_file_ids:
                printMsg("%s [%s] : %s" % \
                        (boldText("Unique File ID:"), ufid.owner_id,
                         ufid.uniq_id.encode("string_escape")))

            # COMM
            for c in tag.comments:
                printMsg("%s: [Description: %s] [Lang: %s]\n%s" %
                         (boldText("Comment"), c.description or "",
                          c.lang or "", c.text or ""))

            # USLT
            for l in tag.lyrics:
                printMsg("%s: [Description: %s] [Lang: %s]\n%s" %
                         (boldText("Lyrics"), l.description or u"",
                          l.lang or "", l.text))

            # TXXX
            for f in tag.user_text_frames:
                printMsg("%s: [Description: %s]\n%s" %
                         (boldText("UserTextFrame"), f.description, f.text))

            # URL frames
            for desc, url in ( ("Artist URL", tag.artist_url),
                               ("Audio source URL", tag.audio_source_url),
                               ("Audio file URL", tag.audio_file_url),
                               ("Internet radio URL", tag.internet_radio_url),
                               ("Commercial URL", tag.commercial_url),
                               ("Payment URL", tag.payment_url),
                               ("Publisher URL", tag.publisher_url),
                               ("Copyright URL", tag.copyright_url),
                             ):
                if url:
                    printMsg("%s: %s" % (boldText(desc), url))


            # user url frames
            for u in tag.user_url_frames:
                printMsg("%s [Description: %s]: %s" % (u.id, u.description,
                                                       u.url))

            # APIC
            for img in tag.images:
                if img.mime_type != ImageFrame.URL_MIME_TYPE:
                    printMsg("%s: [Size: %d bytes] [Type: %s]" %
                        (boldText(img.picTypeToString(img.picture_type) +
                                  " Image"),
                        len(img.image_data),
                        img.mime_type))
                    printMsg("Description: %s" % img.description)
                    printMsg("")
                    if self.args.write_images_dir:
                        img_path = "%s%s" % (self.args.write_images_dir, os.sep)
                        if not os.path.isdir(img_path):
                            raise IOError("Directory does not exist: %s" %
                                          img_path)
                        img_file = self._getDefaultNameForImage(img)
                        count = 1
                        while os.path.exists(os.path.join(img_path, img_file)):
                            img_file = self._getDefaultNameForImage(img,
                                                                    str(count))
                            count += 1
                        printWarning("Writing %s..." % os.path.join(img_path,
                                                                    img_file))
                        with open(os.path.join(img_path, img_file), "wb") as fp:
                            fp.write(img.image_data)
                else:
                    printMsg("%s: [Type: %s] [URL: %s]" %
                        (boldText(img.picTypeToString(img.picture_type) +
                                  " Image"),
                        img.mime_type, img.image_url))
                    printMsg("Description: %s" % img.description)
                    printMsg("")

            # GOBJ
            for obj in tag.objects:
                printMsg("%s: [Size: %d bytes] [Type: %s]" %
                         (boldText("GEOB"), len(obj.object_data),
                          obj.mime_type))
                printMsg("Description: %s" % obj.description)
                printMsg("Filename: %s" % obj.filename)
                printMsg("\n")
                if self.args.write_objects_dir:
                    obj_path = "%s%s" % (self.args.write_objects_dir, os.sep)
                    if not os.path.isdir(obj_path):
                        raise IOError("Directory does not exist: %s" % obj_path)
                    obj_file = self._getDefaultNameForObject(obj)
                    count = 1
                    while os.path.exists(os.path.join(obj_path, obj_file)):
                        obj_file = self._getDefaultNameForObject(obj,
                                                                 str(count))
                        count += 1
                    printWarning("Writing %s..." % os.path.join(obj_path,
                                                                obj_file))
                    with open(os.path.join(obj_path, obj_file), "wb") as fp:
                        fp.write(obj.object_data)

            # PRIV
            for p in tag.privates:
                printMsg("%s: [Data: %d bytes]" % (boldText("PRIV"),
                                                   len(p.data)))
                printMsg("Owner Id: %s" % p.owner_id)

            # MCDI
            if tag.cd_id:
                printMsg("\n%s: [Data: %d bytes]" % (boldText("MCDI"),
                                                     len(tag.cd_id)))

            # USER
            if tag.terms_of_use:
                printMsg("\nTerms of Use (%s): %s" % (boldText("USER"),
                                                      tag.terms_of_use))

            if self.args.verbose:
                printMsg("-" * 79)
                printMsg("%d ID3 Frames:" % len(tag.frame_set))
                for fid in tag.frame_set:
                    num_frames = len(tag.frame_set[fid])
                    count = " x %d" % num_frames if num_frames > 1 else ""
                    printMsg("%s%s" % (fid, count))
        else:
            raise TypeError("Unknown tag type: " + str(type(tag)))
Exemple #7
0
    def handleEdits(self, tag):
        retval = False

        # --remove-all-*, Handling removes first means later options are still
        # applied
        for what, arg, fid in (
            ("comments", self.args.remove_all_comments,
             id3.frames.COMMENT_FID),
            ("lyrics", self.args.remove_all_lyrics, id3.frames.LYRICS_FID),
            ("images", self.args.remove_all_images, id3.frames.IMAGE_FID),
            ("objects", self.args.remove_all_objects, id3.frames.OBJECT_FID),
        ):
            if arg and tag.frame_set[fid]:
                printWarning("Removing all %s..." % what)
                del tag.frame_set[fid]
                retval = True

        # --artist, --title, etc. All common/simple text frames.
        for (what, arg, setFunc) in (
            ("artist", self.args.artist, tag._setArtist),
            ("album", self.args.album, tag._setAlbum),
            ("title", self.args.title, tag._setTitle),
            ("genre", self.args.genre, tag._setGenre),
            ("release date", self.args.release_date, tag._setReleaseDate),
            ("original release date", self.args.orig_release_date,
             tag._setOrigReleaseDate),
            ("recording date", self.args.recording_date,
             tag._setRecordingDate),
            ("encoding date", self.args.encoding_date, tag._setEncodingDate),
            ("tagging date", self.args.tagging_date, tag._setTaggingDate),
            ("beats per minute", self.args.bpm, tag._setBpm),
            ("publisher", self.args.publisher, tag._setPublisher),
        ):
            if arg is not None:
                printWarning("Setting %s: %s" % (what, arg))
                setFunc(arg or None)
                retval = True

        # --track, --track-total
        track_num = self.args.track
        track_total = self.args.track_total
        if (track_num, track_total) != (None, None):
            track_info = (track_num or tag.track_num[0], track_total
                          or tag.track_num[1])

            printWarning("Setting track info: %s" % str(track_info))
            tag.track_num = track_info
            retval = True

        # -Y, --release-year
        if self.args.release_year is not None:
            # empty string means clean, None means not given
            year = self.args.release_year
            printWarning("Setting release year: %s" % year)
            tag.release_date = int(year) if year else None
            retval = True

        # -c , simple comment
        if self.args.simple_comment:
            # Just add it as if it came in --add-comment
            self.args.comments.append(
                (self.args.simple_comment, u"", id3.DEFAULT_LANG))

        # --remove-comment, remove-lyrics, --remove-image, --remove-object
        for what, arg, accessor in (
            ("comment", self.args.remove_comment, tag.comments),
            ("lyrics", self.args.remove_lyrics, tag.lyrics),
            ("image", self.args.remove_image, tag.images),
            ("object", self.args.remove_object, tag.objects),
        ):
            for vals in arg:
                frame = accessor.remove(*vals)
                if frame:
                    printWarning("Removed %s %s" % (what, str(vals)))
                    retval = True
                else:
                    printError("Removing %s failed, %s not found" %
                               (what, str(vals)))

        # --add-comment, --add-lyrics
        for what, arg, accessor in (
            ("comment", self.args.comments, tag.comments),
            ("lyrics", self.args.lyrics, tag.lyrics),
        ):
            for text, desc, lang in arg:
                printWarning("Setting %s: %s/%s" % (what, desc, lang))
                accessor.set(text, desc, lang)
                retval = True

        # --play-count
        playcount_arg = self.args.play_count
        if playcount_arg:
            increment, pc = playcount_arg
            if increment:
                printWarning("Incrementing play count by %d" % pc)
                tag.play_count += pc
            else:
                printWarning("Setting play count to %d" % pc)
                tag.play_count = pc
            retval = True

        # --add-popularty
        for email, rating, play_count in self.args.popularities:
            tag.popularities.set(email, rating, play_count)
            retval = True

        # --remove-popularity
        for email in self.args.remove_popularity:
            popm = tag.popularities.remove(email)
            if popm:
                retval = True

        # --text-frame, --url-frame
        for what, arg, setter in (
            ("text frame", self.args.text_frames, tag.setTextFrame),
            ("url frame", self.args.url_frames, tag._setUrlFrame),
        ):
            for fid, text in arg:
                if text:
                    printWarning("Setting %s %s to '%s'" % (fid, what, text))
                else:
                    printWarning("Removing %s %s" % (fid, what))
                setter(fid, text)
                retval = True

        # --user-text-frame, --user-url-frame
        for what, arg, accessor in (
            ("user text frame", self.args.user_text_frames,
             tag.user_text_frames),
            ("user url frame", self.args.user_url_frames, tag.user_url_frames),
        ):
            for desc, text in arg:
                if text:
                    printWarning("Setting '%s' %s to '%s'" %
                                 (desc, what, text))
                    accessor.set(text, desc)
                else:
                    printWarning("Removing '%s' %s" % (desc, what))
                    accessor.remove(desc)
                retval = True

        # --add-image
        for img_path, img_type, img_mt, img_desc in self.args.images:
            assert (img_path)
            printWarning("Adding image %s" % img_path)
            if img_mt != ImageFrame.URL_MIME_TYPE:
                with open(img_path, "rb") as img_fp:
                    tag.images.set(img_type, img_fp.read(), img_mt, img_desc)
            else:
                tag.images.set(img_type,
                               None,
                               None,
                               img_desc,
                               img_url=img_path)
            retval = True

        # --add-object
        for obj_path, obj_mt, obj_desc, obj_fname in self.args.objects or []:
            assert (obj_path)
            printWarning("Adding object %s" % obj_path)
            with open(obj_path, "rb") as obj_fp:
                tag.objects.set(obj_fp.read(), obj_mt, obj_desc, obj_fname)
            retval = True

        # --unique-file-id
        for arg in self.args.unique_file_ids:
            owner_id, id = arg
            if not id:
                if tag.unique_file_ids.remove(owner_id):
                    printWarning("Removed unique file ID '%s'" % owner_id)
                    retval = True
                else:
                    printWarning("Unique file ID '%s' not found" % owner_id)
            else:
                tag.unique_file_ids.set(id, owner_id)
                printWarning("Setting unique file ID '%s' to %s" %
                             (owner_id, id))
                retval = True

        # --remove-frame
        for fid in self.args.remove_fids:
            if fid in tag.frame_set:
                del tag.frame_set[fid]
                retval = True

        return retval
Exemple #8
0
    def printTag(self, tag):
        if isinstance(tag, id3.Tag):
            if self.args.quiet:
                printMsg(
                    "ID3 %s: %d frames" %
                    (id3.versionToString(tag.version), len(tag.frame_set)))
                return

            printMsg("ID3 %s:" % id3.versionToString(tag.version))
            artist = tag.artist if tag.artist else u""
            title = tag.title if tag.title else u""
            album = tag.album if tag.album else u""
            printMsg("%s: %s" % (boldText("title"), title))
            printMsg("%s: %s" % (boldText("artist"), artist))
            printMsg("%s: %s" % (boldText("album"), album))

            for date, date_label in [
                (tag.release_date, "release date"),
                (tag.original_release_date, "original release date"),
                (tag.recording_date, "recording date"),
                (tag.encoding_date, "encoding date"),
                (tag.tagging_date, "tagging date"),
            ]:
                if date:
                    printMsg("%s: %s" % (boldText(date_label), str(date)))

            track_str = ""
            (track_num, track_total) = tag.track_num
            if track_num != None:
                track_str = str(track_num)
                if track_total:
                    track_str += "/%d" % track_total

            genre = tag.genre
            genre_str = "%s: %s (id %s)" % (boldText("genre"), genre.name,
                                            str(genre.id)) if genre else u""
            printMsg("%s: %s\t\t%s" %
                     (boldText("track"), track_str, genre_str))

            # PCNT
            play_count = tag.play_count
            if tag.play_count is not None:
                printMsg("%s %d" % (boldText("Play Count:"), play_count))

            # POPM
            for popm in tag.popularities:
                printMsg("%s [email: %s] [rating: %d] [play count: %d]" %
                         (boldText("Popularity:"), popm.email, popm.rating,
                          popm.count))

            # TBPM
            bpm = tag.bpm
            if bpm is not None:
                printMsg("%s %d" % (boldText("BPM:"), bpm))

            # TPUB
            pub = tag.publisher
            if pub is not None:
                printMsg("%s %s" % (boldText("Publisher/label:"), pub))

            # UFID
            for ufid in tag.unique_file_ids:
                printMsg("%s [%s] : %s" % \
                        (boldText("Unique File ID:"), ufid.owner_id,
                         ufid.uniq_id.encode("string_escape")))

            # COMM
            for c in tag.comments:
                printMsg("%s: [Description: %s] [Lang: %s]\n%s" %
                         (boldText("Comment"), c.description or "", c.lang
                          or "", c.text or ""))

            # USLT
            for l in tag.lyrics:
                printMsg("%s: [Description: %s] [Lang: %s]\n%s" %
                         (boldText("Lyrics"), l.description or u"", l.lang
                          or "", l.text))

            # TXXX
            for f in tag.user_text_frames:
                printMsg("%s: [Description: %s]\n%s" %
                         (boldText("UserTextFrame"), f.description, f.text))

            # URL frames
            for desc, url in (
                ("Artist URL", tag.artist_url),
                ("Audio source URL", tag.audio_source_url),
                ("Audio file URL", tag.audio_file_url),
                ("Internet radio URL", tag.internet_radio_url),
                ("Commercial URL", tag.commercial_url),
                ("Payment URL", tag.payment_url),
                ("Publisher URL", tag.publisher_url),
                ("Copyright URL", tag.copyright_url),
            ):
                if url:
                    printMsg("%s: %s" % (boldText(desc), url))

            # user url frames
            for u in tag.user_url_frames:
                printMsg("%s [Description: %s]: %s" %
                         (u.id, u.description, u.url))

            # APIC
            for img in tag.images:
                if img.mime_type != ImageFrame.URL_MIME_TYPE:
                    printMsg("%s: [Size: %d bytes] [Type: %s]" % (boldText(
                        img.picTypeToString(img.picture_type) +
                        " Image"), len(img.image_data), img.mime_type))
                    printMsg("Description: %s" % img.description)
                    printMsg("")
                    if self.args.write_images_dir:
                        img_path = "%s%s" % (self.args.write_images_dir,
                                             os.sep)
                        if not os.path.isdir(img_path):
                            raise IOError("Directory does not exist: %s" %
                                          img_path)
                        img_file = self._getDefaultNameForImage(img)
                        count = 1
                        while os.path.exists(os.path.join(img_path, img_file)):
                            img_file = self._getDefaultNameForImage(
                                img, str(count))
                            count += 1
                        printWarning("Writing %s..." %
                                     os.path.join(img_path, img_file))
                        with open(os.path.join(img_path, img_file),
                                  "wb") as fp:
                            fp.write(img.image_data)
                else:
                    printMsg("%s: [Type: %s] [URL: %s]" % (boldText(
                        img.picTypeToString(img.picture_type) +
                        " Image"), img.mime_type, img.image_url))
                    printMsg("Description: %s" % img.description)
                    printMsg("")

            # GOBJ
            for obj in tag.objects:
                printMsg(
                    "%s: [Size: %d bytes] [Type: %s]" %
                    (boldText("GEOB"), len(obj.object_data), obj.mime_type))
                printMsg("Description: %s" % obj.description)
                printMsg("Filename: %s" % obj.filename)
                printMsg("\n")
                if self.args.write_objects_dir:
                    obj_path = "%s%s" % (self.args.write_objects_dir, os.sep)
                    if not os.path.isdir(obj_path):
                        raise IOError("Directory does not exist: %s" %
                                      obj_path)
                    obj_file = self._getDefaultNameForObject(obj)
                    count = 1
                    while os.path.exists(os.path.join(obj_path, obj_file)):
                        obj_file = self._getDefaultNameForObject(
                            obj, str(count))
                        count += 1
                    printWarning("Writing %s..." %
                                 os.path.join(obj_path, obj_file))
                    with open(os.path.join(obj_path, obj_file), "wb") as fp:
                        fp.write(obj.object_data)

            # PRIV
            for p in tag.privates:
                printMsg("%s: [Data: %d bytes]" %
                         (boldText("PRIV"), len(p.data)))
                printMsg("Owner Id: %s" % p.owner_id)

            # MCDI
            if tag.cd_id:
                printMsg("\n%s: [Data: %d bytes]" %
                         (boldText("MCDI"), len(tag.cd_id)))

            # USER
            if tag.terms_of_use:
                printMsg("\nTerms of Use (%s): %s" %
                         (boldText("USER"), tag.terms_of_use))

            if self.args.verbose:
                printMsg("-" * 79)
                printMsg("%d ID3 Frames:" % len(tag.frame_set))
                for fid in tag.frame_set:
                    num_frames = len(tag.frame_set[fid])
                    count = " x %d" % num_frames if num_frames > 1 else ""
                    printMsg("%s%s" % (fid, count))
        else:
            raise TypeError("Unknown tag type: " + str(type(tag)))