SWISH::Filters::ID3toHTML - ID3 tag to HTML filter module


SWISH-Filter documentation Contained in the SWISH-Filter distribution.

Index


Code Index:

NAME

Top

SWISH::Filters::ID3toHTML - ID3 tag to HTML filter module

DESCRIPTION

Top

SWISH::Filters::ID3toHTML translates ID3 tags into HTML metadata for use by the SWISH::Filter module and SWISH-E.

Requires CPAN module MP3::Tag.

SUPPORT

Top

Please contact the Swish-e discussion list. http://swish-e.org/


SWISH-Filter documentation Contained in the SWISH-Filter distribution.

package SWISH::Filters::ID3toHTML;
use strict;
use vars qw( $VERSION @ISA );
$VERSION = '0.15';
@ISA = ('SWISH::Filters::Base');

# Convert known ID3v2 tags to metanames.

my %id3v2_tags = (
    TIT2 => 'song',         # 4.2.1 TIT2 Title/songname/content description
    TYER => 'year',         # 4.2.1 TYER Year
    TRCK => 'track',        # 4.2.1 TRCK Track number/Position in set
    TCOP => 'copyright',    # 4.2.1 TCOP Copyright message
                            # * WinAMP seems to prepend a (C) to this value.

    TPE1 => 'artist',             # 4.2.1 TPE1 Lead performer(s)/Soloist(s)
    TALB => 'album',              # 4.2.1 TALB Album/Movie/Show title
    TENC => 'encoded',            # 4.2.1 TENC Encoded by
    TOPE => 'artist_original',    # 4.2.1 TOPE Original artist(s)/performer(s)
    TCOM => 'composer',           # 4.2.1 TCOM Composer
    TCON => 'genre',              # 4.2.1 TCON Content type

    # 4.3.2 WXXX User defined URL link frame
    WXXX_URL => 'url',            #  * URL => http://URL/HERE
    WXXX_Description =>
        'url_description',  #  * Description => WinAMP provides no description

    # 4.11 COMM Comments
    COMM_Text     => 'comment',         # * Text => COMMENT
    COMM_Language => 'comment_lang',    # * Language => eng
    COMM_short    => 'comment_short'    # * short => WinAMP provides no short

);

sub new {
    my ($class) = @_;

    my $self = bless { mimetypes => [qr!audio/mpeg!], }, $class;
    return $self->use_modules(qw( MP3::Tag ));
}

sub filter {
    my ( $self, $doc ) = @_;

    # We need a file name to pass to the conversion function
    my $file = $doc->fetch_filename;

    my ( $content_ref, $meta ) = $self->get_id3_content_ref( $file, $doc );
    return unless $content_ref;

    # update the document's content type
    $doc->set_content_type('text/html');

    # If filtered must return either a reference to the doc or a pathname.
    return ( \$content_ref, $meta );
}

# =======================================================================
sub get_id3_content_ref {
    my ( $self, $filename, $doc ) = @_;
    my $mp3 = MP3::Tag->new($filename);

    # return unless we have a file with tags
    return format_empty_doc($filename)
        unless ref $mp3 && $mp3->get_tags();

    # Here we will store all of the tag info
    my %metadata;

    # Convert tags to metadata giving ID3v2 precedence
    get_id3v1_tags( $mp3, \%metadata );

    # will replace any v1 tags that are the same
    get_id3v2_tags( $mp3, \%metadata );

    my $user_meta = $doc->meta_data || {};
    $metadata{$_} = $user_meta->{$_} for keys %$user_meta;

    # HTML or bust
    return (
          %metadata
        ? $self->format_as_html( \%metadata )
        : $self->format_empty_doc($filename)
    );
}

sub get_id3v1_tags {
    my ( $mp3, $metadata ) = @_;

    return unless exists $mp3->{ID3v1};

    # Read all ID3v1 tags into metadata hash
    my $id3v1 = $mp3->{ID3v1};
    for (qw/ artist album comment genre song track year /) {
        $metadata->{$_} = $id3v1->$_ if $id3v1->$_;
    }
}

sub get_id3v2_tags {
    my ( $mp3, $metadata ) = @_;

    # Do we even have an ID3 v2 tag?
    return unless exists $mp3->{ID3v2};

    # Get the tag and a hash of frame ids.
    my $id3v2 = $mp3->{ID3v2};

    # keys are 4-character-codes and values are the long names
    my $frameIDs_hash = $id3v2->get_frame_ids;

    # Go through each frame and translate it to usable metadata
    foreach my $frame ( keys %$frameIDs_hash ) {
        my ( $info, $name ) = $id3v2->get_frame($frame);

        # We have a user defined frame
        if ( ref $info ) {

            # $$$ We really only want COMM and WXXX
            while ( my ( $key, $val ) = each %$info ) {

                next
                    if $key =~ /^_/
                        || !$val;    # leading underscore means binary data

                # Concatenate frame and key for our lookup hash
                my $code = ${frame} . "_" . ${key};

                # fails when frame is appended with digits (e.g. "COMM01");
                my $metaname = $id3v2_tags{$code} || $code;

                # Assign value if not empty and has a key
                $metadata->{$metaname} = $val if $val;
            }
        }

        # We have a simple frame
        else {
            my $metaname = $id3v2_tags{$frame} || $frame || 'blank frame';
            $metadata->{$metaname} = $info if $info;
        }
    }
}

sub format_as_html {
    my $self     = shift;
    my $metadata = shift;

    my $title 
        = $metadata->{song}
        || $metadata->{album}
        || $metadata->{artist}
        || 'No Title';

    my $headers = $self->format_meta_headers($metadata);

    my $url = '';
    if ( $metadata->{url} ) {
        my $desc = $metadata->{url_description} || $metadata->{url};
        $url
            = '<p><a href="'
            . $self->escapeXML( $metadata->{url} )
            . "\">$desc</a>";
    }

    my $comment = '';
    if ( $metadata->{comment} ) {
        my $lang = get_iso_lang( $metadata->{comment_lang} || 'en' )
            ;    # wrong assuming "en"?
        $comment = qq[<p name="comment" lang="$lang">]
            . $self->escapeXML( $metadata->{comment} ) . '</p>';
    }

    my $txt = <<EOF;
<html>
 <head>
   <title>$title</title>
   $headers
 </head>
 <body>
   $url
   $comment
  </body>
</html>
EOF

    return ( $txt, $metadata );

}

sub format_empty_doc {
    my $self     = shift;
    my $filename = shift;
    require File::Basename;
    my $base = File::Basename::basename( $filename, '.mp3' );

    return $self->format_as_html( { song => $base, notag => 1 } );
}

sub get_iso_lang {
    my $lang = shift;

    # Do we need to translate undocumented ID3 Lang codes to ISO?
    # 4.11.Comments
    #   Language $xx xx xx
    #   *  WinAMP may be mistaken for using "eng" instead of an ISO designator

    return $lang unless $lang == "eng";
    return "en";
}

1;
__END__