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 
49 {
50  RunProlog();
52  RunEpilog();
53 }
54 
56 
57 
59  MythScreenType(parent, "musicimportfiles"),
60  m_tracks(new vector<TrackInfo*>)
61 {
62  QString lastHost = gCoreContext->GetSetting("MythMusicLastImportHost", gCoreContext->GetMasterHostName());
63  QStringList dirs = StorageGroup::getGroupDirs("Music", lastHost);
64  if (dirs.count() > 0)
65  m_musicStorageDir = StorageGroup::getGroupDirs("Music", lastHost).at(0);
66 }
67 
69 {
71  {
73  gPlayer->stop(true);
74  }
75 
76  if (m_locationEdit)
77  gCoreContext->SaveSetting("MythMusicLastImportDir", m_locationEdit->GetText());
78 
79  delete m_tracks;
80 
82  emit importFinished();
83 }
84 
86 {
87  if (!m_tracks->empty())
88  {
89  // update current
90  //: %1 is the current track,
91  //: %2 is the number of tracks
92  m_currentText->SetText(tr("%1 of %2", "Current track position")
93  .arg(m_currentTrack + 1).arg(m_tracks->size()));
94 
95  MusicMetadata *meta = m_tracks->at(m_currentTrack)->metadata;
99  m_artistText->SetText(meta->Artist());
100  m_albumText->SetText(meta->Album());
101  m_titleText->SetText(meta->Title());
102  m_genreText->SetText(meta->Genre());
103  m_yearText->SetText(QString::number(meta->Year()));
104  m_trackText->SetText(QString::number(meta->Track()));
105  if (m_tracks->at(m_currentTrack)->isNewTune)
106  {
108  m_statusText->SetText(tr("New File"));
109  }
110  else
111  {
113  m_statusText->SetText(tr("Already in Database"));
114  }
115  }
116  else
117  {
118  // update current
119  m_currentText->SetText(tr("Not found"));
123  m_artistText->Reset();
124  m_albumText->Reset();
125  m_titleText->Reset();
126  m_genreText->Reset();
127  m_yearText->Reset();
128  m_trackText->Reset();
129  m_statusText->Reset();
131  }
132 }
133 
134 bool ImportMusicDialog::keyPressEvent(QKeyEvent *event)
135 {
136  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
137  return true;
138 
139  QStringList actions;
140  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
141 
142  for (int i = 0; i < actions.size() && !handled; i++)
143  {
144  QString action = actions[i];
145  handled = true;
146 
147  if (action == "LEFT")
148  {
149  m_prevButton->Push();
150  }
151  else if (action == "RIGHT")
152  {
153  m_nextButton->Push();
154  }
155  else if (action == "EDIT")
156  {
158  }
159  else if (action == "MENU")
160  {
161  ShowMenu();
162  }
163  else if (action == "ESCAPE" && !GetMythMainWindow()->IsExitingToMain())
164  {
165  bool found = false;
166  if (!m_tracks->empty())
167  {
168  uint track = 0;
169  while (track < m_tracks->size())
170  {
171  if (m_tracks->at(track)->isNewTune)
172  {
173  found = true;
174  break;
175  }
176  track++;
177  }
178 
179  if (found)
180  {
181  QString msg = tr("You might have unsaved changes.\nAre you sure you want to exit this screen?");
182  ShowOkPopup(msg, this, SLOT(doExit(bool)), true);
183  }
184  }
185 
186  handled = found;
187  }
188  else if (action == "1")
189  {
190  setCompilation();
191  }
192  else if (action == "2")
193  {
195  }
196  else if (action == "3")
197  {
198  setArtist();
199  }
200  else if (action == "4")
201  {
202  setAlbum();
203  }
204  else if (action == "5")
205  {
206  setGenre();
207  }
208  else if (action == "6")
209  {
210  setYear();
211  }
212  else if (action == "7")
213  {
214  setRating();
215  }
216  else if (action == "8")
217  {
219  }
220  else if (action == "9")
221  {
223  }
224  else if (action == "0")
225  {
226  setTrack();
227  }
228  else
229  handled = false;
230  }
231 
232  if (!handled && MythScreenType::keyPressEvent(event))
233  handled = true;
234 
235  return handled;
236 }
237 
239 {
240  if (!LoadWindowFromXML("music-ui.xml", "import_music", this))
241  return false;
242 
243  bool err = false;
244  UIUtilE::Assign(this, m_locationEdit, "location", &err);
245  UIUtilE::Assign(this, m_locationButton, "directoryfinder", &err);
246  UIUtilE::Assign(this, m_scanButton, "scan", &err);
247  UIUtilE::Assign(this, m_coverartButton, "coverart", &err);
248  UIUtilE::Assign(this, m_filenameText, "filename", &err);
249  UIUtilE::Assign(this, m_compartistText, "compartist", &err);
250  UIUtilE::Assign(this, m_artistText, "artist", &err);
251  UIUtilE::Assign(this, m_albumText, "album", &err);
252  UIUtilE::Assign(this, m_titleText, "title", &err);
253  UIUtilE::Assign(this, m_genreText, "genre", &err);
254  UIUtilE::Assign(this, m_yearText, "year", &err);
255  UIUtilE::Assign(this, m_trackText, "track", &err);
256  UIUtilE::Assign(this, m_currentText, "position", &err);
257  UIUtilE::Assign(this, m_statusText, "status", &err);
258  UIUtilE::Assign(this, m_compilationCheck,"compilation", &err);
259  UIUtilE::Assign(this, m_playButton, "play", &err);
260  UIUtilE::Assign(this, m_nextnewButton, "nextnew", &err);
261  UIUtilE::Assign(this, m_addButton, "add", &err);
262  UIUtilE::Assign(this, m_addallnewButton, "addallnew", &err);
263  UIUtilE::Assign(this, m_nextButton, "next", &err);
264  UIUtilE::Assign(this, m_prevButton, "prev", &err);
265 
266  if (err)
267  {
268  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'import_music'");
269  return false;
270  }
271 
272  connect(m_prevButton, SIGNAL(Clicked()), SLOT(prevPressed()));
273  connect(m_locationButton, SIGNAL(Clicked()), SLOT(locationPressed()));
274  connect(m_scanButton, SIGNAL(Clicked()), SLOT(startScan()));
275  connect(m_coverartButton, SIGNAL(Clicked()), SLOT(coverArtPressed()));
276  connect(m_playButton, SIGNAL(Clicked()), SLOT(playPressed()));
277  connect(m_nextnewButton, SIGNAL(Clicked()), SLOT(nextNewPressed()));
278  connect(m_addButton, SIGNAL(Clicked()), SLOT(addPressed()));
279  connect(m_addallnewButton, SIGNAL(Clicked()), SLOT(addAllNewPressed()));
280  connect(m_nextButton, SIGNAL(Clicked()), SLOT(nextPressed()));
281 
282  fillWidgets();
283 
284  BuildFocusList();
285 
286  m_locationEdit->SetText(gCoreContext->GetSetting("MythMusicLastImportDir", "/"));
287 
288  return true;
289 }
290 
292 {
293  if (ok)
294  Close();
295 }
296 
298 {
299  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
300  MythUIFileBrowser *fb = new MythUIFileBrowser(popupStack, m_locationEdit->GetText());
301  // TODO Install a name filter on supported music formats
302  fb->SetTypeFilter(QDir::AllDirs | QDir::Readable);
303  if (fb->Create())
304  {
305  fb->SetReturnEvent(this, "locationchange");
306  popupStack->AddScreen(fb);
307  }
308  else
309  delete fb;
310 }
311 
313 {
315 }
316 
318 {
319  if (m_tracks->empty())
320  return;
321 
322  m_playingMetaData = m_tracks->at(m_currentTrack)->metadata;
323 
325 }
326 
328 {
329  if (m_currentTrack > 0)
330  {
331  m_currentTrack--;
332  fillWidgets();
333  }
334 }
335 
337 {
338  if (m_currentTrack < (int) m_tracks->size() - 1)
339  {
340  m_currentTrack++;
341  fillWidgets();
342  }
343 }
344 
346 {
347  if (m_tracks->empty())
348  return;
349 
350  MusicMetadata *meta = m_tracks->at(m_currentTrack)->metadata;
351 
352  // is the current track a new file?
353  if (m_tracks->at(m_currentTrack)->isNewTune)
354  {
355  // do we need to update the tags?
356  if (m_tracks->at(m_currentTrack)->metadataHasChanged)
357  {
358  MetaIO *tagger = MetaIO::createTagger(meta->Filename());
359  if (tagger)
360  {
361  tagger->write(meta->Filename(), meta);
362  delete tagger;
363  }
364  }
365 
366  // get the save filename
367  QString origFilename = meta->Filename();
368  QString saveFilename = filenameFromMetadata(meta);
369  QString fullFilename;
370 
371  QUrl url(m_musicStorageDir);
372  fullFilename = gCoreContext->GenMythURL(url.host(), 0, saveFilename, "Music");
373 
374 
375  // we need to manually copy the file extension
376  QFileInfo fi(origFilename);
377  saveFilename += "." + fi.suffix();
378  fullFilename += "." + fi.suffix();
379 
380  LOG(VB_FILE, LOG_INFO, QString("Copying file from: %1").arg(origFilename));
381  LOG(VB_FILE, LOG_INFO, QString("to: ").arg(fullFilename));
382 
383  // copy the file to the new location
384  if (!copyFile(origFilename, fullFilename))
385  {
386  ShowOkPopup(tr("Copy Failed\nCould not copy file to: %1").arg(fullFilename));
387  return;
388  }
389 
390  meta->setFilename(saveFilename);
391  meta->setHostname(url.host());
392  meta->setFileSize((quint64)QFileInfo(origFilename).size());
393 
394  // update the database
395  meta->dumpToDatabase();
396 
397  // find any albumart for this track
398  meta->getAlbumArtImages()->scanForImages();
400 
401  m_somethingWasImported = true;
402 
403  m_tracks->at(m_currentTrack)->isNewTune =
404  isNewTune(meta->Artist(), meta->Album(), meta->Title());
405 
406  // update the UI
407  fillWidgets();
408  }
409  else
410  ShowOkPopup(tr("This track is already in the database"));
411 }
412 
414 {
415  if (m_tracks->empty())
416  return;
417 
418  m_currentTrack = 0;
419  int newCount = 0;
420 
421  while (m_currentTrack < (int) m_tracks->size())
422  {
423  fillWidgets();
424  qApp->processEvents();
425 
426  if (m_tracks->at(m_currentTrack)->isNewTune)
427  {
428  addPressed();
429  newCount++;
430  }
431 
432  qApp->processEvents();
433 
434  m_currentTrack++;
435  }
436 
437  m_currentTrack--;
438 
439  ShowOkPopup(tr("%1 new tracks were added to the database").arg(newCount));
440 }
441 
443 {
444  if (m_tracks->empty())
445  return;
446 
447  uint track = m_currentTrack + 1;
448  while (track < m_tracks->size())
449  {
450  if (m_tracks->at(track)->isNewTune)
451  {
452  m_currentTrack = track;
453  fillWidgets();
454  break;
455  }
456  track++;
457  }
458 }
459 
460 bool ImportMusicDialog::copyFile(const QString &src, const QString &dst)
461 {
462  bool res = false;
463  QString host = QUrl(dst).host();
464 
465  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
466  MythUIBusyDialog *busy =
467  new MythUIBusyDialog(tr("Copying music file to the 'Music' storage group on %1").arg(host),
468  popupStack,
469  "scanbusydialog");
470 
471  if (busy->Create())
472  {
473  popupStack->AddScreen(busy, false);
474  }
475  else
476  {
477  delete busy;
478  busy = nullptr;
479  }
480 
481  FileCopyThread *copy = new FileCopyThread(src, dst);
482  copy->start();
483 
484  while (!copy->isFinished())
485  {
486  usleep(500);
487  qApp->processEvents();
488  }
489 
490  res = copy->GetResult();
491 
492  delete copy;
493 
494  if (busy)
495  busy->Close();
496 
497  return res;
498 }
499 
501 {
502  // sanity check - make sure the user isn't trying to import tracks from the music directory
503  QString location = m_locationEdit->GetText();
504  if (!location.endsWith('/'))
505  location.append('/');
506 
507  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
508  MythUIBusyDialog *busy =
509  new MythUIBusyDialog(tr("Searching for music files"),
510  popupStack,
511  "scanbusydialog");
512 
513  if (busy->Create())
514  {
515  popupStack->AddScreen(busy, false);
516  }
517  else
518  {
519  delete busy;
520  busy = nullptr;
521  }
522  FileScannerThread *scanner = new FileScannerThread(this);
523  scanner->start();
524 
525  while (!scanner->isFinished())
526  {
527  usleep(500);
528  qApp->processEvents();
529  }
530 
531  delete scanner;
532 
533  m_currentTrack = 0;
534  fillWidgets();
535 
536  if (busy)
537  busy->Close();
538 }
539 
541 {
542  m_tracks->clear();
543  m_sourceFiles.clear();
544  QString location = m_locationEdit->GetText();
545  scanDirectory(location, m_tracks);
546 }
547 
548 void ImportMusicDialog::scanDirectory(QString &directory, vector<TrackInfo*> *tracks)
549 {
550  QDir d(directory);
551 
552  if (!d.exists())
553  return;
554 
555  d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
556  const QFileInfoList list = d.entryInfoList();
557  if (list.isEmpty())
558  return;
559 
560  QFileInfoList::const_iterator it = list.begin();
561  const QFileInfo *fi;
562 
563  while (it != list.end())
564  {
565  fi = &(*it);
566  ++it;
567  QString filename = fi->absoluteFilePath();
568  if (fi->isDir())
569  scanDirectory(filename, tracks);
570  else
571  {
572  MetaIO *tagger = MetaIO::createTagger(filename);
573  if (tagger)
574  {
575  MusicMetadata *metadata = tagger->read(filename);
576  if (metadata)
577  {
578  TrackInfo * track = new TrackInfo;
579  track->metadata = metadata;
580  track->isNewTune = isNewTune(metadata->Artist(), metadata->Album(),
581  metadata->Title());
582  track->metadataHasChanged = false;
583  tracks->push_back(track);
584  m_sourceFiles.append(filename);
585  }
586 
587  delete tagger;
588  }
589  }
590  }
591 }
592 
594 {
595  if (m_tracks->empty())
596  return;
597 
598  MusicMetadata *editMeta = m_tracks->at(m_currentTrack)->metadata;
599 
601 
602  EditMetadataDialog *editDialog = new EditMetadataDialog(mainStack, editMeta);
603 
604  if (!editDialog->Create())
605  {
606  delete editDialog;
607  return;
608  }
609 
610  editDialog->setSaveMetadataOnly();
611 
612  connect(editDialog, SIGNAL(metadataChanged()), this, SLOT(metadataChanged()));
613 
614  mainStack->AddScreen(editDialog);
615 }
616 
618 {
619  MusicMetadata *editMeta = m_tracks->at(m_currentTrack)->metadata;
620  m_tracks->at(m_currentTrack)->metadataHasChanged = true;
621  m_tracks->at(m_currentTrack)->isNewTune =
622  isNewTune(editMeta->Artist(), editMeta->Album(), editMeta->Title());
623  fillWidgets();
624 }
625 
627 {
628  if (m_tracks->empty())
629  return;
630 
631  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
632 
633  MythDialogBox *menu = new MythDialogBox("", popupStack, "importmusicmenu");
634 
635  if (menu->Create())
636  popupStack->AddScreen(menu);
637  else
638  {
639  delete menu;
640  return;
641  }
642 
643  menu->SetReturnEvent(this, "menu");
644  menu->AddButton(tr("Select Where To Save Tracks"), SLOT(chooseBackend()));
645  menu->AddButton(tr("Save Defaults"), SLOT(saveDefaults()));
646 
647  if (m_haveDefaults)
648  {
649  menu->AddButton(tr("Change Compilation Flag"), SLOT(setCompilation()));
650  menu->AddButton(tr("Change Compilation Artist"),
651  SLOT(setCompilationArtist()));
652  menu->AddButton(tr("Change Artist"), SLOT(setArtist()));
653  menu->AddButton(tr("Change Album"), SLOT(setAlbum()));
654  menu->AddButton(tr("Change Genre"), SLOT(setGenre()));
655  menu->AddButton(tr("Change Year"), SLOT(setYear()));
656  menu->AddButton(tr("Change Rating"), SLOT(setRating()));
657  }
658 }
659 
661 {
662  QStringList hostList;
663 
664  // get a list of hosts with a directory defined for the 'Music' storage group
665  MSqlQuery query(MSqlQuery::InitCon());
666  QString sql = "SELECT DISTINCT hostname "
667  "FROM storagegroup "
668  "WHERE groupname = 'Music'";
669  if (!query.exec(sql) || !query.isActive())
670  MythDB::DBError("ImportMusicDialog::chooseBackend get host list", query);
671  else
672  {
673  while(query.next())
674  {
675  hostList.append(query.value(0).toString());
676  }
677  }
678 
679  if (hostList.isEmpty())
680  {
681  LOG(VB_GENERAL, LOG_ERR, "ImportMusicDialog::chooseBackend: No backends found");
682  return;
683  }
684 
685  QString msg = tr("Select where to save tracks");
686 
687  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
688  MythUISearchDialog *searchDlg = new MythUISearchDialog(popupStack, msg, hostList, false, "");
689 
690  if (!searchDlg->Create())
691  {
692  delete searchDlg;
693  return;
694  }
695 
696  connect(searchDlg, SIGNAL(haveResult(QString)), SLOT(setSaveHost(QString)));
697 
698  popupStack->AddScreen(searchDlg);
699 }
700 
701 void ImportMusicDialog::setSaveHost(const QString& host)
702 {
703  gCoreContext->SaveSetting("MythMusicLastImportHost", host);
704 
705  QStringList dirs = StorageGroup::getGroupDirs("Music", host);
706  if (dirs.count() > 0)
707  m_musicStorageDir = StorageGroup::getGroupDirs("Music", host).at(0);
708 
709 }
710 
712 {
713  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
716  m_defaultArtist = data->Artist();
717  m_defaultAlbum = data->Album();
718  m_defaultGenre = data->Genre();
719  m_defaultYear = data->Year();
720  m_defaultRating = data->Rating();
721 
722  m_haveDefaults = true;
723 }
724 
726 {
727  if (!m_haveDefaults)
728  return;
729 
730  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
731 
733  {
736  }
737  else
738  {
741  }
742 
743  fillWidgets();
744 }
745 
747 {
748  if (!m_haveDefaults)
749  return;
750 
751  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
753 
754  fillWidgets();
755 }
756 
758 {
759  if (!m_haveDefaults)
760  return;
761 
762  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
763  data->setArtist(m_defaultArtist);
764 
765  m_tracks->at(m_currentTrack)->isNewTune =
766  isNewTune(data->Artist(), data->Album(), data->Title());
767 
768  fillWidgets();
769 }
770 
772 {
773  if (!m_haveDefaults)
774  return;
775 
776  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
777  data->setAlbum(m_defaultAlbum);
778 
779  m_tracks->at(m_currentTrack)->isNewTune =
780  isNewTune(data->Artist(), data->Album(), data->Title());
781 
782  fillWidgets();
783 }
784 
786 {
787  if (!m_haveDefaults)
788  return;
789 
790  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
791  data->setYear(m_defaultYear);
792 
793  fillWidgets();
794 }
795 
797 {
798  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
799  data->setTrack(data->Track() + 100);
800 
801  fillWidgets();
802 }
803 
805 {
806  if (!m_haveDefaults)
807  return;
808 
809  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
810  data->setGenre(m_defaultGenre);
811 
812  fillWidgets();
813 }
814 
816 {
817  if (!m_haveDefaults)
818  return;
819 
820  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
821  data->setRating(m_defaultRating);
822 }
823 
825 {
826  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
827  QString title = data->Title();
828  bool bFoundCap = false;
829 
830  for (int x = 0; x < title.length(); x++)
831  {
832  if (title[x].isLetter())
833  {
834  if (!bFoundCap)
835  {
836  title[x] = title[x].toUpper();
837  bFoundCap = true;
838  }
839  else
840  title[x] = title[x].toLower();
841  }
842  }
843 
844  data->setTitle(title);
845  fillWidgets();
846 }
847 
849 {
850  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
851  QString title = data->Title();
852  bool bInWord = false;
853 
854  for (int x = 0; x < title.length(); x++)
855  {
856  if (title[x].isSpace())
857  bInWord = false;
858  else
859  {
860  if (title[x].isLetter())
861  {
862  if (!bInWord)
863  {
864  title[x] = title[x].toUpper();
865  bInWord = true;
866  }
867  else
868  title[x] = title[x].toLower();
869  }
870  }
871  }
872 
873  data->setTitle(title);
874  fillWidgets();
875 }
876 
878 {
879  if (m_tracks->empty())
880  return;
881 
882  QFileInfo fi(m_sourceFiles.at(m_currentTrack));
883 
885 
886  ImportCoverArtDialog *import = new ImportCoverArtDialog(mainStack,
887  fi.absolutePath(),
888  m_tracks->at(m_currentTrack)->metadata,
890 
891  if (import->Create())
892  mainStack->AddScreen(import);
893  else
894  delete import;
895 }
896 
898 {
899  if (event->type() == DialogCompletionEvent::kEventType)
900  {
902  if (dce->GetId() == "locationchange")
903  {
905  startScan();
906  }
907  }
908 }
909 
911 
913 {
914  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
915  return true;
916 
917  QStringList actions;
918  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
919 
920  for (int i = 0; i < actions.size() && !handled; i++)
921  {
922  QString action = actions[i];
923  handled = true;
924 
925  if (action == "LEFT")
926  {
927  m_prevButton->Push();
928  }
929  else if (action == "RIGHT")
930  {
931  m_nextButton->Push();
932  }
933  else
934  handled = false;
935  }
936 
937  if (!handled && MythScreenType::keyPressEvent(event))
938  handled = true;
939 
940  return handled;
941 }
942 
944 {
945  if (!LoadWindowFromXML("music-ui.xml", "import_coverart", this))
946  return false;
947 
948  bool err = false;
949  UIUtilE::Assign(this, m_filenameText, "file", &err);
950  UIUtilE::Assign(this, m_currentText, "position", &err);
951  UIUtilE::Assign(this, m_statusText, "status", &err);
952  UIUtilE::Assign(this, m_destinationText, "destination", &err);
953  UIUtilE::Assign(this, m_coverartImage, "coverart", &err);
954  UIUtilE::Assign(this, m_copyButton, "copy", &err);
955  UIUtilE::Assign(this, m_exitButton, "exit", &err);
956  UIUtilE::Assign(this, m_prevButton, "prev", &err);
957  UIUtilE::Assign(this, m_nextButton, "next", &err);
958  UIUtilE::Assign(this, m_typeList, "type", &err);
959 
960  if (err)
961  {
962  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'import_coverart'");
963  return false;
964  }
965 
966  if (m_typeList)
967  {
968  new MythUIButtonListItem(m_typeList, tr("Front Cover"),
969  qVariantFromValue((int)IT_FRONTCOVER));
970  new MythUIButtonListItem(m_typeList, tr("Back Cover"),
971  qVariantFromValue((int)IT_BACKCOVER));
972  new MythUIButtonListItem(m_typeList, tr("CD"),
973  qVariantFromValue((int)IT_CD));
974  new MythUIButtonListItem(m_typeList, tr("Inlay"),
975  qVariantFromValue((int)IT_INLAY));
976  new MythUIButtonListItem(m_typeList, tr("<Unknown>"),
977  qVariantFromValue((int)IT_UNKNOWN));
978 
979  connect(m_typeList, SIGNAL(itemSelected(MythUIButtonListItem *)),
980  SLOT(selectorChanged()));
981  }
982 
983  if (m_copyButton)
984  connect(m_copyButton, SIGNAL(Clicked()), this, SLOT(copyPressed()));
985 
986  if (m_exitButton)
987  connect(m_exitButton, SIGNAL(Clicked()), this, SLOT(Close()));
988 
989  if (m_prevButton)
990  connect(m_prevButton, SIGNAL(Clicked()), this, SLOT(prevPressed()));
991 
992  if (m_nextButton)
993  connect(m_nextButton, SIGNAL(Clicked()), this, SLOT(nextPressed()));
994 
995  BuildFocusList();
996 
997  scanDirectory();
998 
999  return true;
1000 }
1001 
1003 {
1004  updateStatus();
1005 }
1006 
1008 {
1009  if (!m_filelist.empty())
1010  {
1012  {
1013  //: %1 is the filename
1014  ShowOkPopup(tr("Copy CoverArt Failed.\nCopying to %1").arg(m_saveFilename));
1015  return;
1016  }
1017 
1018  // add the file to the database
1019  QString filename = m_saveFilename.section( '/', -1, -1);
1020  AlbumArtImage image;
1021  image.m_description = "";
1022  image.m_embedded = false;
1023  image.m_filename = filename;
1024  image.m_hostname = m_metadata->Hostname();
1025  image.m_imageType = (ImageType)m_typeList->GetItemCurrent()->GetData().toInt();
1026 
1029 
1030  updateStatus();
1031  }
1032 }
1033 
1035 {
1036  if (m_currentFile > 0)
1037  {
1038  m_currentFile--;
1040  updateStatus();
1041  }
1042 }
1043 
1045 {
1046  if (m_currentFile < m_filelist.size() - 1)
1047  {
1048  m_currentFile++;
1050  updateStatus();
1051  }
1052 }
1053 
1055 {
1056  QDir d(m_sourceDir);
1057 
1058  if (!d.exists())
1059  return;
1060 
1061  QString nameFilter = gCoreContext->GetSetting("AlbumArtFilter",
1062  "*.png;*.jpg;*.jpeg;*.gif;*.bmp");
1063 
1064  QFileInfoList list = d.entryInfoList(nameFilter.split(";"),
1065  QDir::Files | QDir::Dirs |
1066  QDir::NoDotAndDotDot);
1067  if (list.isEmpty())
1068  return;
1069 
1070  QFileInfoList::const_iterator it = list.begin();
1071  const QFileInfo *fi;
1072 
1073  while (it != list.end())
1074  {
1075  fi = &(*it);
1076  ++it;
1077  QString filename = fi->absoluteFilePath();
1078  if (!fi->isDir())
1079  {
1080  m_filelist.append(filename);
1081  }
1082  }
1083 
1084  m_currentFile = 0;
1086  updateStatus();
1087 }
1088 
1090 {
1091  if (!m_filelist.empty())
1092  {
1093  if (m_currentText)
1094  //: %1 is the current position of the file being copied,
1095  //: %2 is the total number of files
1096  m_currentText->SetText(tr("%1 of %2", "Current file copied")
1097  .arg(m_currentFile + 1)
1098  .arg(m_filelist.size()));
1101  m_coverartImage->Load();
1102 
1103  QString saveFilename = filenameFromMetadata(m_metadata);
1104  QString fullFilename;
1105 
1106  QUrl url(m_musicStorageDir);
1107  fullFilename = gCoreContext->GenMythURL(url.host(), 0, saveFilename, "Music");
1108  QString dir = fullFilename.section( '/', 0, -2);
1109 
1110  QFileInfo fi(m_filelist[m_currentFile]);
1111  switch (m_typeList->GetItemCurrent()->GetData().toInt())
1112  {
1113  case IT_FRONTCOVER:
1114  saveFilename = "front." + fi.suffix();
1115  break;
1116  case IT_BACKCOVER:
1117  saveFilename = "back." + fi.suffix();
1118  break;
1119  case IT_CD:
1120  saveFilename = "cd." + fi.suffix();
1121  break;
1122  case IT_INLAY:
1123  saveFilename = "inlay." + fi.suffix();
1124  break;
1125  case IT_UNKNOWN:
1126  saveFilename = "unknown." + fi.suffix();
1127  break;
1128  default:
1129  saveFilename = fi.fileName();
1130  }
1131 
1132  m_saveFilename = dir + "/" + saveFilename;
1134 
1135  url.setUrl(m_saveFilename);
1136 
1137  if (!RemoteFile::FindFile(url.path(), "" , "Music").isEmpty())
1138  m_statusText->SetText(tr("File Already Exists"));
1139  else
1140  m_statusText->SetText(tr("New File"));
1141  }
1142  else
1143  {
1144  if (m_currentText)
1145  m_currentText->Reset();
1146  m_statusText->Reset();
1147  m_filenameText->Reset();
1150  }
1151 }
1152 
1154 {
1155  if (m_filelist.empty())
1156  return;
1157 
1158  QString filename = m_filelist[m_currentFile];
1159  QFileInfo fi(filename);
1160  filename = fi.fileName();
1161 
1162  if (filename.contains("front", Qt::CaseInsensitive))
1163  m_typeList->SetValue(tr("Front Cover"));
1164  else if (filename.contains("back", Qt::CaseInsensitive))
1165  m_typeList->SetValue(tr("Back Cover"));
1166  else if (filename.contains("inlay", Qt::CaseInsensitive))
1167  m_typeList->SetValue(tr("Inlay"));
1168  else if (filename.contains("cd", Qt::CaseInsensitive))
1169  m_typeList->SetValue(tr("CD"));
1170  else
1171  m_typeList->SetValue(tr("<Unknown>"));
1172 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
vector< TrackInfo * > * m_tracks
Definition: importmusic.h:115
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
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:294
FileScannerThread(ImportMusicDialog *parent)
Definition: importmusic.cpp:34
MythUIButton * m_playButton
Definition: importmusic.h:141
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.
MythUIText * m_trackText
Definition: importmusic.h:133
MusicPlayer * gPlayer
Definition: musicplayer.cpp:35
void setTrack(int ltrack)
MythUIButtonList * m_typeList
Definition: importmusic.h:204
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.
void playFile(const MusicMetadata &mdata)
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:207
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:199
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:122
QString m_description
Definition: musicmetadata.h:50
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:136
bool Create(void) override
MusicMetadata * m_playingMetaData
Definition: importmusic.h:118
bool isPlaying(void)
Definition: musicplayer.h:102
AlbumArtImages * getAlbumArtImages(void)
static bool CopyFile(const QString &src, const QString &dst, bool overwrite=false, bool verify=false)
Definition: remotefile.cpp:587
MythUIText * m_statusText
Definition: importmusic.h:139
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:135
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:152
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)
ImageType m_imageType
Definition: musicmetadata.h:49
QString m_hostname
Definition: musicmetadata.h:48
bool isNewTune
Definition: importmusic.h:27
Definition: metaio.h:17
QString m_dstFile
Definition: importmusic.h:52
QVariant value(int i) const
Definition: mythdbcon.h:198
virtual void Close()
void setTrack(void)
void setCompilation(bool state)
void setAlbum(void)
QString Artist() const
QStringList m_sourceFiles
Definition: importmusic.h:116
void selectorChanged(void)
void dumpToDatabase(void)
virtual void SetVisible(bool visible)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
void customEvent(QEvent *) override
void playPressed(void)
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:191
QString m_filename
Definition: musicmetadata.h:47
static const uint16_t * d
QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
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:84
void setRating(int lrating)
bool isActive(void) const
Definition: mythdbcon.h:204
void SetTypeFilter(QDir::Filters filter)
int Track() const
QString m_defaultGenre
Definition: importmusic.h:153
void importFinished(void)
MythUIText * m_albumText
Definition: importmusic.h:129
void Reset(void) override
Reset the image back to the default defined in the theme.
MythUIButton * m_addallnewButton
Definition: importmusic.h:143
MythUIText * m_artistText
Definition: importmusic.h:128
QString GetMasterHostName(void)
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:535
QString Album() const
ImportMusicDialog(MythScreenStack *parent)
Definition: importmusic.cpp:58
void setCompilationArtist(void)
QString m_musicStorageDir
Definition: importmusic.h:113
MythUIText * m_titleText
Definition: importmusic.h:130
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:114
MythMainWindow * GetMythMainWindow(void)
void showEditMetadataDialog(void)
MythUIButton * m_exitButton
Definition: importmusic.h:209
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:144
bool Compilation() const
void setSaveHost(const QString &host)
MythUIText * m_statusText
Definition: importmusic.h:200
void dumpToDatabase(void)
saves or updates the image details in the DB
bool Create() override
void setRating(void)
QString m_defaultArtist
Definition: importmusic.h:151
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:208
MythUITextEdit * m_locationEdit
Definition: importmusic.h:121
MythUIImage * m_coverartImage
Definition: importmusic.h:203
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:202
void nextNewPressed(void)
MythUIButton * m_coverartButton
Definition: importmusic.h:124
MythUIText * m_currentText
Definition: importmusic.h:138
QStringList m_filelist
Definition: importmusic.h:188
QString Filename(bool find=true)
QString m_srcFile
Definition: importmusic.h:51
void setTitleInitialCap(void)
void SetCheckState(MythUIStateType::StateType state)
MythUIText * m_destinationText
Definition: importmusic.h:201
QString m_defaultCompArtist
Definition: importmusic.h:150
MythUIButton * m_nextButton
Definition: importmusic.h:206
bool isFinished(void) const
Definition: mthread.cpp:269
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:136
MythUIButton * m_addButton
Definition: importmusic.h:142
void nextPressed(void)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
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:132
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:123
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:131
MythUIText * m_compartistText
Definition: importmusic.h:127
MythUICheckBox * m_compilationCheck
Definition: importmusic.h:146
void scanDirectory(QString &directory, vector< TrackInfo * > *tracks)
MythUIText * m_filenameText
Definition: importmusic.h:126
MythUIText * m_filenameText
Definition: importmusic.h:198
Provide a dialog to quickly find an entry in a list.
QString GetText(void) const
MythUIButtonListItem * GetItemCurrent() const
void setGenre(const QString &lgenre)