MythTV  master
musicutils.cpp
Go to the documentation of this file.
1 // c/c++
2 #include <iostream>
3 
4 // qt
5 #include <QFile>
6 #include <QRegularExpression>
7 #include <QDir>
8 
9 // mythtv
11 #include "libmythbase/mythdirs.h"
13 #include "libmythbase/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 const static QRegularExpression badChars1 { R"((/|\\|:|'|"|\?|\|))" };
25 const static QRegularExpression badChars2 { R"((/|\\|:|'|\,|\!|\(|\)|"|\?|\|))" };
26 
27 QString fixFilename(const QString &filename)
28 {
29  QString ret = filename;
30  return ret.replace(badChars1, "_");
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, 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 = '^' + QRegularExpression::escape(cleanName);
50  QString file = QString("/Icons/%1/%2").arg(type, 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, name));
65 
66  return {};
67 }
68 
69 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  static const QRegularExpression kBadFilenameCharRE { R"((\\|:|'|"|\?|\|))" };
74  token.replace(kBadFilenameCharRE, QString("_"));
75  return token;
76 }
77 
79 {
80  QString filename;
81  QString fntempl = gCoreContext->GetSetting("FilenameTemplate");
82  bool no_ws = gCoreContext->GetBoolSetting("NoWhitespace", false);
83 
84  static const QRegularExpression rx_ws("\\s{1,}");
85  static const QRegularExpression rx("^(.*?)(GENRE|ARTIST|ALBUM|TRACK|TITLE|YEAR)");
86  auto match = rx.match(fntempl);
87  while (match.hasMatch())
88  {
89  filename += match.captured(1);
90 
91  if ((match.captured(2) == "GENRE") &&
92  (!track->Genre().isEmpty()))
93  filename += fixFilename(track->Genre());
94  else if ((match.captured(2) == "ARTIST") &&
95  (!track->FormatArtist().isEmpty()))
96  filename += fixFilename(track->FormatArtist());
97  else if ((match.captured(2) == "ALBUM") &&
98  (!track->Album().isEmpty()))
99  filename += fixFilename(track->Album());
100  else if ((match.captured(2) == "TRACK") && (track->Track() >= 0))
101  filename += fixFilename(QString("%1").arg(track->Track(), 2,10,QChar('0')));
102  else if ((match.captured(2) == "TITLE") &&
103  (!track->FormatTitle().isEmpty()))
104  filename += fixFilename(track->FormatTitle());
105  else if ((match.captured(2) == "YEAR") && (track->Year() >= 0))
106  filename += fixFilename(QString::number(track->Year(), 10));
107  fntempl.remove(0, match.capturedLength());
108  match = rx.match(fntempl);
109  }
110 
111  if (no_ws)
112  filename.replace(rx_ws, "_");
113 
114 
115  if (filename == "" || filename.length() > FILENAME_MAX)
116  {
117  QString tempstr = QString::number(track->Track(), 10);
118  tempstr += " - " + track->FormatTitle();
119  filename = fixFilename(tempstr);
120  LOG(VB_GENERAL, LOG_ERR, "Invalid file storage definition.");
121  }
122 
123  return filename;
124 }
125 
126 bool isNewTune(const QString& artist, const QString& album, const QString& title)
127 {
128 
129  QString matchartist = artist;
130  QString matchalbum = album;
131  QString matchtitle = title;
132 
133  if (! matchartist.isEmpty())
134  matchartist.replace(badChars2, QString("_"));
135 
136  if (! matchalbum.isEmpty())
137  matchalbum.replace(badChars2, QString("_"));
138 
139  if (! matchtitle.isEmpty())
140  matchtitle.replace(badChars2, QString("_"));
141 
142  MSqlQuery query(MSqlQuery::InitCon());
143  QString queryString("SELECT filename, artist_name,"
144  " album_name, name, song_id "
145  "FROM music_songs "
146  "LEFT JOIN music_artists"
147  " ON music_songs.artist_id=music_artists.artist_id "
148  "LEFT JOIN music_albums"
149  " ON music_songs.album_id=music_albums.album_id "
150  "WHERE artist_name LIKE :ARTIST "
151  "AND album_name LIKE :ALBUM "
152  "AND name LIKE :TITLE "
153  "ORDER BY artist_name, album_name,"
154  " name, song_id, filename");
155 
156  query.prepare(queryString);
157 
158  query.bindValue(":ARTIST", matchartist);
159  query.bindValue(":ALBUM", matchalbum);
160  query.bindValue(":TITLE", matchtitle);
161 
162  if (!query.exec() || !query.isActive())
163  {
164  MythDB::DBError("Search music database", query);
165  return true;
166  }
167 
168  return query.size() <= 0;
169 }
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:215
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
findIcon
QString findIcon(const QString &type, const QString &name, bool ignoreCache)
find an image for a artist or genre
Definition: musicutils.cpp:34
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:214
MythCoreContext::GetMasterHostName
QString GetMasterHostName(void)
Definition: mythcorecontext.cpp:807
MusicMetadata::Genre
QString Genre() const
Definition: musicmetadata.h:175
MusicMetadata::FormatArtist
QString FormatArtist()
Definition: musicmetadata.cpp:939
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
MusicMetadata::Track
int Track() const
Definition: musicmetadata.h:199
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MusicMetadata
Definition: musicmetadata.h:80
build_compdb.file
file
Definition: build_compdb.py:55
mythdirs.h
RemoteFile::FindFileList
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.
Definition: remotefile.cpp:1296
musicutils.h
isNewTune
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:126
mythlogging.h
remotefile.h
fixFilename
QString fixFilename(const QString &filename)
remove any bad filename characters
Definition: musicutils.cpp:27
filenameFromMetadata
QString filenameFromMetadata(MusicMetadata *track)
create a filename using the template in the settings and a MusicMetadata object
Definition: musicutils.cpp:78
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
MusicMetadata::Album
QString Album() const
Definition: musicmetadata.h:150
MythCoreContext::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval=false)
Definition: mythcorecontext.cpp:906
badChars1
const static QRegularExpression badChars1
Definition: musicutils.cpp:24
mythcorecontext.h
iconMap
static QMap< QString, QString > iconMap
Definition: musicutils.cpp:33
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
fixFileToken_sl
QString fixFileToken_sl(QString token)
remove any bad filename characters (leaving '/' untouched)
Definition: musicutils.cpp:69
MusicMetadata::FormatTitle
QString FormatTitle()
Definition: musicmetadata.cpp:948
hardwareprofile.distros.shared.html.escape
def escape(tagless_text)
Definition: html.py:27
build_compdb.filename
filename
Definition: build_compdb.py:21
musicmetadata.h
badChars2
const static QRegularExpression badChars2
Definition: musicutils.cpp:25
MusicMetadata::Year
int Year() const
Definition: musicmetadata.h:196
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:898
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838