MythTV  master
cdrip.cpp
Go to the documentation of this file.
1 // Unix C includes
2 #include <sys/types.h>
3 #include <fcntl.h>
4 
5 #include "config.h"
6 
7 // C++ includes
8 #include <chrono>
9 #include <cstdio>
10 #include <cstring>
11 #include <iostream>
12 #include <memory>
13 
14 // Qt includes
15 #include <QApplication>
16 #include <QDir>
17 #include <QEvent>
18 #include <QFile>
19 #include <QKeyEvent>
20 #include <QRegularExpression>
21 #include <QTimer>
22 #include <QUrl>
23 #include <utility>
24 
25 // MythTV includes
26 #include <libmyth/mythcontext.h>
28 #include <libmythbase/lcddevice.h>
29 #include <libmythbase/mythdate.h>
30 #include <libmythbase/mythdb.h>
31 #include <libmythbase/mythdirs.h>
34 #include <libmythbase/remotefile.h>
40 #include <libmythui/mythuibutton.h>
44 #include <libmythui/mythuitext.h>
46 
47 // MythMusic includes
48 #include "cdrip.h"
49 #ifdef HAVE_CDIO
50 #include "cddecoder.h"
51 #endif // HAVE_CDIO
52 #include "editmetadata.h"
53 #include "encoder.h"
54 #include "flacencoder.h"
55 #include "genres.h"
56 #include "lameencoder.h"
57 #include "vorbisencoder.h"
58 
59 #ifdef HAVE_CDIO
60 // libparanoia compatibility
61 #ifndef cdrom_paranoia
62 #define cdrom_paranoia cdrom_paranoia_t
63 #endif // cdrom_paranoia
64 
65 #ifndef CD_FRAMESIZE_RAW
66 # define CD_FRAMESIZE_RAW CDIO_CD_FRAMESIZE_RAW
67 #endif // CD_FRAMESIZE_RAW
68 #endif // HAVE_CDIO
69 
71  (QEvent::Type) QEvent::registerEventType();
73  (QEvent::Type) QEvent::registerEventType();
75  (QEvent::Type) QEvent::registerEventType();
77  (QEvent::Type) QEvent::registerEventType();
79  (QEvent::Type) QEvent::registerEventType();
81  (QEvent::Type) QEvent::registerEventType();
83  (QEvent::Type) QEvent::registerEventType();
85  (QEvent::Type) QEvent::registerEventType();
87  (QEvent::Type) QEvent::registerEventType();
89  (QEvent::Type) QEvent::registerEventType();
90 QEvent::Type RipStatusEvent::kCopyEndEvent =
91  (QEvent::Type) QEvent::registerEventType();
92 QEvent::Type RipStatusEvent::kFinishedEvent =
93  (QEvent::Type) QEvent::registerEventType();
95  (QEvent::Type) QEvent::registerEventType();
96 
98 {
99  RunProlog();
100  m_parent->scanCD();
101  RunEpilog();
102 }
103 
105 
107 {
108  RunProlog();
109  m_parent->ejectCD();
110  RunEpilog();
111 }
112 
114 
115 static long int getSectorCount (QString &cddevice, int tracknum)
116 {
117 #ifdef HAVE_CDIO
118  QByteArray devname = cddevice.toLatin1();
119  cdrom_drive *device = cdda_identify(devname.constData(), 0, nullptr);
120 
121  if (!device)
122  {
123  LOG(VB_GENERAL, LOG_ERR,
124  QString("Error: %1('%2',track=%3) failed at cdda_identify()").
125  arg(__func__, cddevice, QString::number(tracknum)));
126  return -1;
127  }
128 
129  if (cdda_open(device))
130  {
131  LOG(VB_GENERAL, LOG_ERR,
132  QString("Error: %1('%2',track=%3) failed at cdda_open() - cdda not supported").
133  arg(__func__, cddevice, QString::number(tracknum)));
134  cdda_close(device);
135  return -1;
136  }
137 
138  // we only care about audio tracks
139  if (cdda_track_audiop (device, tracknum))
140  {
141  cdda_verbose_set(device, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
142  long int start = cdda_track_firstsector(device, tracknum);
143  long int end = cdda_track_lastsector( device, tracknum);
144  cdda_close(device);
145  return end - start + 1;
146  }
147  LOG(VB_GENERAL, LOG_ERR,
148  QString("Error: cdrip - cdda_track_audiop(%1) returned 0").arg(cddevice));
149 
150  cdda_close(device);
151 #else
152  (void)cddevice; (void)tracknum;
153 #endif // HAVE_CDIO
154  return 0;
155 }
156 
157 #ifdef HAVE_CDIO
158 static void paranoia_cb(long /*status*/, paranoia_cb_mode_t /*mode*/)
159 {
160 }
161 #endif // HAVE_CDIO
162 
164  QVector<RipTrack*> *tracks, int quality) :
165  MThread("CDRipper"),
166  m_parent(parent),
167  m_cdDevice(std::move(device)), m_quality(quality),
168  m_tracks(tracks)
169 {
170 #ifdef WIN32 // libcdio needs the drive letter with no path
171  if (m_cdDevice.endsWith('\\'))
172  m_cdDevice.chop(1);
173 #endif // WIN32
174 
175  QString lastHost = gCoreContext->GetSetting("MythMusicLastRipHost", gCoreContext->GetMasterHostName());
176  QStringList dirs = StorageGroup::getGroupDirs("Music", lastHost);
177  if (dirs.count() > 0)
178  m_musicStorageDir = StorageGroup::getGroupDirs("Music", lastHost).at(0);
179 }
180 
182 {
183  cancel();
184  wait();
185 }
186 
188 {
189  m_quit = true;
190 }
191 
193 {
194  return m_quit;
195 }
196 
198 {
199  RunProlog();
200 
201  if (m_tracks->empty())
202  {
203  RunEpilog();
204  return;
205  }
206 
207  m_totalSectors = 0;
208  m_totalSectorsDone = 0;
209  for (int trackno = 0; trackno < m_tracks->size(); trackno++)
210  {
211  m_totalSectors += getSectorCount(m_cdDevice, trackno + 1);
212  }
213 
214  if (!m_totalSectors)
215  {
216  RunEpilog();
217  return;
218  }
219 
220  MusicMetadata *track = m_tracks->at(0)->metadata;
221  QString tots;
222 
223  if (track->Compilation())
224  {
225  tots = track->CompilationArtist() + " ~ " + track->Album();
226  }
227  else
228  {
229  tots = track->Artist() + " ~ " + track->Album();
230  }
231 
232  QApplication::postEvent(
233  m_parent,
235  QApplication::postEvent(
236  m_parent,
238  QApplication::postEvent(
239  m_parent,
241 
242  QString textstatus;
243  QString encodertype = gCoreContext->GetSetting("EncoderType");
244  bool mp3usevbr = gCoreContext->GetBoolSetting("Mp3UseVBR", false);
245 
246  QApplication::postEvent(m_parent,
248 
249  if (LCD *lcd = LCD::Get())
250  {
251  QString lcd_tots = tr("Importing %1").arg(tots);
252  QList<LCDTextItem> textItems;
253  textItems.append(LCDTextItem(1, ALIGN_CENTERED,
254  lcd_tots, "Generic", false));
255  lcd->switchToGeneric(textItems);
256  }
257 
258  MusicMetadata *titleTrack = nullptr;
259  QString saveDir = GetConfDir() + "/tmp/RipTemp/";
260  QString outfile;
261 
262  std::unique_ptr<Encoder> encoder;
263 
264  for (int trackno = 0; trackno < m_tracks->size(); trackno++)
265  {
266  if (isCancelled())
267  break;
268 
269  QApplication::postEvent(
270  m_parent,
272  QString("Track %1 of %2")
273  .arg(trackno + 1).arg(m_tracks->size())));
274 
275  QApplication::postEvent(
276  m_parent,
278 
279  track = m_tracks->at(trackno)->metadata;
280 
281  if (track)
282  {
283  textstatus = track->Title();
284  QApplication::postEvent(
285  m_parent,
286  new RipStatusEvent(
287  RipStatusEvent::kTrackTextEvent, textstatus));
288  QApplication::postEvent(
289  m_parent,
291  QApplication::postEvent(
292  m_parent,
294 
295  // do we need to start a new file?
296  if (m_tracks->at(trackno)->active)
297  {
298  titleTrack = track;
299  titleTrack->setLength(m_tracks->at(trackno)->length);
300 
301  if (m_quality < 3)
302  {
303  if (encodertype == "mp3")
304  {
305  outfile = QString("track%1.mp3").arg(trackno);
306  encoder = std::make_unique<LameEncoder>(saveDir + outfile, m_quality,
307  titleTrack, mp3usevbr);
308  }
309  else // ogg
310  {
311  outfile = QString("track%1.ogg").arg(trackno);
312  encoder = std::make_unique<VorbisEncoder>(saveDir + outfile, m_quality,
313  titleTrack);
314  }
315  }
316  else
317  {
318  outfile = QString("track%1.flac").arg(trackno);
319  encoder = std::make_unique<FlacEncoder>(saveDir + outfile, m_quality,
320  titleTrack);
321  }
322 
323  if (!encoder->isValid())
324  {
325  QApplication::postEvent(
326  m_parent,
327  new RipStatusEvent(
329  "Encoder failed to open file for writing"));
330  LOG(VB_GENERAL, LOG_ERR, "MythMusic: Encoder failed"
331  " to open file for writing");
332 
333  RunEpilog();
334  return;
335  }
336  }
337 
338  if (!encoder)
339  {
340  // This should never happen.
341  QApplication::postEvent(
342  m_parent,
344  "Failed to create encoder"));
345  LOG(VB_GENERAL, LOG_ERR, "MythMusic: No encoder, failing");
346  RunEpilog();
347  return;
348  }
349  ripTrack(m_cdDevice, encoder.get(), trackno + 1);
350 
351  if (isCancelled())
352  {
353  RunEpilog();
354  return;
355  }
356 
357  if (m_tracks->at(trackno)->active)
358  {
359  QString ext = QFileInfo(outfile).suffix();
360  QString destFile = filenameFromMetadata(titleTrack) + '.' + ext;
361  QUrl url(m_musicStorageDir);
362 
363  // save the metadata to the DB
364  titleTrack->setFilename(destFile);
365  titleTrack->setHostname(url.host());
366  titleTrack->setFileSize((quint64)QFileInfo(outfile).size());
367  titleTrack->dumpToDatabase();
368 
369  // this will delete the encoder which will write the metadata in it's dtor
370  encoder.reset();
371 
372  // copy track to the BE
373  destFile = MythCoreContext::GenMythURL(url.host(), 0, destFile, "Music");
374 
375  QApplication::postEvent(m_parent, new RipStatusEvent(RipStatusEvent::kCopyStartEvent, 0));
376  RemoteFile::CopyFile(saveDir + outfile, destFile, true);
377  QApplication::postEvent(m_parent, new RipStatusEvent(RipStatusEvent::kCopyEndEvent, 0));
378  }
379  }
380  }
381 
382  QString PostRipCDScript = gCoreContext->GetSetting("PostCDRipScript");
383 
384  if (!PostRipCDScript.isEmpty())
385  myth_system(PostRipCDScript);
386 
387  QApplication::postEvent(
389 
390  RunEpilog();
391 }
392 
393 int CDRipperThread::ripTrack(QString &cddevice, Encoder *encoder, int tracknum)
394 {
395 #ifdef HAVE_CDIO
396  QByteArray devname = cddevice.toLatin1();
397  cdrom_drive *device = cdda_identify(devname.constData(), 0, nullptr);
398 
399  if (!device)
400  {
401  LOG(VB_GENERAL, LOG_ERR,
402  QString("cdda_identify failed for device '%1', "
403  "CDRipperThread::ripTrack(tracknum = %2) exiting.")
404  .arg(cddevice).arg(tracknum));
405  return -1;
406  }
407 
408  if (cdda_open(device))
409  {
410  LOG(VB_MEDIA, LOG_INFO,
411  QString("Error: %1('%2',track=%3) failed at cdda_open() - cdda not supported")
412  .arg(__func__, cddevice, QString::number(tracknum)));
413  cdda_close(device);
414  return -1;
415  }
416 
417  cdda_verbose_set(device, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
418  long int start = cdda_track_firstsector(device, tracknum);
419  long int end = cdda_track_lastsector(device, tracknum);
420  LOG(VB_MEDIA, LOG_INFO, QString("%1(%2,track=%3) start=%4 end=%5")
421  .arg(__func__, cddevice).arg(tracknum).arg(start).arg(end));
422 
423  cdrom_paranoia *paranoia = paranoia_init(device);
424  if (gCoreContext->GetSetting("ParanoiaLevel") == "full")
425  {
426  paranoia_modeset(paranoia, PARANOIA_MODE_FULL |
427  PARANOIA_MODE_NEVERSKIP);
428  }
429  else
430  {
431  paranoia_modeset(paranoia, PARANOIA_MODE_OVERLAP);
432  }
433 
434  paranoia_seek(paranoia, start, SEEK_SET);
435 
436  long int curpos = start;
437 
438  QApplication::postEvent(
439  m_parent,
441  m_lastTrackPct = -1;
442  m_lastOverallPct = -1;
443 
444  int every15 = 15;
445  while (curpos < end)
446  {
447  int16_t *buffer = paranoia_read(paranoia, paranoia_cb);
448 
449  if (encoder->addSamples(buffer, CD_FRAMESIZE_RAW))
450  break;
451 
452  curpos++;
453 
454  every15--;
455 
456  if (every15 <= 0)
457  {
458  every15 = 15;
459 
460  // updating the UITypes can be slow - only update if we need to:
461  int newOverallPct = (int) (100.0 / ((double) m_totalSectors /
462  (m_totalSectorsDone + curpos - start)));
463  if (newOverallPct != m_lastOverallPct)
464  {
465  m_lastOverallPct = newOverallPct;
466  QApplication::postEvent(
467  m_parent,
469  newOverallPct));
470  QApplication::postEvent(
471  m_parent,
473  m_totalSectorsDone + curpos - start));
474  }
475 
476  int newTrackPct = (int) (100.0 / ((double) (end - start + 1) / (curpos - start)));
477  if (newTrackPct != m_lastTrackPct)
478  {
479  m_lastTrackPct = newTrackPct;
480  QApplication::postEvent(
481  m_parent,
483  newTrackPct));
484  QApplication::postEvent(
485  m_parent,
487  curpos - start));
488  }
489 
490  if (LCD *lcd = LCD::Get())
491  {
492  float fProgress = (float)(m_totalSectorsDone + (curpos - start))
493  / m_totalSectors;
494  lcd->setGenericProgress(fProgress);
495  }
496  }
497 
498  if (isCancelled())
499  {
500  break;
501  }
502  }
503 
504  m_totalSectorsDone += end - start + 1;
505 
506  paranoia_free(paranoia);
507  cdda_close(device);
508 
509  return (curpos - start + 1) * CD_FRAMESIZE_RAW;
510 #else
511  (void)cddevice; (void)encoder; (void)tracknum;
512  return 0;
513 #endif // HAVE_CDIO
514 }
515 
517 
518 Ripper::Ripper(MythScreenStack *parent, QString device) :
519  MythScreenType(parent, "ripcd"),
520  m_tracks(new QVector<RipTrack*>),
521  m_cdDevice(std::move(device))
522 {
523 #ifndef _WIN32
524  // if the MediaMonitor is running stop it
525  m_mediaMonitorActive = false;
527  if (mon && mon->IsActive())
528  {
529  m_mediaMonitorActive = true;
530  mon->StopMonitoring();
531  }
532 #endif // _WIN32
533 
534  // make sure the directory where we temporarily save the rips is present
535  QDir dir;
536  dir.mkpath(GetConfDir() + "/tmp/RipTemp/");
537 
538  // remove any ripped tracks from the temp rip directory
539  QString command = "rm -f " + GetConfDir() + "/tmp/RipTemp/*";
540  myth_system(command);
541 
542  // get last host and directory we ripped to
543  QString lastHost = gCoreContext->GetSetting("MythMusicLastRipHost", gCoreContext->GetMasterHostName());
544  QStringList dirs = StorageGroup::getGroupDirs("Music", lastHost);
545  if (dirs.count() > 0)
546  m_musicStorageDir = StorageGroup::getGroupDirs("Music", lastHost).at(0);
547 }
548 
550 {
551  // remove any ripped tracks from the temp rip directory
552  QString command = "rm -f " + GetConfDir() + "/tmp/RipTemp/*";
553  myth_system(command);
554 
555  delete m_decoder;
556 
557 #ifndef _WIN32
558  // if the MediaMonitor was active when we started then restart it
560  {
562  if (mon)
563  mon->StartMonitoring();
564  }
565 #endif // _WIN32
566 
568  emit ripFinished();
569 }
570 
571 bool Ripper::Create(void)
572 {
573  if (!LoadWindowFromXML("music-ui.xml", "cdripper", this))
574  return false;
575 
576  m_qualityList = dynamic_cast<MythUIButtonList *>(GetChild("quality"));
577  m_artistEdit = dynamic_cast<MythUITextEdit *>(GetChild("artist"));
578  m_searchArtistButton = dynamic_cast<MythUIButton *>(GetChild("searchartist"));
579  m_albumEdit = dynamic_cast<MythUITextEdit *>(GetChild("album"));
580  m_searchAlbumButton = dynamic_cast<MythUIButton *>(GetChild("searchalbum"));
581  m_genreEdit = dynamic_cast<MythUITextEdit *>(GetChild("genre"));
582  m_yearEdit = dynamic_cast<MythUITextEdit *>(GetChild("year"));
583  m_searchGenreButton = dynamic_cast<MythUIButton *>(GetChild("searchgenre"));
584  m_compilationCheck = dynamic_cast<MythUICheckBox *>(GetChild("compilation"));
585  m_switchTitleArtist = dynamic_cast<MythUIButton *>(GetChild("switch"));
586  m_scanButton = dynamic_cast<MythUIButton *>(GetChild("scan"));
587  m_ripButton = dynamic_cast<MythUIButton *>(GetChild("rip"));
588  m_trackList = dynamic_cast<MythUIButtonList *>(GetChild("tracks"));
589 
590  BuildFocusList();
591 
596  {
597  LOG(VB_GENERAL, LOG_ERR,
598  "Missing theme elements for screen 'cdripper'");
599  return false;
600  }
601 
603  this, qOverload<MythUIButtonListItem *>(&Ripper::toggleTrackActive));
619 
620  // Populate Quality List
621  new MythUIButtonListItem(m_qualityList, tr("Low"), QVariant::fromValue(0));
622  new MythUIButtonListItem(m_qualityList, tr("Medium"), QVariant::fromValue(1));
623  new MythUIButtonListItem(m_qualityList, tr("High"), QVariant::fromValue(2));
624  new MythUIButtonListItem(m_qualityList, tr("Perfect"), QVariant::fromValue(3));
625  m_qualityList->SetValueByData(QVariant::fromValue(
626  gCoreContext->GetNumSetting("DefaultRipQuality", 1)));
627 
628  QTimer::singleShot(500ms, this, &Ripper::startScanCD);
629 
630  return true;
631 }
632 
633 bool Ripper::keyPressEvent(QKeyEvent *event)
634 {
635  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
636  return true;
637 
638  QStringList actions;
639  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
640 
641  for (int i = 0; i < actions.size() && !handled; i++)
642  {
643  QString action = actions[i];
644  handled = true;
645 
646  if (action == "EDIT" || action == "INFO") // INFO purely for historical reasons
648  else if (action == "MENU")
649  ShowMenu();
650  else
651  handled = false;
652  }
653 
654  if (!handled && MythScreenType::keyPressEvent(event))
655  handled = true;
656 
657  return handled;
658 }
659 
661 {
662  if (m_tracks->empty())
663  return;
664 
665  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
666 
667  auto *menu = new MythDialogBox("", popupStack, "ripmusicmenu");
668 
669  if (menu->Create())
670  popupStack->AddScreen(menu);
671  else
672  {
673  delete menu;
674  return;
675  }
676 
677  menu->SetReturnEvent(this, "menu");
678  menu->AddButton(tr("Select Where To Save Tracks"), &Ripper::chooseBackend);
679  menu->AddButton(tr("Edit Track Metadata"),
680  qOverload<>(&Ripper::showEditMetadataDialog));
681 }
682 
684 {
686 }
687 
688 void Ripper::chooseBackend(void) const
689 {
690  QStringList hostList;
691 
692  // get a list of hosts with a directory defined for the 'Music' storage group
693  MSqlQuery query(MSqlQuery::InitCon());
694  QString sql = "SELECT DISTINCT hostname "
695  "FROM storagegroup "
696  "WHERE groupname = 'Music'";
697  if (!query.exec(sql) || !query.isActive())
698  MythDB::DBError("Ripper::chooseBackend get host list", query);
699  else
700  {
701  while(query.next())
702  {
703  hostList.append(query.value(0).toString());
704  }
705  }
706 
707  if (hostList.isEmpty())
708  {
709  LOG(VB_GENERAL, LOG_ERR, "Ripper::chooseBackend: No backends found");
710  return;
711  }
712 
713  QString msg = tr("Select where to save tracks");
714 
715  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
716  auto *searchDlg = new MythUISearchDialog(popupStack, msg, hostList, false, "");
717 
718  if (!searchDlg->Create())
719  {
720  delete searchDlg;
721  return;
722  }
723 
724  connect(searchDlg, &MythUISearchDialog::haveResult, this, &Ripper::setSaveHost);
725 
726  popupStack->AddScreen(searchDlg);
727 }
728 
729 void Ripper::setSaveHost(const QString& host)
730 {
731  gCoreContext->SaveSetting("MythMusicLastRipHost", host);
732 
733  QStringList dirs = StorageGroup::getGroupDirs("Music", host);
734  if (dirs.count() > 0)
735  m_musicStorageDir = StorageGroup::getGroupDirs("Music", host).at(0);
736 }
737 
739 {
740  if (m_scanThread)
741  return;
742 
743  QString message = tr("Scanning CD. Please Wait ...");
744  OpenBusyPopup(message);
745 
746  m_scanThread = new CDScannerThread(this);
747  connect(m_scanThread->qthread(), &QThread::finished, this, &Ripper::ScanFinished);
748  m_scanThread->start();
749 }
750 
752 {
753  delete m_scanThread;
754  m_scanThread = nullptr;
755 
756  m_tracks->clear();
757 
758  if (m_decoder)
759  {
760  bool isCompilation = false;
761 
762  m_artistName.clear();
763  m_albumName.clear();
764  m_genreName.clear();
765  m_year.clear();
766 
767  for (int trackno = 0; trackno < m_decoder->getNumTracks(); trackno++)
768  {
769  auto *ripTrack = new RipTrack;
770 
771  MusicMetadata *metadata = m_decoder->getMetadata(trackno + 1);
772  if (metadata)
773  {
774  ripTrack->metadata = metadata;
775  ripTrack->length = metadata->Length();
776 
777  if (metadata->Compilation())
778  {
779  isCompilation = true;
780  m_artistName = metadata->CompilationArtist();
781  }
782  else if (m_artistName.isEmpty())
783  {
784  m_artistName = metadata->Artist();
785  }
786 
787  if (m_albumName.isEmpty())
788  m_albumName = metadata->Album();
789 
790  if (m_genreName.isEmpty() && !metadata->Genre().isEmpty())
791  m_genreName = metadata->Genre();
792 
793  if (m_year.isEmpty() && metadata->Year() > 0)
794  m_year = QString::number(metadata->Year());
795 
796  QString title = metadata->Title();
797  ripTrack->isNew = isNewTune(m_artistName, m_albumName, title);
798 
799  ripTrack->active = ripTrack->isNew;
800 
801  m_tracks->push_back(ripTrack);
802 
803  }
804  else
805  delete ripTrack;
806  }
807 
812  m_compilationCheck->SetCheckState(isCompilation);
813 
814  if (!isCompilation)
816  else
818  }
819 
820  BuildFocusList();
821  updateTrackList();
822 
823  CloseBusyPopup();
824 }
825 
826 void Ripper::scanCD(void)
827 {
828 #ifdef HAVE_CDIO
829  {
830  LOG(VB_MEDIA, LOG_INFO, QString("Ripper::%1 CD='%2'").
831  arg(__func__, m_cdDevice));
832  (void)cdio_close_tray(m_cdDevice.toLatin1().constData(), nullptr);
833  }
834 #endif // HAVE_CDIO
835 
836  delete m_decoder;
837  m_decoder = new CdDecoder("cda", nullptr, nullptr);
838  if (m_decoder)
840 }
841 
843 {
844  // NOLINTNEXTLINE(readability-qualified-auto) // qt6
845  for (auto it = m_tracks->begin(); it < m_tracks->end(); ++it)
846  {
847  RipTrack *track = (*it);
848  if (track && !track->isNew)
849  {
850  if (deleteExistingTrack(track))
851  {
852  track->isNew = true;
853  toggleTrackActive(track);
854  }
855  }
856  }
857 }
858 
860 {
861  if (!track)
862  return false;
863 
864  MusicMetadata *metadata = track->metadata;
865 
866  if (!metadata)
867  return false;
868 
869  QString artist = metadata->Artist();
870  QString album = metadata->Album();
871  QString title = metadata->Title();
872 
873  MSqlQuery query(MSqlQuery::InitCon());
874  QString queryString("SELECT song_id, "
875  "CONCAT_WS('/', music_directories.path, music_songs.filename) AS filename "
876  "FROM music_songs "
877  "LEFT JOIN music_artists"
878  " ON music_songs.artist_id=music_artists.artist_id "
879  "LEFT JOIN music_albums"
880  " ON music_songs.album_id=music_albums.album_id "
881  "LEFT JOIN music_directories "
882  " ON music_songs.directory_id=music_directories.directory_id "
883  "WHERE artist_name REGEXP \'");
884  QString token = artist;
885  static const QRegularExpression punctuation
886  { R"((/|\\|:|'|\,|\!|\(|\)|"|\?|\|))" };
887  token.replace(punctuation, QString("."));
888  queryString += token + "\' AND " + "album_name REGEXP \'";
889  token = album;
890  token.replace(punctuation, QString("."));
891  queryString += token + "\' AND " + "name REGEXP \'";
892  token = title;
893  token.replace(punctuation, QString("."));
894  queryString += token + "\' ORDER BY artist_name, album_name,"
895  " name, song_id, filename LIMIT 1";
896  query.prepare(queryString);
897 
898  if (!query.exec() || !query.isActive())
899  {
900  MythDB::DBError("Search music database", query);
901  return false;
902  }
903 
904  if (query.next())
905  {
906  int trackID = query.value(0).toInt();
907  QString filename = query.value(1).toString();
908  QUrl url(m_musicStorageDir);
909  filename = MythCoreContext::GenMythURL(url.host(), 0, filename, "Music");
910 
911  // delete file
912  // FIXME: RemoteFile::DeleteFile will only work with files on the master BE
914  {
915  LOG(VB_GENERAL, LOG_NOTICE, QString("Ripper::deleteExistingTrack() "
916  "Could not delete %1")
917  .arg(filename));
918  return false;
919  }
920 
921  // remove database entry
922  MSqlQuery deleteQuery(MSqlQuery::InitCon());
923  deleteQuery.prepare("DELETE FROM music_songs"
924  " WHERE song_id = :SONG_ID");
925  deleteQuery.bindValue(":SONG_ID", trackID);
926  if (!deleteQuery.exec())
927  {
928  MythDB::DBError("Delete Track", deleteQuery);
929  return false;
930  }
931  return true;
932  }
933 
934  return false;
935 }
936 
938 {
939  return m_somethingwasripped;
940 }
941 
943 {
944  QString newartist = m_artistEdit->GetText();
945 
946  if (!m_tracks->empty())
947  {
948  for (const auto *track : qAsConst(*m_tracks))
949  {
950  MusicMetadata *data = track->metadata;
951  if (data)
952  {
954  {
955  data->setCompilationArtist(newartist);
956  }
957  else
958  {
959  data->setArtist(newartist);
960  data->setCompilationArtist("");
961  }
962  }
963  }
964 
965  updateTrackList();
966  }
967 
968  m_artistName = newartist;
969 }
970 
972 {
973  QString newalbum = m_albumEdit->GetText();
974 
975  if (!m_tracks->empty())
976  {
977  for (const auto *track : qAsConst(*m_tracks))
978  {
979  MusicMetadata *data = track->metadata;
980  if (data)
981  data->setAlbum(newalbum);
982  }
983  }
984 
985  m_albumName = newalbum;
986 }
987 
989 {
990  QString newgenre = m_genreEdit->GetText();
991 
992  if (!m_tracks->empty())
993  {
994  for (const auto *track : qAsConst(*m_tracks))
995  {
996  MusicMetadata *data = track->metadata;
997  if (data)
998  data->setGenre(newgenre);
999  }
1000  }
1001 
1002  m_genreName = newgenre;
1003 }
1004 
1006 {
1007  QString newyear = m_yearEdit->GetText();
1008 
1009  if (!m_tracks->empty())
1010  {
1011  for (const auto *track : qAsConst(*m_tracks))
1012  {
1013  MusicMetadata *data = track->metadata;
1014  if (data)
1015  data->setYear(newyear.toInt());
1016  }
1017  }
1018 
1019  m_year = newyear;
1020 }
1021 
1023 {
1024  if (!state)
1025  {
1026  if (!m_tracks->empty())
1027  {
1028  // Update artist MetaData of each track on the ablum...
1029  for (const auto *track : qAsConst(*m_tracks))
1030  {
1031  MusicMetadata *data = track->metadata;
1032  if (data)
1033  {
1034  data->setCompilationArtist("");
1035  data->setArtist(m_artistName);
1036  data->setCompilation(false);
1037  }
1038  }
1039  }
1040 
1042  }
1043  else
1044  {
1045  if (!m_tracks->empty())
1046  {
1047  // Update artist MetaData of each track on the album...
1048  for (const auto *track : qAsConst(*m_tracks))
1049  {
1050  MusicMetadata *data = track->metadata;
1051 
1052  if (data)
1053  {
1055  data->setCompilation(true);
1056  }
1057  }
1058  }
1059 
1061  }
1062 
1063  BuildFocusList();
1064  updateTrackList();
1065 }
1066 
1068 {
1070  return;
1071 
1072  // Switch title and artist for each track
1073  QString tmp;
1074  if (!m_tracks->empty())
1075  {
1076  for (const auto *track : qAsConst(*m_tracks))
1077  {
1078  MusicMetadata *data = track->metadata;
1079 
1080  if (data)
1081  {
1082  tmp = data->Artist();
1083  data->setArtist(data->Title());
1084  data->setTitle(tmp);
1085  }
1086  }
1087 
1088  updateTrackList();
1089  }
1090 }
1091 
1093 {
1094  if (m_tracks->isEmpty())
1095  {
1096  ShowOkPopup(tr("There are no tracks to rip?"));
1097  return;
1098  }
1099 
1101 
1102  int quality = m_qualityList->GetItemCurrent()->GetData().toInt();
1103 
1104  auto *statusDialog = new RipStatus(mainStack, m_cdDevice, m_tracks, quality);
1105 
1106  if (statusDialog->Create())
1107  {
1108  connect(statusDialog, &RipStatus::Result, this, &Ripper::RipComplete);
1109  mainStack->AddScreen(statusDialog);
1110  }
1111  else
1112  delete statusDialog;
1113 }
1114 
1115 void Ripper::RipComplete(bool result)
1116 {
1117  if (result)
1118  {
1119  bool EjectCD = gCoreContext->GetBoolSetting("EjectCDAfterRipping", true);
1120  if (EjectCD)
1121  startEjectCD();
1122 
1123  ShowOkPopup(tr("Rip completed successfully."));
1124 
1125  m_somethingwasripped = true;
1126  }
1127 
1128  if (LCD *lcd = LCD::Get())
1129  lcd->switchToTime();
1130 }
1131 
1132 
1134 {
1135  if (m_ejectThread)
1136  return;
1137 
1138  QString message = tr("Ejecting CD. Please Wait ...");
1139 
1140  OpenBusyPopup(message);
1141 
1142  m_ejectThread = new CDEjectorThread(this);
1143  connect(m_ejectThread->qthread(),
1144  &QThread::finished, this, &Ripper::EjectFinished);
1145  m_ejectThread->start();
1146 }
1147 
1149 {
1150  delete m_ejectThread;
1151  m_ejectThread = nullptr;
1152 
1153  CloseBusyPopup();
1154 }
1155 
1157 {
1158  LOG(VB_MEDIA, LOG_INFO, __PRETTY_FUNCTION__);
1159  bool bEjectCD = gCoreContext->GetBoolSetting("EjectCDAfterRipping",true);
1160  if (bEjectCD)
1161  {
1162 #ifdef HAVE_CDIO
1163  LOG(VB_MEDIA, LOG_INFO, QString("Ripper::%1 '%2'").
1164  arg(__func__, m_cdDevice));
1165  (void)cdio_eject_media_drive(m_cdDevice.toLatin1().constData());
1166 #else
1168  if (mon)
1169  {
1170  QByteArray devname = m_cdDevice.toLatin1();
1171  MythMediaDevice *pMedia = mon->GetMedia(devname.constData());
1172  if (pMedia && mon->ValidateAndLock(pMedia))
1173  {
1174  pMedia->eject();
1175  mon->Unlock(pMedia);
1176  }
1177  }
1178 #endif // HAVE_CDIO
1179  }
1180 }
1181 
1183 {
1184  if (m_tracks->isEmpty())
1185  return;
1186 
1187  if (m_trackList)
1188  {
1189  m_trackList->Reset();
1190 
1191  for (int i = 0; i < m_tracks->size(); i++)
1192  {
1193  if (i >= m_tracks->size())
1194  break;
1195 
1196  RipTrack *track = m_tracks->at(i);
1197  MusicMetadata *metadata = track->metadata;
1198 
1199  auto *item = new MythUIButtonListItem(m_trackList,"");
1200 
1201  item->setCheckable(true);
1202 
1203  item->SetData(QVariant::fromValue(track));
1204 
1205  if (track->isNew)
1206  item->DisplayState("new", "yes");
1207  else
1208  item->DisplayState("new", "no");
1209 
1210  if (track->active)
1211  item->setChecked(MythUIButtonListItem::FullChecked);
1212  else
1213  item->setChecked(MythUIButtonListItem::NotChecked);
1214 
1215  item->SetText(QString::number(metadata->Track()), "track");
1216  item->SetText(metadata->Title(), "title");
1217  item->SetText(metadata->Artist(), "artist");
1218 
1219  if (track->length >= 1s)
1220  {
1221  item->SetText(MythDate::formatTime(track->length, "mm:ss"), "length");
1222  }
1223  else
1224  item->SetText("", "length");
1225 
1226 // if (i == m_currentTrack)
1227 // m_trackList->SetItemCurrent(i);
1228  }
1229  }
1230 }
1231 
1233 {
1234  QString msg = tr("Select an Artist");
1235  QStringList searchList = MusicMetadata::fillFieldList("artist");
1236 
1237  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1238  auto *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, "");
1239 
1240  if (!searchDlg->Create())
1241  {
1242  delete searchDlg;
1243  return;
1244  }
1245 
1246  connect(searchDlg, &MythUISearchDialog::haveResult, this, &Ripper::setArtist);
1247 
1248  popupStack->AddScreen(searchDlg);
1249 }
1250 
1251 void Ripper::setArtist(const QString& artist)
1252 {
1253  m_artistEdit->SetText(artist);
1254 }
1255 
1257 {
1258  QString msg = tr("Select an Album");
1259  QStringList searchList = MusicMetadata::fillFieldList("album");
1260 
1261  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1262  auto *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, "");
1263 
1264  if (!searchDlg->Create())
1265  {
1266  delete searchDlg;
1267  return;
1268  }
1269 
1270  connect(searchDlg, &MythUISearchDialog::haveResult, this, &Ripper::setAlbum);
1271 
1272  popupStack->AddScreen(searchDlg);
1273 }
1274 
1275 void Ripper::setAlbum(const QString& album)
1276 {
1277  m_albumEdit->SetText(album);
1278 }
1279 
1281 {
1282  QString msg = tr("Select a Genre");
1283  QStringList searchList = MusicMetadata::fillFieldList("genre");
1284  // load genre list
1285  m_searchList.clear();
1286  for (const auto & genre : genre_table)
1287  m_searchList.push_back(QString::fromStdString(genre));
1288  m_searchList.sort();
1289 
1290  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1291  auto *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, "");
1292 
1293  if (!searchDlg->Create())
1294  {
1295  delete searchDlg;
1296  return;
1297  }
1298 
1299  connect(searchDlg, &MythUISearchDialog::haveResult, this, &Ripper::setGenre);
1300 
1301  popupStack->AddScreen(searchDlg);
1302 }
1303 
1304 void Ripper::setGenre(const QString& genre)
1305 {
1306  m_genreEdit->SetText(genre);
1307 }
1308 
1310 {
1311  if (!item || m_tracks->isEmpty())
1312  return;
1313 
1314  auto *track = item->GetData().value<RipTrack *>();
1315 
1316  if (!track)
1317  return;
1318 
1319  MusicMetadata *editMeta = track->metadata;
1320 
1322 
1323  auto *editDialog = new EditMetadataDialog(mainStack, editMeta);
1324  editDialog->setSaveMetadataOnly();
1325 
1326  if (!editDialog->Create())
1327  {
1328  delete editDialog;
1329  return;
1330  }
1331 
1332  connect(editDialog, &EditMetadataCommon::metadataChanged, this, &Ripper::metadataChanged);
1333 
1334  mainStack->AddScreen(editDialog);
1335 }
1336 
1338 {
1339  updateTrackList();
1340 }
1341 
1343 {
1344  QVariant data = QVariant::fromValue(track);
1346  if (item)
1347  {
1348  toggleTrackActive(item);
1349  }
1350 }
1351 
1353 {
1354  if (m_tracks->isEmpty() || !item)
1355  return;
1356 
1357  int pos = m_trackList->GetItemPos(item);
1358 
1359  // sanity check item position
1360  if (pos < 0 || pos > m_tracks->count() - 1)
1361  return;
1362 
1363  RipTrack *track = m_tracks->at(pos);
1364 
1365  if (!track->active && !track->isNew)
1366  {
1367  ShowConflictMenu(track);
1368  return;
1369  }
1370 
1371  track->active = !track->active;
1372 
1373  if (track->active)
1375  else
1377 
1379 }
1380 
1382 {
1383  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1384 
1385  QString msg = tr("This track has been disabled because it is already "
1386  "present in the database.\n"
1387  "Do you want to permanently delete the existing "
1388  "file(s)?");
1389  auto *menu = new MythDialogBox(msg, popupStack, "conflictmenu", true);
1390 
1391  if (menu->Create())
1392  popupStack->AddScreen(menu);
1393  else
1394  {
1395  delete menu;
1396  return;
1397  }
1398 
1399  menu->SetReturnEvent(this, "conflictmenu");
1400  menu->AddButton(tr("No, Cancel"));
1401  menu->AddButtonV(tr("Yes, Delete"), QVariant::fromValue(track));
1402  menu->AddButton(tr("Yes, Delete All"));
1403 }
1404 
1406 {
1407  std::chrono::milliseconds length = 0ms;
1408 
1409  // NOLINTNEXTLINE(readability-qualified-auto) // qt6
1410  for (auto it = m_tracks->end() - 1; it == m_tracks->begin(); --it)
1411  {
1412  RipTrack *track = *it;
1413  if (track->active)
1414  {
1415  track->length = length + track->metadata->Length();
1416  length = 0ms;
1417  }
1418  else
1419  {
1420  track->length = 0ms;
1421  length += track->metadata->Length();
1422  }
1423  }
1424 }
1425 
1426 void Ripper::customEvent(QEvent* event)
1427 {
1428  if (event->type() == DialogCompletionEvent::kEventType)
1429  {
1430  auto *dce = dynamic_cast<DialogCompletionEvent *>(event);
1431  if (dce == nullptr)
1432  return;
1433  if (dce->GetId() == "conflictmenu")
1434  {
1435  int buttonNum = dce->GetResult();
1436  auto *track = dce->GetData().value<RipTrack *>();
1437 
1438  switch (buttonNum)
1439  {
1440  case 0:
1441  // Do nothing
1442  break;
1443  case 1:
1444  if (deleteExistingTrack(track))
1445  {
1446  track->isNew = true;
1447  toggleTrackActive(track);
1448  }
1449  break;
1450  case 2:
1452  break;
1453  default:
1454  break;
1455  }
1456  }
1457 
1458  return;
1459  }
1460 
1461  MythUIType::customEvent(event);
1462 }
1463 
1464 
1466 
1468 {
1469  delete m_ripperThread;
1470  if (LCD *lcd = LCD::Get())
1471  lcd->switchToTime();
1472 }
1473 
1475 {
1476  if (!LoadWindowFromXML("music-ui.xml", "ripstatus", this))
1477  return false;
1478 
1479  m_overallText = dynamic_cast<MythUIText *>(GetChild("overall"));
1480  m_trackText = dynamic_cast<MythUIText *>(GetChild("track"));
1481  m_statusText = dynamic_cast<MythUIText *>(GetChild("status"));
1482  m_trackPctText = dynamic_cast<MythUIText *>(GetChild("trackpct"));
1483  m_overallPctText = dynamic_cast<MythUIText *>(GetChild("overallpct"));
1484 
1485  m_overallProgress = dynamic_cast<MythUIProgressBar *>(GetChild("overall_progress"));
1486  m_trackProgress = dynamic_cast<MythUIProgressBar *>(GetChild("track_progress"));
1487 
1488  BuildFocusList();
1489 
1490  startRip();
1491 
1492  return true;
1493 }
1494 
1495 bool RipStatus::keyPressEvent(QKeyEvent *event)
1496 {
1497  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
1498  return true;
1499 
1500  QStringList actions;
1501  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
1502 
1503  for (int i = 0; i < actions.size() && !handled; i++)
1504  {
1505  QString action = actions[i];
1506  handled = true;
1507 
1508 
1509  if (action == "ESCAPE" &&
1511  {
1512  MythConfirmationDialog *dialog =
1513  ShowOkPopup(tr("Cancel ripping the CD?"), true);
1514  if (dialog)
1515  dialog->SetReturnEvent(this, "stop_ripping");
1516  }
1517  else
1518  handled = false;
1519  }
1520 
1521  if (!handled && MythScreenType::keyPressEvent(event))
1522  handled = true;
1523 
1524  return handled;
1525 }
1526 
1527 void RipStatus::customEvent(QEvent *event)
1528 {
1529  if (event->type() == DialogCompletionEvent::kEventType)
1530  {
1531  auto *dce = dynamic_cast<DialogCompletionEvent *>(event);
1532  if (dce == nullptr)
1533  return;
1534  if ((dce->GetId() == "stop_ripping") && (dce->GetResult() != 0))
1535  {
1537  m_ripperThread->wait();
1538  Close();
1539  }
1540 
1541  return;
1542  }
1543 
1544  auto *rse = dynamic_cast<RipStatusEvent *> (event);
1545  if (!rse)
1546  return;
1547 
1548  if (event->type() == RipStatusEvent::kTrackTextEvent)
1549  {
1550  if (m_trackText)
1551  m_trackText->SetText(rse->m_text);
1552  }
1553  else if (event->type() == RipStatusEvent::kOverallTextEvent)
1554  {
1555  if (m_overallText)
1556  m_overallText->SetText(rse->m_text);
1557  }
1558  else if (event->type() == RipStatusEvent::kStatusTextEvent)
1559  {
1560  if (m_statusText)
1561  m_statusText->SetText(rse->m_text);
1562  }
1563  else if (event->type() == RipStatusEvent::kTrackProgressEvent)
1564  {
1565  if (m_trackProgress)
1566  m_trackProgress->SetUsed(rse->m_value);
1567  }
1568  else if (event->type() == RipStatusEvent::kTrackPercentEvent)
1569  {
1570  if (m_trackPctText)
1571  m_trackPctText->SetText(QString("%1%").arg(rse->m_value));
1572  }
1573  else if (event->type() == RipStatusEvent::kTrackStartEvent)
1574  {
1575  if (m_trackProgress)
1576  m_trackProgress->SetTotal(rse->m_value);
1577  }
1578  else if (event->type() == RipStatusEvent::kCopyStartEvent)
1579  {
1580  if (m_trackPctText)
1581  m_trackPctText->SetText(tr("Copying Track ..."));
1582  }
1583  else if (event->type() == RipStatusEvent::kCopyEndEvent)
1584  {
1585  if (m_trackPctText)
1586  m_trackPctText->SetText("");
1587  }
1588  else if (event->type() == RipStatusEvent::kOverallProgressEvent)
1589  {
1590  if (m_overallProgress)
1591  m_overallProgress->SetUsed(rse->m_value);
1592  }
1593  else if (event->type() == RipStatusEvent::kOverallStartEvent)
1594  {
1595  if (m_overallProgress)
1596  m_overallProgress->SetTotal(rse->m_value);
1597  }
1598  else if (event->type() == RipStatusEvent::kOverallPercentEvent)
1599  {
1600  if (m_overallPctText)
1601  m_overallPctText->SetText(QString("%1%").arg(rse->m_value));
1602  }
1603  else if (event->type() == RipStatusEvent::kFinishedEvent)
1604  {
1605  emit Result(true);
1606  Close();
1607  }
1608  else if (event->type() == RipStatusEvent::kEncoderErrorEvent)
1609  {
1610  ShowOkPopup(tr("The encoder failed to create the file.\n"
1611  "Do you have write permissions"
1612  " for the music directory?"));
1613  Close();
1614  }
1615  else
1616  {
1617  LOG(VB_GENERAL, LOG_ERR, "Received an unknown event type!");
1618  }
1619 }
1620 
1622 {
1623  delete m_ripperThread;
1625  m_ripperThread->start();
1626 }
CdDecoder::getMetadata
MusicMetadata * getMetadata(void)
Definition: cddecoder.cpp:508
Ripper::EjectFinished
void EjectFinished(void)
Definition: cdrip.cpp:1148
MythUIButton::Clicked
void Clicked()
FilterAlpha
@ FilterAlpha
Definition: mythuitextedit.h:22
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:216
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:807
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
MythMainWindow::GetMainStack
MythScreenStack * GetMainStack()
Definition: mythmainwindow.cpp:315
Ripper::m_artistEdit
MythUITextEdit * m_artistEdit
Definition: cdrip.h:153
MythUITextEdit::SetMaxLength
void SetMaxLength(int length)
Definition: mythuitextedit.cpp:192
MythUISearchDialog
Provide a dialog to quickly find an entry in a list.
Definition: mythdialogbox.h:399
MusicMetadata::Title
QString Title() const
Definition: musicmetadata.h:162
Ripper::m_genreName
QString m_genreName
Definition: cdrip.h:174
MythUIButtonList::GetItemCurrent
MythUIButtonListItem * GetItemCurrent() const
Definition: mythuibuttonlist.cpp:1587
RipTrack::metadata
MusicMetadata * metadata
Definition: cdrip.h:52
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
Ripper::m_somethingwasripped
bool m_somethingwasripped
Definition: cdrip.h:177
MythCoreContext::GetMasterHostName
QString GetMasterHostName(void)
Definition: mythcorecontext.cpp:805
RipStatusEvent::kTrackPercentEvent
static Type kTrackPercentEvent
Definition: cdrip.h:203
CDEjectorThread::m_parent
Ripper * m_parent
Definition: cdrip.h:47
MythUIButtonList::SetValueByData
void SetValueByData(const QVariant &data)
Definition: mythuibuttonlist.cpp:1539
mythuitext.h
mythuiprogressbar.h
Ripper::m_cdDevice
QString m_cdDevice
Definition: cdrip.h:180
CDRipperThread::run
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: cdrip.cpp:197
CDEjectorThread::run
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: cdrip.cpp:106
Ripper::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: cdrip.cpp:633
MusicMetadata::Genre
QString Genre() const
Definition: musicmetadata.h:175
Ripper::m_qualityList
MythUIButtonList * m_qualityList
Definition: cdrip.h:161
editmetadata.h
CDEjectorThread
Definition: cdrip.h:39
Ripper::m_year
QString m_year
Definition: cdrip.h:175
Ripper::m_tracks
QVector< RipTrack * > * m_tracks
Definition: cdrip.h:170
RipStatus::Create
bool Create(void) override
Definition: cdrip.cpp:1474
RipTrack::length
std::chrono::milliseconds length
Definition: cdrip.h:54
mythdb.h
Ripper::m_searchGenreButton
MythUIButton * m_searchGenreButton
Definition: cdrip.h:168
CDRipperThread::isCancelled
bool isCancelled(void) const
Definition: cdrip.cpp:192
Ripper::setGenre
void setGenre(const QString &genre)
Definition: cdrip.cpp:1304
Ripper::m_searchArtistButton
MythUIButton * m_searchArtistButton
Definition: cdrip.h:166
Ripper::deleteExistingTrack
bool deleteExistingTrack(RipTrack *track)
Definition: cdrip.cpp:859
CDScannerThread::m_parent
Ripper * m_parent
Definition: cdrip.h:36
MythScreenType::Close
virtual void Close()
Definition: mythscreentype.cpp:386
MediaMonitor::GetMediaMonitor
static MediaMonitor * GetMediaMonitor(void)
Definition: mythmediamonitor.cpp:75
RipStatusEvent::kCopyEndEvent
static Type kCopyEndEvent
Definition: cdrip.h:209
RipStatus::m_trackText
MythUIText * m_trackText
Definition: cdrip.h:241
MusicMetadata::CompilationArtist
QString CompilationArtist() const
Definition: musicmetadata.h:138
MusicMetadata::fillFieldList
static QStringList fillFieldList(const QString &field)
Definition: musicmetadata.cpp:1213
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
CDRipperThread::m_lastOverallPct
int m_lastOverallPct
Definition: cdrip.h:89
getSectorCount
static long int getSectorCount(QString &cddevice, int tracknum)
Definition: cdrip.cpp:115
MythUIType::GetChild
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:133
mythscreenstack.h
MythConfirmationDialog::SetReturnEvent
void SetReturnEvent(QObject *retobject, const QString &resultid)
Definition: mythdialogbox.cpp:524
RipStatusEvent::kOverallTextEvent
static Type kOverallTextEvent
Definition: cdrip.h:200
flacencoder.h
MythUISearchDialog::haveResult
void haveResult(QString)
DialogCompletionEvent::kEventType
static Type kEventType
Definition: mythdialogbox.h:57
Ripper::ejectCD
void ejectCD(void)
Definition: cdrip.cpp:1156
Ripper::yearChanged
void yearChanged(void)
Definition: cdrip.cpp:1005
MediaMonitor::StartMonitoring
virtual void StartMonitoring(void)
Start the monitoring thread if needed.
Definition: mythmediamonitor.cpp:446
Ripper::toggleTrackActive
void toggleTrackActive(MythUIButtonListItem *item)
Definition: cdrip.cpp:1352
MythUICheckBox::GetBooleanCheckState
bool GetBooleanCheckState(void) const
Definition: mythuicheckbox.cpp:103
RipStatus
Definition: cdrip.h:214
CDRipperThread::m_cdDevice
QString m_cdDevice
Definition: cdrip.h:81
Ripper::scanCD
void scanCD(void)
Definition: cdrip.cpp:826
MythUIProgressBar::SetUsed
void SetUsed(int value)
Definition: mythuiprogressbar.cpp:69
genre_table
const genre_table_array genre_table
Definition: genres.cpp:4
mythdialogbox.h
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:205
MythScreenStack
Definition: mythscreenstack.h:16
RipStatus::m_trackProgress
MythUIProgressBar * m_trackProgress
Definition: cdrip.h:246
mythmediamonitor.h
MythUIButtonListItem::FullChecked
@ FullChecked
Definition: mythuibuttonlist.h:48
MythDate::formatTime
QString formatTime(std::chrono::milliseconds msecs, QString fmt)
Format a milliseconds time value.
Definition: mythdate.cpp:233
Ripper::deleteAllExistingTracks
void deleteAllExistingTracks(void)
Definition: cdrip.cpp:842
vorbisencoder.h
MythUIType::customEvent
void customEvent(QEvent *event) override
Definition: mythuitype.cpp:1006
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:608
Ripper::compilationChanged
void compilationChanged(bool state)
Definition: cdrip.cpp:1022
MythUITextEdit
A text entry and edit widget.
Definition: mythuitextedit.h:34
RipStatus::m_ripperThread
CDRipperThread * m_ripperThread
Definition: cdrip.h:248
MusicMetadata::setAlbum
void setAlbum(const QString &lalbum, const QString &lalbum_sort=nullptr)
Definition: musicmetadata.h:152
MusicMetadata::Track
int Track() const
Definition: musicmetadata.h:199
Encoder
Definition: mythplugins/mythmusic/mythmusic/encoder.h:13
Ripper::m_decoder
CdDecoder * m_decoder
Definition: cdrip.h:151
genres.h
MythScreenType::OpenBusyPopup
void OpenBusyPopup(const QString &message="")
Definition: mythscreentype.cpp:320
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MThread::RunProlog
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
MythScreenType
Screen in which all other widgets are contained and rendered.
Definition: mythscreentype.h:45
InputFilter
InputFilter
Definition: mythuitextedit.h:19
RipStatus::m_trackPctText
MythUIText * m_trackPctText
Definition: cdrip.h:244
Ripper::m_scanButton
MythUIButton * m_scanButton
Definition: cdrip.h:164
MusicMetadata
Definition: musicmetadata.h:80
MediaMonitor::GetMedia
MythMediaDevice * GetMedia(const QString &path)
Get media device by pathname. Must be locked with ValidateAndLock().
Definition: mythmediamonitor.cpp:532
CDRipperThread::m_musicStorageDir
QString m_musicStorageDir
Definition: cdrip.h:91
MusicMetadata::Length
std::chrono::milliseconds Length() const
Definition: musicmetadata.h:205
MythUITextEdit::GetText
QString GetText(void) const
Definition: mythuitextedit.h:50
mythdirs.h
CDRipperThread::CDRipperThread
CDRipperThread(RipStatus *parent, QString device, QVector< RipTrack * > *tracks, int quality)
Definition: cdrip.cpp:163
myth_system
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
Definition: mythsystemlegacy.cpp:506
RipStatus::m_overallText
MythUIText * m_overallText
Definition: cdrip.h:240
MusicMetadata::Artist
QString Artist() const
Definition: musicmetadata.h:126
mythuibuttonlist.h
Ripper::chooseBackend
void chooseBackend(void) const
Definition: cdrip.cpp:688
Ripper::m_trackList
MythUIButtonList * m_trackList
Definition: cdrip.h:160
LCD::Get
static LCD * Get(void)
Definition: lcddevice.cpp:69
RipStatusEvent::kCopyStartEvent
static Type kCopyStartEvent
Definition: cdrip.h:208
musicutils.h
mythprogressdialog.h
Ripper::ripFinished
void ripFinished(void)
MythUIButtonList::GetItemByData
MythUIButtonListItem * GetItemByData(const QVariant &data)
Definition: mythuibuttonlist.cpp:1681
RipStatus::~RipStatus
~RipStatus(void) override
Definition: cdrip.cpp:1467
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
MythScreenType::GetFocusWidget
MythUIType * GetFocusWidget(void) const
Definition: mythscreentype.cpp:113
mythsystemlegacy.h
RipStatus::startRip
void startRip(void)
Definition: cdrip.cpp:1621
Ripper::m_mediaMonitorActive
bool m_mediaMonitorActive
Definition: cdrip.h:178
MythUICheckBox::toggled
void toggled(bool)
cddecoder.h
isNewTune
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:126
MythUIButtonList::GetItemPos
int GetItemPos(MythUIButtonListItem *item) const
Definition: mythuibuttonlist.cpp:1695
Ripper::m_searchList
QStringList m_searchList
Definition: cdrip.h:176
MythUIButtonListItem
Definition: mythuibuttonlist.h:41
RipStatusEvent::kTrackTextEvent
static Type kTrackTextEvent
Definition: cdrip.h:199
MythUITextEdit::SetText
void SetText(const QString &text, bool moveCursor=true)
Definition: mythuitextedit.cpp:197
mythdate.h
CDRipperThread::m_totalSectors
long int m_totalSectors
Definition: cdrip.h:85
MythUIProgressBar::SetTotal
void SetTotal(int value)
Definition: mythuiprogressbar.cpp:81
mythlogging.h
Ripper::searchArtist
void searchArtist(void) const
Definition: cdrip.cpp:1232
MusicMetadata::setHostname
void setHostname(const QString &host)
Definition: musicmetadata.h:231
MythCoreContext::GenMythURL
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
Definition: mythcorecontext.cpp:758
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:224
CdDecoder
Definition: cddecoder.h:20
Ripper::genreChanged
void genreChanged(void)
Definition: cdrip.cpp:988
MythUIButtonList::itemClicked
void itemClicked(MythUIButtonListItem *item)
MythMainWindow::TranslateKeyPress
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
Definition: mythmainwindow.cpp:1104
Ripper::startScanCD
void startScanCD(void)
Definition: cdrip.cpp:738
RipStatus::Result
void Result(bool)
remotefile.h
Ripper::m_searchAlbumButton
MythUIButton * m_searchAlbumButton
Definition: cdrip.h:167
MediaMonitor::StopMonitoring
void StopMonitoring(void)
Stop the monitoring thread if needed.
Definition: mythmediamonitor.cpp:469
MythUIProgressBar
Progress bar widget.
Definition: mythuiprogressbar.h:12
MusicMetadata::setLength
void setLength(T llength)
Definition: musicmetadata.h:207
CDRipperThread::m_tracks
QVector< RipTrack * > * m_tracks
Definition: cdrip.h:83
CDRipperThread::m_totalSectorsDone
long int m_totalSectorsDone
Definition: cdrip.h:86
MythDialogBox
Basic menu dialog, message and a list of options.
Definition: mythdialogbox.h:166
cdrip.h
filenameFromMetadata
QString filenameFromMetadata(MusicMetadata *track)
create a filename using the template in the settings and a MusicMetadata object
Definition: musicutils.cpp:78
menu
static MythThemedMenu * menu
Definition: mythtv-setup.cpp:58
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:540
RipStatus::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: cdrip.cpp:1495
Ripper::m_scanThread
CDScannerThread * m_scanThread
Definition: cdrip.h:183
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:227
CDRipperThread::m_parent
RipStatus * m_parent
Definition: cdrip.h:79
FilterPunct
@ FilterPunct
Definition: mythuitextedit.h:26
MusicMetadata::setGenre
void setGenre(const QString &lgenre)
Definition: musicmetadata.h:176
CDScannerThread::run
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: cdrip.cpp:97
MythScreenType::BuildFocusList
void BuildFocusList(void)
Definition: mythscreentype.cpp:206
RipStatusEvent::kEncoderErrorEvent
static Type kEncoderErrorEvent
Definition: cdrip.h:211
CDRipperThread::m_quit
bool m_quit
Definition: cdrip.h:80
MusicMetadata::setTitle
void setTitle(const QString &ltitle, const QString &ltitle_sort=nullptr)
Definition: musicmetadata.h:164
MythUIButton
A single button widget.
Definition: mythuibutton.h:21
MusicMetadata::setFileSize
void setFileSize(uint64_t lfilesize)
Definition: musicmetadata.h:234
EditMetadataDialog
Definition: editvideometadata.h:19
FilterSymbols
@ FilterSymbols
Definition: mythuitextedit.h:25
StorageGroup::getGroupDirs
static QStringList getGroupDirs(const QString &groupname, const QString &host)
Definition: storagegroup.cpp:816
Ripper::Ripper
Ripper(MythScreenStack *parent, QString device)
Definition: cdrip.cpp:518
MThread::qthread
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:233
Ripper::albumChanged
void albumChanged(void)
Definition: cdrip.cpp:971
Ripper::setArtist
void setArtist(const QString &artist)
Definition: cdrip.cpp:1251
Ripper::m_artistName
QString m_artistName
Definition: cdrip.h:173
MediaMonitor::Unlock
void Unlock(MythMediaDevice *pMedia)
decrements the MythMediaDevices reference count
Definition: mythmediamonitor.cpp:509
MThread::RunEpilog
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
ALIGN_CENTERED
@ ALIGN_CENTERED
Definition: lcddevice.h:61
RipStatusEvent::kTrackStartEvent
static Type kTrackStartEvent
Definition: cdrip.h:204
storagegroup.h
MusicMetadata::setFilename
void setFilename(const QString &lfilename)
Definition: musicmetadata.cpp:953
RipTrack::active
bool active
Definition: cdrip.h:53
Ripper::searchGenre
void searchGenre(void)
Definition: cdrip.cpp:1280
RemoteFile::DeleteFile
static bool DeleteFile(const QString &url)
Definition: remotefile.cpp:411
MythUITextEdit::SetFilter
void SetFilter(InputFilter filter)
Definition: mythuitextedit.h:52
MythUIButtonListItem::GetData
QVariant GetData()
Definition: mythuibuttonlist.cpp:3665
Ripper::ShowMenu
void ShowMenu(void) override
Definition: cdrip.cpp:660
RipStatusEvent
Definition: cdrip.h:187
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:54
MythUICheckBox
A checkbox widget supporting three check states - on,off,half and two conditions - selected and unsel...
Definition: mythuicheckbox.h:15
CdDecoder::setDevice
void setDevice(const QString &dev)
Definition: cddecoder.cpp:105
RemoteFile::CopyFile
static bool CopyFile(const QString &src, const QString &dst, bool overwrite=false, bool verify=false)
Definition: remotefile.cpp:580
MediaMonitor
Definition: mythmediamonitor.h:49
Ripper::updateTrackLengths
void updateTrackLengths(void)
Definition: cdrip.cpp:1405
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:910
CDRipperThread::m_lastTrackPct
int m_lastTrackPct
Definition: cdrip.h:88
Ripper::m_musicStorageDir
QString m_musicStorageDir
Definition: cdrip.h:149
CDRipperThread::~CDRipperThread
~CDRipperThread() override
Definition: cdrip.cpp:181
Ripper::m_switchTitleArtist
MythUIButton * m_switchTitleArtist
Definition: cdrip.h:163
MusicMetadata::Album
QString Album() const
Definition: musicmetadata.h:150
MythCoreContext::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval=false)
Definition: mythcorecontext.cpp:904
CdDecoder::getNumTracks
int getNumTracks()
Definition: cddecoder.cpp:455
RipStatus::customEvent
void customEvent(QEvent *event) override
Definition: cdrip.cpp:1527
Ripper::updateTrackList
void updateTrackList(void)
Definition: cdrip.cpp:1182
RipStatus::m_overallPctText
MythUIText * m_overallPctText
Definition: cdrip.h:243
MusicMetadata::setCompilationArtist
void setCompilationArtist(const QString &lcompilation_artist, const QString &lcompilation_artist_sort=nullptr)
Definition: musicmetadata.h:140
CDScannerThread
Definition: cdrip.h:28
RipStatusEvent::kFinishedEvent
static Type kFinishedEvent
Definition: cdrip.h:210
RipStatusEvent::kOverallProgressEvent
static Type kOverallProgressEvent
Definition: cdrip.h:205
MythMediaDevice::eject
virtual MythMediaError eject(bool open_close=true)
Definition: mythmedia.cpp:307
Ripper::Create
bool Create(void) override
Definition: cdrip.cpp:571
RipStatusEvent::kTrackProgressEvent
static Type kTrackProgressEvent
Definition: cdrip.h:202
Ripper::RipComplete
void RipComplete(bool result)
Definition: cdrip.cpp:1115
MusicMetadata::dumpToDatabase
void dumpToDatabase(void)
Definition: musicmetadata.cpp:665
MythUIText
All purpose text widget, displays a text string.
Definition: mythuitext.h:28
MythScreenType::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythscreentype.cpp:404
MusicMetadata::setArtist
void setArtist(const QString &lartist, const QString &lartist_sort=nullptr)
Definition: musicmetadata.h:128
Ripper::customEvent
void customEvent(QEvent *event) override
Definition: cdrip.cpp:1426
Ripper::startEjectCD
void startEjectCD(void)
Definition: cdrip.cpp:1133
encoder.h
RipStatus::m_cdDevice
QString m_cdDevice
Definition: cdrip.h:238
MythConfirmationDialog
Dialog asking for user confirmation. Ok and optional Cancel button.
Definition: mythdialogbox.h:272
mythuitextedit.h
MythUICheckBox::SetCheckState
void SetCheckState(MythUIStateType::StateType state)
Definition: mythuicheckbox.cpp:66
XMLParseBase::LoadWindowFromXML
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
Definition: xmlparsebase.cpp:695
RipStatus::m_overallProgress
MythUIProgressBar * m_overallProgress
Definition: cdrip.h:245
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:883
std
Definition: mythchrono.h:23
RipStatus::m_statusText
MythUIText * m_statusText
Definition: cdrip.h:242
MediaMonitor::IsActive
bool IsActive(void) const
Definition: mythmediamonitor.h:57
DialogCompletionEvent
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
Ripper::m_genreEdit
MythUITextEdit * m_genreEdit
Definition: cdrip.h:155
MusicMetadata::Compilation
bool Compilation() const
Definition: musicmetadata.h:252
Ripper::showEditMetadataDialog
void showEditMetadataDialog(void)
Definition: cdrip.cpp:683
MythUIText::SetText
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:132
Ripper::m_yearEdit
MythUITextEdit * m_yearEdit
Definition: cdrip.h:156
MythUIType::SetVisible
virtual void SetVisible(bool visible)
Definition: mythuitype.cpp:1108
mythcontext.h
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
LCDTextItem
Definition: lcddevice.h:63
lameencoder.h
RipStatus::m_tracks
QVector< RipTrack * > * m_tracks
Definition: cdrip.h:236
MythUIButtonList::Reset
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuibuttonlist.cpp:116
Ripper::ShowConflictMenu
void ShowConflictMenu(RipTrack *track)
Definition: cdrip.cpp:1381
Ripper::searchAlbum
void searchAlbum(void) const
Definition: cdrip.cpp:1256
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:102
MThread::isRunning
bool isRunning(void) const
Definition: mthread.cpp:263
RipStatusEvent::kStatusTextEvent
static Type kStatusTextEvent
Definition: cdrip.h:201
build_compdb.action
action
Definition: build_compdb.py:9
CDRipperThread::m_quality
int m_quality
Definition: cdrip.h:82
CDRipperThread::cancel
void cancel(void)
Definition: cdrip.cpp:187
Ripper::setSaveHost
void setSaveHost(const QString &host)
Definition: cdrip.cpp:729
Ripper::startRipper
void startRipper(void)
Definition: cdrip.cpp:1092
mythuibutton.h
Ripper::m_albumEdit
MythUITextEdit * m_albumEdit
Definition: cdrip.h:154
MediaMonitor::ValidateAndLock
bool ValidateAndLock(MythMediaDevice *pMedia)
Validates the MythMediaDevice and increments its reference count.
Definition: mythmediamonitor.cpp:492
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:320
MusicMetadata::setYear
void setYear(int lyear)
Definition: musicmetadata.h:197
MythUITextEdit::valueChanged
void valueChanged()
Ripper::switchTitlesAndArtists
void switchTitlesAndArtists()
Definition: cdrip.cpp:1067
DialogCompletionEvent::GetResult
int GetResult() const
Definition: mythdialogbox.h:53
MythMediaDevice
Definition: mythmedia.h:48
CDRipperThread::ripTrack
int ripTrack(QString &cddevice, Encoder *encoder, int tracknum)
Definition: cdrip.cpp:393
MythThemedMenuState::Create
bool Create(void) override
Definition: myththemedmenu.cpp:34
Ripper::artistChanged
void artistChanged(void)
Definition: cdrip.cpp:942
MusicMetadata::setCompilation
void setCompilation(bool state)
Definition: musicmetadata.h:253
EditMetadataCommon::metadataChanged
void metadataChanged(void)
Ripper::setAlbum
void setAlbum(const QString &album)
Definition: cdrip.cpp:1275
Ripper::m_ejectThread
CDEjectorThread * m_ejectThread
Definition: cdrip.h:182
MythCoreContext::SaveSetting
void SaveSetting(const QString &key, int newValue)
Definition: mythcorecontext.cpp:879
Ripper::m_ripButton
MythUIButton * m_ripButton
Definition: cdrip.h:165
lcddevice.h
mythuicheckbox.h
MythUIButtonList
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
Definition: mythuibuttonlist.h:191
build_compdb.filename
filename
Definition: build_compdb.py:21
CDRipperThread
Definition: cdrip.h:62
RipTrack
Definition: cdrip.h:50
Ripper::metadataChanged
void metadataChanged(void)
Definition: cdrip.cpp:1337
RipTrack::isNew
bool isNew
Definition: cdrip.h:55
MythUIButtonListItem::setChecked
void setChecked(CheckState state)
Definition: mythuibuttonlist.cpp:3629
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:50
RipStatus::m_quality
int m_quality
Definition: cdrip.h:237
ShowOkPopup
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
Definition: mythdialogbox.cpp:562
RipStatusEvent::kOverallStartEvent
static Type kOverallStartEvent
Definition: cdrip.h:207
LCD
Definition: lcddevice.h:173
MythScreenType::CloseBusyPopup
void CloseBusyPopup(void)
Definition: mythscreentype.cpp:338
MusicMetadata::Year
int Year() const
Definition: musicmetadata.h:196
Ripper::~Ripper
~Ripper(void) override
Definition: cdrip.cpp:549
Ripper::ScanFinished
void ScanFinished(void)
Definition: cdrip.cpp:751
Encoder::addSamples
virtual int addSamples(int16_t *bytes, unsigned int len)=0
RipStatusEvent::kOverallPercentEvent
static Type kOverallPercentEvent
Definition: cdrip.h:206
Ripper::somethingWasRipped
bool somethingWasRipped() const
Definition: cdrip.cpp:937
Ripper::m_albumName
QString m_albumName
Definition: cdrip.h:172
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:896
MythUIButtonListItem::NotChecked
@ NotChecked
Definition: mythuibuttonlist.h:46
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:832
Ripper::m_compilationCheck
MythUICheckBox * m_compilationCheck
Definition: cdrip.h:158