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