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