MythTV  0.28pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
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  QString file = QString("Icons/%1/%2").arg(type).arg(cleanName);
50  QStringList imageExtensions = QStringList() << ".jpg" << ".jpeg" << ".png" << ".gif";
51  QString filename;
52 
53  // TODO also look on any slave BEs?
54  for (int x = 0; x < imageExtensions.count(); x++)
55  {
56  filename = RemoteFile::FindFile(file + imageExtensions[x], gCoreContext->GetMasterHostName(), "MusicArt");
57  if (!filename.isEmpty())
58  {
59  LOG(VB_FILE, LOG_INFO, QString("findicon: found at %1").arg(filename));
60  iconMap.insert(type + name, filename);
61  return filename;
62  }
63  }
64 
65  iconMap.insert(type + name, QString());
66 
67  LOG(VB_FILE, LOG_INFO, QString("findicon: not found type: %1, name: %2").arg(type).arg(name));
68 
69  return QString();
70 }
71 
72 inline QString fixFileToken_sl(QString token)
73 {
74  // this version doesn't remove fwd-slashes so we can
75  // pick them up later and create directories as required
76  token.replace(QRegExp("(\\\\|:|\'|\"|\\?|\\|)"), QString("_"));
77  return token;
78 }
79 
81 {
82  QString filename;
83  QString fntempl = gCoreContext->GetSetting("FilenameTemplate");
84  bool no_ws = gCoreContext->GetNumSetting("NoWhitespace", 0);
85 
86  QRegExp rx_ws("\\s{1,}");
87  QRegExp rx("(GENRE|ARTIST|ALBUM|TRACK|TITLE|YEAR)");
88  int i = 0;
89  int old_i = 0;
90  while (i >= 0)
91  {
92  i = rx.indexIn(fntempl, i);
93  if (i >= 0)
94  {
95  if (i > 0)
96  filename += fixFileToken_sl(fntempl.mid(old_i,i-old_i));
97  i += rx.matchedLength();
98  old_i = i;
99 
100  if ((rx.capturedTexts()[1] == "GENRE") && (!track->Genre().isEmpty()))
101  filename += fixFilename(track->Genre());
102 
103  if ((rx.capturedTexts()[1] == "ARTIST")
104  && (!track->FormatArtist().isEmpty()))
105  filename += fixFilename(track->FormatArtist());
106 
107  if ((rx.capturedTexts()[1] == "ALBUM") && (!track->Album().isEmpty()))
108  filename += fixFilename(track->Album());
109 
110  if ((rx.capturedTexts()[1] == "TRACK") && (track->Track() >= 0))
111  {
112  QString tempstr = QString::number(track->Track(), 10);
113  if (track->Track() < 10)
114  tempstr.prepend('0');
115  filename += fixFilename(tempstr);
116  }
117 
118  if ((rx.capturedTexts()[1] == "TITLE")
119  && (!track->FormatTitle().isEmpty()))
120  filename += fixFilename(track->FormatTitle());
121 
122  if ((rx.capturedTexts()[1] == "YEAR") && (track->Year() >= 0))
123  filename += fixFilename(QString::number(track->Year(), 10));
124  }
125  }
126 
127  if (no_ws)
128  filename.replace(rx_ws, "_");
129 
130 
131  if (filename == "" || filename.length() > FILENAME_MAX)
132  {
133  QString tempstr = QString::number(track->Track(), 10);
134  tempstr += " - " + track->FormatTitle();
135  filename = fixFilename(tempstr);
136  LOG(VB_GENERAL, LOG_ERR, "Invalid file storage definition.");
137  }
138 
139  return filename;
140 }
141 
142 bool isNewTune(const QString& artist, const QString& album, const QString& title)
143 {
144 
145  QString matchartist = artist;
146  QString matchalbum = album;
147  QString matchtitle = title;
148 
149  if (! matchartist.isEmpty())
150  {
151  matchartist.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
152  }
153 
154  if (! matchalbum.isEmpty())
155  {
156  matchalbum.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
157  }
158 
159  if (! matchtitle.isEmpty())
160  {
161  matchtitle.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
162  }
163 
164  MSqlQuery query(MSqlQuery::InitCon());
165  QString queryString("SELECT filename, artist_name,"
166  " album_name, name, song_id "
167  "FROM music_songs "
168  "LEFT JOIN music_artists"
169  " ON music_songs.artist_id=music_artists.artist_id "
170  "LEFT JOIN music_albums"
171  " ON music_songs.album_id=music_albums.album_id "
172  "WHERE artist_name LIKE :ARTIST "
173  "AND album_name LIKE :ALBUM "
174  "AND name LIKE :TITLE "
175  "ORDER BY artist_name, album_name,"
176  " name, song_id, filename");
177 
178  query.prepare(queryString);
179 
180  query.bindValue(":ARTIST", matchartist);
181  query.bindValue(":ALBUM", matchalbum);
182  query.bindValue(":TITLE", matchtitle);
183 
184  if (!query.exec() || !query.isActive())
185  {
186  MythDB::DBError("Search music database", query);
187  return true;
188  }
189 
190  if (query.size() > 0)
191  return false;
192 
193  return true;
194 }
QString FormatArtist()
QString FormatTitle()
void bindValue(const QString &placeholder, const QVariant &val)
Definition: mythdbcon.cpp:857
static QString FindFile(const QString &filename, const QString &host, const QString &storageGroup)
Search all BE's for a file in the give storage group.
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:72
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:529
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:790
GLuint GLfloat x
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:597
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:142
QString filenameFromMetadata(MusicMetadata *track)
create a filename using the template in the settings and a MusicMetadata object
Definition: musicutils.cpp:80