[mythtv] MythMusic patch for users w/o ID3 tags on mp3 files.

Chris Pinkham cpinkham at bc2va.org
Wed Jan 29 17:47:49 EST 2003


When I wrote a perl script to start ripping my CDs to mp3 files, I never
added in the functionality to append ID3 tags to the mp3s.  So, about
90-95% of my mp3 files don't have any ID3 info.  I still wanted to be able
to use mytmmusic to play mp3 files though so I coded up this patch.

The attached file is a patch to mythmusic's maddecoder.cpp, maddecoder.h,
and mythmusic-settings.txt files.  It creates 2 new options in 
mythmusic-settings.txt, here's an example:

str Ignore_ID3=TRUE
str NonID3FileNameFormat=GENRE/ARTIST/ALBUM/TRACK_TITLE

If Ignore_ID3 is set to TRUE, mythmusic will try to determine the
Genre, Artist, Album, Track Number, and Title from the filename of the
mp3 file.  The NonID3FileNameFormat variable should be set to the
directory/file format where the mp3 files are stored.  For instance, I
store mine in the above shown Genre/Artist/Album/Track format.  Mythmusic
will then use this information to fill in the proper fields when it
populates the musicmetadata table rather than searching for an ID3 tag in
the mp3 file.

The files can be layed out in any format, such as:

Genre/Artist/Album/Title
Artist/Genre/Album/Title
Artist/Album/Title (with Genre left as Unknown)

The track number is optional but can be specified with the title by using
the TRACK_TITLE keyword instead of TITLE.  If TRACK_TITLE is used,
then the filename can have a space, hyphen, or underscore separating the
track number from the track title.  Keywords are case insensitive, so if
you specify GENRE it's the same as Genre in the format field.

The Ignore_ID3 option does not disable the code that determines the track
length, just the portion that tries to read ID3 info.

The patch is against CVS as of around 17:15 EST on 1/29/2003, but should
apply cleanly for other checkout dates since it does not include any major
changes.

If anyone else is in my situation (little/no ID3 tags), can you check this
out to see how well it works for you?

Chris

*****************************************************************************
** Chris Pinkham                  Linux v2.2.18, Sane v1.0.4, Cajun v3.0-8 **
** cpinkham at bc2va.org                          http://www.bc2va.org/chris/ **
*****************************************************************************
-------------- next part --------------
Index: mythmusic/maddecoder.cpp
===================================================================
RCS file: /var/lib/cvs/mythmusic/mythmusic/maddecoder.cpp,v
retrieving revision 1.3
diff -u -r1.3 maddecoder.cpp
--- mythmusic/maddecoder.cpp	30 Oct 2002 16:12:29 -0000	1.3
+++ mythmusic/maddecoder.cpp	29 Jan 2003 22:14:50 -0000
@@ -4,6 +4,7 @@
 #include <iostream>
 #include <math.h>
 #include <stdio.h>
+#include <qregexp.h>
 using namespace std;
 
 #include <mad.h>
@@ -15,6 +16,8 @@
 #include "buffer.h"
 #include "output.h"
 
+#include <mythtv/mythcontext.h>
+
 #define XING_MAGIC     (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')
 
 MadDecoder::MadDecoder(MythContext *context, const QString &file, 
@@ -42,6 +45,14 @@
     output_bytes = 0;
     output_at = 0;
     output_size = 0;
+
+    filename_format = context->GetSetting( "NonID3FileNameFormat" ).upper();
+    QString ignore_id3_str = context->GetSetting( "Ignore_ID3" );
+    if ( ignore_id3_str == "TRUE" ) {
+        ignore_id3 = 1;
+    } else {
+        ignore_id3 = 0;
+    }
 }
 
 MadDecoder::~MadDecoder(void)
@@ -531,74 +542,113 @@
     QString artist = "", album = "", title = "", genre = "";
     int year = 0, tracknum = 0, length = 0;
 
