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  auto *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 = MythCoreContext::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  auto *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  auto *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  auto *busy = new MythUIBusyDialog(tr("Searching for music files"),
509  popupStack, "scanbusydialog");
510 
511  if (busy->Create())
512  {
513  popupStack->AddScreen(busy, false);
514  }
515  else
516  {
517  delete busy;
518  busy = nullptr;
519  }
520  auto *scanner = new FileScannerThread(this);
521  scanner->start();
522 
523  while (!scanner->isFinished())
524  {
525  usleep(500);
526  qApp->processEvents();
527  }
528 
529  delete scanner;
530 
531  m_currentTrack = 0;
532  fillWidgets();
533 
534  if (busy)
535  busy->Close();
536 }
537 
539 {
540  m_tracks->clear();
541  m_sourceFiles.clear();
542  QString location = m_locationEdit->GetText();
543  scanDirectory(location, m_tracks);
544 }
545 
546 void ImportMusicDialog::scanDirectory(QString &directory, vector<TrackInfo*> *tracks)
547 {
548  QDir d(directory);
549 
550  if (!d.exists())
551  return;
552 
553  d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
554  const QFileInfoList list = d.entryInfoList();
555  if (list.isEmpty())
556  return;
557 
558  QFileInfoList::const_iterator it = list.begin();
559  while (it != list.end())
560  {
561  const QFileInfo *fi = &(*it);
562  ++it;
563  QString filename = fi->absoluteFilePath();
564  if (fi->isDir())
565  scanDirectory(filename, tracks);
566  else
567  {
569  if (tagger)
570  {
571  MusicMetadata *metadata = tagger->read(filename);
572  if (metadata)
573  {
574  auto * track = new TrackInfo;
575  track->metadata = metadata;
576  track->isNewTune = isNewTune(metadata->Artist(), metadata->Album(),
577  metadata->Title());
578  track->metadataHasChanged = false;
579  tracks->push_back(track);
580  m_sourceFiles.append(filename);
581  }
582 
583  delete tagger;
584  }
585  }
586  }
587 }
588 
590 {
591  if (m_tracks->empty())
592  return;
593 
594  MusicMetadata *editMeta = m_tracks->at(m_currentTrack)->metadata;
595 
597 
598  auto *editDialog = new EditMetadataDialog(mainStack, editMeta);
599 
600  if (!editDialog->Create())
601  {
602  delete editDialog;
603  return;
604  }
605 
606  editDialog->setSaveMetadataOnly();
607 
608  connect(editDialog, SIGNAL(metadataChanged()), this, SLOT(metadataChanged()));
609 
610  mainStack->AddScreen(editDialog);
611 }
612 
614 {
615  MusicMetadata *editMeta = m_tracks->at(m_currentTrack)->metadata;
616  m_tracks->at(m_currentTrack)->metadataHasChanged = true;
617  m_tracks->at(m_currentTrack)->isNewTune =
618  isNewTune(editMeta->Artist(), editMeta->Album(), editMeta->Title());
619  fillWidgets();
620 }
621 
623 {
624  if (m_tracks->empty())
625  return;
626 
627  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
628 
629  auto *menu = new MythDialogBox("", popupStack, "importmusicmenu");
630 
631  if (menu->Create())
632  popupStack->AddScreen(menu);
633  else
634  {
635  delete menu;
636  return;
637  }
638 
639  menu->SetReturnEvent(this, "menu");
640  menu->AddButton(tr("Select Where To Save Tracks"), SLOT(chooseBackend()));
641  menu->AddButton(tr("Save Defaults"), SLOT(saveDefaults()));
642 
643  if (m_haveDefaults)
644  {
645  menu->AddButton(tr("Change Compilation Flag"), SLOT(setCompilation()));
646  menu->AddButton(tr("Change Compilation Artist"),
647  SLOT(setCompilationArtist()));
648  menu->AddButton(tr("Change Artist"), SLOT(setArtist()));
649  menu->AddButton(tr("Change Album"), SLOT(setAlbum()));
650  menu->AddButton(tr("Change Genre"), SLOT(setGenre()));
651  menu->AddButton(tr("Change Year"), SLOT(setYear()));
652  menu->AddButton(tr("Change Rating"), SLOT(setRating()));
653  }
654 }
655 
657 {
658  QStringList hostList;
659 
660  // get a list of hosts with a directory defined for the 'Music' storage group
661  MSqlQuery query(MSqlQuery::InitCon());
662  QString sql = "SELECT DISTINCT hostname "
663  "FROM storagegroup "
664  "WHERE groupname = 'Music'";
665  if (!query.exec(sql) || !query.isActive())
666  MythDB::DBError("ImportMusicDialog::chooseBackend get host list", query);
667  else
668  {
669  while(query.next())
670  {
671  hostList.append(query.value(0).toString());
672  }
673  }
674 
675  if (hostList.isEmpty())
676  {
677  LOG(VB_GENERAL, LOG_ERR, "ImportMusicDialog::chooseBackend: No backends found");
678  return;
679  }
680 
681  QString msg = tr("Select where to save tracks");
682 
683  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
684  auto *searchDlg = new MythUISearchDialog(popupStack, msg, hostList, false, "");
685 
686  if (!searchDlg->Create())
687  {
688  delete searchDlg;
689  return;
690  }
691 
692  connect(searchDlg, SIGNAL(haveResult(QString)), SLOT(setSaveHost(QString)));
693 
694  popupStack->AddScreen(searchDlg);
695 }
696 
697 void ImportMusicDialog::setSaveHost(const QString& host)
698 {
699  gCoreContext->SaveSetting("MythMusicLastImportHost", host);
700 
701  QStringList dirs = StorageGroup::getGroupDirs("Music", host);
702  if (dirs.count() > 0)
703  m_musicStorageDir = StorageGroup::getGroupDirs("Music", host).at(0);
704 
705 }
706 
708 {
709  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
712  m_defaultArtist = data->Artist();
713  m_defaultAlbum = data->Album();
714  m_defaultGenre = data->Genre();
715  m_defaultYear = data->Year();
716  m_defaultRating = data->Rating();
717 
718  m_haveDefaults = true;
719 }
720 
722 {
723  if (!m_haveDefaults)
724  return;
725 
726  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
727 
729  {
732  }
733  else
734  {
737  }
738 
739  fillWidgets();
740 }
741 
743 {
744  if (!m_haveDefaults)
745  return;
746 
747  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
749 
750  fillWidgets();
751 }
752 
754 {
755  if (!m_haveDefaults)
756  return;
757 
758  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
759  data->setArtist(m_defaultArtist);
760 
761  m_tracks->at(m_currentTrack)->isNewTune =
762  isNewTune(data->Artist(), data->Album(), data->Title());
763 
764  fillWidgets();
765 }
766 
768 {
769  if (!m_haveDefaults)
770  return;
771 
772  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
773  data->setAlbum(m_defaultAlbum);
774 
775  m_tracks->at(m_currentTrack)->isNewTune =
776  isNewTune(data->Artist(), data->Album(), data->Title());
777 
778  fillWidgets();
779 }
780 
782 {
783  if (!m_haveDefaults)
784  return;
785 
786  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
787  data->setYear(m_defaultYear);
788 
789  fillWidgets();
790 }
791 
793 {
794  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
795  data->setTrack(data->Track() + 100);
796 
797  fillWidgets();
798 }
799 
801 {
802  if (!m_haveDefaults)
803  return;
804 
805  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
806  data->setGenre(m_defaultGenre);
807 
808  fillWidgets();
809 }
810 
812 {
813  if (!m_haveDefaults)
814  return;
815 
816  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
817  data->setRating(m_defaultRating);
818 }
819 
821 {
822  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
823  QString title = data->Title();
824  bool bFoundCap = false;
825 
826  for (int x = 0; x < title.length(); x++)
827  {
828  if (title[x].isLetter())
829  {
830  if (!bFoundCap)
831  {
832  title[x] = title[x].toUpper();
833  bFoundCap = true;
834  }
835  else
836  title[x] = title[x].toLower();
837  }
838  }
839 
840  data->setTitle(title);
841  fillWidgets();
842 }
843 
845 {
846  MusicMetadata *data = m_tracks->at(m_currentTrack)->metadata;
847  QString title = data->Title();
848  bool bInWord = false;
849 
850  for (int x = 0; x < title.length(); x++)
851  {
852  if (title[x].isSpace())
853  bInWord = false;
854  else
855  {
856  if (title[x].isLetter())
857  {
858  if (!bInWord)
859  {
860  title[x] = title[x].toUpper();
861  bInWord = true;
862  }
863  else
864  title[x] = title[x].toLower();
865  }
866  }
867  }
868 
869  data->setTitle(title);
870  fillWidgets();
871 }
872 
874 {
875  if (m_tracks->empty())
876  return;
877 
878  QFileInfo fi(m_sourceFiles.at(m_currentTrack));
879 
881 
882  auto *import = new ImportCoverArtDialog(mainStack,
883  fi.absolutePath(),
884  m_tracks->at(m_currentTrack)->metadata,
886 
887  if (import->Create())
888  mainStack->AddScreen(import);
889  else
890  delete import;
891 }
892 
894 {
895  if (auto dce = dynamic_cast<DialogCompletionEvent*>(event))
896  {
897  if (dce->GetId() == "locationchange")
898  {
899  m_locationEdit->SetText(dce->GetResultText());
900  startScan();
901  }
902  }
903 }
904 
906 
908 {
909  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
910  return true;
911 
912  QStringList actions;
913  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
914 
915  for (int i = 0; i < actions.size() && !handled; i++)
916  {
917  QString action = actions[i];
918  handled = true;
919 
920  if (action == "LEFT")
921  {
922  m_prevButton->Push();
923  }
924  else if (action == "RIGHT")
925  {
926  m_nextButton->Push();
927  }
928  else
929  handled = false;
930  }
931 
932  if (!handled && MythScreenType::keyPressEvent(event))
933  handled = true;
934 
935  return handled;
936 }
937 
939 {
940  if (!LoadWindowFromXML("music-ui.xml", "import_coverart", this))
941  return false;
942 
943  bool err = false;
944  UIUtilE::Assign(this, m_filenameText, "file", &err);
945  UIUtilE::Assign(this, m_currentText, "position", &err);
946  UIUtilE::Assign(this, m_statusText, "status", &err);
947  UIUtilE::Assign(this, m_destinationText, "destination", &err);
948  UIUtilE::Assign(this, m_coverartImage, "coverart", &err);
949  UIUtilE::Assign(this, m_copyButton, "copy", &err);
950  UIUtilE::Assign(this, m_exitButton, "exit", &err);
951  UIUtilE::Assign(this, m_prevButton, "prev", &err);
952  UIUtilE::Assign(this, m_nextButton, "next", &err);
953  UIUtilE::Assign(this, m_typeList, "type", &err);
954 
955  if (err)
956  {
957  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'import_coverart'");
958  return false;
959  }
960 
961  if (m_typeList)
962  {
963  new MythUIButtonListItem(m_typeList, tr("Front Cover"),
964  qVariantFromValue((int)IT_FRONTCOVER));
965  new MythUIButtonListItem(m_typeList, tr("Back Cover"),
966  qVariantFromValue((int)IT_BACKCOVER));
967  new MythUIButtonListItem(m_typeList, tr("CD"),
968  qVariantFromValue((int)IT_CD));
969  new MythUIButtonListItem(m_typeList, tr("Inlay"),
970  qVariantFromValue((int)IT_INLAY));
971  new MythUIButtonListItem(m_typeList, tr("<Unknown>"),
972  qVariantFromValue((int)IT_UNKNOWN));
973 
974  connect(m_typeList, SIGNAL(itemSelected(MythUIButtonListItem *)),
975  SLOT(selectorChanged()));
976  }
977 
978  if (m_copyButton)
979  connect(m_copyButton, SIGNAL(Clicked()), this, SLOT(copyPressed()));
980 
981  if (m_exitButton)
982  connect(m_exitButton, SIGNAL(Clicked()), this, SLOT(Close()));
983 
984  if (m_prevButton)
985  connect(m_prevButton, SIGNAL(Clicked()), this, SLOT(prevPressed()));
986 
987  if (m_nextButton)
988  connect(m_nextButton, SIGNAL(Clicked()), this, SLOT(nextPressed()));
989 
990  BuildFocusList();
991 
992  scanDirectory();
993 
994  return true;
995 }
996 
998 {
999  updateStatus();
1000 }
1001 
1003 {
1004  if (!m_filelist.empty())
1005  {
1007  {
1008  //: %1 is the filename
1009  ShowOkPopup(tr("Copy CoverArt Failed.\nCopying to %1").arg(m_saveFilename));
1010  return;
1011  }
1012 
1013  // add the file to the database
1014  QString filename = m_saveFilename.section( '/', -1, -1);
1015  AlbumArtImage image;
1016  image.m_description = "";
1017  image.m_embedded = false;
1018  image.m_filename = filename;
1019  image.m_hostname = m_metadata->Hostname();
1020  image.m_imageType = (ImageType)m_typeList->GetItemCurrent()->GetData().toInt();
1021 
1024 
1025  updateStatus();
1026  }
1027 }
1028 
1030 {
1031  if (m_currentFile > 0)
1032  {
1033  m_currentFile--;
1035  updateStatus();
1036  }
1037 }
1038 
1040 {
1041  if (m_currentFile < m_filelist.size() - 1)
1042  {
1043  m_currentFile++;
1045  updateStatus();
1046  }
1047 }
1048 
1050 {
1051  QDir d(m_sourceDir);
1052 
1053  if (!d.exists())
1054  return;
1055 
1056  QString nameFilter = gCoreContext->GetSetting("AlbumArtFilter",
1057  "*.png;*.jpg;*.jpeg;*.gif;*.bmp");
1058 
1059  QFileInfoList list = d.entryInfoList(nameFilter.split(";"),
1060  QDir::Files | QDir::Dirs |
1061  QDir::NoDotAndDotDot);
1062  if (list.isEmpty())
1063  return;
1064 
1065  QFileInfoList::const_iterator it = list.begin();
1066  while (it != list.end())
1067  {
1068  const QFileInfo *fi = &(*it);
1069  ++it;
1070  QString filename = fi->absoluteFilePath();
1071  if (!fi->isDir())
1072  {
1073  m_filelist.append(filename);
1074  }
1075  }
1076 
1077  m_currentFile = 0;
1079  updateStatus();
1080 }
1081 
1083 {
1084  if (!m_filelist.empty())
1085  {
1086  if (m_currentText)
1087  //: %1 is the current position of the file being copied,
1088  //: %2 is the total number of files
1089  m_currentText->SetText(tr("%1 of %2", "Current file copied")
1090  .arg(m_currentFile + 1)
1091  .arg(m_filelist.size()));
1094  m_coverartImage->Load();
1095 
1096  QString saveFilename = filenameFromMetadata(m_metadata);
1097  QString fullFilename;
1098 
1099  QUrl url(m_musicStorageDir);
1100  fullFilename = MythCoreContext::GenMythURL(url.host(), 0, saveFilename, "Music");
1101  QString dir = fullFilename.section( '/', 0, -2);
1102 
1103  QFileInfo fi(m_filelist[m_currentFile]);
1104  switch (m_typeList->GetItemCurrent()->GetData().toInt())
1105  {
1106  case IT_FRONTCOVER:
1107  saveFilename = "front." + fi.suffix();
1108  break;
1109  case IT_BACKCOVER:
1110  saveFilename = "back." + fi.suffix();
1111  break;
1112  case IT_CD:
1113  saveFilename = "cd." + fi.suffix();
1114  break;
1115  case IT_INLAY:
1116  saveFilename = "inlay." + fi.suffix();
1117  break;
1118  case IT_UNKNOWN:
1119  saveFilename = "unknown." + fi.suffix();
1120  break;
1121  default:
1122  saveFilename = fi.fileName();
1123  }
1124 
1125  m_saveFilename = dir + "/" + saveFilename;
1127 
1128  url.setUrl(m_saveFilename);
1129 
1130  if (!RemoteFile::FindFile(url.path(), "" , "Music").isEmpty())
1131  m_statusText->SetText(tr("File Already Exists"));
1132  else
1133  m_statusText->SetText(tr("New File"));
1134  }
1135  else
1136  {
1137  if (m_currentText)
1138  m_currentText->Reset();
1139  m_statusText->Reset();
1140  m_filenameText->Reset();
1143  }
1144 }
1145 
1147 {
1148  if (m_filelist.empty())
1149  return;
1150 
1151  QString filename = m_filelist[m_currentFile];
1152  QFileInfo fi(filename);
1153  filename = fi.fileName();
1154 
1155  if (filename.contains("front", Qt::CaseInsensitive))
1156  m_typeList->SetValue(tr("Front Cover"));
1157  else if (filename.contains("back", Qt::CaseInsensitive))
1158  m_typeList->SetValue(tr("Back Cover"));
1159  else if (filename.contains("inlay", Qt::CaseInsensitive))
1160  m_typeList->SetValue(tr("Inlay"));
1161  else if (filename.contains("cd", Qt::CaseInsensitive))
1162  m_typeList->SetValue(tr("CD"));
1163  else
1164  m_typeList->SetValue(tr("<Unknown>"));
1165 }
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
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.
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:32
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:135
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)
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
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
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
void metadataChanged(void)
void chooseBackend(void)
static 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
static 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:83
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
unsigned int uint
Definition: compat.h:140
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: importmusic.cpp:48
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
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
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 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)
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)