MythTV  0.28pre
musicutils.cpp
Go to the documentation of this file.
1 // c/c++
2 #include <iostream>
3 
4 // qt
5 #include <QFile>
6 #include <QRegExp>
7 #include <QDir>
8 
9 // mythtv
10 #include <mythdirs.h>
11 #include <mythlogging.h>
12 #include <mythcorecontext.h>
13 #include <remotefile.h>
14 
15 extern "C" {
16 #include <libavformat/avformat.h>
17 #include <libavcodec/avcodec.h>
18 }
19 
20 // libmythmetadata
21 #include "musicmetadata.h"
22 #include "musicutils.h"
23 
24 static QRegExp badChars = QRegExp("(/|\\\\|:|\'|\"|\\?|\\|)");
25 
26 QString fixFilename(const QString &filename)
27 {
28  QString ret = filename;
29  ret.replace(badChars, "_");
30  return ret;
31 }
32 
33 static QMap<QString, QString> iconMap;
34 QString findIcon(const QString &type, const QString &name, bool ignoreCache)
35 {
36  LOG(VB_FILE, LOG_INFO, QString("findicon: looking for type: %1, name: %2").arg(type).arg(name));
37 
38  if (!ignoreCache)
39  {
40  QMap<QString, QString>::iterator i = iconMap.find(type + name);
41  if (i != iconMap.end())
42  {
43  LOG(VB_FILE, LOG_INFO, QString("findicon: found in cache %1").arg(i.value()));
44  return i.value();
45  }
46  }
47 
48  QString cleanName = fixFilename(name) + '.';
49  cleanName = '^' + QRegExp::escape(cleanName);
50  QString file = QString("/Icons/%1/%2").arg(type).arg(cleanName);
51  QString imageExtensions = "(jpg|jpeg|png|gif)";
52  QStringList fileList;
53 
54  fileList = RemoteFile::FindFileList(file + imageExtensions, gCoreContext->GetMasterHostName(), "MusicArt", true, true);
55  if (!fileList.isEmpty())
56  {
57  LOG(VB_FILE, LOG_INFO, QString("findicon: found %1 icons using %2").arg(fileList.size()).arg(fileList[0]));
58  iconMap.insert(type + name, fileList[0]);
59  return fileList[0];
60  }
61 
62  iconMap.insert(type + name, QString());
63 
64  LOG(VB_FILE, LOG_INFO, QString("findicon: not found type: %1, name: %2").arg(type).arg(name));
65 
66  return QString();
67 }
68 
69 inline QString fixFileToken_sl(QString token)
70 {
71  // this version doesn't remove fwd-slashes so we can
72  // pick them up later and create directories as required
73  token.replace(QRegExp("(\\\\|:|\'|\"|\\?|\\|)"), QString("_"));
74  return token;
75 }
76 
78 {
79  QString filename;
80  QString fntempl = gCoreContext->GetSetting("FilenameTemplate");
81  bool no_ws = gCoreContext->GetNumSetting("NoWhitespace", 0);
82 
83  QRegExp rx_ws("\\s{1,}");
84  QRegExp rx("(GENRE|ARTIST|ALBUM|TRACK|TITLE|YEAR)");
85  int i = 0;
86  int old_i = 0;
87  while (i >= 0)
88  {
89  i = rx.indexIn(fntempl, i);
90  if (i >= 0)
91  {
92  if (i > 0)
93  filename += fixFileToken_sl(fntempl.mid(old_i,i-old_i));
94  i += rx.matchedLength();
95  old_i = i;
96 
97  if ((rx.capturedTexts()[1] == "GENRE") && (!track->Genre().isEmpty()))
98  filename += fixFilename(track->Genre());
99 
100  if ((rx.capturedTexts()[1] == "ARTIST")
101  && (!track->FormatArtist().isEmpty()))
102  filename += fixFilename(track->FormatArtist());
103 
104  if ((rx.capturedTexts()[1] == "ALBUM") && (!track->Album().isEmpty()))
105  filename += fixFilename(track->Album());
106 
107  if ((rx.capturedTexts()[1] == "TRACK") && (track->Track() >= 0))
108  {
109  QString tempstr = QString::number(track->Track(), 10);
110  if (track->Track() < 10)
111  tempstr.prepend('0');
112  filename += fixFilename(tempstr);
113  }
114 
115  if ((rx.capturedTexts()[1] == "TITLE")
116  && (!track->FormatTitle().isEmpty()))
117  filename += fixFilename(track->FormatTitle());
118 
119  if ((rx.capturedTexts()[1] == "YEAR") && (track->Year() >= 0))
120  filename += fixFilename(QString::number(track->Year(), 10));
121  }
122  }
123 
124  if (no_ws)
125  filename.replace(rx_ws, "_");
126 
127 
128  if (filename == "" || filename.length() > FILENAME_MAX)
129  {
130  QString tempstr = QString::number(track->Track(), 10);
131  tempstr += " - " + track->FormatTitle();
132  filename = fixFilename(tempstr);
133  LOG(VB_GENERAL, LOG_ERR, "Invalid file storage definition.");
134  }
135 
136  return filename;
137 }
138 
139 bool isNewTune(const QString& artist, const QString& album, const QString& title)
140 {
141 
142  QString matchartist = artist;
143  QString matchalbum = album;
144  QString matchtitle = title;
145 
146  if (! matchartist.isEmpty())
147  {
148  matchartist.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
149  }
150 
151  if (! matchalbum.isEmpty())
152  {
153  matchalbum.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
154  }
155 
156  if (! matchtitle.isEmpty())
157  {
158  matchtitle.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
159  }
160 
161  MSqlQuery query(MSqlQuery::InitCon());
162  QString queryString("SELECT filename, artist_name,"
163  " album_name, name, song_id "
164  "FROM music_songs "
165  "LEFT JOIN music_artists"
166  " ON music_songs.artist_id=music_artists.artist_id "
167  "LEFT JOIN music_albums"
168  " ON music_songs.album_id=music_albums.album_id "
169  "WHERE artist_name LIKE :ARTIST "
170  "AND album_name LIKE :ALBUM "
171  "AND name LIKE :TITLE "
172  "ORDER BY artist_name, album_name,"
173  " name, song_id, filename");
174 
175  query.prepare(queryString);
176 
177  query.bindValue(":ARTIST", matchartist);
178  query.bindValue(":ALBUM", matchalbum);
179  query.bindValue(":TITLE", matchtitle);
180 
181  if (!query.exec() || !query.isActive())
182  {
183  MythDB::DBError("Search music database", query);
184  return true;
185  }
186 
187  if (query.size() > 0)
188  return false;
189 
190  return true;
191 }
QString FormatArtist()
QString FormatTitle()
void bindValue(const QString &placeholder, const QVariant &val)
Definition: mythdbcon.cpp:884
QString Genre() const
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
const char * filename
Definition: ioapi.h:135
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
GLint GLenum GLsizei GLint GLenum GLenum type
static QMap< QString, QString > iconMap
Definition: musicutils.cpp:33
int size(void) const
Definition: mythdbcon.h:187
static QRegExp badChars
Definition: musicutils.cpp:24
QString fixFileToken_sl(QString token)
Definition: musicutils.cpp:69
int Track() const
int Year() const
QString GetSetting(const QString &key, const QString &defaultval="")
QString findIcon(const QString &type, const QString &name, bool ignoreCache)
find an image for a artist or genre
Definition: musicutils.cpp:34
QString GetMasterHostName(void)
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:542
QString Album() const
const char * name
Definition: ParseText.cpp:338
bool isActive(void) const
Definition: mythdbcon.h:188
int GetNumSetting(const QString &key, int defaultval=0)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:810
static QStringList FindFileList(const QString &filename, const QString &host, const QString &storageGroup, bool useRegex=false, bool allowFallback=false)
Search all BE's for files in the give storage group.
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:610
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:181
QString fixFilename(const QString &filename)
remove any bad filename characters
Definition: musicutils.cpp:26
bool isNewTune(const QString &artist, const QString &album, const QString &title)
try to find a track in the db using the given artist, album and title
Definition: musicutils.cpp:139
QString filenameFromMetadata(MusicMetadata *track)
create a filename using the template in the settings and a MusicMetadata object
Definition: musicutils.cpp:77