MythTV master
editmetadata.cpp
Go to the documentation of this file.
1// C++
2#include <utility>
3
4// qt
5#include <QKeyEvent>
6
7// MythTV
28
29// mythmusic
30#include "decoder.h"
31#include "genres.h"
32#include "musicdata.h"
33#include "musicplayer.h"
34
35
36#include "editmetadata.h"
37
38// these need to be static so both screens can pick them up
42
44 MusicMetadata *source_metadata,
45 const QString &name) :
46 MythScreenType(parent, name)
47{
48 // make a copy so we can abandon changes
49 s_metadata = new MusicMetadata(*source_metadata);
50 s_sourceMetadata = source_metadata;
51
52 s_metadataOnly = false;
53}
54
56{
57 // do we need to save anything?
59 {
61
62 // force a reload of the images for any tracks affected
64 for (int x = 0; x < allMusic->count(); x++)
65 {
66 if ((allMusic->at(x)->ID() == s_sourceMetadata->ID()) ||
67 (allMusic->at(x)->getDirectoryId() == s_sourceMetadata->getDirectoryId()))
68 {
69 allMusic->at(x)->reloadAlbumArtImages();
70 gPlayer->sendAlbumArtChangedEvent(allMusic->at(x)->ID());
71 }
72 }
73 }
74}
75
77{
78 bool err = false;
79
80 UIUtilE::Assign(this, m_doneButton, "donebutton", &err);
81
83
84 return err;
85}
86
88{
90 return true;
91
92 QStringList actions;
93 bool handled = GetMythMainWindow()->TranslateKeyPress("Music", event, actions);
94
95 for (int i = 0; i < actions.size() && !handled; i++)
96 {
97 const QString& action = actions[i];
98 handled = true;
99
100 if (action == "ESCAPE")
101 showSaveMenu();
102 else
103 handled = false;
104 }
105
106 if (!handled && MythScreenType::keyPressEvent(event))
107 handled = true;
108
109 return handled;
110}
111
113{
114 MythUITextEdit *edit = dynamic_cast<MythUITextEdit *>(GetChild("albumedit"));
115 if (edit)
116 s_metadata->setAlbum(edit->GetText());
117
118 edit = dynamic_cast<MythUITextEdit *>(GetChild("artistedit"));
119 if (edit)
120 s_metadata->setArtist(edit->GetText());
121
122 edit = dynamic_cast<MythUITextEdit *>(GetChild("compartistedit"));
123 if (edit)
125
126 edit = dynamic_cast<MythUITextEdit *>(GetChild("titleedit"));
127 if (edit)
128 s_metadata->setTitle(edit->GetText());
129
130 edit = dynamic_cast<MythUITextEdit *>(GetChild("genreedit"));
131 if (edit)
132 s_metadata->setGenre(edit->GetText());
133
134 MythUISpinBox *spin = dynamic_cast<MythUISpinBox *>(GetChild("yearspin"));
135 if (spin)
137
138 spin = dynamic_cast<MythUISpinBox *>(GetChild("tracknumspin"));
139 if (spin)
141
142 spin = dynamic_cast<MythUISpinBox *>(GetChild("discnumspin"));
143 if (spin)
145
146 spin = dynamic_cast<MythUISpinBox *>(GetChild("ratingspin"));
147 if (spin)
149
150 MythUICheckBox *check = dynamic_cast<MythUICheckBox *>(GetChild("compilationcheck"));
151 if (check)
153}
154
156{
158
159 if (!hasMetadataChanged())
160 {
161 Close();
162 return;
163 }
164
165 QString label = tr("Save Changes?");
166
167 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
168
169 auto *menu = new MythDialogBox(label, popupStack, "savechangesmenu");
170
171 if (!menu->Create())
172 {
173 delete menu;
174 return;
175 }
176
177 menu->SetReturnEvent(this, "savechangesmenu");
178
179 if (s_metadataOnly)
180 menu->AddButton(tr("Save Changes"), &EditMetadataCommon::saveToMetadata);
181 else
182 menu->AddButton(tr("Save Changes"), &EditMetadataCommon::saveAll);
183
184 menu->AddButton(tr("Exit/Do Not Save"), &EditMetadataCommon::cleanupAndClose);
185
186 popupStack->AddScreen(menu);
187}
188
190{
191 if (s_metadata)
192 {
193 delete s_metadata;
194 s_metadata = nullptr;
195 }
196
197 Close();
198}
199
201{
203 emit metadataChanged();
205}
206
208{
212
215
217}
218
220{
222
223 // only write to the tag if it's enabled by the user
224 if (GetMythDB()->GetBoolSetting("AllowTagWriting", false))
225 {
226 QStringList strList;
227 strList << "MUSIC_TAG_UPDATE_METADATA %1 %2"
228 << s_metadata->Hostname()
229 << QString::number(s_metadata->ID());
230
231 auto *thread = new SendStringListThread(strList);
232 MThreadPool::globalInstance()->start(thread, "UpdateMetadata");
233 }
234
236}
237
239{
240 s_metadataOnly = true;
241
242 MythUIButton *albumartButton = dynamic_cast<MythUIButton *>(GetChild("albumartbutton"));
243 if (albumartButton)
244 albumartButton->Hide();
245}
246
248{
249 bool changed = false;
250
251 changed |= (s_metadata->Album() != s_sourceMetadata->Album());
252 changed |= (s_metadata->Artist() != s_sourceMetadata->Artist());
254 changed |= (s_metadata->Title() != s_sourceMetadata->Title());
255 changed |= (s_metadata->Genre() != s_sourceMetadata->Genre());
256 changed |= (s_metadata->Year() != s_sourceMetadata->Year());
257 changed |= (s_metadata->Track() != s_sourceMetadata->Track());
258 changed |= (s_metadata->DiscNumber() != s_sourceMetadata->DiscNumber());
260 changed |= (s_metadata->Rating() != s_sourceMetadata->Rating());
261
262 return changed;
263}
264
267{
268 QString artist = s_metadata->Artist().replace(' ', '+');
269 artist = QUrl::toPercentEncoding(artist, "+");
270
271 QString album = s_metadata->Album().replace(' ', '+');
272 album = QUrl::toPercentEncoding(album, "+");
273
274 QUrl url("http://www.google.co.uk/images?q=" + artist + "+" + album, QUrl::TolerantMode);
275
276 m_searchType = "album";
277
278 GetMythMainWindow()->HandleMedia("WebBrowser", url.toString(), GetConfDir() + "/MythMusic/", "front.jpg");
279}
280
282{
284}
285
287// EditMatadataDialog
288
290 : EditMetadataCommon(parent, source_metadata, "EditMetadataDialog")
291{
293}
294
296 : EditMetadataCommon(parent, "EditMetadataDialog")
297{
299}
300
302{
304}
305
307{
308 if (! LoadWindowFromXML("music-ui.xml", "editmetadata", this))
309 return false;
310
311 bool err = CreateCommon();
312
313 UIUtilE::Assign(this, m_titleEdit, "titleedit", &err);
314 UIUtilE::Assign(this, m_artistEdit, "artistedit", &err);
315 UIUtilE::Assign(this, m_compArtistEdit, "compartistedit", &err);
316 UIUtilE::Assign(this, m_albumEdit, "albumedit", &err);
317 UIUtilE::Assign(this, m_genreEdit, "genreedit", &err);
318
319 UIUtilE::Assign(this, m_yearSpin, "yearspin", &err);
320 UIUtilE::Assign(this, m_trackSpin, "tracknumspin", &err);
321 UIUtilW::Assign(this, m_discSpin, "discnumspin", &err);
322
323 UIUtilE::Assign(this, m_searchArtistButton, "searchartistbutton", &err);
324 UIUtilE::Assign(this, m_searchCompArtistButton, "searchcompartistbutton", &err);
325 UIUtilE::Assign(this, m_searchAlbumButton, "searchalbumbutton", &err);
326 UIUtilE::Assign(this, m_searchGenreButton, "searchgenrebutton", &err);
327
328 UIUtilW::Assign(this, m_artistIcon, "artisticon", &err);
329 UIUtilW::Assign(this, m_albumIcon, "albumicon", &err);
330 UIUtilW::Assign(this, m_genreIcon, "genreicon", &err);
331
332 UIUtilW::Assign(this, m_ratingState, "ratingstate", &err);
333 UIUtilW::Assign(this, m_ratingSpin, "ratingspin", &err);
334
335 UIUtilW::Assign(this, m_incRatingButton, "incratingbutton", &err);
336 UIUtilW::Assign(this, m_decRatingButton, "decratingbutton", &err);
337
338 UIUtilE::Assign(this, m_compilationCheck, "compilationcheck", &err);
339
340 UIUtilE::Assign(this, m_albumartButton, "albumartbutton", &err);
341
342 if (err)
343 {
344 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'editmetadata'");
345 return false;
346 }
347
348 m_yearSpin->SetRange(QDate::currentDate().year(), 1000, 1);
349 m_yearSpin->AddSelection(0, "None");
350 m_trackSpin->SetRange(0, 999, 1);
351
352 if (m_discSpin)
353 m_discSpin->SetRange(0, 999, 1);
354
355 if (m_ratingSpin)
356 {
357 m_ratingSpin->SetRange(0, 10, 1, 2);
360 }
361
365
370
372 {
375 }
376
378
380
381 fillWidgets();
382
384
385 return true;
386}
387
389{
398
399 if (m_discSpin)
401
402 updateRating();
403
407}
408
410{
412 updateRating();
413}
414
416{
418 updateRating();
419}
420
422{
423 if (item)
424 {
425 int rating = item->GetData().value<int>();
427
428 if (m_ratingState)
429 m_ratingState->DisplayState(QString("%1").arg(s_metadata->Rating()));
430 }
431}
432
434{
435 if (m_ratingState)
436 m_ratingState->DisplayState(QString("%1").arg(s_metadata->Rating()));
437
438 if (m_ratingSpin)
440}
441
442bool EditMetadataDialog::keyPressEvent(QKeyEvent *event)
443{
445 return true;
446
447 QStringList actions;
448 bool handled = GetMythMainWindow()->TranslateKeyPress("Music", event, actions);
449
450 for (int i = 0; i < actions.size() && !handled; i++)
451 {
452 const QString& action = actions[i];
453 handled = true;
454
455 if (action == "THMBUP")
456 incRating();
457 else if (action == "THMBDOWN")
458 decRating();
459 else if (action == "MENU")
460 showMenu();
461 else
462 handled = false;
463 }
464
465 if (!handled && EditMetadataCommon::keyPressEvent(event))
466 handled = true;
467
468 return handled;
469}
470
472{
473 if (s_metadataOnly)
474 return;
475
476 QString label = tr("Options");
477
478 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
479
480 auto *menu = new MythDialogBox(label, popupStack, "optionsmenu");
481
482 if (!menu->Create())
483 {
484 delete menu;
485 return;
486 }
487
488 menu->SetReturnEvent(this, "optionsmenu");
489
490 menu->AddButton(tr("Edit Albumart Images"));
491 menu->AddButton(tr("Search Internet For Artist Image"));
492 menu->AddButton(tr("Search Internet For Album Image"));
493 menu->AddButton(tr("Search Internet For Genre Image"));
494 menu->AddButton(tr("Check Track Length"));
495
496 popupStack->AddScreen(menu);
497}
498
500{
502
504
505 auto *editDialog = new EditAlbumartDialog(mainStack);
506
507 if (!editDialog->Create())
508 {
509 delete editDialog;
510 return;
511 }
512
513 mainStack->AddScreen(editDialog);
514
515 Close();
516}
517
519{
520 if (!state)
521 {
523 }
524 else
525 {
526 if (m_compArtistEdit->GetText().isEmpty() ||
528 {
529 m_compArtistEdit->SetText(tr("Various Artists"));
530 }
531 }
532}
533
535{
536 QString msg = tr("Select an Artist");
537 QStringList searchList = MusicMetadata::fillFieldList("artist");
538 QString s = s_metadata->Artist();
539
540 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
541 auto *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, s);
542
543 if (!searchDlg->Create())
544 {
545 delete searchDlg;
546 return;
547 }
548
550
551 popupStack->AddScreen(searchDlg);
552}
553
554void EditMetadataDialog::setArtist(const QString& artist)
555{
556 m_artistEdit->SetText(artist);
558}
559
561{
562 QString artist = m_artistEdit->GetText();
563
564 if (m_artistIcon)
565 {
566 QString file = findIcon("artist", artist.toLower(), true);
567 if (!file.isEmpty())
568 {
571 }
572 else
573 {
575 }
576 }
577}
578
580{
581 QString msg = tr("Select a Compilation Artist");
582 QStringList searchList = MusicMetadata::fillFieldList("compilation_artist");
583 QString s = s_metadata->CompilationArtist();
584
585 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
586 auto *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, s);
587
588 if (!searchDlg->Create())
589 {
590 delete searchDlg;
591 return;
592 }
593
595
596 popupStack->AddScreen(searchDlg);
597}
598
599void EditMetadataDialog::setCompArtist(const QString& compArtist)
600{
601 m_compArtistEdit->SetText(compArtist);
602}
603
605{
606 QString msg = tr("Select an Album");
607 QStringList searchList = MusicMetadata::fillFieldList("album");
608 QString s = s_metadata->Album();
609
610 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
611 auto *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, s);
612
613 if (!searchDlg->Create())
614 {
615 delete searchDlg;
616 return;
617 }
618
620
621 popupStack->AddScreen(searchDlg);
622}
623
624void EditMetadataDialog::setAlbum(const QString& album)
625{
626 m_albumEdit->SetText(album);
628}
629
631{
632 if (m_albumIcon)
633 {
634 QString file = s_metadata->getAlbumArtFile();
635 if (!file.isEmpty())
636 {
638 m_albumIcon->Load();
639 }
640 else
641 {
643 }
644 }
645}
646
648{
649 QString msg = tr("Select a Genre");
650 QStringList searchList = MusicMetadata::fillFieldList("genre");
651 // load genre list
652 /*
653 searchList.clear();
654 for (const auto &genre : genre_table)
655 searchList.push_back(QString::fromStdString(genre));
656 searchList.sort();
657 */
658
659 QString s = s_metadata->Genre();
660
661 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
662 auto *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, s);
663
664 if (!searchDlg->Create())
665 {
666 delete searchDlg;
667 return;
668 }
669
671
672 popupStack->AddScreen(searchDlg);
673}
674
675void EditMetadataDialog::setGenre(const QString& genre)
676{
677 m_genreEdit->SetText(genre);
679}
680
682{
683 QString genre = m_genreEdit->GetText();
684
685 if (m_genreIcon)
686 {
687 QString file = findIcon("genre", genre.toLower(), true);
688 if (!file.isEmpty())
689 {
691 m_genreIcon->Load();
692 }
693 else
694 {
696 }
697 }
698}
699
701{
703}
704
706{
708}
709
711{
713}
714
717{
718 QString genre= s_metadata->Genre().replace(' ', '+');
719 genre = QUrl::toPercentEncoding(genre, "+");
720
721 QUrl url("http://www.flickr.com/search/groups/?w=908425%40N22&m=pool&q=" + genre, QUrl::TolerantMode);
722
723 m_searchType = "genre";
724 GetMythMainWindow()->HandleMedia("WebBrowser", url.toString(), GetConfDir() + "/MythMusic/", "genre.jpg");
725}
726
729{
730 QString artist = s_metadata->Artist().replace(' ', '+');
731 artist = QUrl::toPercentEncoding(artist, "+");
732
733 QUrl url("http://www.google.co.uk/images?q=" + artist, QUrl::TolerantMode);
734
735 m_searchType = "artist";
736 GetMythMainWindow()->HandleMedia("WebBrowser", url.toString(), GetConfDir() + "/MythMusic/", "artist.jpg");
737}
738
739void EditMetadataDialog::customEvent(QEvent *event)
740{
741 if (event->type() == DialogCompletionEvent::kEventType)
742 {
743 auto *dce = dynamic_cast<DialogCompletionEvent*>(event);
744 // make sure the user didn't ESCAPE out of the menu
745 if ((dce == nullptr) || (dce->GetResult() < 0))
746 return;
747
748 QString resultid = dce->GetId();
749 QString resulttext = dce->GetResultText();
750
751
752 if (resultid == "optionsmenu")
753 {
754 if (resulttext == tr("Edit Albumart Images"))
756 else if (resulttext == tr("Search Internet For Genre Image"))
757 {
760 }
761 else if (resulttext == tr("Search Internet For Artist Image"))
762 {
765 }
766 else if (resulttext == tr("Search Internet For Album Image"))
767 {
770 }
771 else if (resulttext == tr("Check Track Length"))
772 {
773 QStringList strList;
774 strList << "MUSIC_CALC_TRACK_LENGTH"
775 << s_metadata->Hostname()
776 << QString::number(s_metadata->ID());
777
778 auto *thread = new SendStringListThread(strList);
779 MThreadPool::globalInstance()->start(thread, "Send MUSIC_CALC_TRACK_LENGTH");
780
781 ShowOkPopup(tr("Asked the backend to check the tracks length"));
782 }
783 }
784 }
785 else if (event->type() == MythEvent::kMythEventMessage)
786 {
787 auto *me = dynamic_cast<MythEvent *>(event);
788 if (me == nullptr)
789 return;
790 QStringList tokens = me->Message().split(" ", Qt::SkipEmptyParts);
791
792 if (!tokens.isEmpty())
793 {
794 if (tokens[0] == "BROWSER_DOWNLOAD_FINISHED")
795 {
796 QStringList args = me->ExtraDataList();
797 const QString& oldFilename = args[1];
798 int fileSize = args[2].toInt();
799 int errorCode = args[4].toInt();
800
801 if ((errorCode != 0) || (fileSize == 0))
802 return;
803
804 QString newFilename;
805
806 if (m_searchType == "artist")
807 {
808 QString cleanName = fixFilename(s_metadata->Artist().toLower());
809 QString file = QString("Icons/%1/%2.jpg").arg("artist", cleanName);
811 0, file, "MusicArt");
812 }
813 else if (m_searchType == "genre")
814 {
815 QString cleanName = fixFilename(s_metadata->Genre().toLower());
816 QString file = QString("Icons/%1/%2.jpg").arg("genre", cleanName);
818 0, file, "MusicArt");
819 }
820 else if (m_searchType == "album")
821 {
822 // move the image from the MythMusic config dir to the tracks
823 // dir in the 'Music' storage group
824 newFilename = s_metadata->Filename();
825 newFilename = newFilename.section( '/', 0, -2);
826 newFilename = newFilename + '/' + oldFilename.section( '/', -1, -1);
827 }
828 else
829 {
830 LOG(VB_GENERAL, LOG_ERR, QString("Got unknown search type '%1' "
831 "in BROWSER_DOWNLOAD_FINISHED event")
832 .arg(m_searchType));
833 return;
834 }
835
836 RemoteFile::CopyFile(oldFilename, newFilename, true);
837 QFile::remove(oldFilename);
838
839 if (m_searchType == "album")
841
842 // force the icons to update
846
848 // force a reload of the images for any tracks affected
850 for (int x = 0; x < allMusic->count(); x++)
851 {
852 if ((allMusic->at(x)->ID() == s_sourceMetadata->ID()) ||
853 (allMusic->at(x)->getDirectoryId() == s_sourceMetadata->getDirectoryId()))
854 {
855 allMusic->at(x)->reloadAlbumArtImages();
856 gPlayer->sendAlbumArtChangedEvent(allMusic->at(x)->ID());
857 }
858 }
859 }
860 }
861 }
862}
863
865// EditAlbumartDialog
866
868 : EditMetadataCommon(parent, "EditAlbumartDialog")
869{
871}
872
874{
876}
877
879{
880 if (! LoadWindowFromXML("music-ui.xml", "editalbumart", this))
881 return false;
882
883 bool err = CreateCommon();
884
885 UIUtilE::Assign(this, m_coverartList, "coverartlist", &err);
886 UIUtilE::Assign(this, m_imagetypeText, "imagetypetext", &err);
887 UIUtilE::Assign(this, m_imagefilenameText, "imagefilenametext", &err);
888 UIUtilE::Assign(this, m_coverartImage, "coverartimage", &err);
889
890 UIUtilE::Assign(this, m_metadataButton, "metadatabutton", &err);
891
892 if (err)
893 {
894 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'editalbumart'");
895 return false;
896 }
897
899
902
904
906
907 return true;
908}
909
911{
912 if (!item)
913 return;
914
915 if (m_coverartImage)
916 {
917 auto *image = item->GetData().value<AlbumArtImage*>();
918 if (image)
919 {
920 m_coverartImage->SetFilename(image->m_filename);
922 if (m_imagetypeText)
925 {
926 QFileInfo fi(image->m_filename);
927 m_imagefilenameText->SetText(fi.fileName());
928 }
929 }
930 }
931}
932
934{
936
938
939 for (auto *art : std::as_const(*albumArtList))
940 {
941 auto *item = new MythUIButtonListItem(m_coverartList,
942 AlbumArtImages::getTypeName(art->m_imageType),
943 QVariant::fromValue(art));
944 item->SetImage(art->m_filename);
945 QString state = art->m_embedded ? "tag" : "file";
946 item->DisplayState(state, "locationstate");
947 }
948}
949
951{
953 return true;
954
955 QStringList actions;
956 bool handled = GetMythMainWindow()->TranslateKeyPress("Music", event, actions);
957
958 for (int i = 0; i < actions.size() && !handled; i++)
959 {
960 const QString& action = actions[i];
961 handled = true;
962
963 if (action == "MENU")
964 showMenu();
965 else if (action == "INFO")
966 showTypeMenu();
967 else
968 handled = false;
969 }
970
971 if (!handled && EditMetadataCommon::keyPressEvent(event))
972 handled = true;
973
974 return handled;
975}
976
978{
980
981 auto *editDialog = new EditMetadataDialog(mainStack);
982
983 if (!editDialog->Create())
984 {
985 delete editDialog;
986 return;
987 }
988
989 mainStack->AddScreen(editDialog);
990
991 Close();
992}
993
995{
996 if (changeType && m_coverartList->GetCount() == 0)
997 return;
998
999 QString label;
1000
1001 if (changeType)
1002 label = tr("Change Image Type");
1003 else
1004 label = tr("What image type do you want to use for this image?");
1005
1006 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1007
1008 auto *menu = new MythDialogBox(label, popupStack, "typemenu");
1009
1010 if (!menu->Create())
1011 {
1012 delete menu;
1013 return;
1014 }
1015
1016 ImageType imageType = IT_UNKNOWN;
1017 if (changeType)
1018 menu->SetReturnEvent(this, "changetypemenu");
1019 else
1020 {
1021 menu->SetReturnEvent(this, "asktypemenu");
1023 }
1024
1025 menu->AddButtonV(AlbumArtImages::getTypeName(IT_UNKNOWN), QVariant::fromValue((int)IT_UNKNOWN), false, (imageType == IT_UNKNOWN));
1026 menu->AddButtonV(AlbumArtImages::getTypeName(IT_FRONTCOVER), QVariant::fromValue((int)IT_FRONTCOVER), false, (imageType == IT_FRONTCOVER));
1027 menu->AddButtonV(AlbumArtImages::getTypeName(IT_BACKCOVER), QVariant::fromValue((int)IT_BACKCOVER), false, (imageType == IT_BACKCOVER));
1028 menu->AddButtonV(AlbumArtImages::getTypeName(IT_CD), QVariant::fromValue((int)IT_CD), false, (imageType == IT_CD));
1029 menu->AddButtonV(AlbumArtImages::getTypeName(IT_INLAY), QVariant::fromValue((int)IT_INLAY), false, (imageType == IT_INLAY));
1030 menu->AddButtonV(AlbumArtImages::getTypeName(IT_ARTIST), QVariant::fromValue((int)IT_ARTIST), false, (imageType == IT_ARTIST));
1031
1032 popupStack->AddScreen(menu);
1033}
1034
1036{
1037 QString label = tr("Options");
1038
1039 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1040
1041 auto *menu = new MythDialogBox(label, popupStack, "optionsmenu");
1042
1043 if (!menu->Create())
1044 {
1045 delete menu;
1046 return;
1047 }
1048
1049 menu->SetReturnEvent(this, "optionsmenu");
1050
1051 menu->AddButton(tr("Edit Metadata"));
1052 menu->AddButton(tr("Rescan For Images"));
1053
1054
1055 menu->AddButton(tr("Search Internet For Images"));
1056
1058
1060 {
1061 menu->AddButton(tr("Change Image Type"), nullptr, true);
1062
1063 if (GetMythDB()->GetBoolSetting("AllowTagWriting", false))
1064 {
1066 if (item)
1067 {
1068 auto *image = item->GetData().value<AlbumArtImage*>();
1069 if (image)
1070 {
1071 if (!image->m_embedded)
1072 {
1073 if (tagger && tagger->supportsEmbeddedImages())
1074 menu->AddButton(tr("Copy Selected Image To Tag"));
1075 }
1076 else
1077 {
1078 if (tagger && tagger->supportsEmbeddedImages())
1079 menu->AddButton(tr("Remove Selected Image From Tag"));
1080 }
1081 }
1082 }
1083 }
1084 }
1085
1086 if (GetMythDB()->GetBoolSetting("AllowTagWriting", false))
1087 {
1088 if (tagger && tagger->supportsEmbeddedImages())
1089 menu->AddButton(tr("Copy Image To Tag"));
1090 }
1091
1092 delete tagger;
1093
1094 popupStack->AddScreen(menu);
1095}
1096
1098{
1099 if (event->type() == DialogCompletionEvent::kEventType)
1100 {
1101 auto *dce = dynamic_cast<DialogCompletionEvent*>(event);
1102 // make sure the user didn't ESCAPE out of the menu
1103 if ((dce == nullptr) || (dce->GetResult() < 0))
1104 return;
1105
1106 QString resultid = dce->GetId();
1107 QString resulttext = dce->GetResultText();
1108
1109 if (resultid == "changetypemenu")
1110 {
1111 int type = dce->GetData().toInt();
1112
1113 if ((type >= IT_UNKNOWN) && (type < IT_LAST))
1114 {
1115 // get selected image in list
1117 if (item)
1118 {
1120 auto *image = item->GetData().value<AlbumArtImage*>();
1121 if (image)
1122 {
1123 QStringList strList("MUSIC_TAG_CHANGEIMAGE");
1124 strList << s_metadata->Hostname()
1125 << QString::number(s_metadata->ID())
1126 << QString::number(image->m_imageType)
1127 << QString::number(type);
1128
1130
1131 m_albumArtChanged = true;
1132
1133 gridItemChanged(item);
1134 }
1135 }
1136 }
1137 }
1138 else if (resultid == "asktypemenu")
1139 {
1140 int type = dce->GetData().toInt();
1141
1142 if ((type >= IT_UNKNOWN) && (type < IT_LAST))
1144 }
1145 else if (resultid == "optionsmenu")
1146 {
1147 if (resulttext == tr("Edit Metadata"))
1149 else if (resulttext == tr("Rescan For Images"))
1151 else if (resulttext == tr("Search Internet For Images"))
1153 else if (resulttext == tr("Change Image Type"))
1154 showTypeMenu();
1155 else if (resulttext == tr("Copy Selected Image To Tag"))
1157 else if (resulttext == tr("Remove Selected Image From Tag"))
1159 else if (resulttext == tr("Copy Image To Tag"))
1161 }
1162 else if (resultid == "imagelocation")
1163 {
1164 m_imageFilename = resulttext;
1165
1166 // save directory location for next time
1167 QFileInfo fi(m_imageFilename);
1168 gCoreContext->SaveSetting("MusicLastImageLocation", fi.canonicalPath());
1169
1170 showTypeMenu(false);
1171 }
1172 }
1173 else if (event->type() == MythEvent::kMythEventMessage)
1174 {
1175 auto *me = dynamic_cast<MythEvent *>(event);
1176 if (me == nullptr)
1177 return;
1178 QStringList tokens = me->Message().split(" ", Qt::SkipEmptyParts);
1179
1180 if (!tokens.isEmpty())
1181 {
1182 if (tokens[0] == "BROWSER_DOWNLOAD_FINISHED")
1184 else if (tokens[0] == "MUSIC_ALBUMART_CHANGED")
1185 {
1186 if (tokens.size() >= 2)
1187 {
1188 auto songID = (MusicMetadata::IdType)tokens[1].toInt();
1189
1190 if (s_metadata->ID() == songID)
1191 {
1192 // force all the image to reload
1193 for (uint x = 0; x < s_metadata->getAlbumArtImages()->getImageCount(); x++)
1195
1197 }
1198 }
1199 }
1200 }
1201 }
1202}
1203
1206{
1207 // scan the tracks directory and tag for any images
1208 scanForImages();
1209
1211
1213 if (albumArt->getImageCount() > 0)
1214 m_albumArtChanged = true;
1215}
1216
1218{
1219 QString lastLocation = gCoreContext->GetSetting("MusicLastImageLocation", "/");
1220
1221 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1222 auto *fb = new MythUIFileBrowser(popupStack, lastLocation);
1223
1224 fb->SetTypeFilter(QDir::AllDirs | QDir::Files | QDir::Readable);
1225
1226 QStringList filters;
1227 filters << "*.png" << "*.jpg" << "*.jpeg" << "*.gif";
1228 fb->SetNameFilter(filters);
1229
1230 if (fb->Create())
1231 {
1232 fb->SetReturnEvent(this, "imagelocation");
1233 popupStack->AddScreen(fb);
1234 }
1235 else
1236 {
1237 delete fb;
1238 }
1239}
1240
1242{
1243 AlbumArtImage image;
1245 image.m_imageType = imageType;
1246
1247 doCopyImageToTag(&image);
1248}
1249
1251{
1253 if (item)
1254 {
1255 auto *image = item->GetData().value<AlbumArtImage*>();
1256 if (image)
1257 doCopyImageToTag(image);
1258 }
1259}
1260
1262{
1264 if (item)
1265 {
1266 auto *image = item->GetData().value<AlbumArtImage*>();
1267 if (image)
1268 {
1269 QString msg = tr("Are you sure you want to permanently remove this image from the tag?");
1271 }
1272 }
1273}
1274
1276{
1277 if (!doIt)
1278 return;
1279
1281 if (item)
1282 {
1283 auto *image = item->GetData().value<AlbumArtImage*>();
1284 if (image)
1285 {
1286 // ask the backend to remove the image from the tracks tag
1287 QStringList strList("MUSIC_TAG_REMOVEIMAGE");
1288 strList << s_metadata->Hostname()
1289 << QString::number(s_metadata->ID())
1290 << QString::number(image->m_id);
1291
1293
1294 removeCachedImage(image);
1296 }
1297 }
1298}
1299
1301{
1302 public:
1303 explicit CopyImageThread(QStringList strList) :
1304 MThread("CopyImage"), m_strList(std::move(strList)) {}
1305
1306 void run() override // MThread
1307 {
1308 RunProlog();
1310 RunEpilog();
1311 }
1312
1313 QStringList getResult(void) { return m_strList; }
1314
1315 private:
1316 QStringList m_strList;
1317};
1318
1320{
1321 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1322 auto *busy = new MythUIBusyDialog(tr("Copying image to tag..."),
1323 popupStack, "copyimagebusydialog");
1324
1325 if (busy->Create())
1326 {
1327 popupStack->AddScreen(busy, false);
1328 }
1329 else
1330 {
1331 delete busy;
1332 busy = nullptr;
1333 }
1334
1335 // copy the image to the tracks host
1336 QFileInfo fi(image->m_filename);
1337 QString saveFilename = MythCoreContext::GenMythURL(s_metadata->Hostname(), 0,
1338 QString("AlbumArt/") + fi.fileName(),
1339 "MusicArt");
1340
1341 RemoteFile::CopyFile(image->m_filename, saveFilename, true);
1342
1343 // ask the backend to add the image to the tracks tag
1344 QStringList strList("MUSIC_TAG_ADDIMAGE");
1345 strList << s_metadata->Hostname()
1346 << QString::number(s_metadata->ID())
1347 << fi.fileName()
1348 << QString::number(image->m_imageType);
1349
1350 auto *copyThread = new CopyImageThread(strList);
1351 copyThread->start();
1352
1353 while (copyThread->isRunning())
1354 {
1355 qApp->processEvents();
1356 const struct timespec onems {0, 1000000};
1357 nanosleep(&onems, nullptr);
1358 }
1359
1360 strList = copyThread->getResult();
1361
1362 delete copyThread;
1363
1364 if (busy)
1365 busy->Close();
1366
1367 removeCachedImage(image);
1368
1370}
1371
1373{
1374 if (!image->m_embedded)
1375 return;
1376
1378}
QString m_filename
Definition: musicmetadata.h:49
ImageType m_imageType
Definition: musicmetadata.h:51
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:41
static const Type kEventType
Definition: mythdialogbox.h:56
static void removeCachedImage(const AlbumArtImage *image)
MythUIImage * m_coverartImage
Definition: editmetadata.h:182
MythUIButtonList * m_coverartList
Definition: editmetadata.h:183
void updateImageGrid(void)
MythUIButton * m_metadataButton
Definition: editmetadata.h:180
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:185
MythUIText * m_imagetypeText
Definition: editmetadata.h:184
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:131
MythUIImage * m_artistIcon
Definition: editmetadata.h:134
MythUIButton * m_decRatingButton
Definition: editmetadata.h:127
void setAlbum(const QString &album)
void switchToAlbumArt(void)
void updateArtistImage(void)
MythUIButton * m_searchCompArtistButton
Definition: editmetadata.h:130
void searchForArtistImages(void)
search google for artist images
MythUIButton * m_incRatingButton
Definition: editmetadata.h:126
void checkClicked(bool state)
void customEvent(QEvent *levent) override
void setGenre(const QString &genre)
MythUIButton * m_albumartButton
Definition: editmetadata.h:140
void setArtist(const QString &artist)
MythUITextEdit * m_compArtistEdit
Definition: editmetadata.h:115
void searchCompilationArtist(void) const
MythUISpinBox * m_trackSpin
Definition: editmetadata.h:121
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:116
void setCompArtist(const QString &compArtist)
void ratingSpinChanged(MythUIButtonListItem *item)
MythUICheckBox * m_compilationCheck
Definition: editmetadata.h:138
MythUIImage * m_genreIcon
Definition: editmetadata.h:136
MythUITextEdit * m_titleEdit
bool Create() override
void artistLostFocus(void)
MythUITextEdit * m_genreEdit
Definition: editmetadata.h:118
EditMetadataDialog(MythScreenStack *lparent, const QString &lname, VideoMetadata *source_metadata, const VideoMetadataListManager &cache)
void updateRating(void)
MythUIButton * m_searchArtistButton
Definition: editmetadata.h:129
MythUIButton * m_searchGenreButton
Definition: editmetadata.h:132
MythUIImage * m_albumIcon
Definition: editmetadata.h:135
void searchAlbum(void) const
void searchGenre(void) const
void updateGenreImage(void)
~EditMetadataDialog() override
MythUIStateType * m_ratingState
Definition: editmetadata.h:125
MythUISpinBox * m_ratingSpin
Definition: editmetadata.h:123
MythUITextEdit * m_artistEdit
Definition: editmetadata.h:114
MythUISpinBox * m_yearSpin
void albumLostFocus(void)
MythUISpinBox * m_discSpin
Definition: editmetadata.h:122
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:196
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
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:86
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:17
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:26
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:33
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:138
void LosingFocus(void)
static bool CopyFile(const QString &src, const QString &dst, bool overwrite=false, bool verify=false)
Definition: remotefile.cpp:587
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: freesurround.h:24
MusicData * gMusicData
Definition: musicdata.cpp:23
QList< MusicMetadata * > MetadataPtrList
ImageType
Definition: musicmetadata.h:29
@ IT_INLAY
Definition: musicmetadata.h:34
@ IT_BACKCOVER
Definition: musicmetadata.h:32
@ IT_LAST
Definition: musicmetadata.h:36
@ IT_UNKNOWN
Definition: musicmetadata.h:30
@ IT_FRONTCOVER
Definition: musicmetadata.h:31
@ IT_ARTIST
Definition: musicmetadata.h:35
@ IT_CD
Definition: musicmetadata.h:33
QList< AlbumArtImage * > AlbumArtList
Definition: musicmetadata.h:56
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:51
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
QString GetConfDir(void)
Definition: mythdirs.cpp:263
#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
STL namespace.
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27