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