MythTV  master
musicmetadata.cpp
Go to the documentation of this file.
1 
2 #include "musicmetadata.h"
3 
4 // qt
5 #include <QApplication>
6 #include <QDateTime>
7 #include <QDir>
8 #include <QDomDocument>
9 #include <QScopedPointer>
10 #include <utility>
11 
12 // mythtv
13 #include "libmyth/mythcontext.h"
14 #include "libmythbase/mythdate.h"
15 #include "libmythbase/mythdate.h"
16 #include "libmythbase/mythdb.h"
17 #include "libmythbase/mythdirs.h"
21 #include "libmythbase/mythsystem.h"
22 #include "libmythbase/remotefile.h"
24 #include "libmythbase/unziputil.h"
27 
28 // libmythmetadata
29 #include "metaio.h"
30 #include "metaioid3.h"
31 #include "metaiomp4.h"
32 #include "metaioavfcomment.h"
33 #include "metaiooggvorbis.h"
34 #include "metaioflacvorbis.h"
35 #include "metaiowavpack.h"
36 #include "musicutils.h"
37 #include "lyricsdata.h"
38 
39 static QString thePrefix = "the ";
40 
42 {
43  return a.Filename() == b.Filename();
44 }
45 
47 {
48  return a.Filename() != b.Filename();
49 }
50 
51 // this ctor is for radio streams
52 MusicMetadata::MusicMetadata(int lid, QString lbroadcaster, QString lchannel, QString ldescription,
53  const UrlList &lurls, QString llogourl, QString lgenre, QString lmetaformat,
54  QString lcountry, QString llanguage, QString lformat)
55  : m_genre(std::move(lgenre)),
56  m_format(std::move(lformat)),
57  m_id(lid),
58  m_filename(lurls[0]),
59  m_broadcaster(std::move(lbroadcaster)),
60  m_channel(std::move(lchannel)),
61  m_description(std::move(ldescription)),
62  m_logoUrl(std::move(llogourl)),
63  m_metaFormat(std::move(lmetaformat)),
64  m_country(std::move(lcountry)),
65  m_language(std::move(llanguage))
66 {
67  m_urls = lurls;
70 }
71 
73 {
74  if (m_albumArt)
75  {
76  delete m_albumArt;
77  m_albumArt = nullptr;
78  }
79 
80  if (m_lyricsData)
81  {
82  delete m_lyricsData;
83  m_lyricsData = nullptr;
84  }
85 }
86 
87 
89 {
90  if (this == &rhs)
91  return *this;
92 
93  m_artist = rhs.m_artist;
97  m_album = rhs.m_album;
99  m_title = rhs.m_title;
100  m_titleSort = rhs.m_titleSort;
103  m_genre = rhs.m_genre;
104  m_year = rhs.m_year;
105  m_trackNum = rhs.m_trackNum;
107  m_discNum = rhs.m_discNum;
108  m_discCount = rhs.m_discCount;
109  m_length = rhs.m_length;
110  m_rating = rhs.m_rating;
111  m_lastPlay = rhs.m_lastPlay;
113  m_dateAdded = rhs.m_dateAdded;
114  m_playCount = rhs.m_playCount;
117  m_id = rhs.m_id;
118  m_filename = rhs.m_filename;
120  m_hostname = rhs.m_hostname;
122  m_artistId = rhs.m_artistId;
124  m_albumId = rhs.m_albumId;
125  m_genreId = rhs.m_genreId;
126  m_albumArt = nullptr;
127  m_lyricsData = nullptr;
128  m_format = rhs.m_format;
129  m_changed = rhs.m_changed;
130  m_fileSize = rhs.m_fileSize;
132  m_channel = rhs.m_channel;
134 
135  m_urls = rhs.m_urls;
136  m_logoUrl = rhs.m_logoUrl;
138  m_country = rhs.m_country;
139  m_language = rhs.m_language;
140 
141  return *this;
142 }
143 
144 // return true if this == mdata
146 {
147  return (
148  m_artist == mdata->m_artist &&
150  m_album == mdata->m_album &&
151  m_title == mdata->m_title &&
152  m_year == mdata->m_year &&
153  m_trackNum == mdata->m_trackNum &&
154  m_trackCount == mdata->m_trackCount &&
155  m_discNum == mdata->m_discNum &&
156  m_discCount == mdata->m_discCount &&
157  //m_length == mdata->m_length &&
158  m_rating == mdata->m_rating &&
159  m_lastPlay == mdata->m_lastPlay &&
160  m_playCount == mdata->m_playCount &&
161  m_compilation == mdata->m_compilation &&
162  m_filename == mdata->m_filename &&
163  m_directoryId == mdata->m_directoryId &&
164  m_artistId == mdata->m_artistId &&
165  m_compartistId == mdata->m_compartistId &&
166  m_albumId == mdata->m_albumId &&
167  m_genreId == mdata->m_genreId &&
168  m_format == mdata->m_format &&
169  m_fileSize == mdata->m_fileSize
170  );
171 }
172 
174 {
175  if (m_id < 1)
176  return;
177 
178  if (m_tempLastPlay.isValid())
179  {
182 
183  m_tempLastPlay = QDateTime();
184  }
185 
186  MSqlQuery query(MSqlQuery::InitCon());
187  query.prepare("UPDATE music_songs set rating = :RATING , "
188  "numplays = :PLAYCOUNT , lastplay = :LASTPLAY "
189  "where song_id = :ID ;");
190  query.bindValue(":RATING", m_rating);
191  query.bindValue(":PLAYCOUNT", m_playCount);
192  query.bindValue(":LASTPLAY", m_lastPlay);
193  query.bindValue(":ID", m_id);
194 
195  if (!query.exec())
196  MythDB::DBError("music persist", query);
197 
198  m_changed = false;
199 }
200 
202 {
203  if (m_id < 1)
204  return;
205 
206  MSqlQuery query(MSqlQuery::InitCon());
207  query.prepare("UPDATE music_songs SET hostname = :HOSTNAME "
208  "WHERE song_id = :ID ;");
209  query.bindValue(":HOSTNAME", m_hostname);
210  query.bindValue(":ID", m_id);
211 
212  if (!query.exec())
213  MythDB::DBError("music save hostname", query);
214 }
215 
216 // static
218 {
219  // find the trackid for this filename
220  QString sqldir = filename.section('/', 0, -2);
221 
222  QString sqlfilename = filename.section('/', -1);
223 
224  MSqlQuery query(MSqlQuery::InitCon());
225  query.prepare(
226  "SELECT song_id FROM music_songs "
227  "LEFT JOIN music_directories ON music_songs.directory_id=music_directories.directory_id "
228  "WHERE music_songs.filename = :FILENAME "
229  "AND music_directories.path = :DIRECTORY ;");
230  query.bindValue(":FILENAME", sqlfilename);
231  query.bindValue(":DIRECTORY", sqldir);
232 
233  if (!query.exec())
234  {
235  MythDB::DBError("MusicMetadata::createFromFilename", query);
236  return nullptr;
237  }
238 
239  if (!query.next())
240  {
241  LOG(VB_GENERAL, LOG_WARNING,
242  QString("MusicMetadata::createFromFilename: Could not find '%1'")
243  .arg(filename));
244  return nullptr;
245  }
246 
247  int songID = query.value(0).toInt();
248 
249  return MusicMetadata::createFromID(songID);
250 }
251 
252 // static
254 {
255  MSqlQuery query(MSqlQuery::InitCon());
256  query.prepare("SELECT music_artists.artist_name, "
257  "music_comp_artists.artist_name AS compilation_artist, "
258  "music_albums.album_name, music_songs.name, music_genres.genre, "
259  "music_songs.year, music_songs.track, music_songs.length, "
260  "music_songs.song_id, music_songs.rating, music_songs.numplays, "
261  "music_songs.lastplay, music_albums.compilation, music_songs.format, "
262  "music_songs.track_count, music_songs.size, music_songs.date_entered, "
263  "music_songs.disc_number, music_songs.disc_count, "
264  "CONCAT_WS('/', music_directories.path, music_songs.filename) AS filename, "
265  "music_songs.hostname "
266  "FROM music_songs "
267  "LEFT JOIN music_directories ON music_songs.directory_id=music_directories.directory_id "
268  "LEFT JOIN music_artists ON music_songs.artist_id=music_artists.artist_id "
269  "LEFT JOIN music_albums ON music_songs.album_id=music_albums.album_id "
270  "LEFT JOIN music_artists AS music_comp_artists ON music_albums.artist_id=music_comp_artists.artist_id "
271  "LEFT JOIN music_genres ON music_songs.genre_id=music_genres.genre_id "
272  "WHERE music_songs.song_id = :SONGID; ");
273  query.bindValue(":SONGID", trackid);
274 
275  if (query.exec() && query.next())
276  {
277  auto *mdata = new MusicMetadata();
278  mdata->m_artist = query.value(0).toString();
279  mdata->m_compilationArtist = query.value(1).toString();
280  mdata->m_album = query.value(2).toString();
281  mdata->m_title = query.value(3).toString();
282  mdata->m_genre = query.value(4).toString();
283  mdata->m_year = query.value(5).toInt();
284  mdata->m_trackNum = query.value(6).toInt();
285  mdata->m_length = std::chrono::milliseconds(query.value(7).toInt());
286  mdata->m_id = query.value(8).toUInt();
287  mdata->m_rating = query.value(9).toInt();
288  mdata->m_playCount = query.value(10).toInt();
289  mdata->m_lastPlay = query.value(11).toDateTime();
290  mdata->m_compilation = (query.value(12).toInt() > 0);
291  mdata->m_format = query.value(13).toString();
292  mdata->m_trackCount = query.value(14).toInt();
293  mdata->m_fileSize = query.value(15).toULongLong();
294  mdata->m_dateAdded = query.value(16).toDateTime();
295  mdata->m_discNum = query.value(17).toInt();
296  mdata->m_discCount = query.value(18).toInt();
297  mdata->m_filename = query.value(19).toString();
298  mdata->m_hostname = query.value(20).toString();
299  mdata->ensureSortFields();
300 
301  if (!QHostAddress(mdata->m_hostname).isNull()) // A bug caused an IP to replace hostname, reset and it will fix itself
302  {
303  mdata->m_hostname = "";
304  mdata->saveHostname();
305  }
306 
307  return mdata;
308  }
309 
310  return nullptr;
311 }
312 
313 // static
315 {
316  // we are only interested in the global setting so remove any local host setting just in case
317  GetMythDB()->ClearSetting("MusicStreamListModified");
318 
319  // make sure we are not already doing an update
320  if (gCoreContext->GetSetting("MusicStreamListModified") == "Updating")
321  {
322  LOG(VB_GENERAL, LOG_ERR, "MusicMetadata: looks like we are already updating the radio streams list");
323  return false;
324  }
325 
326  QByteArray compressedData;
327  QByteArray uncompressedData;
328 
329  // check if the streamlist has been updated since we last checked
330  QDateTime lastModified = GetMythDownloadManager()->GetLastModified(QString(STREAMUPDATEURL));
331 
332  QDateTime lastUpdate = QDateTime::fromString(gCoreContext->GetSetting("MusicStreamListModified"), Qt::ISODate);
333 
334  if (lastModified <= lastUpdate)
335  {
336  LOG(VB_GENERAL, LOG_INFO, "MusicMetadata: radio streams list is already up to date");
337  return true;
338  }
339 
340  gCoreContext->SaveSettingOnHost("MusicStreamListModified", "Updating", nullptr);
341 
342  LOG(VB_GENERAL, LOG_INFO, "MusicMetadata: downloading radio streams list");
343 
344  // download compressed stream file
345  if (!GetMythDownloadManager()->download(QString(STREAMUPDATEURL), &compressedData), false)
346  {
347  LOG(VB_GENERAL, LOG_ERR, "MusicMetadata: failed to download radio stream list");
348  gCoreContext->SaveSettingOnHost("MusicStreamListModified", "", nullptr);
349  return false;
350  }
351 
352  // uncompress the data
353  uncompressedData = gzipUncompress(compressedData);
354 
355  QString errorMsg;
356  int errorLine = 0;
357  int errorColumn = 0;
358 
359  // load the xml
360  QDomDocument domDoc;
361 
362  if (!domDoc.setContent(uncompressedData, false, &errorMsg,
363  &errorLine, &errorColumn))
364  {
365  LOG(VB_GENERAL, LOG_ERR,
366  "MusicMetadata: Could not read content of streams.xml" +
367  QString("\n\t\t\tError parsing %1").arg(STREAMUPDATEURL) +
368  QString("\n\t\t\tat line: %1 column: %2 msg: %3")
369  .arg(errorLine).arg(errorColumn).arg(errorMsg));
370  gCoreContext->SaveSettingOnHost("MusicStreamListModified", "", nullptr);
371  return false;
372  }
373 
374  MSqlQuery query(MSqlQuery::InitCon());
375  query.prepare("DELETE FROM music_streams;");
376  if (!query.exec() || !query.isActive() || query.numRowsAffected() < 0)
377  {
378  MythDB::DBError("music delete radio streams", query);
379  gCoreContext->SaveSettingOnHost("MusicStreamListModified", "", nullptr);
380  return false;
381  }
382 
383  LOG(VB_GENERAL, LOG_INFO, "MusicMetadata: processing radio streams list");
384 
385  QDomNodeList itemList = domDoc.elementsByTagName("item");
386 
387  QDomNode itemNode;
388  for (int i = 0; i < itemList.count(); i++)
389  {
390  itemNode = itemList.item(i);
391 
392  query.prepare("INSERT INTO music_streams (broadcaster, channel, description, url1, url2, url3, url4, url5,"
393  " logourl, genre, metaformat, country, language) "
394  "VALUES (:BROADCASTER, :CHANNEL, :DESC, :URL1, :URL2, :URL3, :URL4, :URL5,"
395  " :LOGOURL, :GENRE, :META, :COUNTRY, :LANG);");
396 
397  query.bindValue(":BROADCASTER", itemNode.namedItem(QString("broadcaster")).toElement().text());
398  query.bindValue(":CHANNEL", itemNode.namedItem(QString("channel")).toElement().text());
399  query.bindValue(":DESC", itemNode.namedItem(QString("description")).toElement().text());
400  query.bindValue(":URL1", itemNode.namedItem(QString("url1")).toElement().text());
401  query.bindValue(":URL2", itemNode.namedItem(QString("url2")).toElement().text());
402  query.bindValue(":URL3", itemNode.namedItem(QString("url3")).toElement().text());
403  query.bindValue(":URL4", itemNode.namedItem(QString("url4")).toElement().text());
404  query.bindValue(":URL5", itemNode.namedItem(QString("url5")).toElement().text());
405  query.bindValue(":LOGOURL", itemNode.namedItem(QString("logourl")).toElement().text());
406  query.bindValue(":GENRE", itemNode.namedItem(QString("genre")).toElement().text());
407  query.bindValue(":META", itemNode.namedItem(QString("metadataformat")).toElement().text());
408  query.bindValue(":COUNTRY", itemNode.namedItem(QString("country")).toElement().text());
409  query.bindValue(":LANG", itemNode.namedItem(QString("language")).toElement().text());
410 
411  if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
412  {
413  MythDB::DBError("music insert radio stream", query);
414  gCoreContext->SaveSettingOnHost("MusicStreamListModified", "", nullptr);
415  return false;
416  }
417  }
418 
419  gCoreContext->SaveSettingOnHost("MusicStreamListModified", lastModified.toString(Qt::ISODate), nullptr);
420 
421  LOG(VB_GENERAL, LOG_INFO, "MusicMetadata: updating radio streams list completed OK");
422 
423  return true;
424 }
425 
427 {
429 
430  if (!mdata)
431  {
432  LOG(VB_GENERAL, LOG_ERR, QString("MusicMetadata: Asked to reload metadata "
433  "for trackID: %1 but not found!").arg(m_id));
434 
435  return;
436  }
437 
438  *this = *mdata;
439 
440  delete mdata;
441 
442  m_directoryId = -1;
443  m_artistId = -1;
444  m_compartistId = -1;
445  m_albumId = -1;
446  m_genreId = -1;
447 }
448 
450 {
451  if (m_directoryId < 0)
452  {
453  QString sqldir = m_filename.section('/', 0, -2);
454 
456 
457  MSqlQuery query(MSqlQuery::InitCon());
458 
459  if (sqldir.isEmpty())
460  {
461  m_directoryId = 0;
462  }
463  else if (m_directoryId < 0)
464  {
465  // Load the directory id
466  query.prepare("SELECT directory_id FROM music_directories "
467  "WHERE path = :DIRECTORY ;");
468  query.bindValue(":DIRECTORY", sqldir);
469 
470  if (!query.exec() || !query.isActive())
471  {
472  MythDB::DBError("music select directory id", query);
473  return -1;
474  }
475  if (query.next())
476  {
477  m_directoryId = query.value(0).toInt();
478  }
479  else
480  {
481  query.prepare("INSERT INTO music_directories (path) VALUES (:DIRECTORY);");
482  query.bindValue(":DIRECTORY", sqldir);
483 
484  if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
485  {
486  MythDB::DBError("music insert directory", query);
487  return -1;
488  }
489  m_directoryId = query.lastInsertId().toInt();
490  }
491  }
492  }
493 
494  return m_directoryId;
495 }
496 
498 {
499  if (m_artistId < 0)
500  {
501  MSqlQuery query(MSqlQuery::InitCon());
502 
503  // Load the artist id
504  query.prepare("SELECT artist_id FROM music_artists "
505  "WHERE artist_name = :ARTIST ;");
506  query.bindValueNoNull(":ARTIST", m_artist);
507 
508  if (!query.exec() || !query.isActive())
509  {
510  MythDB::DBError("music select artist id", query);
511  return -1;
512  }
513  if (query.next())
514  {
515  m_artistId = query.value(0).toInt();
516  }
517  else
518  {
519  query.prepare("INSERT INTO music_artists (artist_name) VALUES (:ARTIST);");
520  query.bindValueNoNull(":ARTIST", m_artist);
521 
522  if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
523  {
524  MythDB::DBError("music insert artist", query);
525  return -1;
526  }
527  m_artistId = query.lastInsertId().toInt();
528  }
529  }
530 
531  return m_artistId;
532 }
533 
535 {
536  if (m_compartistId < 0) {
537  MSqlQuery query(MSqlQuery::InitCon());
538 
539  // Compilation Artist
541  {
543  }
544  else
545  {
546  query.prepare("SELECT artist_id FROM music_artists "
547  "WHERE artist_name = :ARTIST ;");
548  query.bindValueNoNull(":ARTIST", m_compilationArtist);
549  if (!query.exec() || !query.isActive())
550  {
551  MythDB::DBError("music select compilation artist id", query);
552  return -1;
553  }
554  if (query.next())
555  {
556  m_compartistId = query.value(0).toInt();
557  }
558  else
559  {
560  query.prepare("INSERT INTO music_artists (artist_name) VALUES (:ARTIST);");
561  query.bindValueNoNull(":ARTIST", m_compilationArtist);
562 
563  if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
564  {
565  MythDB::DBError("music insert compilation artist", query);
566  return -1 ;
567  }
568  m_compartistId = query.lastInsertId().toInt();
569  }
570  }
571  }
572 
573  return m_compartistId;
574 }
575 
577 {
578  if (m_albumId < 0)
579  {
580  MSqlQuery query(MSqlQuery::InitCon());
581 
582  query.prepare("SELECT album_id FROM music_albums "
583  "WHERE artist_id = :COMP_ARTIST_ID "
584  " AND album_name = :ALBUM ;");
585  query.bindValueNoNull(":COMP_ARTIST_ID", m_compartistId);
586  query.bindValueNoNull(":ALBUM", m_album);
587  if (!query.exec() || !query.isActive())
588  {
589  MythDB::DBError("music select album id", query);
590  return -1;
591  }
592  if (query.next())
593  {
594  m_albumId = query.value(0).toInt();
595  }
596  else
597  {
598  query.prepare("INSERT INTO music_albums (artist_id, album_name, compilation, year) "
599  "VALUES (:COMP_ARTIST_ID, :ALBUM, :COMPILATION, :YEAR);");
600  query.bindValueNoNull(":COMP_ARTIST_ID", m_compartistId);
601  query.bindValueNoNull(":ALBUM", m_album);
602  query.bindValue(":COMPILATION", m_compilation);
603  query.bindValue(":YEAR", m_year);
604 
605  if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
606  {
607  MythDB::DBError("music insert album", query);
608  return -1;
609  }
610  m_albumId = query.lastInsertId().toInt();
611  }
612  }
613 
614  return m_albumId;
615 }
616 
618 {
619  if (m_genreId < 0)
620  {
621  MSqlQuery query(MSqlQuery::InitCon());
622 
623  query.prepare("SELECT genre_id FROM music_genres "
624  "WHERE genre = :GENRE ;");
625  query.bindValueNoNull(":GENRE", m_genre);
626  if (!query.exec() || !query.isActive())
627  {
628  MythDB::DBError("music select genre id", query);
629  return -1;
630  }
631  if (query.next())
632  {
633  m_genreId = query.value(0).toInt();
634  }
635  else
636  {
637  query.prepare("INSERT INTO music_genres (genre) VALUES (:GENRE);");
638  query.bindValueNoNull(":GENRE", m_genre);
639 
640  if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
641  {
642  MythDB::DBError("music insert genre", query);
643  return -1;
644  }
645  m_genreId = query.lastInsertId().toInt();
646  }
647  }
648 
649  return m_genreId;
650 }
651 
652 void MusicMetadata::setUrl(const QString& url, uint index)
653 {
654  if (index < STREAMURLCOUNT)
655  m_urls[index] = url;
656 }
657 
658 QString MusicMetadata::Url(uint index)
659 {
660  if (index < STREAMURLCOUNT)
661  return m_urls[index];
662 
663  return QString();
664 }
665 
667 {
669 
670  if (m_directoryId < 0)
671  getDirectoryId();
672 
673  if (m_artistId < 0)
674  getArtistId();
675 
676  if (m_compartistId < 0)
678 
679  if (m_albumId < 0)
680  getAlbumId();
681 
682  if (m_genreId < 0)
683  getGenreId();
684 
685  // We have all the id's now. We can insert it.
686  QString strQuery;
687  if (m_id < 1)
688  {
689  strQuery = "INSERT INTO music_songs ( directory_id,"
690  " artist_id, album_id, name, genre_id,"
691  " year, track, length, filename,"
692  " rating, format, date_entered, date_modified,"
693  " numplays, track_count, disc_number, disc_count,"
694  " size, hostname) "
695  "VALUES ( "
696  " :DIRECTORY, "
697  " :ARTIST, :ALBUM, :TITLE, :GENRE,"
698  " :YEAR, :TRACKNUM, :LENGTH, :FILENAME,"
699  " :RATING, :FORMAT, :DATE_ADD, :DATE_MOD,"
700  " :PLAYCOUNT,:TRACKCOUNT, :DISC_NUMBER, :DISC_COUNT,"
701  " :SIZE, :HOSTNAME );";
702  }
703  else
704  {
705  strQuery = "UPDATE music_songs SET"
706  " directory_id = :DIRECTORY"
707  ", artist_id = :ARTIST"
708  ", album_id = :ALBUM"
709  ", name = :TITLE"
710  ", genre_id = :GENRE"
711  ", year = :YEAR"
712  ", track = :TRACKNUM"
713  ", length = :LENGTH"
714  ", filename = :FILENAME"
715  ", rating = :RATING"
716  ", format = :FORMAT"
717  ", date_modified = :DATE_MOD "
718  ", numplays = :PLAYCOUNT "
719  ", track_count = :TRACKCOUNT "
720  ", disc_number = :DISC_NUMBER "
721  ", disc_count = :DISC_COUNT "
722  ", size = :SIZE "
723  ", hostname = :HOSTNAME "
724  "WHERE song_id= :ID ;";
725  }
726 
727  QString sqlfilename = m_filename.section('/', -1);
728 
729  MSqlQuery query(MSqlQuery::InitCon());
730 
731  query.prepare(strQuery);
732 
733  query.bindValue(":DIRECTORY", m_directoryId);
734  query.bindValue(":ARTIST", m_artistId);
735  query.bindValue(":ALBUM", m_albumId);
736  query.bindValue(":TITLE", m_title);
737  query.bindValue(":GENRE", m_genreId);
738  query.bindValue(":YEAR", m_year);
739  query.bindValue(":TRACKNUM", m_trackNum);
740  query.bindValue(":LENGTH", static_cast<qint64>(m_length.count()));
741  query.bindValue(":FILENAME", sqlfilename);
742  query.bindValue(":RATING", m_rating);
743  query.bindValueNoNull(":FORMAT", m_format);
744  query.bindValue(":DATE_MOD", MythDate::current());
745  query.bindValue(":PLAYCOUNT", m_playCount);
746 
747  if (m_id < 1)
748  query.bindValue(":DATE_ADD", MythDate::current());
749  else
750  query.bindValue(":ID", m_id);
751 
752  query.bindValue(":TRACKCOUNT", m_trackCount);
753  query.bindValue(":DISC_NUMBER", m_discNum);
754  query.bindValue(":DISC_COUNT",m_discCount);
755  query.bindValue(":SIZE", (quint64)m_fileSize);
756  query.bindValue(":HOSTNAME", m_hostname);
757 
758  if (!query.exec())
759  MythDB::DBError("MusicMetadata::dumpToDatabase - updating music_songs",
760  query);
761 
762  if (m_id < 1 && query.isActive() && 1 == query.numRowsAffected())
763  m_id = query.lastInsertId().toInt();
764 
765  // save the albumart to the db
766  if (m_albumArt)
768 
769  // update the album
770  query.prepare("UPDATE music_albums SET album_name = :ALBUM_NAME, "
771  "artist_id = :COMP_ARTIST_ID, compilation = :COMPILATION, "
772  "year = :YEAR "
773  "WHERE music_albums.album_id = :ALBUMID");
774  query.bindValue(":ALBUMID", m_albumId);
775  query.bindValue(":ALBUM_NAME", m_album);
776  query.bindValue(":COMP_ARTIST_ID", m_compartistId);
777  query.bindValue(":COMPILATION", m_compilation);
778  query.bindValue(":YEAR", m_year);
779 
780  if (!query.exec() || !query.isActive())
781  {
782  MythDB::DBError("music compilation update", query);
783  return;
784  }
785 }
786 
787 // Default values for formats
788 // NB These will eventually be customizable....
789 QString MusicMetadata::s_formatNormalFileArtist = "ARTIST";
791 QString MusicMetadata::s_formatNormalCdArtist = "ARTIST";
792 QString MusicMetadata::s_formatNormalCdTrack = "TITLE";
793 QString MusicMetadata::s_formatCompilationFileArtist = "COMPARTIST";
794 QString MusicMetadata::s_formatCompilationFileTrack = "TITLE (ARTIST)";
795 QString MusicMetadata::s_formatCompilationCdArtist = "COMPARTIST";
796 QString MusicMetadata::s_formatCompilationCdTrack = "TITLE (ARTIST)";
797 
799 {
800  QString tmp;
801 
802  tmp = gCoreContext->GetSetting("MusicFormatNormalFileArtist");
803  if (!tmp.isEmpty())
805 
806  tmp = gCoreContext->GetSetting("MusicFormatNormalFileTrack");
807  if (!tmp.isEmpty())
809 
810  tmp = gCoreContext->GetSetting("MusicFormatNormalCDArtist");
811  if (!tmp.isEmpty())
813 
814  tmp = gCoreContext->GetSetting("MusicFormatNormalCDTrack");
815  if (!tmp.isEmpty())
817 
818  tmp = gCoreContext->GetSetting("MusicFormatCompilationFileArtist");
819  if (!tmp.isEmpty())
821 
822  tmp = gCoreContext->GetSetting("MusicFormatCompilationFileTrack");
823  if (!tmp.isEmpty())
825 
826  tmp = gCoreContext->GetSetting("MusicFormatCompilationCDArtist");
827  if (!tmp.isEmpty())
829 
830  tmp = gCoreContext->GetSetting("MusicFormatCompilationCDTrack");
831  if (!tmp.isEmpty())
833 }
834 
835 
837 {
838  m_compilation = (!m_compilationArtist.isEmpty()
841  return m_compilation;
842 }
843 
844 
845 inline QString MusicMetadata::formatReplaceSymbols(const QString &format)
846 {
847  QString rv = format;
848  rv.replace("COMPARTIST", m_compilationArtist);
849  rv.replace("ARTIST", m_artist);
850  rv.replace("TITLE", m_title);
851  rv.replace("TRACK", QString("%1").arg(m_trackNum, 2));
852  return rv;
853 }
854 
856 {
857  if (m_artist.isEmpty())
858  {
859  m_artist = tr("Unknown Artist", "Default artist if no artist");
860  m_artistId = -1;
861  }
862  // This should be the same as Artist if it's a compilation track or blank
863  if (!m_compilation || m_compilationArtist.isEmpty())
864  {
866  m_compartistId = -1;
867  }
868  if (m_album.isEmpty())
869  {
870  m_album = tr("Unknown Album", "Default album if no album");
871  m_albumId = -1;
872  }
873  if (m_title.isEmpty())
875  if (m_genre.isEmpty())
876  {
877  m_genre = tr("Unknown Genre", "Default genre if no genre");
878  m_genreId = -1;
879  }
881 }
882 
884 {
885  std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
886 
887  if (m_artistSort.isEmpty() and not m_artist.isEmpty())
888  m_artistSort = sh->doTitle(m_artist);
889  if (m_compilationArtistSort.isEmpty() and not m_compilationArtist.isEmpty())
891  if (m_albumSort.isEmpty() and not m_album.isEmpty())
892  m_albumSort = sh->doTitle(m_album);
893  if (m_titleSort.isEmpty() and not m_title.isEmpty())
894  m_titleSort = sh->doTitle(m_title);
895 }
896 
898 {
899  QString format_artist;
900  QString format_title;
901 
902  if (!m_compilation
903  || "" == m_compilationArtist
905  {
906  if (!cd)
907  {
908  format_artist = s_formatNormalFileArtist;
909  format_title = s_formatNormalFileTrack;
910  }
911  else
912  {
913  format_artist = s_formatNormalCdArtist;
914  format_title = s_formatNormalCdTrack;
915  }
916  }
917  else
918  {
919  if (!cd)
920  {
921  format_artist = s_formatCompilationFileArtist;
922  format_title = s_formatCompilationFileTrack;
923  }
924  else
925  {
926  format_artist = s_formatCompilationCdArtist;
927  format_title = s_formatCompilationCdTrack;
928  }
929  }
930 
931  // NB Could do some comparisons here to save memory with shallow copies...
932  m_formattedArtist = formatReplaceSymbols(format_artist);
933  m_formattedTitle = formatReplaceSymbols(format_title);
934 }
935 
936 
938 {
939  if (m_formattedArtist.isEmpty())
941 
942  return m_formattedArtist;
943 }
944 
945 
947 {
948  if (m_formattedTitle.isEmpty())
950 
951  return m_formattedTitle;
952 }
953 
954 void MusicMetadata::setFilename(const QString& lfilename)
955 {
956  m_filename = lfilename;
957  m_actualFilename.clear();
958 }
959 
961 {
962  // FIXME: for now just use the first url for radio streams
963  if (isRadio())
964  return m_urls[0];
965 
966  // if not asked to find the file just return the raw filename from the DB
967  if (!find)
968  return m_filename;
969 
970  if (!m_actualFilename.isEmpty())
971  return m_actualFilename;
972 
973  // check for a cd track
974  if (m_filename.endsWith(".cda"))
975  {
977  return m_filename;
978  }
979 
980  // check for http urls etc
981  if (m_filename.contains("://"))
982  {
984  return m_filename;
985  }
986 
987  // first check to see if the filename is complete
988  if (QFile::exists(m_filename))
989  {
991  return m_filename;
992  }
993 
994  // maybe it's in our 'Music' storage group
995  QString mythUrl = RemoteFile::FindFile(m_filename, m_hostname, "Music");
996  if (!mythUrl.isEmpty())
997  {
998  m_actualFilename = mythUrl;
999 
1000  QUrl url(mythUrl);
1001  if (url.host() != m_hostname &&
1002  QHostAddress(url.host()).isNull()) // Check that it's not an IP address
1003  {
1004  m_hostname = url.host();
1005  saveHostname();
1006  }
1007 
1008  return mythUrl;
1009  }
1010 
1011  // not found
1012  LOG(VB_GENERAL, LOG_ERR, QString("MusicMetadata: Asked to get the filename for a track but no file found: %1")
1013  .arg(m_filename));
1014 
1016 
1017  return m_actualFilename;
1018 }
1019 
1022 {
1023  // try the file name as is first
1024  if (QFile::exists(m_filename))
1025  return m_filename;
1026 
1027  // not found so now try to find the file in the local 'Music' storage group
1028  StorageGroup storageGroup("Music", gCoreContext->GetHostName(), false);
1029  return storageGroup.FindFile(m_filename);
1030 }
1031 
1032 void MusicMetadata::setField(const QString &field, const QString &data)
1033 {
1034  if (field == "artist")
1035  m_artist = data;
1036  else if (field == "compilation_artist")
1037  m_compilationArtist = data;
1038  else if (field == "album")
1039  m_album = data;
1040  else if (field == "title")
1041  m_title = data;
1042  else if (field == "genre")
1043  m_genre = data;
1044  else if (field == "filename")
1045  m_filename = data;
1046  else if (field == "year")
1047  m_year = data.toInt();
1048  else if (field == "tracknum")
1049  m_trackNum = data.toInt();
1050  else if (field == "trackcount")
1051  m_trackCount = data.toInt();
1052  else if (field == "discnum")
1053  m_discNum = data.toInt();
1054  else if (field == "disccount")
1055  m_discCount = data.toInt();
1056  else if (field == "length")
1057  m_length = std::chrono::milliseconds(data.toInt());
1058  else if (field == "compilation")
1059  m_compilation = (data.toInt() > 0);
1060  else
1061  {
1062  LOG(VB_GENERAL, LOG_ERR, QString("Something asked me to set data "
1063  "for a field called %1").arg(field));
1064  }
1065  ensureSortFields();
1066 }
1067 
1068 void MusicMetadata::getField(const QString &field, QString *data)
1069 {
1070  if (field == "artist")
1071  *data = FormatArtist();
1072  else if (field == "album")
1073  *data = m_album;
1074  else if (field == "title")
1075  *data = FormatTitle();
1076  else if (field == "genre")
1077  *data = m_genre;
1078  else
1079  {
1080  LOG(VB_GENERAL, LOG_ERR, QString("Something asked me to return data "
1081  "about a field called %1").arg(field));
1082  *data = "I Dunno";
1083  }
1084 }
1085 
1086 void MusicMetadata::toMap(InfoMap &metadataMap, const QString &prefix)
1087 {
1088  using namespace MythDate;
1089  metadataMap[prefix + "songid"] = QString::number(m_id);
1090  metadataMap[prefix + "artist"] = m_artist;
1091  metadataMap[prefix + "formatartist"] = FormatArtist();
1092  metadataMap[prefix + "compilationartist"] = m_compilationArtist;
1093 
1094  if (m_album.isEmpty() && ID_TO_REPO(m_id) == RT_Radio)
1095  {
1096  if (m_broadcaster.isEmpty())
1097  metadataMap[prefix + "album"] = m_channel;
1098  else
1099  metadataMap[prefix + "album"] = QString("%1 - %2").arg(m_broadcaster, m_channel);
1100  }
1101  else
1102  metadataMap[prefix + "album"] = m_album;
1103 
1104  metadataMap[prefix + "title"] = m_title;
1105  metadataMap[prefix + "formattitle"] = FormatTitle();
1106  metadataMap[prefix + "tracknum"] = (m_trackNum > 0 ? QString("%1").arg(m_trackNum) : "");
1107  metadataMap[prefix + "trackcount"] = (m_trackCount > 0 ? QString("%1").arg(m_trackCount) : "");
1108  metadataMap[prefix + "discnum"] = (m_discNum > 0 ? QString("%1").arg(m_discNum) : "");
1109  metadataMap[prefix + "disccount"] = (m_discCount > 0 ? QString("%1").arg(m_discCount) : "");
1110  metadataMap[prefix + "genre"] = m_genre;
1111  metadataMap[prefix + "year"] = (m_year > 0 ? QString("%1").arg(m_year) : "");
1112 
1113  QString fmt = (m_length >= 1h) ? "H:mm:ss" : "mm:ss";
1114  metadataMap[prefix + "length"] = MythDate::formatTime(m_length, fmt);
1115 
1116  if (m_lastPlay.isValid())
1117  {
1118  metadataMap[prefix + "lastplayed"] =
1120  }
1121  else
1122  {
1123  metadataMap[prefix + "lastplayed"] = tr("Never Played");
1124  }
1125 
1126  metadataMap[prefix + "dateadded"] = MythDate::toString(
1128 
1129  metadataMap[prefix + "playcount"] = QString::number(m_playCount);
1130 
1131  QLocale locale = gCoreContext->GetQLocale();
1132  QString tmpSize = locale.toString(m_fileSize *
1133  (1.0 / (1024.0 * 1024.0)), 'f', 2);
1134  metadataMap[prefix + "filesize"] = tmpSize;
1135 
1136  metadataMap[prefix + "filename"] = m_filename;
1137 
1138  // radio stream
1139  if (!m_broadcaster.isEmpty())
1140  metadataMap[prefix + "broadcasterchannel"] = m_broadcaster + " - " + m_channel;
1141  else
1142  metadataMap[prefix + "broadcasterchannel"] = m_channel;
1143  metadataMap[prefix + "broadcaster"] = m_broadcaster;
1144  metadataMap[prefix + "channel"] = m_channel;
1145  metadataMap[prefix + "genre"] = m_genre;
1146  metadataMap[prefix + "country"] = m_country;
1147  metadataMap[prefix + "language"] = m_language;
1148  metadataMap[prefix + "description"] = m_description;
1149 
1150  if (isRadio())
1151  {
1152  QUrl url(m_urls[0]);
1153  metadataMap[prefix + "url"] = url.toString(QUrl::RemoveUserInfo);
1154  }
1155  else
1156  metadataMap[prefix + "url"] = m_filename;
1157 
1158  metadataMap[prefix + "logourl"] = m_logoUrl;
1159  metadataMap[prefix + "metadataformat"] = m_metaFormat;
1160 }
1161 
1163 {
1164  if (m_rating > 0)
1165  {
1166  m_rating--;
1167  }
1168  m_changed = true;
1169 }
1170 
1172 {
1173  if (m_rating < 10)
1174  {
1175  m_rating++;
1176  }
1177  m_changed = true;
1178 }
1179 
1180 void MusicMetadata::setLastPlay(const QDateTime& lastPlay)
1181 {
1182  m_tempLastPlay = MythDate::as_utc(lastPlay);
1183  m_changed = true;
1184 }
1185 
1187 {
1189  m_changed = true;
1190 }
1191 
1193 {
1195  m_changed = true;
1196 }
1197 
1199 {
1200  // add the images found in the tag to the ones we got from the DB
1201 
1202  if (!m_albumArt)
1203  m_albumArt = new AlbumArtImages(this, false);
1204 
1205  for (auto *art : qAsConst(albumart))
1206  {
1207  art->m_filename = QString("%1-%2").arg(m_id).arg(art->m_filename);
1208  m_albumArt->addImage(art);
1209  }
1210 
1211  m_changed = true;
1212 }
1213 
1214 QStringList MusicMetadata::fillFieldList(const QString& field)
1215 {
1216  QStringList searchList;
1217  searchList.clear();
1218 
1219  MSqlQuery query(MSqlQuery::InitCon());
1220  if ("artist" == field)
1221  {
1222  query.prepare("SELECT artist_name FROM music_artists ORDER BY artist_name;");
1223  }
1224  else if ("compilation_artist" == field)
1225  {
1226  query.prepare("SELECT DISTINCT artist_name FROM music_artists, music_albums where "
1227  "music_albums.artist_id=music_artists.artist_id ORDER BY artist_name");
1228  }
1229  else if ("album" == field)
1230  {
1231  query.prepare("SELECT album_name FROM music_albums ORDER BY album_name;");
1232  }
1233  else if ("title" == field)
1234  {
1235  query.prepare("SELECT name FROM music_songs ORDER BY name;");
1236  }
1237  else if ("genre" == field)
1238  {
1239  query.prepare("SELECT genre FROM music_genres ORDER BY genre;");
1240  }
1241  else
1242  {
1243  return searchList;
1244  }
1245 
1246  if (query.exec() && query.isActive())
1247  {
1248  while (query.next())
1249  {
1250  searchList << query.value(0).toString();
1251  }
1252  }
1253  return searchList;
1254 }
1255 
1257 {
1258  if (!m_albumArt)
1259  m_albumArt = new AlbumArtImages(this);
1260 
1261  AlbumArtImage *albumart_image = nullptr;
1262  QString res;
1263 
1264  if ((albumart_image = m_albumArt->getImage(IT_FRONTCOVER)))
1265  res = albumart_image->m_filename; // NOLINT(bugprone-branch-clone)
1266  else if ((albumart_image = m_albumArt->getImage(IT_UNKNOWN)))
1267  res = albumart_image->m_filename;
1268  else if ((albumart_image = m_albumArt->getImage(IT_BACKCOVER)))
1269  res = albumart_image->m_filename;
1270  else if ((albumart_image = m_albumArt->getImage(IT_INLAY)))
1271  res = albumart_image->m_filename;
1272  else if ((albumart_image = m_albumArt->getImage(IT_CD)))
1273  res = albumart_image->m_filename;
1274 
1275  // check file exists
1276  if (!res.isEmpty() && albumart_image)
1277  {
1278  int repo = ID_TO_REPO(m_id);
1279  if (repo == RT_Radio)
1280  {
1281  // image is a radio station icon, check if we have already downloaded and cached it
1282  QString path = GetConfDir() + "/MythMusic/AlbumArt/";
1283  QFileInfo fi(res);
1284  QString filename = QString("%1-%2.%3").arg(m_id).arg("front", fi.suffix());
1285 
1286  albumart_image->m_filename = path + filename;
1287 
1288  if (!QFile::exists(albumart_image->m_filename))
1289  {
1290  // file does not exist so try to download and cache it
1291  if (!GetMythDownloadManager()->download(res, albumart_image->m_filename))
1292  {
1293  m_albumArt->getImageList()->removeAll(albumart_image);
1294  return QString("");
1295  }
1296  }
1297 
1298  res = albumart_image->m_filename;
1299  }
1300  else
1301  {
1302  // check for the image in the storage group
1303  QUrl url(res);
1304 
1305  if (url.path().isEmpty() || url.host().isEmpty() || url.userName().isEmpty())
1306  {
1307  return QString("");
1308  }
1309 
1310  if (!RemoteFile::Exists(res))
1311  {
1312  if (albumart_image->m_embedded)
1313  {
1314  if (gCoreContext->IsMasterBackend() &&
1315  url.host() == gCoreContext->GetMasterHostName())
1316  {
1317  QStringList paramList;
1318  paramList.append(QString("--songid='%1'").arg(ID()));
1319  paramList.append(QString("--imagetype='%1'").arg(albumart_image->m_imageType));
1320 
1321  QString command = "mythutil --extractimage " + paramList.join(" ");
1322 
1323  QScopedPointer<MythSystem> cmd(MythSystem::Create(command,
1327  }
1328  else
1329  {
1330  QStringList slist;
1331  slist << "MUSIC_TAG_GETIMAGE"
1332  << Hostname()
1333  << QString::number(ID())
1334  << QString::number(albumart_image->m_imageType);
1336  }
1337  }
1338  }
1339  }
1340 
1341  return res;
1342  }
1343 
1344  return QString("");
1345 }
1346 
1348 {
1349  if (!m_albumArt)
1350  m_albumArt = new AlbumArtImages(this);
1351 
1352  AlbumArtImage *albumart_image = m_albumArt->getImage(type);
1353  if (albumart_image)
1354  return albumart_image->m_filename;
1355 
1356  return QString("");
1357 }
1358 
1360 {
1361  if (!m_albumArt)
1362  m_albumArt = new AlbumArtImages(this);
1363 
1364  return m_albumArt;
1365 }
1366 
1368 {
1369  delete m_albumArt;
1370  m_albumArt = nullptr; //new AlbumArtImages(this);
1371 }
1372 
1374 {
1375  if (!m_lyricsData)
1376  m_lyricsData = new LyricsData(this);
1377 
1378  return m_lyricsData;
1379 }
1380 
1381 // create a MetaIO for the file to read/write any tags etc
1382 // NOTE the caller is responsible for deleting it
1384 {
1385  // the taggers require direct file access so try to find
1386  // the file on the local filesystem
1387 
1388  QString filename = getLocalFilename();
1389 
1390  if (!filename.isEmpty())
1391  {
1392  LOG(VB_FILE, LOG_INFO, QString("MusicMetadata::getTagger - creating tagger for %1").arg(filename));
1394  }
1395 
1396  LOG(VB_GENERAL, LOG_ERR, QString("MusicMetadata::getTagger - failed to find %1 on the local filesystem").arg(Filename(false)));
1397  return nullptr;
1398 }
1399 
1400 //--------------------------------------------------------------------------
1401 
1403 {
1404  RunProlog();
1405  //if you want to simulate a big music collection load
1406  //sleep(3);
1407  m_parent->resync();
1408  RunEpilog();
1409 }
1410 
1412 {
1413  // Start a thread to do data loading and sorting
1414  startLoading();
1415 }
1416 
1418 {
1419  while (!m_allMusic.empty())
1420  {
1421  delete m_allMusic.back();
1422  m_allMusic.pop_back();
1423  }
1424 
1425  while (!m_cdData.empty())
1426  {
1427  delete m_cdData.back();
1428  m_cdData.pop_back();
1429  }
1430 
1431  m_metadataLoader->wait();
1432  delete m_metadataLoader;
1433 }
1434 
1436 {
1437  // If this is still running, the user
1438  // probably selected mythmusic and then
1439  // escaped out right away
1440 
1441  if (m_metadataLoader->isFinished())
1442  {
1443  return true;
1444  }
1445 
1446  m_metadataLoader->wait();
1447  return false;
1448 }
1449 
1462 {
1463  // Set this to false early rather than letting it be
1464  // delayed till the thread calls resync.
1465  m_doneLoading = false;
1466 
1467  if (m_metadataLoader)
1468  {
1469  cleanOutThreads();
1470  delete m_metadataLoader;
1471  }
1472 
1473  m_metadataLoader = new MetadataLoadingThread(this);
1474  m_metadataLoader->start();
1475 
1476  return true;
1477 }
1478 
1481 {
1482  uint added = 0;
1483  uint removed = 0;
1484  uint changed = 0;
1485 
1486  m_doneLoading = false;
1487 
1488  QString aquery = "SELECT music_songs.song_id, music_artists.artist_id, music_artists.artist_name, "
1489  "music_comp_artists.artist_name AS compilation_artist, "
1490  "music_albums.album_id, music_albums.album_name, music_songs.name, music_genres.genre, music_songs.year, "
1491  "music_songs.track, music_songs.length, music_songs.directory_id, "
1492  "CONCAT_WS('/', music_directories.path, music_songs.filename) AS filename, "
1493  "music_songs.rating, music_songs.numplays, music_songs.lastplay, music_songs.date_entered, "
1494  "music_albums.compilation, music_songs.format, music_songs.track_count, "
1495  "music_songs.size, music_songs.hostname, music_songs.disc_number, music_songs.disc_count "
1496  "FROM music_songs "
1497  "LEFT JOIN music_directories ON music_songs.directory_id=music_directories.directory_id "
1498  "LEFT JOIN music_artists ON music_songs.artist_id=music_artists.artist_id "
1499  "LEFT JOIN music_albums ON music_songs.album_id=music_albums.album_id "
1500  "LEFT JOIN music_artists AS music_comp_artists ON music_albums.artist_id=music_comp_artists.artist_id "
1501  "LEFT JOIN music_genres ON music_songs.genre_id=music_genres.genre_id "
1502  "ORDER BY music_songs.song_id;";
1503 
1504  MSqlQuery query(MSqlQuery::InitCon());
1505  if (!query.exec(aquery))
1506  MythDB::DBError("AllMusic::resync", query);
1507 
1508  m_numPcs = query.size() * 2;
1509  m_numLoaded = 0;
1510  QList<MusicMetadata::IdType> idList;
1511 
1512  if (query.isActive() && query.size() > 0)
1513  {
1514  while (query.next())
1515  {
1516  MusicMetadata::IdType id = query.value(0).toInt();
1517 
1518  idList.append(id);
1519 
1520  auto *dbMeta = new MusicMetadata(
1521  query.value(12).toString(), // filename
1522  query.value(2).toString(), // artist
1523  query.value(3).toString(), // compilation artist
1524  query.value(5).toString(), // album
1525  query.value(6).toString(), // title
1526  query.value(7).toString(), // genre
1527  query.value(8).toInt(), // year
1528  query.value(9).toInt(), // track no.
1529  std::chrono::milliseconds(query.value(10).toInt()), // length
1530  query.value(0).toInt(), // id
1531  query.value(13).toInt(), // rating
1532  query.value(14).toInt(), // playcount
1533  query.value(15).toDateTime(), // lastplay
1534  query.value(16).toDateTime(), // date_entered
1535  (query.value(17).toInt() > 0), // compilation
1536  query.value(18).toString()); // format
1537 
1538  dbMeta->setDirectoryId(query.value(11).toInt());
1539  dbMeta->setArtistId(query.value(1).toInt());
1540  dbMeta->setCompilationArtistId(query.value(3).toInt());
1541  dbMeta->setAlbumId(query.value(4).toInt());
1542  dbMeta->setTrackCount(query.value(19).toInt());
1543  dbMeta->setFileSize(query.value(20).toULongLong());
1544  dbMeta->setHostname(query.value(21).toString());
1545  dbMeta->setDiscNumber(query.value(22).toInt());
1546  dbMeta->setDiscCount(query.value(23).toInt());
1547 
1548  if (!m_musicMap.contains(id))
1549  {
1550  // new track
1551 
1552  // Don't delete dbMeta, as the MetadataPtrList now owns it
1553  m_allMusic.append(dbMeta);
1554 
1555  m_musicMap[id] = dbMeta;
1556 
1557  added++;
1558  }
1559  else
1560  {
1561  // existing track, check for any changes
1562  MusicMetadata *cacheMeta = m_musicMap[id];
1563 
1564  if (cacheMeta && !cacheMeta->compare(dbMeta))
1565  {
1566  cacheMeta->reloadMetadata();
1567  changed++;
1568  }
1569 
1570  // we already have this track in the cache so don't need dbMeta anymore
1571  delete dbMeta;
1572  }
1573 
1574  // compute max/min playcount,lastplay for all music
1575  if (query.at() == 0)
1576  {
1577  // first song
1578  m_playCountMin = m_playCountMax = query.value(13).toInt();
1579  m_lastPlayMin = m_lastPlayMax = query.value(14).toDateTime().toSecsSinceEpoch();
1580  }
1581  else
1582  {
1583  int playCount = query.value(13).toInt();
1584  qint64 lastPlay = query.value(14).toDateTime().toSecsSinceEpoch();
1585 
1586  m_playCountMin = std::min(playCount, m_playCountMin);
1587  m_playCountMax = std::max(playCount, m_playCountMax);
1588  m_lastPlayMin = std::min(lastPlay, m_lastPlayMin);
1589  m_lastPlayMax = std::max(lastPlay, m_lastPlayMax);
1590  }
1591  m_numLoaded++;
1592  }
1593  }
1594  else
1595  {
1596  LOG(VB_GENERAL, LOG_ERR, "MythMusic hasn't found any tracks!");
1597  }
1598 
1599  // get a list of tracks in our cache that's now not in the database
1600  QList<MusicMetadata::IdType> deleteList;
1601  for (const auto *track : qAsConst(m_allMusic))
1602  {
1603  if (!idList.contains(track->ID()))
1604  {
1605  deleteList.append(track->ID());
1606  }
1607  }
1608 
1609  // remove the no longer available tracks
1610  for (uint id : deleteList)
1611  {
1612  MusicMetadata *mdata = m_musicMap[id];
1613  m_allMusic.removeAll(mdata);
1614  m_musicMap.remove(id);
1615  removed++;
1616  delete mdata;
1617  }
1618 
1619  // tell any listeners a resync has just finished and they may need to reload/resync
1620  LOG(VB_GENERAL, LOG_DEBUG, QString("AllMusic::resync sending MUSIC_RESYNC_FINISHED added: %1, removed: %2, changed: %3")
1621  .arg(added).arg(removed).arg(changed));
1622  gCoreContext->SendMessage(QString("MUSIC_RESYNC_FINISHED %1 %2 %3").arg(added).arg(removed).arg(changed));
1623 
1624  m_doneLoading = true;
1625 }
1626 
1628 {
1629  if (m_musicMap.contains(an_id))
1630  return m_musicMap[an_id];
1631 
1632  return nullptr;
1633 }
1634 
1635 bool AllMusic::isValidID(int an_id)
1636 {
1637  return m_musicMap.contains(an_id);
1638 }
1639 
1640 bool AllMusic::updateMetadata(int an_id, MusicMetadata *the_track)
1641 {
1642  if (an_id > 0)
1643  {
1644  MusicMetadata *mdata = getMetadata(an_id);
1645  if (mdata)
1646  {
1647  *mdata = *the_track;
1648  return true;
1649  }
1650  }
1651  return false;
1652 }
1653 
1655 void AllMusic::save(void)
1656 {
1657  for (auto *item : qAsConst(m_allMusic))
1658  {
1659  if (item->hasChanged())
1660  item->persist();
1661  }
1662 }
1663 
1664 // cd stuff
1666 {
1667  while (!m_cdData.empty())
1668  {
1669  MusicMetadata *mdata = m_cdData.back();
1670  if (m_musicMap.contains(mdata->ID()))
1671  m_musicMap.remove(mdata->ID());
1672 
1673  delete m_cdData.back();
1674  m_cdData.pop_back();
1675  }
1676 
1677  m_cdTitle = tr("CD -- none");
1678 }
1679 
1680 void AllMusic::addCDTrack(const MusicMetadata &the_track)
1681 {
1682  auto *mdata = new MusicMetadata(the_track);
1683  mdata->setID(m_cdData.count() + 1);
1684  mdata->setRepo(RT_CD);
1685  m_cdData.append(mdata);
1686  m_musicMap[mdata->ID()] = mdata;
1687 }
1688 
1690 {
1691  if (m_cdData.count() < 1)
1692  return false;
1693 
1694  return m_cdData.last()->FormatTitle() == the_track->FormatTitle();
1695 }
1696 
1698 {
1699  for (auto *anit : qAsConst(m_cdData))
1700  {
1701  if (anit->Track() == the_track)
1702  {
1703  return anit;
1704  }
1705  }
1706 
1707  return nullptr;
1708 }
1709 
1710 /**************************************************************************/
1711 
1713 {
1714  loadStreams();
1715 }
1716 
1718 {
1719  while (!m_streamList.empty())
1720  {
1721  delete m_streamList.back();
1722  m_streamList.pop_back();
1723  }
1724 }
1725 
1727 {
1728  for (int x = 0; x < m_streamList.count(); x++)
1729  {
1730  if (m_streamList.at(x)->ID() == an_id)
1731  return true;
1732  }
1733 
1734  return false;
1735 }
1736 
1738 {
1739  for (int x = 0; x < m_streamList.count(); x++)
1740  {
1741  if (m_streamList.at(x)->ID() == an_id)
1742  return m_streamList.at(x);
1743  }
1744 
1745  return nullptr;
1746 }
1747 
1749 {
1750  while (!m_streamList.empty())
1751  {
1752  delete m_streamList.back();
1753  m_streamList.pop_back();
1754  }
1755 
1756  QString aquery = "SELECT intid, broadcaster, channel, description, url1, url2, url3, url4, url5,"
1757  "logourl, genre, metaformat, country, language, format "
1758  "FROM music_radios "
1759  "ORDER BY broadcaster,channel;";
1760 
1761  MSqlQuery query(MSqlQuery::InitCon());
1762  if (!query.exec(aquery))
1763  MythDB::DBError("AllStream::loadStreams", query);
1764 
1765  if (query.isActive() && query.size() > 0)
1766  {
1767  while (query.next())
1768  {
1769  UrlList urls;
1770  for (int x = 0; x < STREAMURLCOUNT; x++)
1771  urls[x] = query.value(4 + x).toString();
1772 
1773  auto *mdata = new MusicMetadata(
1774  query.value(0).toInt(), // intid
1775  query.value(1).toString(), // broadcaster
1776  query.value(2).toString(), // channel
1777  query.value(3).toString(), // description
1778  urls, // array of 5 urls
1779  query.value(9).toString(), // logourl
1780  query.value(10).toString(), // genre
1781  query.value(11).toString(), // metadataformat
1782  query.value(12).toString(), // country
1783  query.value(13).toString(), // language
1784  query.value(14).toString()); // format
1785 
1786  mdata->setRepo(RT_Radio);
1787 
1788  m_streamList.append(mdata);
1789  }
1790  }
1791  else
1792  {
1793  LOG(VB_GENERAL, LOG_WARNING, "MythMusic hasn't found any radio streams!");
1794  }
1795 }
1796 
1798 {
1799  // add the stream to the db
1800  MSqlQuery query(MSqlQuery::InitCon());
1801  query.prepare("INSERT INTO music_radios (broadcaster, channel, description, "
1802  "url1, url2, url3, url4, url5, "
1803  "logourl, genre, country, language, format, metaformat) "
1804  "VALUES (:BROADCASTER, :CHANNEL, :DESCRIPTION, :URL1, :URL2, :URL3, :URL4, :URL5, "
1805  ":LOGOURL, :GENRE, :COUNTRY, :LANGUAGE, :FORMAT, :METAFORMAT);");
1806  query.bindValueNoNull(":BROADCASTER", mdata->Broadcaster());
1807  query.bindValueNoNull(":CHANNEL", mdata->Channel());
1808  query.bindValueNoNull(":DESCRIPTION", mdata->Description());
1809  query.bindValueNoNull(":URL1", mdata->Url(0));
1810  query.bindValueNoNull(":URL2", mdata->Url(1));
1811  query.bindValueNoNull(":URL3", mdata->Url(2));
1812  query.bindValueNoNull(":URL4", mdata->Url(3));
1813  query.bindValueNoNull(":URL5", mdata->Url(4));
1814  query.bindValueNoNull(":LOGOURL", mdata->LogoUrl());
1815  query.bindValueNoNull(":GENRE", mdata->Genre());
1816  query.bindValueNoNull(":COUNTRY", mdata->Country());
1817  query.bindValueNoNull(":LANGUAGE", mdata->Language());
1818  query.bindValueNoNull(":FORMAT", mdata->Format());
1819  query.bindValueNoNull(":METAFORMAT", mdata->MetadataFormat());
1820 
1821  if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
1822  {
1823  MythDB::DBError("music insert radio", query);
1824  return;
1825  }
1826 
1827  mdata->setID(query.lastInsertId().toInt());
1828  mdata->setRepo(RT_Radio);
1829 
1830  loadStreams();
1831 }
1832 
1834 {
1835  // remove the stream from the db
1836  int id = ID_TO_ID(mdata->ID());
1837  MSqlQuery query(MSqlQuery::InitCon());
1838  query.prepare("DELETE FROM music_radios WHERE intid = :ID");
1839  query.bindValue(":ID", id);
1840 
1841  if (!query.exec() || query.numRowsAffected() <= 0)
1842  {
1843  MythDB::DBError("AllStream::removeStream", query);
1844  return;
1845  }
1846 
1847  loadStreams();
1848 }
1849 
1851 {
1852  // update the stream in the db
1853  int id = ID_TO_ID(mdata->ID());
1854  MSqlQuery query(MSqlQuery::InitCon());
1855  query.prepare("UPDATE music_radios set broadcaster = :BROADCASTER, channel = :CHANNEL, description = :DESCRIPTION, "
1856  "url1 = :URL1, url2 = :URL2, url3 = :URL3, url4 = :URL4, url5 = :URL5, "
1857  "logourl = :LOGOURL, genre = :GENRE, country = :COUNTRY, language = :LANGUAGE, "
1858  "format = :FORMAT, metaformat = :METAFORMAT "
1859  "WHERE intid = :ID");
1860  query.bindValueNoNull(":BROADCASTER", mdata->Broadcaster());
1861  query.bindValueNoNull(":CHANNEL", mdata->Channel());
1862  query.bindValueNoNull(":DESCRIPTION", mdata->Description());
1863  query.bindValueNoNull(":URL1", mdata->Url(0));
1864  query.bindValueNoNull(":URL2", mdata->Url(1));
1865  query.bindValueNoNull(":URL3", mdata->Url(2));
1866  query.bindValueNoNull(":URL4", mdata->Url(3));
1867  query.bindValueNoNull(":URL5", mdata->Url(4));
1868  query.bindValueNoNull(":LOGOURL", mdata->LogoUrl());
1869  query.bindValueNoNull(":GENRE", mdata->Genre());
1870  query.bindValueNoNull(":COUNTRY", mdata->Country());
1871  query.bindValueNoNull(":LANGUAGE", mdata->Language());
1872  query.bindValueNoNull(":FORMAT", mdata->Format());
1873  query.bindValueNoNull(":METAFORMAT", mdata->MetadataFormat());
1874  query.bindValue(":ID", id);
1875 
1876  if (!query.exec() || !query.isActive() || query.numRowsAffected() <= 0)
1877  {
1878  MythDB::DBError("AllStream::updateStream", query);
1879  return;
1880  }
1881 
1882  loadStreams();
1883 }
1884 
1885 /**************************************************************************/
1886 
1888  : m_parent(metadata)
1889 {
1890  if (loadFromDB)
1891  findImages();
1892 }
1893 
1895 {
1896  while (!m_imageList.empty())
1897  {
1898  delete m_imageList.back();
1899  m_imageList.pop_back();
1900  }
1901 }
1902 
1904 {
1905  while (!m_imageList.empty())
1906  {
1907  delete m_imageList.back();
1908  m_imageList.pop_back();
1909  }
1910 
1911  if (m_parent == nullptr)
1912  return;
1913 
1914  int trackid = ID_TO_ID(m_parent->ID());
1915  int repo = ID_TO_REPO(m_parent->ID());
1916 
1917  if (repo == RT_Radio)
1918  {
1919  MSqlQuery query(MSqlQuery::InitCon());
1920  //FIXME: this should work with the alternate urls as well eg url2, url3 etc
1921  query.prepare("SELECT logourl FROM music_radios WHERE url1 = :URL;");
1922  query.bindValue(":URL", m_parent->Filename());
1923  if (query.exec())
1924  {
1925  while (query.next())
1926  {
1927  QString logoUrl = query.value(0).toString();
1928 
1929  auto *image = new AlbumArtImage();
1930  image->m_id = -1;
1931  image->m_filename = logoUrl;
1932  image->m_imageType = IT_FRONTCOVER;
1933  image->m_embedded = false;
1934  image->m_hostname = "";
1935  m_imageList.push_back(image);
1936  }
1937  }
1938  }
1939  else
1940  {
1941  if (trackid == 0)
1942  return;
1943 
1944  QFileInfo fi(m_parent->Filename(false));
1945  QString dir = fi.path();
1946 
1947  MSqlQuery query(MSqlQuery::InitCon());
1948  query.prepare("SELECT albumart_id, CONCAT_WS('/', music_directories.path, "
1949  "music_albumart.filename), music_albumart.filename, music_albumart.imagetype, "
1950  "music_albumart.embedded, music_albumart.hostname "
1951  "FROM music_albumart "
1952  "LEFT JOIN music_directories ON "
1953  "music_directories.directory_id = music_albumart.directory_id "
1954  "WHERE music_directories.path = :DIR "
1955  "OR song_id = :SONGID "
1956  "ORDER BY music_albumart.imagetype;");
1957  query.bindValue(":DIR", dir);
1958  query.bindValue(":SONGID", trackid);
1959  if (query.exec())
1960  {
1961  while (query.next())
1962  {
1963  auto *image = new AlbumArtImage();
1964  bool embedded = (query.value(4).toInt() == 1);
1965  image->m_id = query.value(0).toInt();
1966 
1967  QUrl url(m_parent->Filename(true));
1968 
1969  if (embedded)
1970  {
1971  if (url.scheme() == "myth")
1972  {
1973  image->m_filename = MythCoreContext::GenMythURL(url.host(), url.port(),
1974  QString("AlbumArt/") + query.value(1).toString(),
1975  "MusicArt");
1976  }
1977  else
1978  {
1979  image->m_filename = query.value(1).toString();
1980  }
1981  }
1982  else
1983  {
1984  if (url.scheme() == "myth")
1985  {
1986  image->m_filename = MythCoreContext::GenMythURL(url.host(), url.port(),
1987  query.value(1).toString(),
1988  "Music");
1989  }
1990  else
1991  {
1992  image->m_filename = query.value(1).toString();
1993  }
1994  }
1995 
1996  image->m_imageType = (ImageType) query.value(3).toInt();
1997  image->m_embedded = embedded;
1998  image->m_hostname = query.value(5).toString();
1999 
2000  m_imageList.push_back(image);
2001  }
2002  }
2003 
2004  // add any artist images
2005  QString artist = m_parent->Artist().toLower();
2006  if (findIcon("artist", artist) != QString())
2007  {
2008  auto *image = new AlbumArtImage();
2009  image->m_id = -1;
2010  image->m_filename = findIcon("artist", artist);
2011  image->m_imageType = IT_ARTIST;
2012  image->m_embedded = false;
2013 
2014  m_imageList.push_back(image);
2015  }
2016  }
2017 }
2018 
2020 {
2021  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
2022  auto *busy = new MythUIBusyDialog(tr("Scanning for music album art..."),
2023  popupStack, "scanbusydialog");
2024 
2025  if (busy->Create())
2026  {
2027  popupStack->AddScreen(busy, false);
2028  }
2029  else
2030  {
2031  delete busy;
2032  busy = nullptr;
2033  }
2034 
2035  QStringList strList;
2036  strList << "MUSIC_FIND_ALBUMART"
2037  << m_parent->Hostname()
2038  << QString::number(m_parent->ID())
2039  << "1";
2040 
2041  auto *scanThread = new AlbumArtScannerThread(strList);
2042  scanThread->start();
2043 
2044  while (scanThread->isRunning())
2045  {
2046  QCoreApplication::processEvents();
2047  usleep(1000);
2048  }
2049 
2050  strList = scanThread->getResult();
2051 
2052  delete scanThread;
2053 
2054  if (busy)
2055  busy->Close();
2056 
2057  while (!m_imageList.empty())
2058  {
2059  delete m_imageList.back();
2060  m_imageList.pop_back();
2061  }
2062 
2063  for (int x = 2; x < strList.count(); x += 6)
2064  {
2065  auto *image = new AlbumArtImage;
2066  image->m_id = strList[x].toInt();
2067  image->m_imageType = (ImageType) strList[x + 1].toInt();
2068  image->m_embedded = (strList[x + 2].toInt() == 1);
2069  image->m_description = strList[x + 3];
2070 
2071  if (image->m_embedded)
2072  {
2073  image->m_filename = MythCoreContext::GenMythURL(m_parent->Hostname(), 0,
2074  QString("AlbumArt/") + strList[x + 4],
2075  "MusicArt");
2076  }
2077  else
2078  {
2079  image->m_filename = MythCoreContext::GenMythURL(m_parent->Hostname(), 0,
2080  strList[x + 4],
2081  "Music");
2082  }
2083 
2084  image->m_hostname = strList[x + 5];
2085 
2086  LOG(VB_FILE, LOG_INFO, "AlbumArtImages::scanForImages found image");
2087  LOG(VB_FILE, LOG_INFO, QString("ID: %1").arg(image->m_id));
2088  LOG(VB_FILE, LOG_INFO, QString("ImageType: %1").arg(image->m_imageType));
2089  LOG(VB_FILE, LOG_INFO, QString("Embedded: %1").arg(image->m_embedded));
2090  LOG(VB_FILE, LOG_INFO, QString("Description: %1").arg(image->m_description));
2091  LOG(VB_FILE, LOG_INFO, QString("Filename: %1").arg(image->m_filename));
2092  LOG(VB_FILE, LOG_INFO, QString("Hostname: %1").arg(image->m_hostname));
2093  LOG(VB_FILE, LOG_INFO, "-------------------------------");
2094 
2095  addImage(image);
2096 
2097  delete image;
2098  }
2099 }
2100 
2102 {
2103  for (auto *item : qAsConst(m_imageList))
2104  {
2105  if (item->m_imageType == type)
2106  return item;
2107  }
2108 
2109  return nullptr;
2110 }
2111 
2113 {
2114  for (auto *item : qAsConst(m_imageList))
2115  {
2116  if (item->m_id == imageID)
2117  return item;
2118  }
2119 
2120  return nullptr;
2121 }
2122 
2123 QStringList AlbumArtImages::getImageFilenames(void) const
2124 {
2125  QStringList paths;
2126 
2127  for (const auto *item : qAsConst(m_imageList))
2128  paths += item->m_filename;
2129 
2130  return paths;
2131 }
2132 
2134 {
2135  if (index < (uint)m_imageList.size())
2136  return m_imageList[index];
2137 
2138  return nullptr;
2139 }
2140 
2141 // static method to get a translated type name from an ImageType
2143 {
2144  // these const's should match the ImageType enum's
2145  static const std::array<const std::string,6> s_typeStrings {
2146  QT_TR_NOOP("Unknown"), // IT_UNKNOWN
2147  QT_TR_NOOP("Front Cover"), // IT_FRONTCOVER
2148  QT_TR_NOOP("Back Cover"), // IT_BACKCOVER
2149  QT_TR_NOOP("CD"), // IT_CD
2150  QT_TR_NOOP("Inlay"), // IT_INLAY
2151  QT_TR_NOOP("Artist"), // IT_ARTIST
2152  };
2153 
2154  return QCoreApplication::translate("AlbumArtImages",
2155  s_typeStrings[type].c_str());
2156 }
2157 
2158 // static method to get a filename from an ImageType
2160 {
2161  // these const's should match the ImageType enum's
2162  static const std::array<const std::string,6> s_filenameStrings {
2163  QT_TR_NOOP("unknown"), // IT_UNKNOWN
2164  QT_TR_NOOP("front"), // IT_FRONTCOVER
2165  QT_TR_NOOP("back"), // IT_BACKCOVER
2166  QT_TR_NOOP("cd"), // IT_CD
2167  QT_TR_NOOP("inlay"), // IT_INLAY
2168  QT_TR_NOOP("artist") // IT_ARTIST
2169  };
2170 
2171  return QCoreApplication::translate("AlbumArtImages",
2172  s_filenameStrings[type].c_str());
2173 }
2174 
2175 // static method to guess the image type from the filename
2177 {
2179 
2180  if (filename.contains("front", Qt::CaseInsensitive) ||
2181  filename.contains(tr("front"), Qt::CaseInsensitive) ||
2182  filename.contains("cover", Qt::CaseInsensitive) ||
2183  filename.contains(tr("cover"), Qt::CaseInsensitive))
2184  type = IT_FRONTCOVER;
2185  else if (filename.contains("back", Qt::CaseInsensitive) ||
2186  filename.contains(tr("back"), Qt::CaseInsensitive))
2187  type = IT_BACKCOVER;
2188  else if (filename.contains("inlay", Qt::CaseInsensitive) ||
2189  filename.contains(tr("inlay"), Qt::CaseInsensitive))
2190  type = IT_INLAY;
2191  else if (filename.contains("cd", Qt::CaseInsensitive) ||
2192  filename.contains(tr("cd"), Qt::CaseInsensitive))
2193  type = IT_CD;
2194 
2195  return type;
2196 }
2197 
2198 // static method to get image type from the type name
2200 {
2202 
2203  if (name.toLower() == "front")
2204  type = IT_FRONTCOVER;
2205  else if (name.toLower() == "back")
2206  type = IT_BACKCOVER;
2207  else if (name.toLower() == "inlay")
2208  type = IT_INLAY;
2209  else if (name.toLower() == "cd")
2210  type = IT_CD;
2211  else if (name.toLower() == "artist")
2212  type = IT_ARTIST;
2213  else if (name.toLower() == "unknown")
2214  type = IT_UNKNOWN;
2215 
2216  return type;
2217 }
2218 
2219 void AlbumArtImages::addImage(const AlbumArtImage * const newImage)
2220 {
2221  // do we already have an image of this type?
2222  AlbumArtImage *image = nullptr;
2223 
2224  for (auto *item : qAsConst(m_imageList))
2225  {
2226  if (item->m_imageType == newImage->m_imageType
2227  && item->m_embedded == newImage->m_embedded)
2228  {
2229  image = item;
2230  break;
2231  }
2232  }
2233 
2234  if (!image)
2235  {
2236  // not found so just add it to the list
2237  image = new AlbumArtImage(newImage);
2238  m_imageList.push_back(image);
2239  }
2240  else
2241  {
2242  // we already have an image of this type so just update it with the new info
2243  image->m_filename = newImage->m_filename;
2244  image->m_imageType = newImage->m_imageType;
2245  image->m_embedded = newImage->m_embedded;
2246  image->m_description = newImage->m_description;
2247  image->m_hostname = newImage->m_hostname;
2248  }
2249 }
2250 
2253 {
2254  MusicMetadata::IdType trackID = ID_TO_ID(m_parent->ID());
2255  int directoryID = m_parent->getDirectoryId();
2256 
2257  // sanity check we have a valid songid and directoryid
2258  if (trackID == 0 || directoryID == -1)
2259  {
2260  LOG(VB_GENERAL, LOG_ERR, "AlbumArtImages: Asked to save to the DB but "
2261  "have invalid songid or directoryid");
2262  return;
2263  }
2264 
2265  MSqlQuery query(MSqlQuery::InitCon());
2266 
2267  // remove all albumart for this track from the db
2268  query.prepare("DELETE FROM music_albumart "
2269  "WHERE song_id = :SONGID "
2270  "OR (embedded = 0 AND directory_id = :DIRECTORYID)");
2271 
2272  query.bindValue(":SONGID", trackID);
2273  query.bindValue(":DIRECTORYID", directoryID);
2274 
2275  if (!query.exec())
2276  {
2277  MythDB::DBError("AlbumArtImages::dumpToDatabase - "
2278  "deleting existing albumart", query);
2279  }
2280 
2281  // now add the albumart to the db
2282  for (auto *image : qAsConst(m_imageList))
2283  {
2284  //TODO: for the moment just ignore artist images
2285  if (image->m_imageType == IT_ARTIST)
2286  continue;
2287 
2288  if (image->m_id > 0)
2289  {
2290  // re-use the same id this image had before
2291  query.prepare("INSERT INTO music_albumart ( albumart_id, "
2292  "filename, imagetype, song_id, directory_id, embedded, hostname ) "
2293  "VALUES ( :ID, :FILENAME, :TYPE, :SONGID, :DIRECTORYID, :EMBED, :HOSTNAME );");
2294  query.bindValue(":ID", image->m_id);
2295  }
2296  else
2297  {
2298  query.prepare("INSERT INTO music_albumart ( filename, "
2299  "imagetype, song_id, directory_id, embedded, hostname ) VALUES ( "
2300  ":FILENAME, :TYPE, :SONGID, :DIRECTORYID, :EMBED, :HOSTNAME );");
2301  }
2302 
2303  QFileInfo fi(image->m_filename);
2304  query.bindValue(":FILENAME", fi.fileName());
2305 
2306  query.bindValue(":TYPE", image->m_imageType);
2307  query.bindValue(":SONGID", image->m_embedded ? trackID : 0);
2308  query.bindValue(":DIRECTORYID", image->m_embedded ? 0 : directoryID);
2309  query.bindValue(":EMBED", image->m_embedded);
2310  query.bindValue(":HOSTNAME", image->m_hostname);
2311 
2312  if (!query.exec())
2313  {
2314  MythDB::DBError("AlbumArtImages::dumpToDatabase - "
2315  "add/update music_albumart", query);
2316  }
2317  else
2318  {
2319  if (image->m_id <= 0)
2320  image->m_id = query.lastInsertId().toInt();
2321  }
2322  }
2323 }
MusicMetadata::getCompilationArtistId
int getCompilationArtistId()
Definition: musicmetadata.cpp:534
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:213
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:807
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
MusicMetadata::m_genreId
int m_genreId
Definition: musicmetadata.h:350
STREAMURLCOUNT
#define STREAMURLCOUNT
Definition: musicmetadata.h:76
MusicMetadata::getGenreId
int getGenreId()
Definition: musicmetadata.cpp:617
findIcon
QString findIcon(const QString &type, const QString &name, bool ignoreCache)
find an image for a artist or genre
Definition: musicutils.cpp:34
MythCoreContext::SendMessage
void SendMessage(const QString &message)
Definition: mythcorecontext.cpp:1540
MythDate::toString
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:85
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:212
MSqlQuery::bindValueNoNull
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
Definition: mythdbcon.cpp:888
MusicMetadata::Filename
QString Filename(bool find=true)
Definition: musicmetadata.cpp:960
AlbumArtImage::m_imageType
ImageType m_imageType
Definition: musicmetadata.h:51
AllStream::isValidID
bool isValidID(MusicMetadata::IdType an_id)
Definition: musicmetadata.cpp:1726
MythCoreContext::GetMasterHostName
QString GetMasterHostName(void)
Definition: mythcorecontext.cpp:830
AllStream::updateStream
void updateStream(MusicMetadata *mdata)
Definition: musicmetadata.cpp:1850
METADATA_INVALID_FILENAME
#define METADATA_INVALID_FILENAME
Definition: musicmetadata.h:73
MusicMetadata::incPlayCount
void incPlayCount()
Definition: musicmetadata.cpp:1192
MusicMetadata::LogoUrl
QString LogoUrl(void)
Definition: musicmetadata.h:276
unziputil.h
MusicMetadata::Genre
QString Genre() const
Definition: musicmetadata.h:175
AllStream::addStream
void addStream(MusicMetadata *mdata)
Definition: musicmetadata.cpp:1797
AllStream::AllStream
AllStream(void)
Definition: musicmetadata.cpp:1712
getMythSortHelper
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.
Definition: mythsorthelper.cpp:133
MusicMetadata::checkEmptyFields
void checkEmptyFields(void)
Definition: musicmetadata.cpp:855
kMSDontBlockInputDevs
@ kMSDontBlockInputDevs
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:36
MusicMetadata::s_formatCompilationCdArtist
static QString s_formatCompilationCdArtist
Definition: musicmetadata.h:387
mythdb.h
MusicMetadata::m_hostname
QString m_hostname
Definition: musicmetadata.h:364
MythCoreContext::SendReceiveStringList
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
Definition: mythcorecontext.cpp:1394
MythDate::as_utc
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
Definition: mythdate.cpp:28
MusicMetadata::m_description
QString m_description
Definition: musicmetadata.h:372
MusicMetadata::MetadataFormat
QString MetadataFormat(void)
Definition: musicmetadata.h:279
AllMusic::isValidID
bool isValidID(int an_id)
Definition: musicmetadata.cpp:1635
MusicMetadata::FormatArtist
QString FormatArtist()
Definition: musicmetadata.cpp:937
MusicMetadata::getAlbumArtImages
AlbumArtImages * getAlbumArtImages(void)
Definition: musicmetadata.cpp:1359
MusicMetadata::m_rating
int m_rating
Definition: musicmetadata.h:345
MusicMetadata::createFromID
static MusicMetadata * createFromID(int trackid)
Definition: musicmetadata.cpp:253
RemoteFile::Exists
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:462
MusicMetadata::fillFieldList
static QStringList fillFieldList(const QString &field)
Definition: musicmetadata.cpp:1214
MetaIO::createTagger
static MetaIO * createTagger(const QString &filename)
Finds an appropriate tagger for the given file.
Definition: metaio.cpp:32
AlbumArtImages::getImageList
AlbumArtList * getImageList(void)
Definition: musicmetadata.h:535
MusicMetadata::m_lyricsData
LyricsData * m_lyricsData
Definition: musicmetadata.h:360
MusicMetadata::m_urls
UrlList m_urls
Definition: musicmetadata.h:373
MusicMetadata::m_title
QString m_title
Definition: musicmetadata.h:333
StorageGroup::FindFile
QString FindFile(const QString &filename)
Definition: storagegroup.cpp:602
AllStream::getMetadata
MusicMetadata * getMetadata(MusicMetadata::IdType an_id)
Definition: musicmetadata.cpp:1737
MusicMetadata::m_compilationArtistSort
QString m_compilationArtistSort
Definition: musicmetadata.h:330
MusicMetadata::setArtistAndTrackFormats
static void setArtistAndTrackFormats()
Definition: musicmetadata.cpp:798
AlbumArtImages::getImageTypeFromName
static ImageType getImageTypeFromName(const QString &name)
Definition: musicmetadata.cpp:2199
MSqlQuery::lastInsertId
QVariant lastInsertId()
Return the id of the last inserted row.
Definition: mythdbcon.cpp:912
AllMusic::startLoading
bool startLoading(void)
Start loading metadata.
Definition: musicmetadata.cpp:1461
MusicMetadata::isRadio
bool isRadio(void) const
Definition: musicmetadata.h:225
MusicMetadata::m_artistSort
QString m_artistSort
Definition: musicmetadata.h:328
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:202
MythScreenStack
Definition: mythscreenstack.h:16
MetaIO
Definition: metaio.h:17
MythDate::formatTime
QString formatTime(std::chrono::milliseconds msecs, QString fmt)
Format a milliseconds time value.
Definition: mythdate.cpp:234
MusicMetadata::ID
IdType ID() const
Definition: musicmetadata.h:219
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:608
MusicMetadata::m_albumArt
AlbumArtImages * m_albumArt
Definition: musicmetadata.h:358
MusicMetadata::MusicMetadata
MusicMetadata(QString lfilename="", QString lartist="", QString lcompilation_artist="", QString lalbum="", QString ltitle="", QString lgenre="", int lyear=0, int ltracknum=0, std::chrono::milliseconds llength=0ms, int lid=0, int lrating=0, int lplaycount=0, QDateTime llastplay=QDateTime(), QDateTime ldateadded=QDateTime(), bool lcompilation=false, QString lformat="")
Definition: musicmetadata.h:88
STREAMUPDATEURL
#define STREAMUPDATEURL
Definition: musicmetadata.h:75
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
IT_BACKCOVER
@ IT_BACKCOVER
Definition: musicmetadata.h:32
MusicMetadata::compare
bool compare(MusicMetadata *mdata) const
Definition: musicmetadata.cpp:145
MusicMetadata
Definition: musicmetadata.h:80
metaioflacvorbis.h
AlbumArtImages::guessImageType
static ImageType guessImageType(const QString &filename)
Definition: musicmetadata.cpp:2176
GetMythDB
MythDB * GetMythDB(void)
Definition: mythdb.cpp:50
mythdirs.h
mythsorthelper.h
MusicMetadata::getLocalFilename
QString getLocalFilename(void)
try to find the track on the local file system
Definition: musicmetadata.cpp:1021
MusicMetadata::persist
void persist(void)
Definition: musicmetadata.cpp:173
MusicMetadata::Artist
QString Artist() const
Definition: musicmetadata.h:126
MusicMetadata::setEmbeddedAlbumArt
void setEmbeddedAlbumArt(AlbumArtList &albumart)
Definition: musicmetadata.cpp:1198
hardwareprofile.distros.mythtv_data.data_mythtv.prefix
string prefix
Definition: data_mythtv.py:40
MetadataLoadingThread
Definition: musicmetadata.h:403
MusicMetadata::m_format
QString m_format
Definition: musicmetadata.h:338
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
musicutils.h
mythprogressdialog.h
MusicMetadata::m_directoryId
int m_directoryId
Definition: musicmetadata.h:346
AlbumArtImages::getImage
AlbumArtImage * getImage(ImageType type)
Definition: musicmetadata.cpp:2101
MusicMetadata::m_formattedTitle
QString m_formattedTitle
Definition: musicmetadata.h:336
MusicMetadata::~MusicMetadata
~MusicMetadata()
Definition: musicmetadata.cpp:72
tmp
static guint32 * tmp
Definition: goom_core.cpp:32
AllMusic::getCDMetadata
MusicMetadata * getCDMetadata(int m_the_track)
Definition: musicmetadata.cpp:1697
MusicMetadata::getField
void getField(const QString &field, QString *data)
Definition: musicmetadata.cpp:1068
InfoMap
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
MusicMetadata::m_changed
bool m_changed
Definition: musicmetadata.h:367
mythsystem.h
AllMusic::checkCDTrack
bool checkCDTrack(MusicMetadata *the_track)
Definition: musicmetadata.cpp:1689
MusicMetadata::m_tempPlayCount
int m_tempPlayCount
Definition: musicmetadata.h:355
MusicMetadata::formatReplaceSymbols
QString formatReplaceSymbols(const QString &format)
Definition: musicmetadata.cpp:845
MusicMetadata::Url
QString Url(uint index=0)
Definition: musicmetadata.cpp:658
MusicMetadata::Channel
QString Channel(void)
Definition: musicmetadata.h:267
MusicMetadata::m_playCount
int m_playCount
Definition: musicmetadata.h:354
MythCoreContext::IsMasterBackend
bool IsMasterBackend(void)
is this the actual MBE process
Definition: mythcorecontext.cpp:718
mythdate.h
MusicMetadata::m_length
std::chrono::milliseconds m_length
Definition: musicmetadata.h:344
AllMusic::AllMusic
AllMusic(void)
Definition: musicmetadata.cpp:1411
mythlogging.h
MythCoreContext::GenMythURL
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
Definition: mythcorecontext.cpp:783
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:224
MusicMetadata::Description
QString Description(void)
Definition: musicmetadata.h:270
MythCoreContext::GetQLocale
QLocale GetQLocale(void)
Definition: mythcorecontext.cpp:1886
MusicMetadata::m_dateAdded
QDateTime m_dateAdded
Definition: musicmetadata.h:353
MusicMetadata::m_lastPlay
QDateTime m_lastPlay
Definition: musicmetadata.h:351
remotefile.h
MusicMetadata::m_year
int m_year
Definition: musicmetadata.h:339
MusicMetadata::getLyricsData
LyricsData * getLyricsData(void)
Definition: musicmetadata.cpp:1373
RT_Radio
@ RT_Radio
Definition: musicmetadata.h:62
AlbumArtScannerThread
Definition: musicmetadata.h:502
MusicMetadata::toMap
void toMap(InfoMap &metadataMap, const QString &prefix="")
Definition: musicmetadata.cpp:1086
ID_TO_ID
#define ID_TO_ID(x)
Definition: musicmetadata.h:70
MusicMetadata::s_formatCompilationFileTrack
static QString s_formatCompilationFileTrack
Definition: musicmetadata.h:386
operator==
bool operator==(MusicMetadata &a, MusicMetadata &b)
Definition: musicmetadata.cpp:41
RT_CD
@ RT_CD
Definition: musicmetadata.h:61
AllStream::~AllStream
~AllStream()
Definition: musicmetadata.cpp:1717
MusicMetadata::m_tempLastPlay
QDateTime m_tempLastPlay
Definition: musicmetadata.h:352
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:540
MusicMetadata::Language
QString Language(void)
Definition: musicmetadata.h:285
MusicMetadata::getAlbumArtFile
QString getAlbumArtFile(void)
Definition: musicmetadata.cpp:1256
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:227
MusicMetadata::getArtistId
int getArtistId()
Definition: musicmetadata.cpp:497
AllMusic::cleanOutThreads
bool cleanOutThreads()
Definition: musicmetadata.cpp:1435
MusicMetadata::getAlbumId
int getAlbumId()
Definition: musicmetadata.cpp:576
AllMusic::updateMetadata
bool updateMetadata(int an_id, MusicMetadata *the_track)
Definition: musicmetadata.cpp:1640
AlbumArtImages::getImageAt
AlbumArtImage * getImageAt(uint index)
Definition: musicmetadata.cpp:2133
MusicMetadata::operator=
MusicMetadata & operator=(const MusicMetadata &rhs)
Definition: musicmetadata.cpp:88
AlbumArtImages::scanForImages
void scanForImages(void)
Definition: musicmetadata.cpp:2019
MusicMetadata::m_channel
QString m_channel
Definition: musicmetadata.h:371
MusicMetadata::updateStreamList
static bool updateStreamList(void)
Definition: musicmetadata.cpp:314
AlbumArtImages::getTypeFilename
static QString getTypeFilename(ImageType type)
Definition: musicmetadata.cpp:2159
AlbumArtImages::findImages
void findImages(void)
Definition: musicmetadata.cpp:1903
MusicMetadata::m_artistId
int m_artistId
Definition: musicmetadata.h:347
AllMusic::addCDTrack
void addCDTrack(const MusicMetadata &the_track)
Definition: musicmetadata.cpp:1680
AllStream::removeStream
void removeStream(MusicMetadata *mdata)
Definition: musicmetadata.cpp:1833
MusicMetadata::getDirectoryId
int getDirectoryId()
Definition: musicmetadata.cpp:449
metaioavfcomment.h
RemoteFile::FindFile
static QString FindFile(const QString &filename, const QString &host, const QString &storageGroup, bool useRegex=false, bool allowFallback=false)
Search all BE's for a file in the give storage group.
Definition: remotefile.cpp:1283
storagegroup.h
MythUIBusyDialog
Definition: mythprogressdialog.h:36
metaiowavpack.h
gzipUncompress
QByteArray gzipUncompress(const QByteArray &data)
Definition: unziputil.cpp:146
MusicMetadata::setFilename
void setFilename(const QString &lfilename)
Definition: musicmetadata.cpp:954
MusicMetadata::m_trackCount
int m_trackCount
Definition: musicmetadata.h:341
MusicMetadata::m_fileSize
uint64_t m_fileSize
Definition: musicmetadata.h:366
LyricsData
Definition: lyricsdata.h:48
AlbumArtImages::getTypeName
static QString getTypeName(ImageType type)
Definition: musicmetadata.cpp:2142
AllMusic::getMetadata
MusicMetadata * getMetadata(int an_id)
Definition: musicmetadata.cpp:1627
uint
unsigned int uint
Definition: compat.h:79
IT_ARTIST
@ IT_ARTIST
Definition: musicmetadata.h:35
IT_CD
@ IT_CD
Definition: musicmetadata.h:33
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:61
MusicMetadata::m_discNum
int m_discNum
Definition: musicmetadata.h:342
MusicMetadata::m_compartistId
int m_compartistId
Definition: musicmetadata.h:348
AlbumArtImages::getImageByID
AlbumArtImage * getImageByID(int imageID)
Definition: musicmetadata.cpp:2112
MusicMetadata::m_compilation
bool m_compilation
Definition: musicmetadata.h:356
MusicMetadata::m_trackNum
int m_trackNum
Definition: musicmetadata.h:340
MusicMetadata::m_album
QString m_album
Definition: musicmetadata.h:331
kMSAutoCleanup
@ kMSAutoCleanup
automatically delete if backgrounded
Definition: mythsystem.h:45
MusicMetadata::s_formatCompilationCdTrack
static QString s_formatCompilationCdTrack
Definition: musicmetadata.h:388
MythDate::fromString
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:35
AllMusic::resync
void resync()
resync our cache with the database
Definition: musicmetadata.cpp:1480
kMSRunBackground
@ kMSRunBackground
run child in the background
Definition: mythsystem.h:38
MythDate::kSimplify
@ kSimplify
Do Today/Yesterday/Tomorrow transform.
Definition: mythdate.h:26
AlbumArtImages::AlbumArtImages
AlbumArtImages(MusicMetadata *metadata, bool loadFromDB=true)
Definition: musicmetadata.cpp:1887
MusicMetadata::IdType
uint32_t IdType
Definition: musicmetadata.h:86
AlbumArtImage::m_hostname
QString m_hostname
Definition: musicmetadata.h:50
IT_UNKNOWN
@ IT_UNKNOWN
Definition: musicmetadata.h:30
AllMusic::~AllMusic
~AllMusic()
Definition: musicmetadata.cpp:1417
MusicMetadata::reloadMetadata
void reloadMetadata(void)
Definition: musicmetadata.cpp:426
MusicMetadata::dumpToDatabase
void dumpToDatabase(void)
Definition: musicmetadata.cpp:666
MusicMetadata::m_formattedArtist
QString m_formattedArtist
Definition: musicmetadata.h:335
lastUpdate
QDateTime lastUpdate(GrabberScript *script)
Definition: netutils.cpp:338
MusicMetadata::m_albumId
int m_albumId
Definition: musicmetadata.h:349
MusicMetadata::Hostname
QString Hostname(void)
Definition: musicmetadata.h:231
MythDate::kAddYear
@ kAddYear
Add year to string if not included.
Definition: mythdate.h:25
MusicMetadata::decRating
void decRating()
Definition: musicmetadata.cpp:1162
AlbumArtImages
Definition: musicmetadata.h:521
AlbumArtList
QList< AlbumArtImage * > AlbumArtList
Definition: musicmetadata.h:56
MetadataLoadingThread::run
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: musicmetadata.cpp:1402
metaio.h
MythDate
Definition: mythdate.cpp:12
MusicMetadata::Format
QString Format() const
Definition: musicmetadata.h:237
kMSProcessEvents
@ kMSProcessEvents
process events while waiting
Definition: mythsystem.h:39
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:883
MusicMetadata::m_language
QString m_language
Definition: musicmetadata.h:377
MusicMetadata::m_metaFormat
QString m_metaFormat
Definition: musicmetadata.h:375
std
Definition: mythchrono.h:23
MythDate::ISODate
@ ISODate
Default UTC.
Definition: mythdate.h:17
MusicMetadata::m_artist
QString m_artist
Definition: musicmetadata.h:327
MusicMetadata::Country
QString Country(void)
Definition: musicmetadata.h:282
AllMusic::clearCDData
void clearCDData(void)
Definition: musicmetadata.cpp:1665
AlbumArtImage
Definition: musicmetadata.h:39
AlbumArtImage::m_id
int m_id
Definition: musicmetadata.h:48
AllMusic::save
void save()
Check each MusicMetadata entry and save those that have changed (ratings, etc.)
Definition: musicmetadata.cpp:1655
MusicMetadata::m_albumSort
QString m_albumSort
Definition: musicmetadata.h:332
MusicMetadata::m_logoUrl
QString m_logoUrl
Definition: musicmetadata.h:374
MusicMetadata::m_broadcaster
QString m_broadcaster
Definition: musicmetadata.h:370
mythcontext.h
MusicMetadata::FormatTitle
QString FormatTitle()
Definition: musicmetadata.cpp:946
MusicMetadata::setField
void setField(const QString &field, const QString &data)
Definition: musicmetadata.cpp:1032
UrlList
std::array< QString, STREAMURLCOUNT > UrlList
Definition: musicmetadata.h:78
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:102
StorageGroup
Definition: storagegroup.h:11
MusicMetadata::s_formatNormalFileTrack
static QString s_formatNormalFileTrack
Definition: musicmetadata.h:381
MusicMetadata::m_genre
QString m_genre
Definition: musicmetadata.h:337
MusicMetadata::ensureSortFields
void ensureSortFields(void)
Definition: musicmetadata.cpp:883
metaioid3.h
metaiomp4.h
IT_FRONTCOVER
@ IT_FRONTCOVER
Definition: musicmetadata.h:31
thePrefix
static QString thePrefix
Definition: musicmetadata.cpp:39
MusicMetadata::Broadcaster
QString Broadcaster(void)
Definition: musicmetadata.h:264
MusicMetadata::setLastPlay
void setLastPlay()
Definition: musicmetadata.cpp:1186
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:320
MythDate::kDateFull
@ kDateFull
Default local time.
Definition: mythdate.h:19
MusicMetadata::setRepo
void setRepo(RepoType repo)
Definition: musicmetadata.h:221
MSqlQuery::numRowsAffected
int numRowsAffected() const
Definition: mythdbcon.h:215
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:861
MusicMetadata::s_formatCompilationFileArtist
static QString s_formatCompilationFileArtist
Definition: musicmetadata.h:385
MusicMetadata::reloadAlbumArtImages
void reloadAlbumArtImages(void)
Definition: musicmetadata.cpp:1367
MythDownloadManager::GetLastModified
QDateTime GetLastModified(const QString &url)
Gets the Last Modified timestamp for a URI.
Definition: mythdownloadmanager.cpp:1545
MusicMetadata::setCompilationFormatting
void setCompilationFormatting(bool cd=false)
Definition: musicmetadata.cpp:897
MusicMetadata::incRating
void incRating()
Definition: musicmetadata.cpp:1171
lyricsdata.h
MusicMetadata::setID
void setID(IdType lid)
Definition: musicmetadata.h:220
AlbumArtImages::~AlbumArtImages
~AlbumArtImages()
Definition: musicmetadata.cpp:1894
MusicMetadata::m_compilationArtist
QString m_compilationArtist
Definition: musicmetadata.h:329
AllStream::loadStreams
void loadStreams(void)
Definition: musicmetadata.cpp:1748
kMSDontDisableDrawing
@ kMSDontDisableDrawing
avoid disabling UI drawing
Definition: mythsystem.h:37
mythdownloadmanager.h
IT_INLAY
@ IT_INLAY
Definition: musicmetadata.h:34
AlbumArtImage::m_description
QString m_description
Definition: musicmetadata.h:52
MusicMetadata::determineIfCompilation
bool determineIfCompilation(bool cd=false)
Definition: musicmetadata.cpp:836
MusicMetadata::createFromFilename
static MusicMetadata * createFromFilename(const QString &filename)
Definition: musicmetadata.cpp:217
MusicMetadata::m_actualFilename
QString m_actualFilename
Definition: musicmetadata.h:365
ID_TO_REPO
#define ID_TO_REPO(x)
Definition: musicmetadata.h:71
MusicMetadata::m_titleSort
QString m_titleSort
Definition: musicmetadata.h:334
build_compdb.filename
filename
Definition: build_compdb.py:21
operator!=
bool operator!=(MusicMetadata &a, MusicMetadata &b)
Definition: musicmetadata.cpp:46
mythmainwindow.h
MusicMetadata::saveHostname
void saveHostname(void)
Definition: musicmetadata.cpp:201
MusicMetadata::setUrl
void setUrl(const QString &url, uint index=0)
Definition: musicmetadata.cpp:652
AlbumArtImages::addImage
void addImage(const AlbumArtImage *newImage)
Definition: musicmetadata.cpp:2219
AlbumArtImages::dumpToDatabase
void dumpToDatabase(void)
saves or updates the image details in the DB
Definition: musicmetadata.cpp:2252
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:50
MusicMetadata::m_id
IdType m_id
Definition: musicmetadata.h:362
MusicMetadata::m_filename
QString m_filename
Definition: musicmetadata.h:363
MusicMetadata::getTagger
MetaIO * getTagger(void)
Definition: musicmetadata.cpp:1383
MythCoreContext::SaveSettingOnHost
bool SaveSettingOnHost(const QString &key, const QString &newValue, const QString &host)
Definition: mythcorecontext.cpp:914
ImageType
ImageType
Definition: musicmetadata.h:28
musicmetadata.h
AlbumArtImages::m_imageList
AlbumArtList m_imageList
Definition: musicmetadata.h:549
metaiooggvorbis.h
MusicMetadata::s_formatNormalFileArtist
static QString s_formatNormalFileArtist
Definition: musicmetadata.h:380
AlbumArtImages::getImageFilenames
QStringList getImageFilenames(void) const
Definition: musicmetadata.cpp:2123
MSqlQuery::at
int at(void) const
Definition: mythdbcon.h:219
MusicMetadata::m_country
QString m_country
Definition: musicmetadata.h:376
find
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
Definition: dvbstreamhandler.cpp:357
MusicMetadata::m_discCount
int m_discCount
Definition: musicmetadata.h:343
AlbumArtImages::m_parent
MusicMetadata * m_parent
Definition: musicmetadata.h:548
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:921
GetMythDownloadManager
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
Definition: mythdownloadmanager.cpp:145
MusicMetadata::s_formatNormalCdArtist
static QString s_formatNormalCdArtist
Definition: musicmetadata.h:382
MusicMetadata::s_formatNormalCdTrack
static QString s_formatNormalCdTrack
Definition: musicmetadata.h:383
AlbumArtImage::m_filename
QString m_filename
Definition: musicmetadata.h:49
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:832
AlbumArtImage::m_embedded
bool m_embedded
Definition: musicmetadata.h:53
MythSystem::Create
static MythSystem * Create(const QStringList &args, uint flags=kMSNone, const QString &startPath=QString(), Priority cpuPriority=kInheritPriority, Priority diskPriority=kInheritPriority)
Definition: mythsystem.cpp:201
build_compdb.paths
paths
Definition: build_compdb.py:13