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