-    id3_file *id3file = id3_file_open(filename.ascii(), ID3_FILE_MODE_READONLY);
-    if (!id3file)
-    {
-        return NULL;
-    }
-
-    id3_tag *tag = id3_file_tag(id3file);
+    if ( ! ignore_id3 ) {
+        // use ID3 header 
 
-    if (!tag)
-    {
-        id3_file_close(id3file);
-        return NULL;
-    }
+        id3_file *id3file = id3_file_open(filename.ascii(), ID3_FILE_MODE_READONLY);
+        if (!id3file)
+        {
+            return NULL;
+        }
 
-    struct {
-        char const *id;
-        char const *name;
-    } const info[] = {
-        { ID3_FRAME_TITLE,  "Title"  },
-        { ID3_FRAME_ARTIST, "Artist" },
-        { ID3_FRAME_ALBUM,  "Album"  },
-        { ID3_FRAME_YEAR,   "Year"   },
-        { ID3_FRAME_TRACK,  "Track"  },
-        { ID3_FRAME_GENRE,  "Genre"  },
-    };
+        id3_tag *tag = id3_file_tag(id3file);
 
-    for (unsigned int i = 0; i < sizeof(info) / sizeof(info[0]); ++i)
-    {
-        struct id3_frame *frame = id3_tag_findframe(tag, info[i].id, 0);
-        if (!frame)
-            continue;
-
-        id3_ucs4_t const *ucs4;
-        id3_latin1_t *latin1;
-        union id3_field *field;
-        unsigned int nstrings;
+        if (!tag)
+        {
+            id3_file_close(id3file);
+            return NULL;
+        }
 
-        field = &frame->fields[1];
-        nstrings = id3_field_getnstrings(field);
+        struct {
+            char const *id;
+            char const *name;
+        } const info[] = {
+            { ID3_FRAME_TITLE,  "Title"  },
+            { ID3_FRAME_ARTIST, "Artist" },
+            { ID3_FRAME_ALBUM,  "Album"  },
+            { ID3_FRAME_YEAR,   "Year"   },
+            { ID3_FRAME_TRACK,  "Track"  },
+            { ID3_FRAME_GENRE,  "Genre"  },
+        };
 
-        for (unsigned int j = 0; j < nstrings; ++j)
+        for (unsigned int i = 0; i < sizeof(info) / sizeof(info[0]); ++i)
         {
-            ucs4 = id3_field_getstrings(field, j);
-            assert(ucs4);
+            struct id3_frame *frame = id3_tag_findframe(tag, info[i].id, 0);
+            if (!frame)
+                continue;
 
-            if (!strcmp(info[i].id, ID3_FRAME_GENRE))
-                ucs4 = id3_genre_name(ucs4);
+            id3_ucs4_t const *ucs4;
+            id3_latin1_t *latin1;
+            union id3_field *field;
+            unsigned int nstrings;
 
-            latin1 = id3_ucs4_latin1duplicate(ucs4);
-            if (!latin1)
-                continue;
+            field = &frame->fields[1];
+            nstrings = id3_field_getnstrings(field);
 
-            switch (i)
+            for (unsigned int j = 0; j < nstrings; ++j)
             {
-                case 0: title = (char *)latin1; break;
-                case 1: artist = (char *)latin1; break;
-                case 2: album = (char *)latin1; break;
-                case 3: year = atoi((char *)latin1); break;
-                case 4: tracknum = atoi((char *)latin1); break;
-                case 5: genre = (char *)latin1; break;
-                default: break;
+                ucs4 = id3_field_getstrings(field, j);
+                assert(ucs4);
+
+                if (!strcmp(info[i].id, ID3_FRAME_GENRE))
+                    ucs4 = id3_genre_name(ucs4);
+
+                latin1 = id3_ucs4_latin1duplicate(ucs4);
+                if (!latin1)
+                    continue;
+
+                switch (i)
+                {
+                    case 0: title = (char *)latin1; break;
+                    case 1: artist = (char *)latin1; break;
+                    case 2: album = (char *)latin1; break;
+                    case 3: year = atoi((char *)latin1); break;
+                    case 4: tracknum = atoi((char *)latin1); break;
+                    case 5: genre = (char *)latin1; break;
+                    default: break;
+                }
+
+                free(latin1);
             }
+        }
 
-            free(latin1);
+        id3_file_close(id3file);
+    } else {
+        // Ignore_ID3 header 
+        int part_num = 0;
+        QStringList fmt_list = QStringList::split( "/", filename_format );
+        QStringList::iterator fmt_it = fmt_list.begin();
+
+        // go through loop once to get minimum part number
+        for( ; fmt_it != fmt_list.end(); fmt_it++, part_num-- );
+
+        // reset to go through loop for real
+        fmt_it = fmt_list.begin();
+        for( ; fmt_it != fmt_list.end(); fmt_it++, part_num++ )
+        {
+            QString part_str = filename.section( "/", part_num, part_num );
+            part_str.replace( QRegExp(QString("_")), QString(" ") );
+            part_str.replace( QRegExp(QString(".mp3$"), FALSE), QString("") );
+
+            if ( *fmt_it == "GENRE" ) {
+                genre = part_str;
+            } else if ( *fmt_it == "ARTIST" ) {
+                artist = part_str;
+            } else if ( *fmt_it == "ALBUM" ) {
+                album = part_str;
+            } else if ( *fmt_it == "TITLE" ) {
+                title = part_str;
+            } else if ( *fmt_it == "TRACK_TITLE" ) {
+                part_str.replace( QRegExp(QString("-")), QString(" ") );
+                QString s_tmp = part_str;
+                s_tmp.replace( QRegExp(QString(" .*"), FALSE), QString("") );
+                tracknum = s_tmp.toInt();
+                title = part_str;
+                title.replace( QRegExp(QString("^[0-9][0-9] "), FALSE),
+                    QString("") );
+            }
         }
     }
-
-    id3_file_close(id3file);
 
     struct mad_stream stream;
     struct mad_header header;
Index: mythmusic/maddecoder.h
===================================================================
RCS file: /var/lib/cvs/mythmusic/mythmusic/maddecoder.h,v
retrieving revision 1.3
diff -u -r1.3 maddecoder.h
--- mythmusic/maddecoder.h	30 Oct 2002 16:12:29 -0000	1.3
+++ mythmusic/maddecoder.h	29 Jan 2003 22:14:50 -0000
@@ -46,6 +46,9 @@
     long bitrate, freq, len;
     unsigned int bks;
     mad_fixed_t eqbands[32];
+    int ignore_id3;
+
+    QString filename_format;
 
     char *input_buf;
     unsigned long input_bytes;
Index: mythmusic/mythmusic-settings.txt
===================================================================
RCS file: /var/lib/cvs/mythmusic/mythmusic/mythmusic-settings.txt,v
retrieving revision 1.4
diff -u -r1.4 mythmusic-settings.txt
--- mythmusic/mythmusic-settings.txt	2 Dec 2002 20:34:53 -0000	1.4
+++ mythmusic/mythmusic-settings.txt	29 Jan 2003 22:14:50 -0000
@@ -14,6 +14,22 @@
 # Order in which to sort the Music Selection Tree.
 str TreeLevels=artist album title
 
+# Ignore_ID3 TRUE says to skip checking ID3 tags in files and just try to
+# determine Genre, Artist, Album, and Track number and title from the
+# filename.  NonID3FileNameFormat specifies the format of the directory
+# structure for Genre, Artist, Album, etc..  Track number and Title can be
+# separated in the actual filename by either a '_' or '-' character.
+#
+# TRACK_TITLE field can be in any of the following formats or variations
+# thereof (track number at beginning, separator, followed by space or '_'
+# separated title):
+#  11_Hold_Your_Horses.mp3
+#  11-Hold_Your_Horses.mp3
+#  11 Hold Your Horses.mp3
+#
+#str Ignore_ID3=TRUE
+str NonID3FileNameFormat=GENRE/ARTIST/ALBUM/TRACK_TITLE
+
 # Automatically lookup an audio CD if it's present and show its information in
 # the Music Selection Tree
 #int AutoLookupCD=1


More information about the mythtv-dev mailing list