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