MythTV  master
importmusic.cpp
Go to the documentation of this file.
1 // qt
2 #include <QDir>
3 #include <QFontMetrics>
4 #include <QApplication>
5 
6 // myth
7 #include <mythcontext.h>
8 #include <mythdbcon.h>
9 #include <musicmetadata.h>
10 #include <mythdialogbox.h>
11 #include <mythuitext.h>
12 #include <mythuiimage.h>
13 #include <mythuicheckbox.h>
14 #include <mythuitextedit.h>
15 #include <mythuibutton.h>
16 #include <mythuibuttonlist.h>
17 #include <mythprogressdialog.h>
18 #include <mythuifilebrowser.h>
19 #include <mythlogging.h>
20 #include <remotefile.h>
21 #include <storagegroup.h>
22 
23 // mythmusic
24 #include "importmusic.h"
25 #include "genres.h"
26 #include "editmetadata.h"
27 #include "musicplayer.h"
28 #include "metaio.h"
29 #include "musicutils.h"
30 
31 
33 
35  MThread("FileScanner"), m_parent(parent)
36 {
37 }
38 
40 {
41  RunProlog();
42  m_parent->doScan();
43  RunEpilog();
44 }
45 
47 
48 FileCopyThread::FileCopyThread(const QString &src, const QString &dst) :
49  MThread("FileCopy"), m_srcFile(src), m_dstFile(dst), m_result(false)
50 {
51 }
52 
54 {
55  RunProlog();
57  RunEpilog();
58 }
59 
61 
62 
64  MythScreenType(parent, "musicimportfiles"),
65 
66  m_musicStorageDir(""),
67  m_somethingWasImported(false),
68  m_tracks(new vector<TrackInfo*>),
69  m_currentTrack(0),
70  m_playingMetaData(nullptr),
71  // GUI stuff
72  m_locationEdit(nullptr),
73  m_locationButton(nullptr),
74  m_scanButton(nullptr),
75  m_coverartButton(nullptr),
76  m_filenameText(nullptr),
77  m_compartistText(nullptr),
78  m_artistText(nullptr),
79  m_albumText(nullptr),
80  m_titleText(nullptr),
81  m_genreText(nullptr),
82  m_yearText(nullptr),
83  m_trackText(nullptr),
84  m_nextButton(nullptr),
85  m_prevButton(nullptr),
86  m_currentText(nullptr),
87  m_statusText(nullptr),
88  m_playButton(nullptr),
89  m_addButton(nullptr),
90  m_addallnewButton(nullptr),
91  m_nextnewButton(nullptr),
92  m_compilationCheck(nullptr),
93  // default metadata values
94  m_defaultCompilation(false),
95  m_defaultYear(0),
96  m_defaultRating(0),
97  m_haveDefaults(false)
98 {
99  QString lastHost = gCoreContext->GetSetting("MythMusicLastImportHost", gCoreContext->GetMasterHostName());
100  QStringList dirs = StorageGroup::getGroupDirs("Music", lastHost);
101  if (dirs.count() > 0)
102  m_musicStorageDir = StorageGroup::getGroupDirs("Music", lastHost).at(0);
103 }
104 
106 {
108  {
110  gPlayer->stop(true);
111  }
112 
113  if (m_locationEdit)
114  gCoreContext->SaveSetting("MythMusicLastImportDir", m_locationEdit->GetText());
115 
116  delete m_tracks;
117 
119  emit importFinished();
120 }
121 
123 {
124  if (!m_tracks->empty())
125  {
126  // update current
127  //: %1 is the current track,
128  //: %2 is the number of tracks
129  m_currentText->SetText(tr("%1 of %2", "Current track position")
130  .arg(m_currentTrack + 1).arg(m_tracks->size()));
131 
132  MusicMetadata *meta = m_tracks->at(m_currentTrack)->metadata;
133  m_filenameText->SetText(meta->Filename());
136  m_artistText->SetText(meta->Artist());
137  m_albumText->SetText(meta->Album());
138  m_titleText->SetText(meta->Title());
139  m_genreText->SetText(meta->Genre());
140  m_yearText->SetText(QString::number(meta->Year()));
141  m_trackText->SetText(QString::number(meta->Track()));
142  if (m_tracks->at(m_currentTrack)->isNewTune)
143  {
145  m_statusText->SetText(tr("New File"));
146  }
147  else
148  {
150  m_statusText->SetText(tr("Already in Database"));
151  }
152  }
153  else
154  {
155  // update current
156  m_currentText->SetText(tr("Not found"));
160  m_artistText->Reset();
161  m_albumText->Reset();
162  m_titleText->Reset();
163  m_genreText->Reset();
164  m_yearText->Reset();
165  m_trackText->Reset();
166  m_statusText->Reset();
168  }
169 }
170 
171 bool ImportMusicDialog::keyPressEvent(QKeyEvent *event)
172 {
173  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
174  return true;
175 
176  QStringList actions;
177  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
178 
179  for (int i = 0; i < actions.size() && !handled; i++)
180  {
181  QString action = actions[i];
182  handled = true;
183 
184  if (action == "LEFT")
185  {
186  m_prevButton->Push();
187  }
188  else if (action == "RIGHT")
189  {
190  m_nextButton->Push();
191  }
192  else if (action == "EDIT")
193  {
195  }
196  else if (action == "MENU")
197  {
198  ShowMenu();
199  }
200  else if (action == "ESCAPE" && !GetMythMainWindow()->IsExitingToMain())
201  {
202  bool found = false;
203  if (!m_tracks->empty())
204  {
205  uint track = 0;
206  while (track < m_tracks->size())
207  {
208  if (m_tracks->at(track)->isNewTune)
209  {
210  found = true;
211  break;
212  }
213  track++;
214  }
215 
216  if (found)
217  {
218  QString msg = tr("You might have unsaved changes.\nAre you sure you want to exit this screen?");
219  ShowOkPopup(msg, this, SLOT(doExit(bool)), true);
220  }
221  }
222 
223  handled = found;
224  }
225  else if (action == "1")
226  {
227  setCompilation();
228  }
229  else if (action == "2")
230  {
232  }
233  else if (action == "3")
234  {
235  setArtist();
236  }
237  else if (action == "4")
238  {
239  setAlbum();
240  }
241  else if (action == "5")
242  {
243  setGenre();
244  }
245  else if (action == "6")
246  {
247  setYear();
248  }
249  else if (action == "7")
250  {
251  setRating();
252  }
253  else if (action == "8")
254  {
256  }
257  else if (action == "9")
258  {
260  }
261  else if (action == "0")
262  {
263  setTrack();
264  }
265  else
266  handled = false;
267  }
268 
269  if (!handled && MythScreenType::keyPressEvent(event))
270  handled = true;
271 
272  return handled;
273 }
274 
276 {
277  if (!LoadWindowFromXML("music-ui.xml", "import_music", this))
278  return false;
279 
280  bool err = false;
281  UIUtilE::Assign(this, m_locationEdit, "location", &err);
282  UIUtilE::Assign(this, m_locationButton, "directoryfinder", &err);
283  UIUtilE::Assign(this, m_scanButton, "scan", &err);
284  UIUtilE::Assign(this, m_coverartButton, "coverart", &err);
285  UIUtilE::Assign(this, m_filenameText, "filename", &err);
286  UIUtilE::Assign(this, m_compartistText, "compartist", &err);
287  UIUtilE::Assign(this, m_artistText, "artist", &err);
288  UIUtilE::Assign(this, m_albumText, "album", &err);
289  UIUtilE::Assign(this, m_titleText, "title", &err);
290  UIUtilE::Assign(this, m_genreText, "genre", &err);
291  UIUtilE::Assign(this, m_yearText, "year", &err);
292  UIUtilE::Assign(this, m_trackText, "track", &err);
293  UIUtilE::Assign(this, m_currentText, "position", &err);
294  UIUtilE::Assign(this, m_statusText, "status", &err);
295  UIUtilE::Assign(this, m_compilationCheck,"compilation", &err);
296  UIUtilE::Assign(this, m_playButton, "play", &err);
297  UIUtilE::Assign(this, m_nextnewButton, "nextnew", &err);
298  UIUtilE::Assign(this, m_addButton, "add", &err);
299  UIUtilE::Assign(this, m_addallnewButton, "addallnew", &err);
300  UIUtilE::Assign(this, m_nextButton, "next", &err);
301  UIUtilE::Assign(this, m_prevButton, "prev", &err);
302 
303  if (err)
304  {
305  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'import_music'");
306  return false;
307  }
308 
309  connect(m_prevButton, SIGNAL(Clicked()), SLOT(prevPressed()));
310  connect(m_locationButton, SIGNAL(Clicked()), SLOT(locationPressed()));
311  connect(m_scanButton, SIGNAL(Clicked()), SLOT(startScan()));
312  connect(m_coverartButton, SIGNAL(Clicked()), SLOT(coverArtPressed()));
313  connect(m_playButton, SIGNAL(Clicked()), SLOT(playPressed()));
314  connect(m_nextnewButton, SIGNAL(Clicked()), SLOT(nextNewPressed()));
315  connect(m_addButton, SIGNAL(Clicked()), SLOT(addPressed()));
316  connect(m_addallnewButton, SIGNAL(Clicked()), SLOT(addAllNewPressed()));
317  connect(m_nextButton, SIGNAL(Clicked()), SLOT(nextPressed()));
318 
319  fillWidgets();
320 
321  BuildFocusList();
322 
323  m_locationEdit->SetText(gCoreContext->GetSetting("MythMusicLastImportDir", "/"));
324 
325  return true;
326 }
327 
329 {
330  if (ok)
331  Close();
332 }
333 
335 {
336  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
337  MythUIFileBrowser *fb = new MythUIFileBrowser(popupStack, m_locationEdit->GetText());
338  // TODO Install a name filter on supported music formats
339  fb->SetTypeFilter(QDir::AllDirs | QDir::Readable);
340  if (fb->Create())
341  {
342  fb->SetReturnEvent(this, "locationchange");
343  popupStack->AddScreen(fb);
344  }
345  else
346  delete fb;
347 }
348 
350 {
352 }
353 
355 {
356  if (m_tracks->empty())
357  return;
358 
359  m_playingMetaData = m_tracks->at(m_currentTrack)->metadata;
360 
362 }
363 
365 {
366  if (m_currentTrack > 0)
367  {
368  m_currentTrack--;
369  fillWidgets();
370  }
371 }
372 
374 {
375  if (m_currentTrack < (int) m_tracks->size() - 1)
376  {
377  m_currentTrack++;
378  fillWidgets();
379  }
380 }
381 
383 {
384  if (m_tracks->empty())
385  return;
386 
387  MusicMetadata *meta = m_tracks->at(m_currentTrack)->metadata;
388 
389  // is the current track a new file?
390  if (m_tracks->at(m_currentTrack)->isNewTune)
391  {
392  // do we need to update the tags?
393  if (m_tracks->at(m_currentTrack)->metadataHasChanged)
394  {
395  MetaIO *tagger = MetaIO::createTagger(meta->Filename());
396  if (tagger)
397  {
398  tagger->write(meta->Filename(), meta);
399  delete tagger;
400  }
401  }
402 
403  // get the save filename
404  QString origFilename = meta->Filename();
405  QString saveFilename = filenameFromMetadata(meta);
406  QString fullFilename;
407 
408  QUrl url(m_musicStorageDir);
409  fullFilename = gCoreContext->GenMythURL(url.host(), 0, saveFilename, "Music");
410 
411 
412  // we need to manually copy the file extension
413  QFileInfo fi(origFilename);
414  saveFilename += "." + fi.suffix();
415  fullFilename += "." + fi.suffix();
416 
417  LOG(VB_FILE, LOG_INFO, QString("Copying file from: %1").arg(origFilename));
418  LOG(VB_FILE, LOG_INFO, QString("to: ").arg(fullFilename));
419 
420  // copy the file to the new location
421  if (!copyFile(origFilename, fullFilename))
422  {
423  ShowOkPopup(tr("Copy Failed\nCould not copy file to: %1").arg(fullFilename));
424  return;
425  }
426 
427  meta->setFilename(saveFilename);
428  meta->setHostname(url.host());
429  meta->setFileSize((quint64)QFileInfo(origFilename).size());
430 
431  // update the database
432  meta->dumpToDatabase();
433 
434  // find any albumart for this track
435  meta->getAlbumArtImages()->scanForImages();
437 
438  m_somethingWasImported = true;
439 
440  m_tracks->at(m_currentTrack)->isNewTune =
441  isNewTune(meta->Artist(), meta->Album(), meta->Title());
442 
443  // update the UI
444  fillWidgets();
445  }
446  else
447  ShowOkPopup(tr("This track is already in the database"));
448 }
449 
451 {
452  if (m_tracks->empty())
453  return;
454 
455  m_currentTrack = 0;
456  int newCount = 0;
457 
458  while (m_currentTrack < (int) m_tracks->size())
459  {
460  fillWidgets();
461  qApp->processEvents();
462 
463  if (m_tracks->at(m_currentTrack)->isNewTune)
464  {
465  addPressed();
466  newCount++;
467  }
468 
469  qApp->processEvents();
470 
471  m_currentTrack++;
472  }
473 
474  m_currentTrack--;
475 
476  ShowOkPopup(tr("%1 new tracks were added to the database").arg(newCount));
477 }
478 
480 {
481  if (m_tracks->empty())
482  return;
483 
484  uint track = m_currentTrack + 1;
485  while (track < m_tracks->size())
486  {
487  if (m_tracks->at(track)->isNewTune)
488  {
489  m_currentTrack = track;
490  fillWidgets();
491  break;
492  }
493  track++;
494  }
495 }
496 
497 bool ImportMusicDialog::copyFile(const QString &src, const QString &dst)
498 {
499  bool res = false;
500  QString host = QUrl(dst).host();
501 
502  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
503  MythUIBusyDialog *busy =
504  new MythUIBusyDialog(tr("Copying music file to the 'Music' storage group on %1").arg(host),
505  popupStack,
506  "scanbusydialog");
507 
508  if (busy->Create())
509  {
510  popupStack->AddScreen(busy, false);
511  }
512  else
513  {
514  delete busy;
515  busy = nullptr;
516  }
517 
518  FileCopyThread *copy = new FileCopyThread(src, dst);
519  copy->start();
520 
521  while (!copy->isFinished())
522  {
523  usleep(500);
524  qApp->processEvents();
525  }
526 
527  res = copy->GetResult();
528 
529  delete copy;
530 
531  if (busy)
532  busy->Close();
533 
534  return res;
535 }
536 
538 {
539  // sanity check - make sure the user isn't trying to import tracks from the music directory
540  QString location = m_locationEdit->GetText();
541  if (!location.endsWith('/'))
542  location.append('/');
543 
544  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
545  MythUIBusyDialog *busy =
546  new MythUIBusyDialog(tr("Searching for music files"),
547  popupStack,
548  "scanbusydialog");
549 
550  if (busy->Create())
551  {
552  popupStack->AddScreen(busy, false);
553  }
554  else
555  {
556  delete busy;
557  busy = nullptr;
558  }
559  FileScannerThread *scanner = new FileScannerThread(this);
560  scanner->start();
561 
562  while (!scanner->isFinished())
563  {
564  usleep(500);
565  qApp->processEvents();
566  }
567 
568  delete scanner;
569 
570  m_currentTrack = 0;
571  fillWidgets();
572 
573  if (busy)
574  busy->Close();
575 }
576 
578 {
579  m_tracks->clear();
580  m_sourceFiles.clear();
581  QString location = m_locationEdit->GetText();
582  scanDirectory(location, m_tracks);
583 }
584 
585 void ImportMusicDialog::scanDirectory(QString &directory, vector<TrackInfo*> *tracks)
586 {
587  QDir d(directory);
588 
589  if (!d.exists())
590  return;
591 
592  d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
593  const QFileInfoList list = d.entryInfoList();
594  if (list.isEmpty())
595  return;
596 
597  QFileInfoList::const_iterator it = list.begin();
598  const QFileInfo *fi;
599 
600  while (it != list.end())
601  {
602  fi = &(*it);
603  ++it;
604  QString filename = fi->absoluteFilePath();
605  if (fi->isDir())
606  scanDirectory(filename, tracks);
607  else
608  {
609  MetaIO *tagger = MetaIO::createTagger(filename);
610  if (tagger)
611  {
612  MusicMetadata *metadata = tagger->read(filename);
613  if (metadata)
614  {
615  TrackInfo * track = new TrackInfo;
616  track->metadata = metadata;
617  track->isNewTune = isNewTune(metadata->Artist(), metadata->Album(),
618  metadata->Title());
619  track->metadataHasChanged = false;
620  tracks->push_back(track);
621  m_sourceFiles.append(filename);
622  }
623 
624  delete tagger;
625  }
626  }
627  }
628 }
629 
631 {
632  if (m_tracks->empty())
633  return;
634 
635  MusicMetadata *editMeta = m_tracks->at(m_currentTrack)->metadata;
636 
638 
639  EditMetadataDialog *editDialog = new EditMetadataDialog(mainStack, editMeta);
640 
641  if (!editDialog->Create())
642  {
643  delete editDialog;
644  return;
645  }
646 
647  editDialog->setSaveMetadataOnly();
648 
649  connect(editDialog, SIGNAL(metadataChanged()), this, SLOT(metadataChanged()));
650 
651  mainStack->AddScreen(editDialog);
652 }
653 
655 {
656  MusicMetadata *editMeta = m_tracks->at(m_currentTrack)->metadata;
657  m_tracks->at(m_currentTrack)->metadataHasChanged = true;
658  m_tracks->at(m_currentTrack)->isNewTune =
659  isNewTune(editMeta->Artist(), editMeta->Album(), editMeta->Title());
660  fillWidgets();
661 }
662 
664 {
665  if (m_tracks->empty())
666  return;
667 
668  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
669 
670  MythDialogBox *menu = new MythDialogBox("", popupStack, "importmusicmenu");
671 
672  if (menu->Create())
673  popupStack->AddScreen(menu);
674  else
675  {
676  delete menu;
677  return;
678  }
679 
680  menu->SetReturnEvent(this, "menu");
681  menu->AddButton(tr("Select Where To Save Tracks"), SLOT(chooseBackend()));
682  menu->AddButton(tr("Save Defaults"), SLOT(saveDefaults()));
683 
684  if (m_haveDefaults)
685  {
686  menu->AddButton(tr("Change Compilation Flag"), SLOT(setCompilation()));
687  menu->AddButton(tr("Change Compilation Artist"),
688  SLOT(setCompilationArtist()));
689  menu->AddButton(tr("Change Artist"), SLOT(setArtist()));
690  menu->AddButton(tr("Change Album"), SLOT(setAlbum()));
691  menu->AddButton(tr("Change Genre"), SLOT(setGenre()));
692  menu->AddButton(tr("Change Year"), SLOT(setYear()));
693  menu->AddButton(tr("Change Rating"), SLOT(setRating()));
694  }
695 }
696 
698 {
699  QStringList hostList;
700 
701  // get a list of hosts with a directory defined for the 'Music' storage group
702  MSqlQuery query(MSqlQuery::InitCon());
703  QString sql = "SELECT DISTINCT hostname "
704  "FROM storagegroup "
705  "WHERE groupname = 'Music'";
706  if (!query.exec(sql) || !query.isActive())
707  MythDB::DBError("ImportMusicDialog::chooseBackend get host list", query);
708  else
709  {
710  while(query.next())
711  {
712  hostList.append(query.value(0).toString());
713  }
714  }
715 
716  if (hostList.isEmpty())
717  {
718  LOG(VB_GENERAL, LOG_ERR, "ImportMusicDialog::chooseBackend: No backends found");
719  return;
720  }
721 
722  QString msg = tr("Select where to save tracks");
723 
724  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
725  MythUISearchDialog *searchDlg = new MythUISearchDialog(popupStack, msg, hostList, false, "");
726 
727  if (!searchDlg->Create())
728  {
729  delete searchDlg;
730  return;
731  }
732 
733  connect(searchDlg, SIGNAL(haveResult(QString)), SLOT(setSaveHost(QString)));
734 
735  popupStack->AddScreen(searchDlg);
736 }
737 
739 {
740  gCoreContext->SaveSetting("MythMusicLastImportHost", host);
741 
742  QStringList dirs = StorageGroup::getGroupDirs("Music", host);
743  if (dirs.count() > 0)
744  m_musicStorageDir = StorageGroup::getGroupDirs("Music", host).at(0);
745 
746 }
747 
749 {
750  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
753  m_defaultArtist = data->Artist();
754  m_defaultAlbum = data->Album();
755  m_defaultGenre = data->Genre();
756  m_defaultYear = data->Year();
757  m_defaultRating = data->Rating();
758 
759  m_haveDefaults = true;
760 }
761 
763 {
764  if (!m_haveDefaults)
765  return;
766 
767  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
768 
770  {
773  }
774  else
775  {
778  }
779 
780  fillWidgets();
781 }
782 
784 {
785  if (!m_haveDefaults)
786  return;
787 
788  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
790 
791  fillWidgets();
792 }
793 
795 {
796  if (!m_haveDefaults)
797  return;
798 
799  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
800  data->setArtist(m_defaultArtist);
801 
802  m_tracks->at(m_currentTrack)->isNewTune =
803  isNewTune(data->Artist(), data->Album(), data->Title());
804 
805  fillWidgets();
806 }
807 
809 {
810  if (!m_haveDefaults)
811  return;
812 
813  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
814  data->setAlbum(m_defaultAlbum);
815 
816  m_tracks->at(m_currentTrack)->isNewTune =
817  isNewTune(data->Artist(), data->Album(), data->Title());
818 
819  fillWidgets();
820 }
821 
823 {
824  if (!m_haveDefaults)
825  return;
826 
827  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
828  data->setYear(m_defaultYear);
829 
830  fillWidgets();
831 }
832 
834 {
835  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
836  data->setTrack(data->Track() + 100);
837 
838  fillWidgets();
839 }
840 
842 {
843  if (!m_haveDefaults)
844  return;
845 
846  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
847  data->setGenre(m_defaultGenre);
848 
849  fillWidgets();
850 }
851 
853 {
854  if (!m_haveDefaults)
855  return;
856 
857  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
858  data->setRating(m_defaultRating);
859 }
860 
862 {
863  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
864  QString title = data->Title();
865  bool bFoundCap = false;
866 
867  for (int x = 0; x < title.length(); x++)
868  {
869  if (title[x].isLetter())
870  {
871  if (bFoundCap == false)
872  {
873  title[x] = title[x].toUpper();
874  bFoundCap = true;
875  }
876  else
877  title[x] = title[x].toLower();
878  }
879  }
880 
881  data->setTitle(title);
882  fillWidgets();
883 }
884 
886 {
887  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
888  QString title = data->Title();
889  bool bInWord = false;
890 
891  for (int x = 0; x < title.length(); x++)
892  {
893  if (title[x].isSpace())
894  bInWord = false;
895  else
896  {
897  if (title[x].isLetter())
898  {
899  if (!bInWord)
900  {
901  title[x] = title[x].toUpper();
902  bInWord = true;
903  }
904  else
905  title[x] = title[x].toLower();
906  }
907  }
908  }
909 
910  data->setTitle(title);
911  fillWidgets();
912 }
913 
915 {
916  if (m_tracks->empty())
917  return;
918 
919  QFileInfo fi(m_sourceFiles.at(m_currentTrack));
920 
922 
923  ImportCoverArtDialog *import = new ImportCoverArtDialog(mainStack,
924  fi.absolutePath(),
925  m_tracks->at(m_currentTrack)->metadata,
927 
928  if (import->Create())
929  mainStack->AddScreen(import);
930  else
931  delete import;
932 }
933 
935 {
936  if (event->type() == DialogCompletionEvent::kEventType)
937  {
939  if (dce->GetId() == "locationchange")
940  {
942  startScan();
943  }
944  }
945 }
946 
948 
950  const QString &sourceDir,
951  MusicMetadata *metadata,
952  const QString &storageDir) :
953  MythScreenType(parent, "import_coverart"),
954  m_sourceDir(sourceDir),
955  m_musicStorageDir(storageDir),
956  m_metadata(metadata),
957  m_currentFile(0),
958  // GUI stuff
959  m_filenameText(nullptr),
960  m_currentText(nullptr),
961  m_statusText(nullptr),
962  m_destinationText(nullptr),
963  m_coverartImage(nullptr),
964  m_typeList(nullptr),
965  m_nextButton(nullptr),
966  m_prevButton(nullptr),
967  m_copyButton(nullptr),
968  m_exitButton(nullptr)
969 {
970 }
971 
973 {
974  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
975  return true;
976 
977  QStringList actions;
978  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
979 
980  for (int i = 0; i < actions.size() && !handled; i++)
981  {
982  QString action = actions[i];
983  handled = true;
984 
985  if (action == "LEFT")
986  {
987  m_prevButton->Push();
988  }
989  else if (action == "RIGHT")
990  {
991  m_nextButton->Push();
992  }
993  else
994  handled = false;
995  }
996 
997  if (!handled && MythScreenType::keyPressEvent(event))
998  handled = true;
999 
1000  return handled;
1001 }
1002 
1004 {
1005  if (!LoadWindowFromXML("music-ui.xml", "import_coverart", this))
1006  return false;
1007 
1008  bool err = false;
1009  UIUtilE::Assign(this, m_filenameText, "file", &err);
1010  UIUtilE::Assign(this, m_currentText, "position", &err);
1011  UIUtilE::Assign(this, m_statusText, "status", &err);
1012  UIUtilE::Assign(this, m_destinationText, "destination", &err);
1013  UIUtilE::Assign(this, m_coverartImage, "coverart", &err);
1014  UIUtilE::Assign(this, m_copyButton, "copy", &err);
1015  UIUtilE::Assign(this, m_exitButton, "exit", &err);
1016  UIUtilE::Assign(this, m_prevButton, "prev", &err);
1017  UIUtilE::Assign(this, m_nextButton, "next", &err);
1018  UIUtilE::Assign(this, m_typeList, "type", &err);
1019 
1020  if (err)
1021  {
1022  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'import_coverart'");
1023  return false;
1024  }
1025 
1026  if (m_typeList)
1027  {
1028  new MythUIButtonListItem(m_typeList, tr("Front Cover"),
1029  qVariantFromValue((int)IT_FRONTCOVER));
1030  new MythUIButtonListItem(m_typeList, tr("Back Cover"),
1031  qVariantFromValue((int)IT_BACKCOVER));
1032  new MythUIButtonListItem(m_typeList, tr("CD"),
1033  qVariantFromValue((int)IT_CD));
1034  new MythUIButtonListItem(m_typeList, tr("Inlay"),
1035  qVariantFromValue((int)IT_INLAY));
1036  new MythUIButtonListItem(m_typeList, tr("<Unknown>"),
1037  qVariantFromValue((int)IT_UNKNOWN));
1038 
1039  connect(m_typeList, SIGNAL(itemSelected(MythUIButtonListItem *)),
1040  SLOT(selectorChanged()));
1041  }
1042 
1043  if (m_copyButton)
1044  connect(m_copyButton, SIGNAL(Clicked()), this, SLOT(copyPressed()));
1045 
1046  if (m_exitButton)
1047  connect(m_exitButton, SIGNAL(Clicked()), this, SLOT(Close()));
1048 
1049  if (m_prevButton)
1050  connect(m_prevButton, SIGNAL(Clicked()), this, SLOT(prevPressed()));
1051 
1052  if (m_nextButton)
1053  connect(m_nextButton, SIGNAL(Clicked()), this, SLOT(nextPressed()));
1054 
1055  BuildFocusList();
1056 
1057  scanDirectory();
1058 
1059  return true;
1060 }
1061 
1063 {
1064  updateStatus();
1065 }
1066 
1068 {
1069  if (m_filelist.size() > 0)
1070  {
1072  {
1073  //: %1 is the filename
1074  ShowOkPopup(tr("Copy CoverArt Failed.\nCopying to %1").arg(m_saveFilename));
1075  return;
1076  }
1077 
1078  // add the file to the database
1079  QString filename = m_saveFilename.section( '/', -1, -1);
1080  AlbumArtImage image;
1081  image.description = "";
1082  image.embedded = false;
1083  image.filename = filename;
1084  image.hostname = m_metadata->Hostname();
1085  image.imageType = (ImageType)m_typeList->GetItemCurrent()->GetData().toInt();
1086 
1089 
1090  updateStatus();
1091  }
1092 }
1093 
1095 {
1096  if (m_currentFile > 0)
1097  {
1098  m_currentFile--;
1100  updateStatus();
1101  }
1102 }
1103 
1105 {
1106  if (m_currentFile < m_filelist.size() - 1)
1107  {
1108  m_currentFile++;
1110  updateStatus();
1111  }
1112 }
1113 
1115 {
1116  QDir d(m_sourceDir);
1117 
1118  if (!d.exists())
1119  return;
1120 
1121  QString nameFilter = gCoreContext->GetSetting("AlbumArtFilter",
1122  "*.png;*.jpg;*.jpeg;*.gif;*.bmp");
1123 
1124  QFileInfoList list = d.entryInfoList(nameFilter.split(";"),
1125  QDir::Files | QDir::Dirs |
1126  QDir::NoDotAndDotDot);
1127  if (list.isEmpty())
1128  return;
1129 
1130  QFileInfoList::const_iterator it = list.begin();
1131  const QFileInfo *fi;
1132 
1133  while (it != list.end())
1134  {
1135  fi = &(*it);
1136  ++it;
1137  QString filename = fi->absoluteFilePath();
1138  if (!fi->isDir())
1139  {
1140  m_filelist.append(filename);
1141  }
1142  }
1143 
1144  m_currentFile = 0;
1146  updateStatus();
1147 }
1148 
1150 {
1151  if (m_filelist.size() > 0)
1152  {
1153  if (m_currentText)
1154  //: %1 is the current position of the file being copied,
1155  //: %2 is the total number of files
1156  m_currentText->SetText(tr("%1 of %2", "Current file copied")
1157  .arg(m_currentFile + 1)
1158  .arg(m_filelist.size()));
1161  m_coverartImage->Load();
1162 
1163  QString saveFilename = filenameFromMetadata(m_metadata);
1164  QString fullFilename;
1165 
1166  QUrl url(m_musicStorageDir);
1167  fullFilename = gCoreContext->GenMythURL(url.host(), 0, saveFilename, "Music");
1168  QString dir = fullFilename.section( '/', 0, -2);
1169 
1170  QFileInfo fi(m_filelist[m_currentFile]);
1171  switch (m_typeList->GetItemCurrent()->GetData().toInt())
1172  {
1173  case IT_FRONTCOVER:
1174  saveFilename = "front." + fi.suffix();
1175  break;
1176  case IT_BACKCOVER:
1177  saveFilename = "back." + fi.suffix();
1178  break;
1179  case IT_CD:
1180  saveFilename = "cd." + fi.suffix();
1181  break;
1182  case IT_INLAY:
1183  saveFilename = "inlay." + fi.suffix();
1184  break;
1185  case IT_UNKNOWN:
1186  saveFilename = "unknown." + fi.suffix();
1187  break;
1188  default:
1189  saveFilename = fi.fileName();
1190  }
1191 
1192  m_saveFilename = dir + "/" + saveFilename;
1194 
1195  url.setUrl(m_saveFilename);
1196 
1197  if (!RemoteFile::FindFile(url.path(), "" , "Music").isEmpty())
1198  m_statusText->SetText(tr("File Already Exists"));
1199  else
1200  m_statusText->SetText(tr("New File"));
1201  }
1202  else
1203  {
1204  if (m_currentText)
1205  m_currentText->Reset();
1206  m_statusText->Reset();
1207  m_filenameText->Reset();
1210  }
1211 }
1212 
1214 {
1215  if (m_filelist.size() == 0)
1216  return;
1217 
1218  QString filename = m_filelist[m_currentFile];
1219  QFileInfo fi(filename);
1220  filename = fi.fileName();
1221 
1222  if (filename.contains("front", Qt::CaseInsensitive) > 0)
1223  m_typeList->SetValue(tr("Front Cover"));
1224  else if (filename.contains("back", Qt::CaseInsensitive) > 0)
1225  m_typeList->SetValue(tr("Back Cover"));
1226  else if (filename.contains("inlay", Qt::CaseInsensitive) > 0)
1227  m_typeList->SetValue(tr("Inlay"));
1228  else if (filename.contains("cd", Qt::CaseInsensitive) > 0)
1229  m_typeList->SetValue(tr("CD"));
1230  else
1231  m_typeList->SetValue(tr("<Unknown>"));
1232 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:216
vector< TrackInfo * > * m_tracks
Definition: importmusic.h:114
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:794
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: importmusic.cpp:39
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:295
FileScannerThread(ImportMusicDialog *parent)
Definition: importmusic.cpp:34
MythUIButton * m_playButton
Definition: importmusic.h:140
void saveDefaults(void)
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
bool keyPressEvent(QKeyEvent *) override
Key event handler.
ImageType imageType
Definition: musicmetadata.h:49
MythUIText * m_trackText
Definition: importmusic.h:132
MusicPlayer * gPlayer
Definition: musicplayer.cpp:38
void setTrack(int ltrack)
QString GenMythURL(QString host=QString(), QString port=QString(), QString path=QString(), QString storageGroup=QString())
MythUIButtonList * m_typeList
Definition: importmusic.h:201
MusicMetadata * getCurrentMetadata(void)
get the metadata for the current track in the playlist
void ShowMenu(void) override
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
void setYear(int lyear)
void prevPressed(void)
MythUIButton * m_prevButton
Definition: importmusic.h:204
void SaveSetting(const QString &key, int newValue)
virtual void SetValue(int value)
void startScan(void)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
QString Genre() const
QString Hostname(void)
Basic menu dialog, message and a list of options.
void SetReturnEvent(QObject *retobject, const QString &resultid)
static QString FindFile(const QString &filename, const QString &host, const QString &storageGroup, bool useRegex=false, bool allowFallback=false)
Search all BE's for a file in the give storage group.
MythUIText * m_currentText
Definition: importmusic.h:196
static MetaIO * createTagger(const QString &filename)
Finds an appropriate tagger for the given file.
Definition: metaio.cpp:33
void addPressed(void)
MythUIButton * m_locationButton
Definition: importmusic.h:121
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:168
bool Create(void) override
MusicMetadata * m_playingMetaData
Definition: importmusic.h:117
bool isPlaying(void)
Definition: musicplayer.h:100
AlbumArtImages * getAlbumArtImages(void)
QString filename
Definition: musicmetadata.h:47
MythUIText * m_statusText
Definition: importmusic.h:138
void setArtist(void)
MythScreenStack * GetStack(const QString &stackname)
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythUIButton * m_nextButton
Definition: importmusic.h:134
void setFilename(const QString &lfilename)
MythScreenStack * GetMainStack()
bool Create(void) override
long long copy(QFile &dst, QFile &src, uint block_size)
Copies src file to dst file.
static MythThemedMenu * menu
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
void BuildFocusList(void)
QString m_defaultAlbum
Definition: importmusic.h:151
int Rating() const
static Type kEventType
Definition: mythdialogbox.h:50
void doExit(bool ok)
void setCompilation(void)
void setHostname(const QString &host)
void scanForImages(void)
void addAllNewPressed(void)
static QStringList getGroupDirs(const QString &groupname, const QString &host)
bool isNewTune
Definition: importmusic.h:27
Definition: metaio.h:17
QString m_dstFile
Definition: importmusic.h:51
QVariant value(int i) const
Definition: mythdbcon.h:182
virtual void Close()
void setTrack(void)
void setCompilation(bool state)
void setAlbum(void)
QString Artist() const
QStringList m_sourceFiles
Definition: importmusic.h:115
void selectorChanged(void)
FileCopyThread(IconView *parent, bool move)
Definition: iconview.cpp:75
void dumpToDatabase(void)
virtual void SetVisible(bool visible)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
void customEvent(QEvent *) override
void playPressed(void)
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
virtual MusicMetadata * read(const QString &filename)=0
Reads MusicMetadata from a file.
bool Create(void) override
bool Create(void) override
void metadataChanged(void)
void chooseBackend(void)
bool copyFile(const QString &src, const QString &dst)
MusicMetadata * m_metadata
Definition: importmusic.h:188
static const uint16_t * d
QString GetSetting(const QString &key, const QString &defaultval="")
void setAlbum(const QString &lalbum, const QString &lalbum_sort=nullptr)
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:116
void setRating(int lrating)
bool isActive(void) const
Definition: mythdbcon.h:188
void SetTypeFilter(QDir::Filters filter)
QString hostname
Definition: musicmetadata.h:48
int Track() const
QString m_defaultGenre
Definition: importmusic.h:152
void importFinished(void)
MythUIText * m_albumText
Definition: importmusic.h:128
void Reset(void) override
Reset the image back to the default defined in the theme.
MythUIButton * m_addallnewButton
Definition: importmusic.h:142
MythUIText * m_artistText
Definition: importmusic.h:127
QString GetMasterHostName(void)
QString description
Definition: musicmetadata.h:50
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
void setTitleWordCaps(void)
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:547
QString Album() const
ImportMusicDialog(MythScreenStack *parent)
Definition: importmusic.cpp:63
static bool CopyFile(const QString &src, const QString &dest, bool overwrite=false, bool verify=false)
Definition: remotefile.cpp:594
void setCompilationArtist(void)
QString m_musicStorageDir
Definition: importmusic.h:112
MythUIText * m_titleText
Definition: importmusic.h:129
void updateTypeSelector(void)
void setTitle(const QString &ltitle, const QString &ltitle_sort=nullptr)
void stop(bool stopAll=false)
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 setYear(void)
bool m_somethingWasImported
Definition: importmusic.h:113
MythMainWindow * GetMythMainWindow(void)
void showEditMetadataDialog(void)
MythUIButton * m_exitButton
Definition: importmusic.h:206
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: iconview.cpp:80
bool metadataHasChanged
Definition: importmusic.h:28
MythUIButton * m_nextnewButton
Definition: importmusic.h:143
bool Compilation() const
MythUIText * m_statusText
Definition: importmusic.h:197
void dumpToDatabase(void)
saves or updates the image details in the DB
bool Create() override
void setRating(void)
QString m_defaultArtist
Definition: importmusic.h:150
bool keyPressEvent(QKeyEvent *) override
Key event handler.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
int Year() const
virtual bool write(const QString &filename, MusicMetadata *mdata)=0
Writes all metadata back to a file.
MythUIButton * m_copyButton
Definition: importmusic.h:205
MythUITextEdit * m_locationEdit
Definition: importmusic.h:120
MythUIImage * m_coverartImage
Definition: importmusic.h:200
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 coverArtPressed(void)
void setFileSize(uint64_t lfilesize)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:203
void nextNewPressed(void)
MythUIButton * m_coverartButton
Definition: importmusic.h:123
MythUIText * m_currentText
Definition: importmusic.h:137
QStringList m_filelist
Definition: importmusic.h:185
QString Filename(bool find=true)
QString m_srcFile
Definition: importmusic.h:50
void setTitleInitialCap(void)
void SetCheckState(MythUIStateType::StateType state)
ImportCoverArtDialog(MythScreenStack *parent, const QString &sourceDir, MusicMetadata *metadata, const QString &storageDir)
MythUIText * m_destinationText
Definition: importmusic.h:198
QString m_defaultCompArtist
Definition: importmusic.h:149
MythUIButton * m_nextButton
Definition: importmusic.h:203
bool isFinished(void) const
Definition: mthread.cpp:270
void setSaveHost(QString host)
bool Create(void) override
void showImportCoverArtDialog()
MusicMetadata * metadata
Definition: importmusic.h:26
ImageType
Definition: musicmetadata.h:26
QString CompilationArtist() const
MythUIButton * m_prevButton
Definition: importmusic.h:135
MythUIButton * m_addButton
Definition: importmusic.h:141
void nextPressed(void)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:615
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
Screen in which all other widgets are contained and rendered.
bool isNewTune(const QString &artist, const QString &album, const QString &title)
try to find a track in the db using the given artist, album and title
Definition: musicutils.cpp:139
MythUIText * m_yearText
Definition: importmusic.h:131
QString filenameFromMetadata(MusicMetadata *track)
create a filename using the template in the settings and a MusicMetadata object
Definition: musicutils.cpp:77
ImportMusicDialog * m_parent
Definition: importmusic.h:38
MythUIButton * m_scanButton
Definition: importmusic.h:122
void setGenre(void)
void setCompilationArtist(const QString &lcompilation_artist, const QString &lcompilation_artist_sort=nullptr)
void locationPressed(void)
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:37
void addImage(const AlbumArtImage &newImage)
void Push(bool lock=false)
bool keyPressEvent(QKeyEvent *) override
Key event handler.
MythUIText * m_genreText
Definition: importmusic.h:130
MythUIText * m_compartistText
Definition: importmusic.h:126
MythUICheckBox * m_compilationCheck
Definition: importmusic.h:145
void playFile(const MusicMetadata &meta)
void scanDirectory(QString &directory, vector< TrackInfo * > *tracks)
MythUIText * m_filenameText
Definition: importmusic.h:125
MythUIText * m_filenameText
Definition: importmusic.h:195
Provide a dialog to quickly find an entry in a list.
QString GetText(void) const
MythUIButtonListItem * GetItemCurrent() const
void setGenre(const QString &lgenre)