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
14
15extern "C" {
16#include <libavformat/avformat.h>
17#include <libavcodec/avcodec.h>
18}
19
20// libmythmetadata
21#include "musicmetadata.h"
22#include "musicutils.h"
23
24const static QRegularExpression badChars1 { R"((/|\\|:|'|"|\?|\|))" };
25const static QRegularExpression badChars2 { R"((/|\\|:|'|\,|\!|\‍(|\)|"|\?|\|))" };
26
27QString fixFilename(const QString &filename)
28{
29 QString ret = filename;
30 return ret.replace(badChars1, "_");
31}
32
33static QMap<QString, QString> iconMap;
34QString 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
69QString 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()))
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
126bool 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
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}
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
int Year() const
QString FormatArtist()
int Track() const
QString Genre() const
QString FormatTitle()
QString Album() const
QString GetSetting(const QString &key, const QString &defaultval="")
QString GetMasterHostName(void)
bool GetBoolSetting(const QString &key, bool defaultval=false)
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
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 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
QString fixFilename(const QString &filename)
remove any bad filename characters
Definition: musicutils.cpp:27
QString fixFileToken_sl(QString token)
remove any bad filename characters (leaving '/' untouched)
Definition: musicutils.cpp:69
static const QRegularExpression badChars2
Definition: musicutils.cpp:25
QString findIcon(const QString &type, const QString &name, bool ignoreCache)
find an image for a artist or genre
Definition: musicutils.cpp:34
static QMap< QString, QString > iconMap
Definition: musicutils.cpp:33
QString filenameFromMetadata(MusicMetadata *track)
create a filename using the template in the settings and a MusicMetadata object
Definition: musicutils.cpp:78
static const QRegularExpression badChars1
Definition: musicutils.cpp:24
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
def escape(tagless_text)
Definition: html.py:27