00001
00002 #include <iostream>
00003
00004
00005 #include <QFile>
00006 #include <QRegExp>
00007 #include <QDir>
00008
00009
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
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
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
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
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
00128 avformat_close_input(&inputFC);
00129 inputFC = NULL;
00130
00131 return duration;
00132 }
00133
00134 inline QString fixFileToken_sl(QString token)
00135 {
00136
00137
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 }