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  MythDialogBox *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  SendStringListThread *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  MythDialogBox *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  EditAlbumartDialog *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  MythUISearchDialog *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  {
569  m_artistIcon->SetFilename(file);
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  MythUISearchDialog *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  MythUISearchDialog *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  {
634  file = s_metadata->getAlbumArtFile();
635  if (!file.isEmpty())
636  {
637  m_albumIcon->SetFilename(file);
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  MythUISearchDialog *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  {
689  m_genreIcon->SetFilename(file);
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  {
741 
742  // make sure the user didn't ESCAPE out of the menu
743  if (dce->GetResult() < 0)
744  return;
745 
746  QString resultid = dce->GetId();
747  QString resulttext = dce->GetResultText();
748 
749 
750  if (resultid == "optionsmenu")
751  {
752  if (resulttext == tr("Edit Albumart Images"))
754  else if (resulttext == tr("Search Internet For Genre Image"))
755  {
756  updateMetadata();
758  }
759  else if (resulttext == tr("Search Internet For Artist Image"))
760  {
761  updateMetadata();
763  }
764  else if (resulttext == tr("Search Internet For Album Image"))
765  {
766  updateMetadata();
768  }
769  else if (resulttext == tr("Check Track Length"))
770  {
771  QStringList strList;
772  strList << "MUSIC_CALC_TRACK_LENGTH"
773  << s_metadata->Hostname()
774  << QString::number(s_metadata->ID());
775 
776  SendStringListThread *thread = new SendStringListThread(strList);
777  MThreadPool::globalInstance()->start(thread, "Send MUSIC_CALC_TRACK_LENGTH");
778 
779  ShowOkPopup(tr("Asked the backend to check the tracks length"));
780  }
781  }
782  }
783  else if (event->type() == MythEvent::MythEventMessage)
784  {
785  MythEvent *me = static_cast<MythEvent *>(event);
786  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
787 
788  if (!tokens.isEmpty())
789  {
790  if (tokens[0] == "BROWSER_DOWNLOAD_FINISHED")
791  {
792  QStringList args = me->ExtraDataList();
793  QString oldFilename = args[1];
794  int fileSize = args[2].toInt();
795  int errorCode = args[4].toInt();
796 
797  if ((errorCode != 0) || (fileSize == 0))
798  return;
799 
800  QString newFilename;
801 
802  if (m_searchType == "artist")
803  {
804  QString cleanName = fixFilename(s_metadata->Artist().toLower());
805  QString file = QString("Icons/%1/%2.jpg").arg("artist").arg(cleanName);
807  0, file, "MusicArt");
808  }
809  else if (m_searchType == "genre")
810  {
811  QString cleanName = fixFilename(s_metadata->Genre().toLower());
812  QString file = QString("Icons/%1/%2.jpg").arg("genre").arg(cleanName);
814  0, file, "MusicArt");
815  }
816  else if (m_searchType == "album")
817  {
818  // move the image from the MythMusic config dir to the tracks
819  // dir in the 'Music' storage group
820  newFilename = s_metadata->Filename();
821  newFilename = newFilename.section( '/', 0, -2);
822  newFilename = newFilename + '/' + oldFilename.section( '/', -1, -1);
823  }
824  else
825  {
826  LOG(VB_GENERAL, LOG_ERR, QString("Got unknown search type '%1' "
827  "in BROWSER_DOWNLOAD_FINISHED event")
828  .arg(m_searchType));
829  return;
830  }
831 
832  RemoteFile::CopyFile(oldFilename, newFilename, true);
833  QFile::remove(oldFilename);
834 
835  if (m_searchType == "album")
836  scanForImages();
837 
838  // force the icons to update
842 
844  // force a reload of the images for any tracks affected
846  for (int x = 0; x < allMusic->count(); x++)
847  {
848  if ((allMusic->at(x)->ID() == s_sourceMetadata->ID()) ||
849  (allMusic->at(x)->getDirectoryId() == s_sourceMetadata->getDirectoryId()))
850  {
851  allMusic->at(x)->reloadAlbumArtImages();
852  gPlayer->sendAlbumArtChangedEvent(allMusic->at(x)->ID());
853  }
854  }
855  }
856  }
857  }
858 }
859 
861 // EditAlbumartDialog
862 
864  : EditMetadataCommon(parent, "EditAlbumartDialog")
865 {
866  gCoreContext->addListener(this);
867 }
868 
870 {
872 }
873 
875 {
876  if (! LoadWindowFromXML("music-ui.xml", "editalbumart", this))
877  return false;
878 
879  bool err = CreateCommon();
880 
881  UIUtilE::Assign(this, m_coverartList, "coverartlist", &err);
882  UIUtilE::Assign(this, m_imagetypeText, "imagetypetext", &err);
883  UIUtilE::Assign(this, m_imagefilenameText, "imagefilenametext", &err);
884  UIUtilE::Assign(this, m_coverartImage, "coverartimage", &err);
885 
886  UIUtilE::Assign(this, m_metadataButton, "metadatabutton", &err);
887 
888  if (err)
889  {
890  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'editalbumart'");
891  return false;
892  }
893 
894  updateImageGrid();
895 
896  connect(m_coverartList, SIGNAL(itemSelected(MythUIButtonListItem*)),
897  this, SLOT(gridItemChanged(MythUIButtonListItem*)));
898 
899  connect(m_metadataButton, SIGNAL(Clicked()), SLOT(switchToMetadata()));
900 
901  BuildFocusList();
902 
903  return true;
904 }
905 
907 {
908  if (!item)
909  return;
910 
911  if (m_coverartImage)
912  {
913  AlbumArtImage *image = item->GetData().value<AlbumArtImage*>();
914  if (image)
915  {
918  if (m_imagetypeText)
921  {
922  QFileInfo fi(image->m_filename);
923  m_imagefilenameText->SetText(fi.fileName());
924  }
925  }
926  }
927 }
928 
930 {
932 
934 
935  for (int x = 0; x < albumArtList->size(); x++)
936  {
937  MythUIButtonListItem *item =
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  EditMetadataDialog *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  MythDialogBox *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  MythDialogBox *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  AlbumArtImage *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  {
1099 
1100  // make sure the user didn't ESCAPE out of the menu
1101  if (dce->GetResult() < 0)
1102  return;
1103 
1104  QString resultid = dce->GetId();
1105  QString resulttext = dce->GetResultText();
1106 
1107  if (resultid == "changetypemenu")
1108  {
1109  int type = dce->GetData().toInt();
1110 
1111  if ((type >= IT_UNKNOWN) && (type < IT_LAST))
1112  {
1113  // get selected image in list
1115  if (item)
1116  {
1118  AlbumArtImage *image = item->GetData().value<AlbumArtImage*>();
1119  if (image)
1120  {
1121  QStringList strList("MUSIC_TAG_CHANGEIMAGE");
1122  strList << s_metadata->Hostname()
1123  << QString::number(s_metadata->ID())
1124  << QString::number(image->m_imageType)
1125  << QString::number(type);
1126 
1128 
1129  m_albumArtChanged = true;
1130 
1131  gridItemChanged(item);
1132  }
1133  }
1134  }
1135  }
1136  else if (resultid == "asktypemenu")
1137  {
1138  int type = dce->GetData().toInt();
1139 
1140  if ((type >= IT_UNKNOWN) && (type < IT_LAST))
1142  }
1143  else if (resultid == "optionsmenu")
1144  {
1145  if (resulttext == tr("Edit Metadata"))
1146  switchToMetadata();
1147  else if (resulttext == tr("Rescan For Images"))
1148  rescanForImages();
1149  else if (resulttext == tr("Search Internet For Images"))
1151  else if (resulttext == tr("Change Image Type"))
1152  showTypeMenu();
1153  else if (resulttext == tr("Copy Selected Image To Tag"))
1155  else if (resulttext == tr("Remove Selected Image From Tag"))
1157  else if (resulttext == tr("Copy Image To Tag"))
1159  }
1160  else if (resultid == "imagelocation")
1161  {
1162  m_imageFilename = resulttext;
1163 
1164  // save directory location for next time
1165  QFileInfo fi(m_imageFilename);
1166  gCoreContext->SaveSetting("MusicLastImageLocation", fi.canonicalPath());
1167 
1168  showTypeMenu(false);
1169  }
1170  }
1171  else if (event->type() == MythEvent::MythEventMessage)
1172  {
1173  MythEvent *me = static_cast<MythEvent *>(event);
1174  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
1175 
1176  if (!tokens.isEmpty())
1177  {
1178  if (tokens[0] == "BROWSER_DOWNLOAD_FINISHED")
1179  rescanForImages();
1180  else if (tokens[0] == "MUSIC_ALBUMART_CHANGED")
1181  {
1182  if (tokens.size() >= 2)
1183  {
1184  MusicMetadata::IdType songID = (MusicMetadata::IdType)tokens[1].toInt();
1185 
1186  if (s_metadata->ID() == songID)
1187  {
1188  // force all the image to reload
1189  for (uint x = 0; x < s_metadata->getAlbumArtImages()->getImageCount(); x++)
1191 
1192  updateImageGrid();
1193  }
1194  }
1195  }
1196  }
1197  }
1198 }
1199 
1202 {
1203  // scan the tracks directory and tag for any images
1204  scanForImages();
1205 
1206  updateImageGrid();
1207 
1209  if (albumArt->getImageCount() > 0)
1210  m_albumArtChanged = true;
1211 }
1212 
1214 {
1215  QString lastLocation = gCoreContext->GetSetting("MusicLastImageLocation", "/");
1216 
1217  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1218  MythUIFileBrowser *fb = new MythUIFileBrowser(popupStack, lastLocation);
1219 
1220  fb->SetTypeFilter(QDir::AllDirs | QDir::Files | QDir::Readable);
1221 
1222  QStringList filters;
1223  filters << "*.png" << "*.jpg" << "*.jpeg" << "*.gif";
1224  fb->SetNameFilter(filters);
1225 
1226  if (fb->Create())
1227  {
1228  fb->SetReturnEvent(this, "imagelocation");
1229  popupStack->AddScreen(fb);
1230  }
1231  else
1232  delete fb;
1233 }
1234 
1236 {
1237  AlbumArtImage image;
1238  image.m_filename = m_imageFilename;
1239  image.m_imageType = imageType;
1240 
1241  doCopyImageToTag(&image);
1242 }
1243 
1245 {
1247  if (item)
1248  {
1249  AlbumArtImage *image = item->GetData().value<AlbumArtImage*>();
1250  if (image)
1251  doCopyImageToTag(image);
1252  }
1253 }
1254 
1256 {
1258  if (item)
1259  {
1260  AlbumArtImage *image = item->GetData().value<AlbumArtImage*>();
1261  if (image)
1262  {
1263  QString msg = tr("Are you sure you want to permanently remove this image from the tag?");
1264  ShowOkPopup(msg, this, SLOT(doRemoveImageFromTag(bool)), true);
1265  }
1266  }
1267 }
1268 
1270 {
1271  if (!doIt)
1272  return;
1273 
1275  if (item)
1276  {
1277  AlbumArtImage *image = item->GetData().value<AlbumArtImage*>();
1278  if (image)
1279  {
1280  // ask the backend to remove the image from the tracks tag
1281  QStringList strList("MUSIC_TAG_REMOVEIMAGE");
1282  strList << s_metadata->Hostname()
1283  << QString::number(s_metadata->ID())
1284  << QString::number(image->m_id);
1285 
1287 
1288  removeCachedImage(image);
1289  rescanForImages();
1290  }
1291  }
1292 }
1293 
1295 {
1296  public:
1297  explicit CopyImageThread(QStringList strList) :
1298  MThread("CopyImage"), m_strList(std::move(strList)) {}
1299 
1300  void run() override // MThread
1301  {
1302  RunProlog();
1304  RunEpilog();
1305  }
1306 
1307  QStringList getResult(void) { return m_strList; }
1308 
1309  private:
1310  QStringList m_strList;
1311 };
1312 
1314 {
1315  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1316  MythUIBusyDialog *busy = new MythUIBusyDialog(tr("Copying image to tag..."),
1317  popupStack, "copyimagebusydialog");
1318 
1319  if (busy->Create())
1320  {
1321  popupStack->AddScreen(busy, false);
1322  }
1323  else
1324  {
1325  delete busy;
1326  busy = nullptr;
1327  }
1328 
1329  // copy the image to the tracks host
1330  QFileInfo fi(image->m_filename);
1331  QString saveFilename = gCoreContext->GenMythURL(s_metadata->Hostname(), 0,
1332  QString("AlbumArt/") + fi.fileName(),
1333  "MusicArt");
1334 
1335  RemoteFile::CopyFile(image->m_filename, saveFilename, true);
1336 
1337  // ask the backend to add the image to the tracks tag
1338  QStringList strList("MUSIC_TAG_ADDIMAGE");
1339  strList << s_metadata->Hostname()
1340  << QString::number(s_metadata->ID())
1341  << fi.fileName()
1342  << QString::number(image->m_imageType);
1343 
1344  CopyImageThread *copyThread = new CopyImageThread(strList);
1345  copyThread->start();
1346 
1347  while (copyThread->isRunning())
1348  {
1349  qApp->processEvents();
1350  usleep(1000);
1351  }
1352 
1353  strList = copyThread->getResult();
1354 
1355  delete copyThread;
1356 
1357  if (busy)
1358  busy->Close();
1359 
1360  removeCachedImage(image);
1361 
1362  rescanForImages();
1363 }
1364 
1366 {
1367  if (!image->m_embedded)
1368  return;
1369 
1371 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
void setCompArtist(const QString &compArtist)
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
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.
QString Genre() const
QString Hostname(void)
Basic menu dialog, message and a list of options.
void SetReturnEvent(QObject *retobject, const QString &resultid)
static MetaIO * createTagger(const QString &filename)
Finds an appropriate tagger for the given file.
Definition: metaio.cpp:33
QList< AlbumArtImage * > AlbumArtList
Definition: musicmetadata.h:54
void setGenre(const QString &genre)
void removeCachedImage(const AlbumArtImage *image)
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:136
bool Create(void) override
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)
unsigned int uint
Definition: compat.h:140
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
void SetImage(MythImage *image, const QString &name="")
Sets an image directly, should only be used in special circumstances since it bypasses the cache.
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 SetNameFilter(QStringList filter)
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.
This class is used as a container for messages.
Definition: mythevent.h:16
void sendMetadataChangedEvent(int trackID)
def rating(profile, smoonURL, gate)
Definition: scan.py:25
virtual bool supportsEmbeddedImages(void)
Does the tag support embedded cover art.
Definition: metaio.h:60
bool Create(void) override
void searchForArtistImages(void)
search google for artist images
void setArtistId(int lartistid)
void setAlbumId(int lalbumid)
QString m_filename
Definition: musicmetadata.h:47
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)
bool isRunning(void) const
Definition: mthread.cpp:274
void SetTypeFilter(QDir::Filters filter)
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="")
QString Album() const
static MusicMetadata * s_metadata
const char * name
Definition: ParseText.cpp:328
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)
QList< MusicMetadata * > MetadataPtrList
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)
bool Create(void) override
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
const QString & Message() const
Definition: mythevent.h:58
MetadataPtrList * getAllMetadata(void)
void setCompilationArtist(const QString &lcompilation_artist, const QString &lcompilation_artist_sort=nullptr)
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:37
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:132
void DisplayState(const QString &state, const QString &name)
void setAlbum(const QString &album)
void metadataChanged(void)
uint32_t IdType
Definition: musicmetadata.h:84
const QStringList & ExtraDataList() const
Definition: mythevent.h:60
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)