MythTV  master
mythmusic/mythmusic/editmetadata.cpp
Go to the documentation of this file.
1 // qt
2 #include <QKeyEvent>
3 #include <utility>
4 
5 // mythtv
6 #include <mythcontext.h>
7 #include <mythdbcon.h>
8 #include <mythuihelper.h>
9 #include <mythdirs.h>
10 #include <mythdialogbox.h>
11 #include <mythuitextedit.h>
12 #include <mythuitext.h>
13 #include <mythuibutton.h>
14 #include <mythuicheckbox.h>
15 #include <mythuistatetype.h>
16 #include <mythuibuttonlist.h>
17 #include <mythuispinbox.h>
18 #include <mythuiimage.h>
19 #include <mythuiwebbrowser.h>
20 #include <mythuifilebrowser.h>
21 #include <musicutils.h>
22 #include <mythprogressdialog.h>
23 #include <remotefile.h>
24 #include <mthreadpool.h>
25 
26 // mythmusic
27 #include "musicdata.h"
28 #include "decoder.h"
29 #include "genres.h"
30 #include "metaio.h"
31 #include "musicplayer.h"
32 
33 
34 #include "editmetadata.h"
35 
36 // these need to be static so both screens can pick them up
40 
42  MusicMetadata *source_metadata,
43  const QString &name) :
44  MythScreenType(parent, name)
45 {
46  // make a copy so we can abandon changes
47  s_metadata = new MusicMetadata(*source_metadata);
48  s_sourceMetadata = source_metadata;
49 
50  s_metadataOnly = false;
51 }
52 
54 {
55  // do we need to save anything?
57  {
59 
60  // force a reload of the images for any tracks affected
62  for (int x = 0; x < allMusic->count(); x++)
63  {
64  if ((allMusic->at(x)->ID() == s_sourceMetadata->ID()) ||
65  (allMusic->at(x)->getDirectoryId() == s_sourceMetadata->getDirectoryId()))
66  {
67  allMusic->at(x)->reloadAlbumArtImages();
68  gPlayer->sendAlbumArtChangedEvent(allMusic->at(x)->ID());
69  }
70  }
71  }
72 }
73 
75 {
76  bool err = false;
77 
78  UIUtilE::Assign(this, m_doneButton, "donebutton", &err);
79 
80  connect(m_doneButton, SIGNAL(Clicked()), SLOT(showSaveMenu()));
81 
82  return err;
83 }
84 
85 bool EditMetadataCommon::keyPressEvent(QKeyEvent *event)
86 {
87  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
88  return true;
89 
90  QStringList actions;
91  bool handled = GetMythMainWindow()->TranslateKeyPress("Music", event, actions);
92 
93  for (int i = 0; i < actions.size() && !handled; i++)
94  {
95  QString action = actions[i];
96  handled = true;
97 
98  if (action == "ESCAPE")
99  showSaveMenu();
100  else
101  handled = false;
102  }
103 
104  if (!handled && MythScreenType::keyPressEvent(event))
105  handled = true;
106 
107  return handled;
108 }
109 
111 {
112  MythUITextEdit *edit = dynamic_cast<MythUITextEdit *>(GetChild("albumedit"));
113  if (edit)
114  s_metadata->setAlbum(edit->GetText());
115 
116  edit = dynamic_cast<MythUITextEdit *>(GetChild("artistedit"));
117  if (edit)
118  s_metadata->setArtist(edit->GetText());
119 
120  edit = dynamic_cast<MythUITextEdit *>(GetChild("compartistedit"));
121  if (edit)
123 
124  edit = dynamic_cast<MythUITextEdit *>(GetChild("titleedit"));
125  if (edit)
126  s_metadata->setTitle(edit->GetText());
127 
128  edit = dynamic_cast<MythUITextEdit *>(GetChild("genreedit"));
129  if (edit)
130  s_metadata->setGenre(edit->GetText());
131 
132  MythUISpinBox *spin = dynamic_cast<MythUISpinBox *>(GetChild("yearspin"));
133  if (spin)
134  s_metadata->setYear(spin->GetIntValue());
135 
136  spin = dynamic_cast<MythUISpinBox *>(GetChild("tracknumspin"));
137  if (spin)
138  s_metadata->setTrack(spin->GetIntValue());
139 
140  spin = dynamic_cast<MythUISpinBox *>(GetChild("discnumspin"));
141  if (spin)
143 
144  spin = dynamic_cast<MythUISpinBox *>(GetChild("ratingspin"));
145  if (spin)
146  s_metadata->setRating(spin->GetIntValue());
147 
148  MythUICheckBox *check = dynamic_cast<MythUICheckBox *>(GetChild("compilationcheck"));
149  if (check)
151 }
152 
154 {
155  updateMetadata();
156 
157  if (!hasMetadataChanged())
158  {
159  Close();
160  return;
161  }
162 
163  QString label = tr("Save Changes?");
164 
165  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
166 
167  auto *menu = new MythDialogBox(label, popupStack, "savechangesmenu");
168 
169  if (!menu->Create())
170  {
171  delete menu;
172  return;
173  }
174 
175  menu->SetReturnEvent(this, "savechangesmenu");
176 
177  if (s_metadataOnly)
178  menu->AddButton(tr("Save Changes"), SLOT(saveToMetadata()));
179  else
180  menu->AddButton(tr("Save Changes"), SLOT(saveAll()));
181 
182  menu->AddButton(tr("Exit/Do Not Save"), SLOT(cleanupAndClose()));
183 
184  popupStack->AddScreen(menu);
185 }
186 
188 {
189  if (s_metadata)
190  {
191  delete s_metadata;
192  s_metadata = nullptr;
193  }
194 
195  Close();
196 }
197 
199 {
201  emit metadataChanged();
202  cleanupAndClose();
203 }
204 
206 {
207  s_metadata->setAlbumId(-1);
208  s_metadata->setGenreId(-1);
209  s_metadata->setArtistId(-1);
210 
213 
215 }
216 
218 {
219  saveToDatabase();
220 
221  // only write to the tag if it's enabled by the user
222  if (GetMythDB()->GetBoolSetting("AllowTagWriting", false))
223  {
224  QStringList strList;
225  strList << "MUSIC_TAG_UPDATE_METADATA %1 %2"
226  << s_metadata->Hostname()
227  << QString::number(s_metadata->ID());
228 
229  auto *thread = new SendStringListThread(strList);
230  MThreadPool::globalInstance()->start(thread, "UpdateMetadata");
231  }
232 
233  cleanupAndClose();
234 }
235 
237 {
238  s_metadataOnly = true;
239 
240  MythUIButton *albumartButton = dynamic_cast<MythUIButton *>(GetChild("albumartbutton"));
241  if (albumartButton)
242  albumartButton->Hide();
243 }
244 
246 {
247  bool changed = false;
248 
249  changed |= (s_metadata->Album() != s_sourceMetadata->Album());
250  changed |= (s_metadata->Artist() != s_sourceMetadata->Artist());
252  changed |= (s_metadata->Title() != s_sourceMetadata->Title());
253  changed |= (s_metadata->Genre() != s_sourceMetadata->Genre());
254  changed |= (s_metadata->Year() != s_sourceMetadata->Year());
255  changed |= (s_metadata->Track() != s_sourceMetadata->Track());
256  changed |= (s_metadata->DiscNumber() != s_sourceMetadata->DiscNumber());
257  changed |= (s_metadata->Compilation() != s_sourceMetadata->Compilation());
258  changed |= (s_metadata->Rating() != s_sourceMetadata->Rating());
259 
260  return changed;
261 }
262 
265 {
266  QString artist = s_metadata->Artist().replace(' ', '+');
267  artist = QUrl::toPercentEncoding(artist, "+");
268 
269  QString album = s_metadata->Album().replace(' ', '+');
270  album = QUrl::toPercentEncoding(album, "+");
271 
272  QUrl url("http://www.google.co.uk/images?q=" + artist + "+" + album, QUrl::TolerantMode);
273 
274  m_searchType = "album";
275 
276  GetMythMainWindow()->HandleMedia("WebBrowser", url.toString(), GetConfDir() + "/MythMusic/", "front.jpg");
277 }
278 
280 {
282 }
283 
285 // EditMatadataDialog
286 
288  : EditMetadataCommon(parent, source_metadata, "EditMetadataDialog")
289 {
290  gCoreContext->addListener(this);
291 }
292 
294  : EditMetadataCommon(parent, "EditMetadataDialog")
295 {
296  gCoreContext->addListener(this);
297 }
298 
300 {
302 }
303 
305 {
306  if (! LoadWindowFromXML("music-ui.xml", "editmetadata", this))
307  return false;
308 
309  bool err = CreateCommon();
310 
311  UIUtilE::Assign(this, m_titleEdit, "titleedit", &err);
312  UIUtilE::Assign(this, m_artistEdit, "artistedit", &err);
313  UIUtilE::Assign(this, m_compArtistEdit, "compartistedit", &err);
314  UIUtilE::Assign(this, m_albumEdit, "albumedit", &err);
315  UIUtilE::Assign(this, m_genreEdit, "genreedit", &err);
316 
317  UIUtilE::Assign(this, m_yearSpin, "yearspin", &err);
318  UIUtilE::Assign(this, m_trackSpin, "tracknumspin", &err);
319  UIUtilW::Assign(this, m_discSpin, "discnumspin", &err);
320 
321  UIUtilE::Assign(this, m_searchArtistButton, "searchartistbutton", &err);
322  UIUtilE::Assign(this, m_searchCompArtistButton, "searchcompartistbutton", &err);
323  UIUtilE::Assign(this, m_searchAlbumButton, "searchalbumbutton", &err);
324  UIUtilE::Assign(this, m_searchGenreButton, "searchgenrebutton", &err);
325 
326  UIUtilW::Assign(this, m_artistIcon, "artisticon", &err);
327  UIUtilW::Assign(this, m_albumIcon, "albumicon", &err);
328  UIUtilW::Assign(this, m_genreIcon, "genreicon", &err);
329 
330  UIUtilW::Assign(this, m_ratingState, "ratingstate", &err);
331  UIUtilW::Assign(this, m_ratingSpin, "ratingspin", &err);
332 
333  UIUtilW::Assign(this, m_incRatingButton, "incratingbutton", &err);
334  UIUtilW::Assign(this, m_decRatingButton, "decratingbutton", &err);
335 
336  UIUtilE::Assign(this, m_compilationCheck, "compilationcheck", &err);
337 
338  UIUtilE::Assign(this, m_albumartButton, "albumartbutton", &err);
339 
340  if (err)
341  {
342  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'editmetadata'");
343  return false;
344  }
345 
346  m_yearSpin->SetRange(QDate::currentDate().year(), 1000, 1);
347  m_yearSpin->AddSelection(0, "None");
348  m_trackSpin->SetRange(0, 999, 1);
349 
350  if (m_discSpin)
351  m_discSpin->SetRange(0, 999, 1);
352 
353  if (m_ratingSpin)
354  {
355  m_ratingSpin->SetRange(0, 10, 1, 2);
356  connect(m_ratingSpin, SIGNAL(itemSelected(MythUIButtonListItem*)),
358  }
359 
360  connect(m_artistEdit, SIGNAL(LosingFocus()), SLOT(artistLostFocus()));
361  connect(m_albumEdit, SIGNAL(LosingFocus()), SLOT(albumLostFocus()));
362  connect(m_genreEdit, SIGNAL(LosingFocus()), SLOT(genreLostFocus()));
363 
364  connect(m_searchArtistButton, SIGNAL(Clicked()), SLOT(searchArtist()));
365  connect(m_searchCompArtistButton, SIGNAL(Clicked()), SLOT(searchCompilationArtist()));
366  connect(m_searchAlbumButton, SIGNAL(Clicked()), SLOT(searchAlbum()));
367  connect(m_searchGenreButton, SIGNAL(Clicked()), SLOT(searchGenre()));
368 
370  {
371  connect(m_incRatingButton, SIGNAL(Clicked()), SLOT(incRating()));
372  connect(m_decRatingButton, SIGNAL(Clicked()), SLOT(decRating()));
373  }
374 
375  connect(m_compilationCheck, SIGNAL(toggled(bool)), SLOT(checkClicked(bool)));
376 
377  connect(m_albumartButton, SIGNAL(Clicked()), SLOT(switchToAlbumArt()));
378 
379  fillWidgets();
380 
381  BuildFocusList();
382 
383  return true;
384 }
385 
387 {
396 
397  if (m_discSpin)
399 
400  updateRating();
401 
405 }
406 
408 {
410  updateRating();
411 }
412 
414 {
416  updateRating();
417 }
418 
420 {
421  if (item)
422  {
423  int rating = item->GetData().value<int>();
425 
426  if (m_ratingState)
427  m_ratingState->DisplayState(QString("%1").arg(s_metadata->Rating()));
428  }
429 }
430 
432 {
433  if (m_ratingState)
434  m_ratingState->DisplayState(QString("%1").arg(s_metadata->Rating()));
435 
436  if (m_ratingSpin)
438 }
439 
440 bool EditMetadataDialog::keyPressEvent(QKeyEvent *event)
441 {
442  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
443  return true;
444 
445  QStringList actions;
446  bool handled = GetMythMainWindow()->TranslateKeyPress("Music", event, actions);
447 
448  for (int i = 0; i < actions.size() && !handled; i++)
449  {
450  QString action = actions[i];
451  handled = true;
452 
453  if (action == "THMBUP")
454  incRating();
455  else if (action == "THMBDOWN")
456  decRating();
457  else if (action == "MENU")
458  showMenu();
459  else
460  handled = false;
461  }
462 
463  if (!handled && EditMetadataCommon::keyPressEvent(event))
464  handled = true;
465 
466  return handled;
467 }
468 
470 {
471  if (s_metadataOnly)
472  return;
473 
474  QString label = tr("Options");
475 
476  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
477 
478  auto *menu = new MythDialogBox(label, popupStack, "optionsmenu");
479 
480  if (!menu->Create())
481  {
482  delete menu;
483  return;
484  }
485 
486  menu->SetReturnEvent(this, "optionsmenu");
487 
488  menu->AddButton(tr("Edit Albumart Images"));
489  menu->AddButton(tr("Search Internet For Artist Image"));
490  menu->AddButton(tr("Search Internet For Album Image"));
491  menu->AddButton(tr("Search Internet For Genre Image"));
492  menu->AddButton(tr("Check Track Length"));
493 
494  popupStack->AddScreen(menu);
495 }
496 
498 {
499  updateMetadata();
500 
502 
503  auto *editDialog = new EditAlbumartDialog(mainStack);
504 
505  if (!editDialog->Create())
506  {
507  delete editDialog;
508  return;
509  }
510 
511  mainStack->AddScreen(editDialog);
512 
513  Close();
514 }
515 
517 {
518  if (!state)
519  {
521  }
522  else
523  {
524  if (m_compArtistEdit->GetText().isEmpty() ||
526  {
527  m_compArtistEdit->SetText(tr("Various Artists"));
528  }
529  }
530 }
531 
533 {
534  QString msg = tr("Select an Artist");
535  QStringList searchList = MusicMetadata::fillFieldList("artist");
536  QString s = s_metadata->Artist();
537 
538  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
539  auto *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, s);
540 
541  if (!searchDlg->Create())
542  {
543  delete searchDlg;
544  return;
545  }
546 
547  connect(searchDlg, SIGNAL(haveResult(QString)), SLOT(setArtist(QString)));
548 
549  popupStack->AddScreen(searchDlg);
550 }
551 
552 void EditMetadataDialog::setArtist(const QString& artist)
553 {
554  m_artistEdit->SetText(artist);
556 }
557 
559 {
560  QString artist = m_artistEdit->GetText();
561 
562  QString file;
563 
564  if (m_artistIcon)
565  {
566  file = findIcon("artist", artist.toLower(), true);
567  if (!file.isEmpty())
568  {
570  m_artistIcon->Load();
571  }
572  else
573  m_artistIcon->Reset();
574  }
575 }
576 
578 {
579  QString msg = tr("Select a Compilation Artist");
580  QStringList searchList = MusicMetadata::fillFieldList("compilation_artist");
581  QString s = s_metadata->CompilationArtist();
582 
583  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
584  auto *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, s);
585 
586  if (!searchDlg->Create())
587  {
588  delete searchDlg;
589  return;
590  }
591 
592  connect(searchDlg, SIGNAL(haveResult(QString)), SLOT(setCompArtist(QString)));
593 
594  popupStack->AddScreen(searchDlg);
595 }
596 
597 void EditMetadataDialog::setCompArtist(const QString& compArtist)
598 {
599  m_compArtistEdit->SetText(compArtist);
600 }
601 
603 {
604  QString msg = tr("Select an Album");
605  QStringList searchList = MusicMetadata::fillFieldList("album");
606  QString s = s_metadata->Album();
607 
608  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
609  auto *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, s);
610 
611  if (!searchDlg->Create())
612  {
613  delete searchDlg;
614  return;
615  }
616 
617  connect(searchDlg, SIGNAL(haveResult(QString)), SLOT(setAlbum(QString)));
618 
619  popupStack->AddScreen(searchDlg);
620 }
621 
622 void EditMetadataDialog::setAlbum(const QString& album)
623 {
624  m_albumEdit->SetText(album);
626 }
627 
629 {
630  QString file;
631 
632  if (m_albumIcon)
633  {
635  if (!file.isEmpty())
636  {
638  m_albumIcon->Load();
639  }
640  else
641  m_albumIcon->Reset();
642  }
643 }
644 
646 {
647  QString msg = tr("Select a Genre");
648  QStringList searchList = MusicMetadata::fillFieldList("genre");
649  // load genre list
650  /*
651  searchList.clear();
652  for (int x = 0; x < genre_table_size; x++)
653  searchList.push_back(QString(genre_table[x]));
654  searchList.sort();
655  */
656 
657  QString s = s_metadata->Genre();
658 
659  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
660  auto *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, s);
661 
662  if (!searchDlg->Create())
663  {
664  delete searchDlg;
665  return;
666  }
667 
668  connect(searchDlg, SIGNAL(haveResult(QString)), SLOT(setGenre(QString)));
669 
670  popupStack->AddScreen(searchDlg);
671 }
672 
673 void EditMetadataDialog::setGenre(const QString& genre)
674 {
675  m_genreEdit->SetText(genre);
677 }
678 
680 {
681  QString genre = m_genreEdit->GetText();
682  QString file;
683 
684  if (m_genreIcon)
685  {
686  file = findIcon("genre", genre.toLower(), true);
687  if (!file.isEmpty())
688  {
690  m_genreIcon->Load();
691  }
692  else
693  m_genreIcon->Reset();
694  }
695 }
696 
698 {
700 }
701 
703 {
705 }
706 
708 {
710 }
711 
714 {
715  QString genre= s_metadata->Genre().replace(' ', '+');
716  genre = QUrl::toPercentEncoding(genre, "+");
717 
718  QUrl url("http://www.flickr.com/search/groups/?w=908425%40N22&m=pool&q=" + genre, QUrl::TolerantMode);
719 
720  m_searchType = "genre";
721  GetMythMainWindow()->HandleMedia("WebBrowser", url.toString(), GetConfDir() + "/MythMusic/", "genre.jpg");
722 }
723 
726 {
727  QString artist = s_metadata->Artist().replace(' ', '+');
728  artist = QUrl::toPercentEncoding(artist, "+");
729 
730  QUrl url("http://www.google.co.uk/images?q=" + artist, QUrl::TolerantMode);
731 
732  m_searchType = "artist";
733  GetMythMainWindow()->HandleMedia("WebBrowser", url.toString(), GetConfDir() + "/MythMusic/", "artist.jpg");
734 }
735 
736 void EditMetadataDialog::customEvent(QEvent *event)
737 {
738  if (event->type() == DialogCompletionEvent::kEventType)
739  {
740  auto dce = dynamic_cast<DialogCompletionEvent*>(event);
741  // make sure the user didn't ESCAPE out of the menu
742  if ((dce == nullptr) || (dce->GetResult() < 0))
743  return;
744 
745  QString resultid = dce->GetId();
746  QString resulttext = dce->GetResultText();
747 
748 
749  if (resultid == "optionsmenu")
750  {
751  if (resulttext == tr("Edit Albumart Images"))
753  else if (resulttext == tr("Search Internet For Genre Image"))
754  {
755  updateMetadata();
757  }
758  else if (resulttext == tr("Search Internet For Artist Image"))
759  {
760  updateMetadata();
762  }
763  else if (resulttext == tr("Search Internet For Album Image"))
764  {
765  updateMetadata();
767  }
768  else if (resulttext == tr("Check Track Length"))
769  {
770  QStringList strList;
771  strList << "MUSIC_CALC_TRACK_LENGTH"
772  << s_metadata->Hostname()
773  << QString::number(s_metadata->ID());
774 
775  auto *thread = new SendStringListThread(strList);
776  MThreadPool::globalInstance()->start(thread, "Send MUSIC_CALC_TRACK_LENGTH");
777 
778  ShowOkPopup(tr("Asked the backend to check the tracks length"));
779  }
780  }
781  }
782  else if (event->type() == MythEvent::MythEventMessage)
783  {
784  auto *me = dynamic_cast<MythEvent *>(event);
785  if (me == nullptr)
786  return;
787  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
788 
789  if (!tokens.isEmpty())
790  {
791  if (tokens[0] == "BROWSER_DOWNLOAD_FINISHED")
792  {
793  QStringList args = me->ExtraDataList();
794  QString oldFilename = args[1];
795  int fileSize = args[2].toInt();
796  int errorCode = args[4].toInt();
797 
798  if ((errorCode != 0) || (fileSize == 0))
799  return;
800 
801  QString newFilename;
802 
803  if (m_searchType == "artist")
804  {
805  QString cleanName = fixFilename(s_metadata->Artist().toLower());
806  QString file = QString("Icons/%1/%2.jpg").arg("artist").arg(cleanName);
808  0, file, "MusicArt");
809  }
810  else if (m_searchType == "genre")
811  {
812  QString cleanName = fixFilename(s_metadata->Genre().toLower());
813  QString file = QString("Icons/%1/%2.jpg").arg("genre").arg(cleanName);
815  0, file, "MusicArt");
816  }
817  else if (m_searchType == "album")
818  {
819  // move the image from the MythMusic config dir to the tracks
820  // dir in the 'Music' storage group
821  newFilename = s_metadata->Filename();
822  newFilename = newFilename.section( '/', 0, -2);
823  newFilename = newFilename + '/' + oldFilename.section( '/', -1, -1);
824  }
825  else
826  {
827  LOG(VB_GENERAL, LOG_ERR, QString("Got unknown search type '%1' "
828  "in BROWSER_DOWNLOAD_FINISHED event")
829  .arg(m_searchType));
830  return;
831  }
832 
833  RemoteFile::CopyFile(oldFilename, newFilename, true);
834  QFile::remove(oldFilename);
835 
836  if (m_searchType == "album")
837  scanForImages();
838 
839  // force the icons to update
843 
845  // force a reload of the images for any tracks affected
847  for (int x = 0; x < allMusic->count(); x++)
848  {
849  if ((allMusic->at(x)->ID() == s_sourceMetadata->ID()) ||
850  (allMusic->at(x)->getDirectoryId() == s_sourceMetadata->getDirectoryId()))
851  {
852  allMusic->at(x)->reloadAlbumArtImages();
853  gPlayer->sendAlbumArtChangedEvent(allMusic->at(x)->ID());
854  }
855  }
856  }
857  }
858  }
859 }
860 
862 // EditAlbumartDialog
863 
865  : EditMetadataCommon(parent, "EditAlbumartDialog")
866 {
867  gCoreContext->addListener(this);
868 }
869 
871 {
873 }
874 
876 {
877  if (! LoadWindowFromXML("music-ui.xml", "editalbumart", this))
878  return false;
879 
880  bool err = CreateCommon();
881 
882  UIUtilE::Assign(this, m_coverartList, "coverartlist", &err);
883  UIUtilE::Assign(this, m_imagetypeText, "imagetypetext", &err);
884  UIUtilE::Assign(this, m_imagefilenameText, "imagefilenametext", &err);
885  UIUtilE::Assign(this, m_coverartImage, "coverartimage", &err);
886 
887  UIUtilE::Assign(this, m_metadataButton, "metadatabutton", &err);
888 
889  if (err)
890  {
891  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'editalbumart'");
892  return false;
893  }
894 
895  updateImageGrid();
896 
897  connect(m_coverartList, SIGNAL(itemSelected(MythUIButtonListItem*)),
898  this, SLOT(gridItemChanged(MythUIButtonListItem*)));
899 
900  connect(m_metadataButton, SIGNAL(Clicked()), SLOT(switchToMetadata()));
901 
902  BuildFocusList();
903 
904  return true;
905 }
906 
908 {
909  if (!item)
910  return;
911 
912  if (m_coverartImage)
913  {
914  auto *image = item->GetData().value<AlbumArtImage*>();
915  if (image)
916  {
917  m_coverartImage->SetFilename(image->m_filename);
919  if (m_imagetypeText)
922  {
923  QFileInfo fi(image->m_filename);
924  m_imagefilenameText->SetText(fi.fileName());
925  }
926  }
927  }
928 }
929 
931 {
933 
935 
936  for (int x = 0; x < albumArtList->size(); x++)
937  {
938  auto *item = new MythUIButtonListItem(m_coverartList,
939  AlbumArtImages::getTypeName(albumArtList->at(x)->m_imageType),
940  qVariantFromValue(albumArtList->at(x)));
941  item->SetImage(albumArtList->at(x)->m_filename);
942  QString state = albumArtList->at(x)->m_embedded ? "tag" : "file";
943  item->DisplayState(state, "locationstate");
944  }
945 }
946 
947 bool EditAlbumartDialog::keyPressEvent(QKeyEvent *event)
948 {
949  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
950  return true;
951 
952  QStringList actions;
953  bool handled = GetMythMainWindow()->TranslateKeyPress("Music", event, actions);
954 
955  for (int i = 0; i < actions.size() && !handled; i++)
956  {
957  QString action = actions[i];
958  handled = true;
959 
960  if (action == "MENU")
961  showMenu();
962  else if (action == "INFO")
963  showTypeMenu();
964  else
965  handled = false;
966  }
967 
968  if (!handled && EditMetadataCommon::keyPressEvent(event))
969  handled = true;
970 
971  return handled;
972 }
973 
975 {
977 
978  auto *editDialog = new EditMetadataDialog(mainStack);
979 
980  if (!editDialog->Create())
981  {
982  delete editDialog;
983  return;
984  }
985 
986  mainStack->AddScreen(editDialog);
987 
988  Close();
989 }
990 
991 void EditAlbumartDialog::showTypeMenu(bool changeType)
992 {
993  if (changeType && m_coverartList->GetCount() == 0)
994  return;
995 
996  QString label;
997 
998  if (changeType)
999  label = tr("Change Image Type");
1000  else
1001  label = tr("What image type do you want to use for this image?");
1002 
1003  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1004 
1005  auto *menu = new MythDialogBox(label, popupStack, "typemenu");
1006 
1007  if (!menu->Create())
1008  {
1009  delete menu;
1010  return;
1011  }
1012 
1013  ImageType imageType = IT_UNKNOWN;
1014  if (changeType)
1015  menu->SetReturnEvent(this, "changetypemenu");
1016  else
1017  {
1018  menu->SetReturnEvent(this, "asktypemenu");
1020  }
1021 
1022  menu->AddButton(AlbumArtImages::getTypeName(IT_UNKNOWN), qVariantFromValue((int)IT_UNKNOWN), false, (imageType == IT_UNKNOWN));
1023  menu->AddButton(AlbumArtImages::getTypeName(IT_FRONTCOVER), qVariantFromValue((int)IT_FRONTCOVER), false, (imageType == IT_FRONTCOVER));
1024  menu->AddButton(AlbumArtImages::getTypeName(IT_BACKCOVER), qVariantFromValue((int)IT_BACKCOVER), false, (imageType == IT_BACKCOVER));
1025  menu->AddButton(AlbumArtImages::getTypeName(IT_CD), qVariantFromValue((int)IT_CD), false, (imageType == IT_CD));
1026  menu->AddButton(AlbumArtImages::getTypeName(IT_INLAY), qVariantFromValue((int)IT_INLAY), false, (imageType == IT_INLAY));
1027  menu->AddButton(AlbumArtImages::getTypeName(IT_ARTIST), qVariantFromValue((int)IT_ARTIST), false, (imageType == IT_ARTIST));
1028 
1029  popupStack->AddScreen(menu);
1030 }
1031 
1033 {
1034  QString label = tr("Options");
1035 
1036  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1037 
1038  auto *menu = new MythDialogBox(label, popupStack, "optionsmenu");
1039 
1040  if (!menu->Create())
1041  {
1042  delete menu;
1043  return;
1044  }
1045 
1046  menu->SetReturnEvent(this, "optionsmenu");
1047 
1048  menu->AddButton(tr("Edit Metadata"));
1049  menu->AddButton(tr("Rescan For Images"));
1050 
1051 
1052  menu->AddButton(tr("Search Internet For Images"));
1053 
1054  MetaIO *tagger = MetaIO::createTagger(s_metadata->Filename(false));
1055 
1057  {
1058  menu->AddButton(tr("Change Image Type"), nullptr, true);
1059 
1060  if (GetMythDB()->GetBoolSetting("AllowTagWriting", false))
1061  {
1063  if (item)
1064  {
1065  auto *image = item->GetData().value<AlbumArtImage*>();
1066  if (image)
1067  {
1068  if (!image->m_embedded)
1069  {
1070  if (tagger && tagger->supportsEmbeddedImages())
1071  menu->AddButton(tr("Copy Selected Image To Tag"));
1072  }
1073  else
1074  {
1075  if (tagger && tagger->supportsEmbeddedImages())
1076  menu->AddButton(tr("Remove Selected Image From Tag"));
1077  }
1078  }
1079  }
1080  }
1081  }
1082 
1083  if (GetMythDB()->GetBoolSetting("AllowTagWriting", false))
1084  {
1085  if (tagger && tagger->supportsEmbeddedImages())
1086  menu->AddButton(tr("Copy Image To Tag"));
1087  }
1088 
1089  delete tagger;
1090 
1091  popupStack->AddScreen(menu);
1092 }
1093 
1095 {
1096  if (event->type() == DialogCompletionEvent::kEventType)
1097  {
1098  auto dce = dynamic_cast<DialogCompletionEvent*>(event);
1099  // make sure the user didn't ESCAPE out of the menu
1100  if ((dce == nullptr) || (dce->GetResult() < 0))
1101  return;
1102 
1103  QString resultid = dce->GetId();
1104  QString resulttext = dce->GetResultText();
1105 
1106  if (resultid == "changetypemenu")
1107  {
1108  int type = dce->GetData().toInt();
1109 
1110  if ((type >= IT_UNKNOWN) && (type < IT_LAST))
1111  {
1112  // get selected image in list
1114  if (item)
1115  {
1117  auto *image = item->GetData().value<AlbumArtImage*>();
1118  if (image)
1119  {
1120  QStringList strList("MUSIC_TAG_CHANGEIMAGE");
1121  strList << s_metadata->Hostname()
1122  << QString::number(s_metadata->ID())
1123  << QString::number(image->m_imageType)
1124  << QString::number(type);
1125 
1127 
1128  m_albumArtChanged = true;
1129 
1130  gridItemChanged(item);
1131  }
1132  }
1133  }
1134  }
1135  else if (resultid == "asktypemenu")
1136  {
1137  int type = dce->GetData().toInt();
1138 
1139  if ((type >= IT_UNKNOWN) && (type < IT_LAST))
1141  }
1142  else if (resultid == "optionsmenu")
1143  {
1144  if (resulttext == tr("Edit Metadata"))
1145  switchToMetadata();
1146  else if (resulttext == tr("Rescan For Images"))
1147  rescanForImages();
1148  else if (resulttext == tr("Search Internet For Images"))
1150  else if (resulttext == tr("Change Image Type"))
1151  showTypeMenu();
1152  else if (resulttext == tr("Copy Selected Image To Tag"))
1154  else if (resulttext == tr("Remove Selected Image From Tag"))
1156  else if (resulttext == tr("Copy Image To Tag"))
1158  }
1159  else if (resultid == "imagelocation")
1160  {
1161  m_imageFilename = resulttext;
1162 
1163  // save directory location for next time
1164  QFileInfo fi(m_imageFilename);
1165  gCoreContext->SaveSetting("MusicLastImageLocation", fi.canonicalPath());
1166 
1167  showTypeMenu(false);
1168  }
1169  }
1170  else if (event->type() == MythEvent::MythEventMessage)
1171  {
1172  auto *me = dynamic_cast<MythEvent *>(event);
1173  if (me == nullptr)
1174  return;
1175  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
1176 
1177  if (!tokens.isEmpty())
1178  {
1179  if (tokens[0] == "BROWSER_DOWNLOAD_FINISHED")
1180  rescanForImages();
1181  else if (tokens[0] == "MUSIC_ALBUMART_CHANGED")
1182  {
1183  if (tokens.size() >= 2)
1184  {
1185  auto songID = (MusicMetadata::IdType)tokens[1].toInt();
1186 
1187  if (s_metadata->ID() == songID)
1188  {
1189  // force all the image to reload
1190  for (uint x = 0; x < s_metadata->getAlbumArtImages()->getImageCount(); x++)
1192 
1193  updateImageGrid();
1194  }
1195  }
1196  }
1197  }
1198  }
1199 }
1200 
1203 {
1204  // scan the tracks directory and tag for any images
1205  scanForImages();
1206 
1207  updateImageGrid();
1208 
1210  if (albumArt->getImageCount() > 0)
1211  m_albumArtChanged = true;
1212 }
1213 
1215 {
1216  QString lastLocation = gCoreContext->GetSetting("MusicLastImageLocation", "/");
1217 
1218  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1219  auto *fb = new MythUIFileBrowser(popupStack, lastLocation);
1220 
1221  fb->SetTypeFilter(QDir::AllDirs | QDir::Files | QDir::Readable);
1222 
1223  QStringList filters;
1224  filters << "*.png" << "*.jpg" << "*.jpeg" << "*.gif";
1225  fb->SetNameFilter(filters);
1226 
1227  if (fb->Create())
1228  {
1229  fb->SetReturnEvent(this, "imagelocation");
1230  popupStack->AddScreen(fb);
1231  }
1232  else
1233  delete fb;
1234 }
1235 
1237 {
1238  AlbumArtImage image;
1239  image.m_filename = m_imageFilename;
1240  image.m_imageType = imageType;
1241 
1242  doCopyImageToTag(&image);
1243 }
1244 
1246 {
1248  if (item)
1249  {
1250  auto *image = item->GetData().value<AlbumArtImage*>();
1251  if (image)
1252  doCopyImageToTag(image);
1253  }
1254 }
1255 
1257 {
1259  if (item)
1260  {
1261  auto *image = item->GetData().value<AlbumArtImage*>();
1262  if (image)
1263  {
1264  QString msg = tr("Are you sure you want to permanently remove this image from the tag?");
1265  ShowOkPopup(msg, this, SLOT(doRemoveImageFromTag(bool)), true);
1266  }
1267  }
1268 }
1269 
1271 {
1272  if (!doIt)
1273  return;
1274 
1276  if (item)
1277  {
1278  auto *image = item->GetData().value<AlbumArtImage*>();
1279  if (image)
1280  {
1281  // ask the backend to remove the image from the tracks tag
1282  QStringList strList("MUSIC_TAG_REMOVEIMAGE");
1283  strList << s_metadata->Hostname()
1284  << QString::number(s_metadata->ID())
1285  << QString::number(image->m_id);
1286 
1288 
1289  removeCachedImage(image);
1290  rescanForImages();
1291  }
1292  }
1293 }
1294 
1296 {
1297  public:
1298  explicit CopyImageThread(QStringList strList) :
1299  MThread("CopyImage"), m_strList(std::move(strList)) {}
1300 
1301  void run() override // MThread
1302  {
1303  RunProlog();
1305  RunEpilog();
1306  }
1307 
1308  QStringList getResult(void) { return m_strList; }
1309 
1310  private:
1311  QStringList m_strList;
1312 };
1313 
1315 {
1316  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1317  auto *busy = new MythUIBusyDialog(tr("Copying image to tag..."),
1318  popupStack, "copyimagebusydialog");
1319 
1320  if (busy->Create())
1321  {
1322  popupStack->AddScreen(busy, false);
1323  }
1324  else
1325  {
1326  delete busy;
1327  busy = nullptr;
1328  }
1329 
1330  // copy the image to the tracks host
1331  QFileInfo fi(image->m_filename);
1332  QString saveFilename = MythCoreContext::GenMythURL(s_metadata->Hostname(), 0,
1333  QString("AlbumArt/") + fi.fileName(),
1334  "MusicArt");
1335 
1336  RemoteFile::CopyFile(image->m_filename, saveFilename, true);
1337 
1338  // ask the backend to add the image to the tracks tag
1339  QStringList strList("MUSIC_TAG_ADDIMAGE");
1340  strList << s_metadata->Hostname()
1341  << QString::number(s_metadata->ID())
1342  << fi.fileName()
1343  << QString::number(image->m_imageType);
1344 
1345  auto *copyThread = new CopyImageThread(strList);
1346  copyThread->start();
1347 
1348  while (copyThread->isRunning())
1349  {
1350  qApp->processEvents();
1351  usleep(1000);
1352  }
1353 
1354  strList = copyThread->getResult();
1355 
1356  delete copyThread;
1357 
1358  if (busy)
1359  busy->Close();
1360 
1361  removeCachedImage(image);
1362 
1363  rescanForImages();
1364 }
1365 
1367 {
1368  if (!image->m_embedded)
1369  return;
1370 
1372 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
QList< AlbumArtImage * > AlbumArtList
Definition: musicmetadata.h:54
void setCompArtist(const QString &compArtist)
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
void AddSelection(int value, const QString &label="")
Add a special label for a value of the spinbox, it does not need to be in the range.
void copyImageToTag(ImageType imageType)
int DiscNumber() const
MusicPlayer * gPlayer
Definition: musicplayer.cpp:35
void setGenreId(int lgenreid)
void setTrack(int ltrack)
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
void LosingFocus()
void doCopyImageToTag(const AlbumArtImage *image)
A widget for offering a range of numerical values where only the the bounding values and interval are...
Definition: mythuispinbox.h:16
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
EditMetadataDialog(MythScreenStack *lparent, const QString &lname, VideoMetadata *source_metadata, const VideoMetadataListManager &cache)
uint getImageCount()
void setYear(int lyear)
A checkbox widget supporting three check states - on,off,half and two conditions - selected and unsel...
void gridItemChanged(MythUIButtonListItem *item)
void SaveSetting(const QString &key, int newValue)
static Type MythEventMessage
Definition: mythevent.h:66
void removeListener(QObject *listener)
Remove a listener to the observable.
uint32_t IdType
Definition: musicmetadata.h:84
QString Genre() const
QString Hostname(void)
Basic menu dialog, message and a list of options.
static MetaIO * createTagger(const QString &filename)
Finds an appropriate tagger for the given file.
Definition: metaio.cpp:32
void setGenre(const QString &genre)
static void removeCachedImage(const AlbumArtImage *image)
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:135
void customEvent(QEvent *levent) override
AlbumArtImages * getAlbumArtImages(void)
void setDiscNumber(int discnum)
static bool CopyFile(const QString &src, const QString &dst, bool overwrite=false, bool verify=false)
Definition: remotefile.cpp:587
static QStringList fillFieldList(const QString &field)
MythScreenStack * GetStack(const QString &stackname)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void SetValue(int val) override
Definition: mythuispinbox.h:26
MythScreenStack * GetMainStack()
void addListener(QObject *listener)
Add a listener to the observable.
bool Create(void) override
static QString getTypeName(ImageType type)
static MythThemedMenu * menu
AlbumArtList * getImageList(void)
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
void BuildFocusList(void)
int Rating() const
static Type kEventType
Definition: mythdialogbox.h:50
int GetIntValue(void) const override
Definition: mythuispinbox.h:33
void scanForImages(void)
MythUISpinBox * m_yearSpin
ImageType m_imageType
Definition: musicmetadata.h:49
Definition: metaio.h:17
MythUITextEdit * m_titleEdit
QString GetConfDir(void)
Definition: mythdirs.cpp:224
void Hide(void)
virtual void Close()
void setCompilation(bool state)
QString Artist() const
void SetRange(int low, int high, int step, uint pageMultiple=5)
Set the lower and upper bounds of the spinbox, the interval and page amount.
void dumpToDatabase(void)
bool GetBooleanCheckState(void) const
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
void rescanForImages(void)
search the tracks tag and the tracks directory for images
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
void sendMetadataChangedEvent(int trackID)
def rating(profile, smoonURL, gate)
Definition: scan.py:39
virtual bool supportsEmbeddedImages(void)
Does the tag support embedded cover art.
Definition: metaio.h:60
void searchForArtistImages(void)
search google for artist images
void setArtistId(int lartistid)
void setAlbumId(int lalbumid)
QString m_filename
Definition: musicmetadata.h:47
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
QString GetSetting(const QString &key, const QString &defaultval="")
A text entry and edit widget.
IdType ID() const
QString findIcon(const QString &type, const QString &name, bool ignoreCache)
find an image for a artist or genre
Definition: musicutils.cpp:34
void setAlbum(const QString &lalbum, const QString &lalbum_sort=nullptr)
void setRating(int lrating)
QList< MusicMetadata * > MetadataPtrList
QString getAlbumArtFile(void)
int Track() const
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
void Reset(void) override
Reset the image back to the default defined in the theme.
QString GetMasterHostName(void)
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
EditMetadataCommon(MythScreenStack *parent, MusicMetadata *source_metadata, const QString &name)
void SetText(const QString &text, const QString &name="", const QString &state="")
unsigned int uint
Definition: compat.h:140
QString Album() const
static MusicMetadata * s_metadata
MythUIHelper * GetMythUI()
void setTitle(const QString &ltitle, const QString &ltitle_sort=nullptr)
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
void SetText(const QString &text, bool moveCursor=true)
MythUIType * GetFocusWidget(void) const
void setArtist(const QString &lartist, const QString &lartist_sort=nullptr)
void searchForGenreImages(void)
search flickr for genre images
A single button widget.
Definition: mythuibutton.h:21
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
MythMainWindow * GetMythMainWindow(void)
static ImageType guessImageType(const QString &filename)
AlbumArtImage * getImageAt(uint index)
bool Compilation() const
static MThreadPool * globalInstance(void)
void sendAlbumArtChangedEvent(int trackID)
void dumpToDatabase(void)
saves or updates the image details in the DB
bool Create() override
void customEvent(QEvent *event) override
bool keyPressEvent(QKeyEvent *e) override
Key event handler.
bool keyPressEvent(QKeyEvent *) override
Key event handler.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
int Year() const
void start(QRunnable *runnable, const QString &debugName, int priority=0)
QString Title() const
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
void haveResult(bool ok, ArchiveItem *item)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
QString Filename(bool find=true)
bool DisplayState(const QString &name)
MusicData * gMusicData
Definition: musicdata.cpp:20
void SetCheckState(MythUIStateType::StateType state)
void setArtist(const QString &artist)
void ratingSpinChanged(MythUIButtonListItem *item)
void showTypeMenu(bool changeType=true)
ImageType
Definition: musicmetadata.h:26
QString CompilationArtist() const
MythDB * GetMythDB(void)
Definition: mythdb.cpp:46
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
bool HandleMedia(const QString &handler, const QString &mrl, const QString &plot="", const QString &title="", const QString &subtitle="", const QString &director="", int season=0, int episode=0, const QString &inetref="", int lenMins=120, const QString &year="1895", const QString &id="", bool useBookmarks=false)
Screen in which all other widgets are contained and rendered.
QString fixFilename(const QString &filename)
remove any bad filename characters
Definition: musicutils.cpp:26
MetadataPtrList * getAllMetadata(void)
void setCompilationArtist(const QString &lcompilation_artist, const QString &lcompilation_artist_sort=nullptr)
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:132
void setAlbum(const QString &album)
void metadataChanged(void)
static MusicMetadata * s_sourceMetadata
void searchForAlbumImages(void)
search Google for images
send a message to the master BE without blocking the UI thread
Definition: musicdata.h:20
EditAlbumartDialog(MythScreenStack *parent)
AllMusic * m_all_music
Definition: musicdata.h:54
Provide a dialog to quickly find an entry in a list.
QString GetText(void) const
void RemoveFromCacheByFile(const QString &fname)
MythUIButtonListItem * GetItemCurrent() const
void setGenre(const QString &lgenre)