MythTV  0.26-pre
musicutils.cpp
Go to the documentation of this file.
00001 // c/c++
00002 #include <iostream>
00003 
00004 // qt
00005 #include <QFile>
00006 #include <QRegExp>
00007 #include <QDir>
00008 
00009 // mythtv
00010 #include <mythdirs.h>
00011 #include <mythlogging.h>
00012 #include <mythcorecontext.h>
00013 
00014 extern "C" {
00015 #include <libavformat/avformat.h>
00016 #include <libavcodec/avcodec.h>
00017 }
00018 
00019 // mythmusic
00020 #include "metadata.h"
00021 #include "musicutils.h"
00022 
00023 static QRegExp badChars = QRegExp("(/|\\\\|:|\'|\"|\\?|\\|)");
00024 
00025 QString fixFilename(const QString &filename)
00026 {
00027     QString ret = filename;
00028     ret.replace(badChars, "_");
00029     return ret;
00030 }
00031 
00032 QString findIcon(const QString &type, const QString &name)
00033 {
00034     QString cleanName = fixFilename(name);
00035     QString file = GetConfDir() + QString("/MythMusic/Icons/%1/%2").arg(type).arg(cleanName);
00036 
00037     if (QFile::exists(file + ".jpg"))
00038         return file + ".jpg";
00039 
00040     if (QFile::exists(file + ".jpeg"))
00041         return file + ".jpeg";
00042 
00043     if (QFile::exists(file + ".png"))
00044         return file + ".png";
00045 
00046     if (QFile::exists(file + ".gif"))
00047         return file + ".gif";
00048 
00049     return QString();
00050 }
00051 
00052 //TODO this needs updating to also use storage groups
00053 uint calcTrackLength(const QString &musicFile)
00054 {
00055     const char *type = NULL;
00056 
00057     AVFormatContext *inputFC = NULL;
00058     AVInputFormat *fmt = NULL;
00059 
00060     if (type)
00061         fmt = av_find_input_format(type);
00062 
00063     // Open recording
00064     LOG(VB_GENERAL, LOG_DEBUG, QString("calcTrackLength: Opening '%1'")
00065             .arg(musicFile));
00066 
00067     QByteArray inFileBA = musicFile.toLocal8Bit();
00068 
00069     int ret = avformat_open_input(&inputFC, inFileBA.constData(), fmt, NULL);
00070 
00071     if (ret)
00072     {
00073         LOG(VB_GENERAL, LOG_ERR, "calcTrackLength: Couldn't open input file" +
00074                                   ENO);
00075         return 0;
00076     }
00077 
00078     // Getting stream information
00079     ret = avformat_find_stream_info(inputFC, NULL);
00080 
00081     if (ret < 0)
00082     {
00083         LOG(VB_GENERAL, LOG_ERR,
00084             QString("calcTrackLength: Couldn't get stream info, error #%1").arg(ret));
00085         avformat_close_input(&inputFC);
00086         inputFC = NULL;
00087         return 0;
00088     }
00089 
00090     uint duration = 0;
00091     long long time = 0;
00092 
00093     for (uint i = 0; i < inputFC->nb_streams; i++)
00094     {
00095         AVStream *st = inputFC->streams[i];
00096         char buf[256];
00097 
00098         avcodec_string(buf, sizeof(buf), st->codec, false);
00099 
00100         switch (inputFC->streams[i]->codec->codec_type)
00101         {
00102             case AVMEDIA_TYPE_AUDIO:
00103             {
00104                 AVPacket pkt;
00105                 av_init_packet(&pkt);
00106 
00107                 while (av_read_frame(inputFC, &pkt) >= 0)
00108                 {
00109                     if (pkt.stream_index == (int)i)
00110                         time = time + pkt.duration;
00111 
00112                     av_free_packet(&pkt);
00113                 }
00114 
00115                 duration = time * av_q2d(inputFC->streams[i]->time_base);
00116                 break;
00117             }
00118 
00119             default:
00120                 LOG(VB_GENERAL, LOG_ERR,
00121                     QString("Skipping unsupported codec %1 on stream %2")
00122                         .arg(inputFC->streams[i]->codec->codec_type).arg(i));
00123                 break;
00124         }
00125     }
00126 
00127     // Close input file
00128     avformat_close_input(&inputFC);
00129     inputFC = NULL;
00130 
00131     return duration;
00132 }
00133 
00134 inline QString fixFileToken_sl(QString token)
00135 {
00136     // this version doesn't remove fwd-slashes so we can
00137     // pick them up later and create directories as required
00138     token.replace(QRegExp("(\\\\|:|\'|\"|\\?|\\|)"), QString("_"));
00139     return token;
00140 }
00141 
00142 QString filenameFromMetadata(Metadata *track, bool createDir)
00143 {
00144     QDir directoryQD(gMusicData->musicDir);
00145     QString filename;
00146     QString fntempl = gCoreContext->GetSetting("FilenameTemplate");
00147     bool no_ws = gCoreContext->GetNumSetting("NoWhitespace", 0);
00148 
00149     QRegExp rx_ws("\\s{1,}");
00150     QRegExp rx("(GENRE|ARTIST|ALBUM|TRACK|TITLE|YEAR)");
00151     int i = 0;
00152     int old_i = 0;
00153     while (i >= 0)
00154     {
00155         i = rx.indexIn(fntempl, i);
00156         if (i >= 0)
00157         {
00158             if (i > 0)
00159                 filename += fixFileToken_sl(fntempl.mid(old_i,i-old_i));
00160             i += rx.matchedLength();
00161             old_i = i;
00162 
00163             if ((rx.capturedTexts()[1] == "GENRE") && (!track->Genre().isEmpty()))
00164                 filename += fixFilename(track->Genre());
00165 
00166             if ((rx.capturedTexts()[1] == "ARTIST")
00167                     && (!track->FormatArtist().isEmpty()))
00168                 filename += fixFilename(track->FormatArtist());
00169 
00170             if ((rx.capturedTexts()[1] == "ALBUM") && (!track->Album().isEmpty()))
00171                 filename += fixFilename(track->Album());
00172 
00173             if ((rx.capturedTexts()[1] == "TRACK") && (track->Track() >= 0))
00174             {
00175                 QString tempstr = QString::number(track->Track(), 10);
00176                 if (track->Track() < 10)
00177                     tempstr.prepend('0');
00178                 filename += fixFilename(tempstr);
00179             }
00180 
00181             if ((rx.capturedTexts()[1] == "TITLE")
00182                     && (!track->FormatTitle().isEmpty()))
00183                 filename += fixFilename(track->FormatTitle());
00184 
00185             if ((rx.capturedTexts()[1] == "YEAR") && (track->Year() >= 0))
00186                 filename += fixFilename(QString::number(track->Year(), 10));
00187         }
00188     }
00189 
00190     if (no_ws)
00191         filename.replace(rx_ws, "_");
00192 
00193 
00194     if (filename == "" || filename.length() > FILENAME_MAX)
00195     {
00196         QString tempstr = QString::number(track->Track(), 10);
00197         tempstr += " - " + track->FormatTitle();
00198         filename = fixFilename(tempstr);
00199         LOG(VB_GENERAL, LOG_ERR, "Invalid file storage definition.");
00200     }
00201 
00202     if (createDir)
00203     {
00204         QFileInfo fi(filename);
00205         if (!directoryQD.mkpath(gMusicData->musicDir + fi.path()))
00206             LOG(VB_GENERAL, LOG_ERR,
00207                 QString("Ripper: Failed to create directory path: '%1'").arg(gMusicData->musicDir + filename));
00208     }
00209 
00210     return filename;
00211 }
00212 
00213 bool isNewTune(const QString& artist, const QString& album, const QString& title)
00214 {
00215 
00216     QString matchartist = artist;
00217     QString matchalbum = album;
00218     QString matchtitle = title;
00219 
00220     if (! matchartist.isEmpty())
00221     {
00222         matchartist.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
00223     }
00224 
00225     if (! matchalbum.isEmpty())
00226     {
00227         matchalbum.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
00228     }
00229 
00230     if (! matchtitle.isEmpty())
00231     {
00232         matchtitle.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
00233     }
00234 
00235     MSqlQuery query(MSqlQuery::InitCon());
00236     QString queryString("SELECT filename, artist_name,"
00237                         " album_name, name, song_id "
00238                         "FROM music_songs "
00239                         "LEFT JOIN music_artists"
00240                         " ON music_songs.artist_id=music_artists.artist_id "
00241                         "LEFT JOIN music_albums"
00242                         " ON music_songs.album_id=music_albums.album_id "
00243                         "WHERE artist_name LIKE :ARTIST "
00244                         "AND album_name LIKE :ALBUM "
00245                         "AND name LIKE :TITLE "
00246                         "ORDER BY artist_name, album_name,"
00247                         " name, song_id, filename");
00248 
00249     query.prepare(queryString);
00250 
00251     query.bindValue(":ARTIST", matchartist);
00252     query.bindValue(":ALBUM", matchalbum);
00253     query.bindValue(":TITLE", matchtitle);
00254 
00255     if (!query.exec() || !query.isActive())
00256     {
00257         MythDB::DBError("Search music database", query);
00258         return true;
00259     }
00260 
00261     if (query.size() > 0)
00262         return false;
00263 
00264     return true;
00265 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends