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