00001
00002 #include <cstdio>
00003 #include <cstring>
00004
00005
00006 #include <unistd.h>
00007 #include <sys/stat.h>
00008 #include <sys/types.h>
00009 #include <fcntl.h>
00010
00011
00012 #include "config.h"
00013 #ifdef HAVE_CDAUDIO
00014 #include <cdaudio.h>
00015 extern "C" {
00016 #include <cdda_interface.h>
00017 #include <cdda_paranoia.h>
00018 }
00019 #endif
00020
00021
00022 #include <iostream>
00023 #include <memory>
00024 using namespace std;
00025
00026
00027 #include <qapplication.h>
00028 #include <qdir.h>
00029 #include <qregexp.h>
00030
00031
00032 #include <mythtv/mythcontext.h>
00033 #include <mythtv/mythdbcon.h>
00034 #include <mythtv/mythwidgets.h>
00035 #include <mythtv/lcddevice.h>
00036 #include <mythtv/dialogbox.h>
00037 #include <mythtv/mythmediamonitor.h>
00038
00039
00040 #include "cdrip.h"
00041 #include "cddecoder.h"
00042 #include "encoder.h"
00043 #include "vorbisencoder.h"
00044 #include "lameencoder.h"
00045 #include "flacencoder.h"
00046 #include "genres.h"
00047 #include "editmetadata.h"
00048
00049
00050 CDScannerThread::CDScannerThread(Ripper *ripper)
00051 {
00052 m_parent = ripper;
00053 }
00054
00055 void CDScannerThread::run()
00056 {
00057 m_parent->scanCD();
00058 }
00059
00061
00062 CDEjectorThread::CDEjectorThread(Ripper *ripper)
00063 {
00064 m_parent = ripper;
00065 }
00066
00067 void CDEjectorThread::run()
00068 {
00069 m_parent->ejectCD();
00070 }
00071
00073
00074 static long int getSectorCount (QString &cddevice, int tracknum)
00075 {
00076 #ifdef HAVE_CDAUDIO
00077 cdrom_drive *device = cdda_identify(cddevice.ascii(), 0, NULL);
00078
00079 if (!device)
00080 return -1;
00081
00082 if (cdda_open(device))
00083 {
00084 cdda_close(device);
00085 return -1;
00086 }
00087
00088
00089 if (cdda_track_audiop (device, tracknum))
00090 {
00091 cdda_verbose_set(device, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
00092 long int start = cdda_track_firstsector(device, tracknum);
00093 long int end = cdda_track_lastsector( device, tracknum);
00094 cdda_close(device);
00095 return end - start + 1;
00096 }
00097
00098 cdda_close(device);
00099 #else
00100 (void)cddevice; (void)tracknum;
00101 #endif
00102 return 0;
00103 }
00104
00105 static void paranoia_cb(long inpos, int function)
00106 {
00107 inpos = inpos; function = function;
00108 }
00109
00110 CDRipperThread::CDRipperThread(RipStatus *parent, QString device,
00111 vector<RipTrack*> *tracks, int quality)
00112 {
00113 m_parent = parent;
00114 m_tracks = tracks;
00115 m_quality = quality;
00116 m_quit = false;
00117 m_totalTracks = m_tracks->size();
00118 m_CDdevice = device;
00119 }
00120
00121 CDRipperThread::~CDRipperThread(void)
00122 {
00123 cancel();
00124 wait();
00125 }
00126
00127 void CDRipperThread::cancel(void)
00128 {
00129 m_quit = true;
00130 }
00131
00132 bool CDRipperThread::isCancelled(void)
00133 {
00134 return m_quit;
00135 }
00136
00137 void CDRipperThread::sendEvent(int eventType, const QString &value)
00138 {
00139 StatusData *sd = new StatusData;
00140 sd->type = eventType;
00141 sd->text = value;
00142 sd->value = 0;
00143 QApplication::postEvent(m_parent, new QCustomEvent(QEvent::User, sd));
00144 }
00145
00146 void CDRipperThread::sendEvent(int eventType, int value)
00147 {
00148 StatusData *sd = new StatusData;
00149 sd->type = eventType;
00150 sd->text = "";
00151 sd->value = value;
00152 QApplication::postEvent(m_parent, new QCustomEvent(QEvent::User, sd));
00153 }
00154
00155 void CDRipperThread::run(void)
00156 {
00157 Metadata *track = m_tracks->at(0)->metadata;
00158 QString tots;
00159
00160 if (track->Compilation())
00161 {
00162 tots = track->CompilationArtist() + " ~ " + track->Album();
00163 }
00164 else
00165 {
00166 tots = track->Artist() + " ~ " + track->Album();
00167 }
00168
00169 sendEvent(ST_OVERALL_TEXT, tots);
00170 sendEvent(ST_OVERALL_PROGRESS, 0);
00171 sendEvent(ST_TRACK_PROGRESS, 0);
00172
00173 QString textstatus;
00174 QString encodertype = gContext->GetSetting("EncoderType");
00175 bool mp3usevbr = gContext->GetNumSetting("Mp3UseVBR", 0);
00176
00177 m_totalSectors = 0;
00178 m_totalSectorsDone = 0;
00179 for (int trackno = 0; trackno < m_totalTracks; trackno++)
00180 {
00181 m_totalSectors += getSectorCount(m_CDdevice, trackno + 1);
00182 }
00183
00184 sendEvent(ST_OVERALL_START, m_totalSectors);
00185
00186 if (class LCD * lcd = LCD::Get())
00187 {
00188 QString lcd_tots = QObject::tr("Importing ") + tots;
00189 QPtrList<LCDTextItem> textItems;
00190 textItems.setAutoDelete(true);
00191 textItems.append(new LCDTextItem(1, ALIGN_CENTERED,
00192 lcd_tots, "Generic", false));
00193 lcd->switchToGeneric(&textItems);
00194 }
00195
00196 Metadata *titleTrack = NULL;
00197 QString outfile = "";
00198
00199 std::auto_ptr<Encoder> encoder;
00200
00201 for (int trackno = 0; trackno < m_totalTracks; trackno++)
00202 {
00203 if (isCancelled())
00204 return;
00205
00206 sendEvent(ST_STATUS_TEXT, QString("Track %1 of %2")
00207 .arg(trackno + 1).arg(m_totalTracks));
00208
00209 sendEvent(ST_TRACK_PROGRESS, 0);
00210
00211 track = m_tracks->at(trackno)->metadata;
00212
00213 if (track)
00214 {
00215 textstatus = track->Title();
00216 sendEvent(ST_TRACK_TEXT, textstatus);
00217
00218 sendEvent(ST_TRACK_PROGRESS, 0);
00219 sendEvent(ST_TRACK_PERCENT, 0);
00220
00221
00222 if (m_tracks->at(trackno)->active)
00223 {
00224 titleTrack = track;
00225 titleTrack->setLength(m_tracks->at(trackno)->length);
00226
00227 outfile = Ripper::filenameFromMetadata(track);
00228
00229 if (m_quality < 3)
00230 {
00231 if (encodertype == "mp3")
00232 {
00233 outfile += ".mp3";
00234 encoder.reset(new LameEncoder(outfile, m_quality,
00235 titleTrack, mp3usevbr));
00236 }
00237 else
00238 {
00239 outfile += ".ogg";
00240 encoder.reset(new VorbisEncoder(outfile, m_quality,
00241 titleTrack));
00242 }
00243 }
00244 else
00245 {
00246 outfile += ".flac";
00247 encoder.reset(new FlacEncoder(outfile, m_quality,
00248 titleTrack));
00249 }
00250
00251 if (!encoder->isValid())
00252 {
00253 sendEvent(ST_ENCODER_ERROR, "Encoder failed to open file for writing");
00254 VERBOSE(VB_IMPORTANT,
00255 QString("MythMusic: Encoder failed to open file for writing"));
00256
00257 return;
00258 }
00259 }
00260
00261 if (!encoder.get())
00262 {
00263
00264 sendEvent(ST_ENCODER_ERROR, "Failed to create encoder");
00265 VERBOSE(VB_IMPORTANT,
00266 QString("MythMusic: Error: No encoder, failing"));
00267 return;
00268 }
00269 ripTrack(m_CDdevice, encoder.get(), trackno + 1);
00270
00271 if (isCancelled())
00272 return;
00273
00274
00275 if (m_tracks->at(trackno)->active)
00276 {
00277 titleTrack->setFilename(outfile);
00278 titleTrack->dumpToDatabase();
00279 }
00280 }
00281 }
00282
00283 QString PostRipCDScript = gContext->GetSetting("PostCDRipScript");
00284
00285 if (!PostRipCDScript.isEmpty())
00286 {
00287 VERBOSE(VB_IMPORTANT,
00288 QString("PostCDRipScript: %1").arg(PostRipCDScript));
00289 pid_t child = fork();
00290 if (child < 0)
00291 {
00292 perror("fork");
00293 }
00294 else if (child == 0)
00295 {
00296 execl("/bin/sh", "sh", "-c", PostRipCDScript.ascii(), NULL);
00297 perror("exec");
00298 _exit(1);
00299 }
00300 }
00301
00302 sendEvent(ST_FINISHED, "");
00303 }
00304
00305 int CDRipperThread::ripTrack(QString &cddevice, Encoder *encoder, int tracknum)
00306 {
00307 #ifdef HAVE_CDAUDIO // && HAVE_CDPARANOIA
00308 cdrom_drive *device = cdda_identify(cddevice.ascii(), 0, NULL);
00309
00310 if (!device)
00311 {
00312 VERBOSE(VB_IMPORTANT,
00313 QString("Error: cdda_identify failed for device '%1', "
00314 "CDRipperThread::ripTrack(tracknum = %2) exiting.")
00315 .arg(cddevice).arg(tracknum));
00316 return -1;
00317 }
00318
00319 if (cdda_open(device))
00320 {
00321 cdda_close(device);
00322 return -1;
00323 }
00324
00325 cdda_verbose_set(device, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
00326 long int start = cdda_track_firstsector(device, tracknum);
00327 long int end = cdda_track_lastsector(device, tracknum);
00328
00329 cdrom_paranoia *paranoia = paranoia_init(device);
00330 if (gContext->GetSetting("ParanoiaLevel") == "full")
00331 paranoia_modeset(paranoia, PARANOIA_MODE_FULL |
00332 PARANOIA_MODE_NEVERSKIP);
00333 else
00334 paranoia_modeset(paranoia, PARANOIA_MODE_OVERLAP);
00335
00336 paranoia_seek(paranoia, start, SEEK_SET);
00337
00338 long int curpos = start;
00339 int16_t *buffer;
00340
00341 sendEvent(ST_TRACK_START, end - start + 1);
00342 m_lastTrackPct = -1;
00343 m_lastOverallPct = -1;
00344
00345 int every15 = 15;
00346 while (curpos < end)
00347 {
00348 buffer = paranoia_read(paranoia, paranoia_cb);
00349
00350 if (encoder->addSamples(buffer, CD_FRAMESIZE_RAW))
00351 break;
00352
00353 curpos++;
00354
00355 every15--;
00356
00357 if (every15 <= 0)
00358 {
00359 every15 = 15;
00360
00361
00362 int newOverallPct = (int) (100.0 / (double) ((double) m_totalSectors /
00363 (double) (m_totalSectorsDone + curpos - start)));
00364 if (newOverallPct != m_lastOverallPct)
00365 {
00366 m_lastOverallPct = newOverallPct;
00367 sendEvent(ST_OVERALL_PERCENT, newOverallPct);
00368 sendEvent(ST_OVERALL_PROGRESS, m_totalSectorsDone + curpos - start);
00369 }
00370
00371 int newTrackPct = (int) (100.0 / (double) ((double) (end - start + 1) /
00372 (double) (curpos - start)));
00373 if (newTrackPct != m_lastTrackPct)
00374 {
00375 m_lastTrackPct = newTrackPct;
00376 sendEvent(ST_TRACK_PERCENT, newTrackPct);
00377 sendEvent(ST_TRACK_PROGRESS, curpos - start);
00378 }
00379
00380 if (class LCD * lcd = LCD::Get())
00381 {
00382 float fProgress = (float)(m_totalSectorsDone + (curpos - start)) / m_totalSectors;
00383 lcd->setGenericProgress(fProgress);
00384 }
00385 }
00386
00387 if (isCancelled())
00388 {
00389 break;
00390 }
00391 }
00392
00393 m_totalSectorsDone += end - start + 1;
00394
00395 paranoia_free(paranoia);
00396 cdda_close(device);
00397
00398 return (curpos - start + 1) * CD_FRAMESIZE_RAW;
00399 #else
00400 (void)cddevice; (void)encoder; (void)tracknum;
00401 #endif
00402
00403 return 0;
00404 }
00405
00407
00408 Ripper::Ripper(QString device, MythMainWindow *parent, const char *name)
00409 : MythThemedDialog(parent, "cdripper", "music-", name, true)
00410 {
00411 m_CDdevice = device;
00412
00413 #ifndef _WIN32
00414
00415 m_mediaMonitorActive = false;
00416 MediaMonitor *mon = MediaMonitor::GetMediaMonitor();
00417 if (mon && mon->IsActive())
00418 {
00419 m_mediaMonitorActive = true;
00420 mon->StopMonitoring();
00421 }
00422 #endif
00423
00424
00425
00426 m_somethingwasripped = false;
00427 wireupTheme();
00428 m_decoder = NULL;
00429 m_tracks = new vector<RipTrack*>;
00430
00431 QTimer::singleShot(500, this, SLOT(startScanCD()));
00432 }
00433
00434 Ripper::~Ripper(void)
00435 {
00436 if (m_decoder)
00437 delete m_decoder;
00438
00439 #ifndef _WIN32
00440
00441 if (m_mediaMonitorActive)
00442 {
00443 MediaMonitor *mon = MediaMonitor::GetMediaMonitor();
00444 if (mon)
00445 mon->StartMonitoring();
00446 }
00447 #endif
00448 }
00449
00450 void Ripper::wireupTheme(void)
00451 {
00452 m_qualitySelector = getUISelectorType("quality_selector");
00453 if (m_qualitySelector)
00454 {
00455 m_qualitySelector->addItem(0, tr("Low"));
00456 m_qualitySelector->addItem(1, tr("Medium"));
00457 m_qualitySelector->addItem(2, tr("High"));
00458 m_qualitySelector->addItem(3, tr("Perfect"));
00459 m_qualitySelector->setToItem(gContext->GetNumSetting("DefaultRipQuality", 1));
00460 }
00461
00462 m_artistEdit = getUIRemoteEditType("artist_edit");
00463 if (m_artistEdit)
00464 {
00465 m_artistEdit->createEdit(this);
00466 connect(m_artistEdit, SIGNAL(textChanged(QString)), this, SLOT(artistChanged(QString)));
00467 }
00468
00469 m_searchArtistButton = getUIPushButtonType("searchartist_button");
00470 if (m_searchArtistButton)
00471 {
00472 connect(m_searchArtistButton, SIGNAL(pushed()), this, SLOT(searchArtist()));
00473 }
00474
00475 m_albumEdit = getUIRemoteEditType("album_edit");
00476 if (m_albumEdit)
00477 {
00478 m_albumEdit->createEdit(this);
00479 connect(m_albumEdit, SIGNAL(textChanged(QString)), this, SLOT(albumChanged(QString)));
00480 }
00481
00482 m_searchAlbumButton = getUIPushButtonType("searchalbum_button");
00483 if (m_searchAlbumButton)
00484 {
00485 connect(m_searchAlbumButton, SIGNAL(pushed()), this, SLOT(searchAlbum()));
00486 }
00487
00488 m_genreEdit = getUIRemoteEditType("genre_edit");
00489 if (m_genreEdit)
00490 {
00491 m_genreEdit->createEdit(this);
00492 connect(m_genreEdit, SIGNAL(textChanged(QString)), this, SLOT(genreChanged(QString)));
00493 }
00494
00495 m_yearEdit = getUIRemoteEditType("year_edit");
00496 if (m_yearEdit)
00497 {
00498 m_yearEdit->createEdit(this);
00499 connect(m_yearEdit, SIGNAL(textChanged(QString)), this, SLOT(yearChanged(QString)));
00500 }
00501
00502 m_searchGenreButton = getUIPushButtonType("searchgenre_button");
00503 if (m_searchGenreButton)
00504 {
00505 connect(m_searchGenreButton, SIGNAL(pushed()), this, SLOT(searchGenre()));
00506 }
00507
00508 m_compilation = getUICheckBoxType("compilation_check");
00509 if (m_compilation)
00510 {
00511 connect(m_compilation, SIGNAL(pushed(bool)),
00512 this, SLOT(compilationChanged(bool)));
00513 }
00514
00515 m_switchTitleArtist = getUITextButtonType("switch_text");
00516 if (m_switchTitleArtist)
00517 {
00518 m_switchTitleArtist->setText(tr("Switch Titles"));
00519 connect(m_switchTitleArtist, SIGNAL(pushed()), this,
00520 SLOT(switchTitlesAndArtists()));
00521 }
00522
00523 m_scanButton = getUITextButtonType("scan_button");
00524 if (m_scanButton)
00525 {
00526 m_scanButton->setText(tr("Scan CD"));
00527 connect(m_scanButton, SIGNAL(pushed()), this,
00528 SLOT(startScanCD()));
00529 }
00530
00531 m_ripButton = getUITextButtonType("rip_button");
00532 if (m_ripButton)
00533 {
00534 m_ripButton->setText(tr("Rip CD"));
00535 connect(m_ripButton, SIGNAL(pushed()), this,
00536 SLOT(startRipper()));
00537 }
00538
00539 m_switchTitleArtist = getUITextButtonType("switch_button");
00540 {
00541 m_switchTitleArtist->setText("Switch Titles");
00542 connect(m_switchTitleArtist, SIGNAL(pushed()), this,
00543 SLOT(switchTitlesAndArtists()));
00544 m_switchTitleArtist->hide();
00545 }
00546
00547 m_trackList = (UIListType*) getUIObject("track_list");
00548
00549 buildFocusList();
00550 assignFirstFocus();
00551 }
00552
00553 void Ripper::keyPressEvent(QKeyEvent *e)
00554 {
00555 bool handled = false;
00556
00557 QStringList actions;
00558 gContext->GetMainWindow()->TranslateKeyPress("Global", e, actions, true);
00559
00560 for (unsigned int i = 0; i < actions.size() && !handled; i++)
00561 {
00562 QString action = actions[i];
00563 handled = true;
00564
00565 if (action == "SELECT")
00566 {
00567 if (getCurrentFocusWidget() == m_trackList)
00568 toggleTrackActive();
00569 else
00570 activateCurrent();
00571 }
00572 else if (action == "ESCAPE")
00573 {
00574 reject();
00575 }
00576 else if (action == "UP")
00577 {
00578 if (getCurrentFocusWidget() == m_trackList)
00579 {
00580 trackListUp(false);
00581 }
00582 else
00583 nextPrevWidgetFocus(false);
00584 }
00585 else if (action == "DOWN")
00586 {
00587 if (getCurrentFocusWidget() == m_trackList)
00588 {
00589 trackListDown(false);
00590 }
00591 else
00592 nextPrevWidgetFocus(true);
00593 }
00594 else if (action == "LEFT")
00595 {
00596 if (getCurrentFocusWidget() == m_qualitySelector)
00597 {
00598 m_qualitySelector->push(false);
00599 }
00600 else
00601 nextPrevWidgetFocus(false);
00602 }
00603 else if (action == "RIGHT")
00604 {
00605 if (getCurrentFocusWidget() == m_qualitySelector)
00606 {
00607 m_qualitySelector->push(true);
00608 }
00609 else
00610 nextPrevWidgetFocus(true);
00611 }
00612 else if (action == "PAGEUP")
00613 {
00614 if (getCurrentFocusWidget() == m_trackList)
00615 {
00616 trackListUp(true);
00617 }
00618 }
00619 else if (action == "PAGEDOWN")
00620 {
00621 if (getCurrentFocusWidget() == m_trackList)
00622 {
00623 trackListDown(true);
00624 }
00625 }
00626 else if (action == "INFO")
00627 {
00628 showEditMetadataDialog();
00629 }
00630 else if (action == "1")
00631 m_scanButton->push();
00632 else if (action == "2")
00633 m_ripButton->push();
00634 else
00635 handled = false;
00636 }
00637 }
00638
00639 void Ripper::startScanCD(void)
00640 {
00641 MythBusyDialog *busy = new MythBusyDialog("Scanning CD. Please Wait ...");
00642 CDScannerThread *scanner = new CDScannerThread(this);
00643 busy->start();
00644 scanner->start();
00645
00646 while (!scanner->finished())
00647 {
00648 usleep(500);
00649 qApp->processEvents();
00650 }
00651
00652 delete scanner;
00653
00654 m_tracks->clear();
00655
00656 bool isCompilation = false;
00657 bool newTune = true;
00658 if (m_decoder)
00659 {
00660 QString label;
00661 Metadata *metadata;
00662
00663 m_artistName = "";
00664 m_albumName = "";
00665 m_genreName = "";
00666 m_year = "";
00667 bool yesToAll = false;
00668 bool noToAll = false;
00669
00670 for (int trackno = 0; trackno < m_decoder->getNumTracks(); trackno++)
00671 {
00672 RipTrack *ripTrack = new RipTrack;
00673
00674 metadata = m_decoder->getMetadata(trackno + 1);
00675 if (metadata)
00676 {
00677 ripTrack->metadata = metadata;
00678 ripTrack->length = metadata->Length();
00679 ripTrack->active = true;
00680
00681 if (metadata->Compilation())
00682 {
00683 isCompilation = true;
00684 m_artistName = metadata->CompilationArtist();
00685 }
00686 else if ("" == m_artistName)
00687 {
00688 m_artistName = metadata->Artist();
00689 }
00690
00691 if ("" == m_albumName)
00692 m_albumName = metadata->Album();
00693
00694 if ("" == m_genreName
00695 && "" != metadata->Genre())
00696 {
00697 m_genreName = metadata->Genre();
00698 }
00699
00700 if ("" == m_year
00701 && 0 != metadata->Year())
00702 {
00703 m_year = QString::number(metadata->Year());
00704 }
00705
00706 QString title = metadata->Title();
00707 newTune = Ripper::isNewTune(m_artistName, m_albumName, title);
00708
00709 if (newTune)
00710 {
00711 m_tracks->push_back(ripTrack);
00712 }
00713 else
00714 {
00715 if (yesToAll)
00716 {
00717 deleteTrack(m_artistName, m_albumName, title);
00718 m_tracks->push_back(ripTrack);
00719 }
00720 else if (noToAll)
00721 {
00722 delete ripTrack;
00723 delete metadata;
00724 continue;
00725 }
00726 else
00727 {
00728 DialogBox *dlg = new DialogBox(
00729 gContext->GetMainWindow(),
00730 tr("Artist: %1\n"
00731 "Album: %2\n"
00732 "Track: %3\n\n"
00733 "This track is already in the database. \n"
00734 "Do you want to remove the existing track?")
00735 .arg(m_artistName).arg(m_albumName).arg(title));
00736
00737 dlg->AddButton("No");
00738 dlg->AddButton("No To All");
00739 dlg->AddButton("Yes");
00740 dlg->AddButton("Yes To All");
00741 DialogCode res = dlg->exec();
00742 dlg->deleteLater();
00743 dlg = NULL;
00744
00745 if (kDialogCodeButton0 == res)
00746 {
00747 delete ripTrack;
00748 delete metadata;
00749 }
00750 else if (kDialogCodeButton1 == res)
00751 {
00752 noToAll = true;
00753 delete ripTrack;
00754 delete metadata;
00755 }
00756 else if (kDialogCodeButton2 == res)
00757 {
00758 deleteTrack(m_artistName, m_albumName, title);
00759 m_tracks->push_back(ripTrack);
00760 }
00761 else if (kDialogCodeButton3 == res)
00762 {
00763 yesToAll = true;
00764 deleteTrack(m_artistName, m_albumName, title);
00765 m_tracks->push_back(ripTrack);
00766 }
00767 else
00768 {
00769 delete ripTrack;
00770 delete metadata;
00771 }
00772 }
00773 }
00774 }
00775 }
00776
00777 m_artistEdit->setText(m_artistName);
00778 m_albumEdit->setText(m_albumName);
00779 m_genreEdit->setText(m_genreName);
00780 m_yearEdit->setText(m_year);
00781 m_compilation->setState(isCompilation);
00782
00783 if (!isCompilation)
00784 m_switchTitleArtist->hide();
00785 else
00786 m_switchTitleArtist->show();
00787
00788 m_totalTracks = m_tracks->size();
00789 }
00790
00791 m_currentTrack = 0;
00792
00793 buildFocusList();
00794 updateTrackList();
00795
00796 busy->deleteLater();
00797 }
00798
00799 void Ripper::scanCD(void)
00800 {
00801 #ifdef HAVE_CDAUDIO
00802 int cdrom_fd = cd_init_device((char*)m_CDdevice.ascii());
00803 VERBOSE(VB_MEDIA, "Ripper::scanCD() - dev:" + m_CDdevice);
00804 if (cdrom_fd == -1)
00805 {
00806 perror("Could not open cdrom_fd");
00807 return;
00808 }
00809 cd_close(cdrom_fd);
00810 cd_finish(cdrom_fd);
00811 #endif
00812
00813 if (m_decoder)
00814 delete m_decoder;
00815
00816 m_decoder = new CdDecoder("cda", NULL, NULL, NULL);
00817 if (m_decoder)
00818 m_decoder->setDevice(m_CDdevice);
00819 }
00820
00821
00822 bool Ripper::isNewTune(const QString& artist, const QString& album, const QString& title)
00823 {
00824
00825 QString matchartist = artist;
00826 QString matchalbum = album;
00827 QString matchtitle = title;
00828
00829 if (! matchartist.isEmpty())
00830 {
00831 matchartist.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
00832 }
00833
00834 if (! matchalbum.isEmpty())
00835 {
00836 matchalbum.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
00837 }
00838
00839 if (! matchtitle.isEmpty())
00840 {
00841 matchtitle.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("_"));
00842 }
00843
00844 MSqlQuery query(MSqlQuery::InitCon());
00845 QString queryString("SELECT filename, artist_name, album_name, name, song_id "
00846 "FROM music_songs "
00847 "LEFT JOIN music_artists ON music_songs.artist_id=music_artists.artist_id "
00848 "LEFT JOIN music_albums ON music_songs.album_id=music_albums.album_id "
00849 "WHERE artist_name LIKE :ARTIST "
00850 "AND album_name LIKE :ALBUM "
00851 "AND name LIKE :TITLE "
00852 "ORDER BY artist_name, album_name, name, song_id, filename");
00853
00854 query.prepare(queryString);
00855
00856 query.bindValue(":ARTIST", matchartist);
00857 query.bindValue(":ALBUM", matchalbum);
00858 query.bindValue(":TITLE", matchtitle);
00859
00860 if (!query.exec() || !query.isActive())
00861 {
00862 MythContext::DBError("Search music database", query);
00863 return true;
00864 }
00865
00866 if (query.numRowsAffected() > 0)
00867 {
00868 return false;
00869 }
00870
00871 return true;
00872 }
00873
00874 void Ripper::deleteTrack(QString& artist, QString& album, QString& title)
00875 {
00876 MSqlQuery query(MSqlQuery::InitCon());
00877 QString queryString("SELECT song_id, filename "
00878 "FROM music_songs "
00879 "LEFT JOIN music_artists ON music_songs.artist_id=music_artists.artist_id "
00880 "LEFT JOIN music_albums ON music_songs.album_id=music_albums.album_id "
00881 "WHERE artist_name REGEXP \'");
00882 QString token = artist;
00883 token.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("."));
00884
00885 queryString += token + "\' AND " + "album_name REGEXP \'";
00886 token = album;
00887 token.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("."));
00888 queryString += token + "\' AND " + "name REGEXP \'";
00889 token = title;
00890 token.replace(QRegExp("(/|\\\\|:|\'|\\,|\\!|\\(|\\)|\"|\\?|\\|)"), QString("."));
00891 queryString += token + "\' ORDER BY artist_name, album_name, name, song_id, filename";
00892 query.prepare(queryString);
00893
00894 if (!query.exec() || !query.isActive())
00895 {
00896 MythContext::DBError("Search music database", query);
00897 return;
00898 }
00899
00900 if (query.numRowsAffected() > 0)
00901 {
00902 while (query.next())
00903 {
00904 int trackID = query.value(0).toInt();
00905 QString filename = query.value(1).toString();
00906
00907
00908 QString musicdir = gContext->GetSetting("MusicLocation");
00909 musicdir = QDir::cleanDirPath(musicdir);
00910 if (!musicdir.endsWith("/"))
00911 musicdir += "/";
00912 QFile::remove(musicdir + filename);
00913
00914
00915 MSqlQuery deleteQuery(MSqlQuery::InitCon());
00916 deleteQuery.prepare("DELETE FROM music_songs WHERE song_id = :SONG_ID");
00917 deleteQuery.bindValue(":SONG_ID", trackID);
00918 if (!deleteQuery.exec())
00919 MythContext::DBError("Delete Track", deleteQuery);
00920 }
00921 }
00922 }
00923
00924
00925
00926 QString Ripper::filenameFromMetadata(Metadata *track, bool createDir)
00927 {
00928 QString musicdir = gContext->GetSetting("MusicLocation");
00929 musicdir = QDir::cleanDirPath(musicdir);
00930 if (!musicdir.endsWith("/"))
00931 musicdir += "/";
00932
00933 QDir directoryQD(musicdir);
00934 QString filename = "";
00935 QString fntempl = gContext->GetSetting("FilenameTemplate");
00936 bool no_ws = gContext->GetNumSetting("NoWhitespace", 0);
00937
00938 QRegExp rx_ws("\\s{1,}");
00939 QRegExp rx("(GENRE|ARTIST|ALBUM|TRACK|TITLE|YEAR)");
00940 int i = 0;
00941 int old_i = 0;
00942 while (i >= 0)
00943 {
00944 i = rx.search(fntempl, i);
00945 if (i >= 0)
00946 {
00947 if (i > 0)
00948 filename += fixFileToken_sl(fntempl.mid(old_i,i-old_i));
00949 i += rx.matchedLength();
00950 old_i = i;
00951
00952 if ((rx.capturedTexts()[1] == "GENRE") && (track->Genre() != ""))
00953 filename += fixFileToken(track->Genre());
00954
00955 if ((rx.capturedTexts()[1] == "ARTIST") && (track->FormatArtist() != ""))
00956 filename += fixFileToken(track->FormatArtist());
00957
00958 if ((rx.capturedTexts()[1] == "ALBUM") && (track->Album() != ""))
00959 filename += fixFileToken(track->Album());
00960
00961 if ((rx.capturedTexts()[1] == "TRACK") && (track->Track() >= 0))
00962 {
00963 QString tempstr = QString::number(track->Track(), 10);
00964 if (track->Track() < 10)
00965 tempstr.prepend('0');
00966 filename += fixFileToken(tempstr);
00967 }
00968
00969 if ((rx.capturedTexts()[1] == "TITLE") && (track->FormatTitle() != ""))
00970 filename += fixFileToken(track->FormatTitle());
00971
00972 if ((rx.capturedTexts()[1] == "YEAR") && (track->Year() >= 0))
00973 filename += fixFileToken(QString::number(track->Year(), 10));
00974 }
00975 }
00976
00977 if (no_ws)
00978 filename.replace(rx_ws, "_");
00979
00980 if (filename == musicdir || filename.length() > FILENAME_MAX)
00981 {
00982 QString tempstr = QString::number(track->Track(), 10);
00983 tempstr += " - " + track->FormatTitle();
00984 filename = musicdir + fixFileToken(tempstr);
00985 VERBOSE(VB_GENERAL, QString("Invalid file storage definition."));
00986 }
00987
00988 filename = filename.local8Bit();
00989
00990 QStringList directoryList = QStringList::split("/", filename);
00991 for (unsigned i = 0; i < (directoryList.size() - 1); i++)
00992 {
00993 musicdir += "/" + directoryList[i];
00994 if (createDir)
00995 {
00996 umask(022);
00997 directoryQD.mkdir(musicdir, true);
00998 directoryQD.cd(musicdir, true);
00999 }
01000 }
01001
01002 filename = QDir::cleanDirPath(musicdir) + "/" + directoryList.last();
01003
01004 return filename;
01005 }
01006
01007 inline QString Ripper::fixFileToken(QString token)
01008 {
01009 token.replace(QRegExp("(/|\\\\|:|\'|\"|\\?|\\|)"), QString("_"));
01010 return token;
01011 }
01012
01013 inline QString Ripper::fixFileToken_sl(QString token)
01014 {
01015
01016 token.replace(QRegExp("(\\\\|:|\'|\"|\\?|\\|)"), QString("_"));
01017 return token;
01018 }
01019
01020
01021 bool Ripper::somethingWasRipped()
01022 {
01023 return m_somethingwasripped;
01024 }
01025
01026 void Ripper::artistChanged(QString newartist)
01027 {
01028 Metadata *data;
01029
01030 for (int trackno = 0; trackno < m_totalTracks; ++trackno)
01031 {
01032 data = m_tracks->at(trackno)->metadata;
01033
01034 if (data)
01035 {
01036 if (m_compilation->getState())
01037 {
01038 data->setCompilationArtist(newartist);
01039 }
01040 else
01041 {
01042 data->setArtist(newartist);
01043 data->setCompilationArtist("");
01044 }
01045 }
01046 }
01047
01048 updateTrackList();
01049 m_artistName = newartist;
01050 }
01051
01052 void Ripper::albumChanged(QString newalbum)
01053 {
01054 Metadata *data;
01055
01056 for (int trackno = 0; trackno < m_totalTracks; ++trackno)
01057 {
01058 data = m_tracks->at(trackno)->metadata;
01059
01060 if (data)
01061 data->setAlbum(newalbum);
01062 }
01063
01064 m_albumName = newalbum;
01065 }
01066
01067 void Ripper::genreChanged(QString newgenre)
01068 {
01069 Metadata *data;
01070
01071 for (int trackno = 0; trackno < m_totalTracks; ++trackno)
01072 {
01073 data = m_tracks->at(trackno)->metadata;
01074
01075 if (data)
01076 data->setGenre(newgenre);
01077 }
01078
01079 m_genreName = newgenre;
01080 }
01081
01082 void Ripper::yearChanged(QString newyear)
01083 {
01084 Metadata *data;
01085
01086 for (int trackno = 0; trackno < m_totalTracks; ++trackno)
01087 {
01088 data = m_tracks->at(trackno)->metadata;
01089
01090 if (data)
01091 data->setYear(newyear.toInt());
01092 }
01093
01094 m_year = newyear;
01095 }
01096
01097 void Ripper::compilationChanged(bool state)
01098 {
01099 if (!state)
01100 {
01101 Metadata *data;
01102
01103
01104 for (int trackno = 0; trackno < m_totalTracks; ++trackno)
01105 {
01106 data = m_tracks->at(trackno)->metadata;
01107
01108 if (data)
01109 {
01110 data->setCompilationArtist("");
01111 data->setArtist(m_artistName);
01112 data->setCompilation(false);
01113 }
01114 }
01115
01116 m_switchTitleArtist->hide();
01117 }
01118 else
01119 {
01120
01121 for (int trackno = 0; trackno < m_totalTracks; ++trackno)
01122 {
01123 Metadata *data;
01124 data = m_tracks->at(trackno)->metadata;
01125
01126 if (data)
01127 {
01128 data->setCompilationArtist(m_artistName);
01129 data->setCompilation(true);
01130 }
01131 }
01132
01133 m_switchTitleArtist->show();
01134 }
01135
01136 buildFocusList();
01137 updateTrackList();
01138 }
01139
01140 void Ripper::switchTitlesAndArtists()
01141 {
01142 if (!m_compilation->getState())
01143 return;
01144
01145 Metadata *data;
01146
01147
01148 QString tmp;
01149
01150 for (int track = 0; track < m_totalTracks; ++track)
01151 {
01152 data = m_tracks->at(track)->metadata;
01153
01154 if (data)
01155 {
01156 tmp = data->Artist();
01157 data->setArtist(data->Title());
01158 data->setTitle(tmp);
01159 }
01160 }
01161
01162 updateTrackList();
01163 }
01164
01165 void Ripper::reject()
01166 {
01167 if (!gContext->GetMainWindow()->IsExitingToMain())
01168 startEjectCD();
01169
01170 done(Rejected);
01171 }
01172
01173 void Ripper::startRipper(void)
01174 {
01175 if (m_tracks->size() == 0)
01176 {
01177 MythPopupBox::showOkPopup(gContext->GetMainWindow(), tr("No tracks"),
01178 tr("There are no tracks to rip?"));
01179 return;
01180 }
01181
01182 RipStatus statusDialog(m_CDdevice, m_tracks, m_qualitySelector->getCurrentInt(),
01183 gContext->GetMainWindow(), "edit metadata");
01184 DialogCode rescode = statusDialog.exec();
01185 if (kDialogCodeAccepted == rescode)
01186 {
01187 bool EjectCD = gContext->GetNumSetting("EjectCDAfterRipping", 1);
01188 if (EjectCD)
01189 startEjectCD();
01190
01191 MythPopupBox::showOkPopup(gContext->GetMainWindow(), tr("Success"),
01192 tr("Rip completed successfully."));
01193
01194 m_somethingwasripped = true;
01195 }
01196 else
01197 {
01198 MythPopupBox::showOkPopup(gContext->GetMainWindow(), tr("Encoding Failed"),
01199 tr("Encoding failed with the following error:-\n\n")
01200 + statusDialog.getErrorMessage());
01201 }
01202
01203 if (class LCD * lcd = LCD::Get())
01204 lcd->switchToTime();
01205 }
01206
01207
01208 void Ripper::startEjectCD()
01209 {
01210 MythBusyDialog *busy = new MythBusyDialog(tr("Ejecting CD. Please Wait ..."));
01211 CDEjectorThread *ejector = new CDEjectorThread(this);
01212 busy->start();
01213 ejector->start();
01214
01215 while (!ejector->finished())
01216 {
01217 usleep(500);
01218 qApp->processEvents();
01219 }
01220
01221 delete ejector;
01222 busy->deleteLater();
01223
01224 if (class LCD * lcd = LCD::Get())
01225 lcd->switchToTime();
01226 }
01227
01228 void Ripper::ejectCD()
01229 {
01230 bool bEjectCD = gContext->GetNumSetting("EjectCDAfterRipping",1);
01231 if (bEjectCD)
01232 {
01233 #ifdef HAVE_CDAUDIO
01234 int cdrom_fd;
01235 cdrom_fd = cd_init_device((char*)m_CDdevice.ascii());
01236 VERBOSE(VB_MEDIA, "Ripper::ejectCD() - dev " + m_CDdevice);
01237 if (cdrom_fd != -1)
01238 {
01239 if (cd_eject(cdrom_fd) == -1)
01240 perror("Failed on cd_eject");
01241
01242 cd_finish(cdrom_fd);
01243 }
01244 else
01245 perror("Failed on cd_init_device");
01246 #else
01247 MediaMonitor *mon = MediaMonitor::GetMediaMonitor();
01248 if (mon)
01249 {
01250 MythMediaDevice *pMedia = mon->GetMedia(m_CDdevice.ascii());
01251 if (pMedia && mon->ValidateAndLock(pMedia))
01252 {
01253 pMedia->eject();
01254 mon->Unlock(pMedia);
01255 }
01256 }
01257 #endif
01258 }
01259 }
01260
01261 void Ripper::updateTrackList(void)
01262 {
01263 QString tmptitle;
01264 if (m_trackList)
01265 {
01266 int trackListSize = m_trackList->GetItems();
01267 m_trackList->ResetList();
01268 if (m_trackList->isFocused())
01269 m_trackList->SetActive(true);
01270
01271 int skip;
01272 if (m_totalTracks <= trackListSize || m_currentTrack <= trackListSize / 2)
01273 skip = 0;
01274 else if (m_currentTrack >= m_totalTracks - trackListSize + trackListSize / 2)
01275 skip = m_totalTracks - trackListSize;
01276 else
01277 skip = m_currentTrack - trackListSize / 2;
01278 m_trackList->SetUpArrow(skip > 0);
01279 m_trackList->SetDownArrow(skip + trackListSize < m_totalTracks);
01280
01281 int i;
01282 for (i = 0; i < trackListSize; i++)
01283 {
01284 if (i + skip >= m_totalTracks)
01285 break;
01286
01287 RipTrack *track = m_tracks->at(i + skip);
01288 Metadata *metadata = track->metadata;
01289
01290 if (track->active)
01291 m_trackList->SetItemText(i, 1, QString::number(metadata->Track()));
01292 else
01293 m_trackList->SetItemText(i, 1, "-");
01294
01295 m_trackList->SetItemText(i, 2, metadata->Title());
01296 m_trackList->SetItemText(i, 3, metadata->Artist());
01297
01298 int length = track->length / 1000;
01299 if (length > 0)
01300 {
01301 int min, sec;
01302 min = length / 60;
01303 sec = length % 60;
01304 QString s;
01305 s.sprintf("%02d:%02d", min, sec);
01306 m_trackList->SetItemText(i, 4, s);
01307 }
01308 else
01309 m_trackList->SetItemText(i, 4, "-");
01310
01311 if (i + skip == m_currentTrack)
01312 {
01313 m_trackList->SetItemCurrent(i);
01314 }
01315 }
01316
01317 m_trackList->refresh();
01318 }
01319 }
01320
01321 void Ripper::trackListDown(bool page)
01322 {
01323 if (m_currentTrack < m_totalTracks - 1)
01324 {
01325 int trackListSize = m_trackList->GetItems();
01326
01327 m_currentTrack += (page ? trackListSize : 1);
01328 if (m_currentTrack > m_totalTracks - 1)
01329 m_currentTrack = m_totalTracks - 1;
01330
01331 updateTrackList();
01332 }
01333 }
01334
01335 void Ripper::trackListUp(bool page)
01336 {
01337 if (m_currentTrack > 0)
01338 {
01339 int trackListSize = m_trackList->GetItems();
01340
01341 m_currentTrack -= (page ? trackListSize : 1);
01342 if (m_currentTrack < 0)
01343 m_currentTrack = 0;
01344
01345 updateTrackList();
01346 }
01347 }
01348
01349 void Ripper::searchArtist()
01350 {
01351 QString s;
01352
01353 m_searchList = Metadata::fillFieldList("artist");
01354
01355 s = m_artistEdit->getText();
01356 if (showList(tr("Select an Artist"), s))
01357 {
01358 m_artistEdit->setText(s);
01359 artistChanged(s);
01360 updateTrackList();
01361 }
01362 }
01363
01364 void Ripper::searchAlbum()
01365 {
01366 QString s;
01367
01368 m_searchList = Metadata::fillFieldList("album");
01369
01370 s = m_albumEdit->getText();
01371 if (showList(tr("Select an Album"), s))
01372 {
01373 m_albumEdit->setText(s);
01374 albumChanged(s);
01375 }
01376 }
01377
01378 void Ripper::searchGenre()
01379 {
01380 QString s;
01381
01382
01383 m_searchList.clear();
01384 for (int x = 0; x < genre_table_size; x++)
01385 m_searchList.push_back(QString(genre_table[x]));
01386 m_searchList.sort();
01387
01388 s = m_genreEdit->getText();
01389 if (showList(tr("Select a Genre"), s))
01390 {
01391 m_genreEdit->setText(s);
01392 genreChanged(s);
01393 }
01394 }
01395
01396 bool Ripper::showList(QString caption, QString &value)
01397 {
01398 bool res = false;
01399
01400 MythSearchDialog *searchDialog = new MythSearchDialog(gContext->GetMainWindow(), "");
01401 searchDialog->setCaption(caption);
01402 searchDialog->setSearchText(value);
01403 searchDialog->setItems(m_searchList);
01404 DialogCode rescode = searchDialog->ExecPopupAtXY(-1, 8);
01405 if (kDialogCodeRejected != rescode)
01406 {
01407 value = searchDialog->getResult();
01408 res = true;
01409 }
01410
01411 searchDialog->deleteLater();
01412 setActiveWindow();
01413
01414 return res;
01415 }
01416
01417 void Ripper::showEditMetadataDialog()
01418 {
01419 Metadata *editMeta = m_tracks->at(m_currentTrack)->metadata;
01420
01421 EditMetadataDialog editDialog(editMeta, gContext->GetMainWindow(),
01422 "edit_metadata", "music-", "edit metadata");
01423 editDialog.setSaveMetadataOnly();
01424
01425 if (kDialogCodeRejected != editDialog.exec())
01426 {
01427 updateTrackList();
01428 }
01429 }
01430
01431 void Ripper::toggleTrackActive()
01432 {
01433 if (m_currentTrack == 0)
01434 return;
01435
01436 RipTrack *track = m_tracks->at(m_currentTrack);
01437
01438 track->active = !track->active;
01439
01440 updateTrackLengths();
01441 updateTrackList();
01442 }
01443
01444 void Ripper::updateTrackLengths()
01445 {
01446 vector<RipTrack*>::reverse_iterator it;
01447 RipTrack *track;
01448 int length = 0;
01449
01450 for (it = m_tracks->rbegin(); it != m_tracks->rend(); ++it)
01451 {
01452 track = *it;
01453 if (track->active)
01454 {
01455 track->length = length + track->metadata->Length();
01456 length = 0;
01457 }
01458 else
01459 {
01460 track->length = 0;
01461 length += track->metadata->Length();
01462 }
01463 }
01464 }
01465
01467
01468 RipStatus::RipStatus(const QString &device, vector<RipTrack*> *tracks,
01469 int quality, MythMainWindow *parent, const char *name)
01470 : MythThemedDialog(parent, "ripstatus", "music-", name, true)
01471 {
01472 m_CDdevice = device;
01473 m_tracks = tracks;
01474 m_quality = quality;
01475 m_ripperThread = NULL;
01476
01477 wireupTheme();
01478 QTimer::singleShot(500, this, SLOT(startRip()));
01479 }
01480
01481 RipStatus::~RipStatus(void)
01482 {
01483 if (m_ripperThread)
01484 delete m_ripperThread;
01485
01486 if (class LCD * lcd = LCD::Get())
01487 lcd->switchToTime();
01488 }
01489
01490 void RipStatus::wireupTheme(void)
01491 {
01492 m_overallText = getUITextType("overall_text");
01493 m_trackText = getUITextType("track_text");
01494 m_statusText = getUITextType("status_text");
01495 m_trackPctText = getUITextType("trackpct_text");
01496 m_overallPctText = getUITextType("overallpct_text");
01497
01498 m_overallProgress = getUIStatusBarType("overall_progress");
01499 if (m_overallProgress)
01500 {
01501 m_overallProgress->SetUsed(0);
01502 m_overallProgress->SetTotal(1);
01503 }
01504
01505 m_trackProgress = getUIStatusBarType("track_progress");
01506 if (m_trackProgress)
01507 {
01508 m_trackProgress->SetUsed(0);
01509 m_trackProgress->SetTotal(1);
01510 }
01511
01512 buildFocusList();
01513 assignFirstFocus();
01514 }
01515
01516 void RipStatus::keyPressEvent(QKeyEvent *e)
01517 {
01518 bool handled = false;
01519
01520 QStringList actions;
01521 gContext->GetMainWindow()->TranslateKeyPress("Global", e, actions, false);
01522
01523 for (unsigned int i = 0; i < actions.size() && !handled; i++)
01524 {
01525 QString action = actions[i];
01526 handled = true;
01527
01528 if (action == "ESCAPE")
01529 {
01530 if (m_ripperThread && m_ripperThread->running())
01531 {
01532 if (MythPopupBox::showOkCancelPopup(gContext->GetMainWindow(), tr("Stop Rip?"),
01533 tr("Are you sure you want to cancel ripping the CD?"), false))
01534 {
01535 m_ripperThread->cancel();
01536 m_ripperThread->wait();
01537 m_errorMessage = tr("Cancelled by the user");
01538 done(Rejected);
01539 }
01540 }
01541 }
01542 else
01543 handled = false;
01544 }
01545 }
01546
01547 void RipStatus::customEvent(QCustomEvent *e)
01548 {
01549 if (!e || (e->type() != QEvent::User))
01550 return;
01551
01552 StatusData *sd = (StatusData*) (e->data());
01553
01554 if (!sd) return;
01555
01556 switch(sd->type)
01557 {
01558 case ST_TRACK_TEXT:
01559 {
01560 m_trackText->SetText(sd->text);
01561 break;
01562 }
01563
01564 case ST_OVERALL_TEXT:
01565 {
01566 m_overallText->SetText(sd->text);
01567 break;
01568 }
01569
01570 case ST_STATUS_TEXT:
01571 {
01572 m_statusText->SetText(sd->text);
01573 break;
01574 }
01575
01576 case ST_TRACK_PROGRESS:
01577 {
01578 m_trackProgress->SetUsed(sd->value);
01579 break;
01580 }
01581
01582 case ST_TRACK_PERCENT:
01583 {
01584 m_trackPctText->SetText(QString("%1%").arg(sd->value));
01585 break;
01586 }
01587
01588 case ST_TRACK_START:
01589 {
01590 m_trackProgress->SetTotal(sd->value);
01591 break;
01592 }
01593
01594 case ST_OVERALL_PROGRESS:
01595 {
01596 m_overallProgress->SetUsed(sd->value);
01597 break;
01598 }
01599
01600 case ST_OVERALL_START:
01601 {
01602 m_overallProgress->SetTotal(sd->value);
01603 break;
01604 }
01605
01606 case ST_OVERALL_PERCENT:
01607 {
01608 m_overallPctText->SetText(QString("%1%").arg(sd->value));
01609 break;
01610 }
01611
01612 case ST_FINISHED:
01613 {
01614 done(Accepted);
01615 break;
01616 }
01617
01618 case ST_ENCODER_ERROR:
01619 {
01620 m_errorMessage = tr("The encoder failed to create the file.\n"
01621 "Do you have write permissions for the music directory?");
01622 done(Rejected);
01623 break;
01624 }
01625
01626 default:
01627 cout << "Received an unknown event type!" << endl;
01628 break;
01629 }
01630
01631 delete sd;
01632 }
01633
01634 void RipStatus::startRip(void)
01635 {
01636 if (m_ripperThread)
01637 delete m_ripperThread;
01638
01639 m_ripperThread = new CDRipperThread(this, m_CDdevice, m_tracks, m_quality);
01640 m_ripperThread->start();
01641 }