diff --git a/src/ui/ripcd.cpp b/src/ui/ripcd.cpp deleted file mode 100644 index cbcd62670..000000000 --- a/src/ui/ripcd.cpp +++ /dev/null @@ -1,520 +0,0 @@ -/* This file is part of Clementine. - Copyright 2014, Andre Siviero - - Clementine is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Clementine is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Clementine. If not, see . - */ - -#include "ripcd.h" -#include "config.h" -#include "ui_ripcd.h" -#include "transcoder/transcoder.h" -#include "transcoder/transcoderoptionsdialog.h" -#include "ui/iconloader.h" -#include "core/closure.h" -#include "core/logging.h" -#include "core/tagreaderclient.h" -#include "core/utilities.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// winspool.h defines this :( -#ifdef AddJob -#undef AddJob -#endif - -namespace { -bool ComparePresetsByName(const TranscoderPreset& left, - const TranscoderPreset& right) { - return left.name_ < right.name_; -} - -const char kWavHeaderRiffMarker[] = "RIFF"; -const char kWavFileTypeFormatChunk[] = "WAVEfmt "; -const char kWavDataString[] = "data"; - -const int kCheckboxColumn = 0; -const int kTrackNumberColumn = 1; -const int kTrackTitleColumn = 2; -} -const char* RipCD::kSettingsGroup = "Transcoder"; -const int RipCD::kProgressInterval = 500; -const int RipCD::kMaxDestinationItems = 10; - -RipCD::RipCD(QWidget* parent) - : QDialog(parent), - transcoder_(new Transcoder(this)), - queued_(0), - finished_success_(0), - finished_failed_(0), - ui_(new Ui_RipCD), - cancel_requested_(false), - files_tagged_(0) { - cdio_ = cdio_open(NULL, DRIVER_UNKNOWN); - // Init - ui_->setupUi(this); - - // Set column widths in the QTableWidget. - ui_->tableWidget->horizontalHeader()->setSectionResizeMode( - kCheckboxColumn, QHeaderView::ResizeToContents); - ui_->tableWidget->horizontalHeader()->setSectionResizeMode( - kTrackNumberColumn, QHeaderView::ResizeToContents); - ui_->tableWidget->horizontalHeader()->setSectionResizeMode(kTrackTitleColumn, - QHeaderView::Stretch); - - // Add a rip button - rip_button_ = ui_->button_box->addButton(tr("Start ripping"), - QDialogButtonBox::ActionRole); - cancel_button_ = ui_->button_box->button(QDialogButtonBox::Cancel); - close_button_ = ui_->button_box->button(QDialogButtonBox::Close); - - // Hide elements - cancel_button_->hide(); - ui_->progress_group->hide(); - - connect(ui_->select_all_button, SIGNAL(clicked()), SLOT(SelectAll())); - connect(ui_->select_none_button, SIGNAL(clicked()), SLOT(SelectNone())); - connect(ui_->invert_selection_button, SIGNAL(clicked()), - SLOT(InvertSelection())); - connect(rip_button_, SIGNAL(clicked()), SLOT(ClickedRipButton())); - connect(cancel_button_, SIGNAL(clicked()), SLOT(Cancel())); - connect(close_button_, SIGNAL(clicked()), SLOT(hide())); - - connect(transcoder_, SIGNAL(JobComplete(QString, QString, bool)), - SLOT(TranscodingJobComplete(QString, QString, bool))); - connect(transcoder_, SIGNAL(AllJobsComplete()), - SLOT(AllTranscodingJobsComplete())); - connect(transcoder_, SIGNAL(LogLine(QString)), SLOT(LogLine(QString))); - connect(this, SIGNAL(RippingComplete()), SLOT(ThreadedTranscoding())); - connect(this, SIGNAL(SignalUpdateProgress()), SLOT(UpdateProgress())); - - connect(ui_->options, SIGNAL(clicked()), SLOT(Options())); - connect(ui_->select, SIGNAL(clicked()), SLOT(AddDestination())); - - setWindowTitle(tr("Rip CD")); - AddDestinationDirectory(QDir::homePath()); - - // Get presets - QList presets = Transcoder::GetAllPresets(); - qSort(presets.begin(), presets.end(), ComparePresetsByName); - for (const TranscoderPreset& preset : presets) { - ui_->format->addItem( - QString("%1 (.%2)").arg(preset.name_, preset.extension_), - QVariant::fromValue(preset)); - } - - // Load settings - QSettings s; - s.beginGroup(kSettingsGroup); - last_add_dir_ = s.value("last_add_dir", QDir::homePath()).toString(); - - QString last_output_format = s.value("last_output_format", "ogg").toString(); - for (int i = 0; i < ui_->format->count(); ++i) { - if (last_output_format == - ui_->format->itemData(i).value().extension_) { - ui_->format->setCurrentIndex(i); - break; - } - } - - ui_->progress_bar->setValue(0); - ui_->progress_bar->setMaximum(100); -} - -RipCD::~RipCD() { cdio_destroy(cdio_); } - -/* - * WAV Header documentation - * as taken from: - * http://www.topherlee.com/software/pcm-tut-wavformat.html - * Pos Value Description - * 0-3 | "RIFF" | Marks the file as a riff file. - * | Characters are each 1 byte long. - * 4-7 | File size (integer) | Size of the overall file - 8 bytes, - * | in bytes (32-bit integer). - * 8-11 | "WAVE" | File Type Header. For our purposes, - * | it always equals "WAVE". - * 13-16 | "fmt " | Format chunk marker. Includes trailing null. - * 17-20 | 16 | Length of format data as listed above - * 21-22 | 1 | Type of format (1 is PCM) - 2 byte integer - * 23-24 | 2 | Number of Channels - 2 byte integer - * 25-28 | 44100 | Sample Rate - 32 byte integer. Common values - * | are 44100 (CD), 48000 (DAT). - * | Sample Rate = Number of Samples per second, or Hertz. - * 29-32 | 176400 | (Sample Rate * BitsPerSample * Channels) / 8. - * 33-34 | 4 | (BitsPerSample * Channels) / 8.1 - 8 bit mono2 - 8 bit stereo/16 - * bit mono4 - 16 bit stereo - * 35-36 | 16 | Bits per sample - * 37-40 | "data" | "data" chunk header. - * | Marks the beginning of the data section. - * 41-44 | File size (data) | Size of the data section. - */ -void RipCD::WriteWAVHeader(QFile* stream, int32_t i_bytecount) { - QDataStream data_stream(stream); - data_stream.setByteOrder(QDataStream::LittleEndian); - // sizeof() - 1 to avoid including "\0" in the file too - data_stream.writeRawData(kWavHeaderRiffMarker, - sizeof(kWavHeaderRiffMarker) - 1); /* 0-3 */ - data_stream << qint32(i_bytecount + 44 - 8); /* 4-7 */ - data_stream.writeRawData(kWavFileTypeFormatChunk, - sizeof(kWavFileTypeFormatChunk) - 1); /* 8-15 */ - data_stream << (qint32)16; /* 16-19 */ - data_stream << (qint16)1; /* 20-21 */ - data_stream << (qint16)2; /* 22-23 */ - data_stream << (qint32)44100; /* 24-27 */ - data_stream << (qint32)(44100 * 2 * 2); /* 28-31 */ - data_stream << (qint16)4; /* 32-33 */ - data_stream << (qint16)16; /* 34-35 */ - data_stream.writeRawData(kWavDataString, - sizeof(kWavDataString) - 1); /* 36-39 */ - data_stream << (qint32)i_bytecount; /* 40-43 */ -} - -int RipCD::NumTracksToRip() { - int k = 0; - for (int i = 0; i < checkboxes_.length(); i++) { - if (checkboxes_.value(i)->isChecked()) { - k++; - } - } - return k; -} - -void RipCD::ThreadClickedRipButton() { - temporary_directory_ = Utilities::MakeTempDir() + "/"; - finished_success_ = 0; - finished_failed_ = 0; - ui_->progress_bar->setMaximum(NumTracksToRip() * 2 * 100); - - // Set up progress bar - emit(SignalUpdateProgress()); - - for (const TrackInformation& track : tracks_) { - QString filename = - QString("%1%2.wav").arg(temporary_directory_).arg(track.track_number); - QFile* destination_file = new QFile(filename); - destination_file->open(QIODevice::WriteOnly); - - lsn_t i_first_lsn = cdio_get_track_lsn(cdio_, track.track_number); - lsn_t i_last_lsn = cdio_get_track_last_lsn(cdio_, track.track_number); - WriteWAVHeader(destination_file, - (i_last_lsn - i_first_lsn + 1) * CDIO_CD_FRAMESIZE_RAW); - - QByteArray buffered_input_bytes(CDIO_CD_FRAMESIZE_RAW, '\0'); - for (lsn_t i_cursor = i_first_lsn; i_cursor <= i_last_lsn; i_cursor++) { - { - QMutexLocker l(&mutex_); - if (cancel_requested_) { - qLog(Debug) << "CD ripping canceled."; - return; - } - } - if (cdio_read_audio_sector(cdio_, buffered_input_bytes.data(), - i_cursor) == DRIVER_OP_SUCCESS) { - destination_file->write(buffered_input_bytes.data(), - buffered_input_bytes.size()); - } else { - qLog(Error) << "CD read error"; - break; - } - } - finished_success_++; - emit(SignalUpdateProgress()); - TranscoderPreset preset = ui_->format->itemData(ui_->format->currentIndex()) - .value(); - - transcoder_->AddJob(filename, preset, track.transcoded_filename); - } - emit(RippingComplete()); -} - -QString RipCD::GetOutputFileName(const QString& basename) const { - QString path = - ui_->destination->itemData(ui_->destination->currentIndex()).toString(); - QString extension = ui_->format->itemData(ui_->format->currentIndex()) - .value() - .extension_; - return path + '/' + basename + '.' + extension; -} - -QString RipCD::ParseFileFormatString(const QString& file_format, - int track_no) const { - QString to_return = file_format; - to_return.replace(QString("%artist%"), ui_->artistLineEdit->text()); - to_return.replace(QString("%album%"), ui_->albumLineEdit->text()); - to_return.replace(QString("%genre%"), ui_->genreLineEdit->text()); - to_return.replace(QString("%year%"), ui_->yearLineEdit->text()); - to_return.replace(QString("%tracknum%"), QString::number(track_no)); - to_return.replace(QString("%track%"), - track_names_.value(track_no - 1)->text()); - return to_return; -} - -void RipCD::UpdateProgress() { - int progress = (finished_success_ + finished_failed_) * 100; - QMap current_jobs = transcoder_->GetProgress(); - for (float value : current_jobs.values()) { - progress += qBound(0, static_cast(value * 100), 99); - } - - ui_->progress_bar->setValue(progress); -} - -void RipCD::ThreadedTranscoding() { - transcoder_->Start(); - TranscoderPreset preset = ui_->format->itemData(ui_->format->currentIndex()) - .value(); - // Save the last output format - QSettings s; - s.beginGroup(kSettingsGroup); - s.setValue("last_output_format", preset.extension_); -} - -void RipCD::ClickedRipButton() { - if (cdio_ && cdio_get_media_changed(cdio_)) { - QMessageBox cdio_fail(QMessageBox::Critical, tr("Error Ripping CD"), - tr("Media has changed. Reloading")); - cdio_fail.exec(); - if (CheckCDIOIsValid()) { - BuildTrackListTable(); - } else { - ui_->tableWidget->clearContents(); - } - return; - } - - // Add tracks to the rip list. - tracks_.clear(); - for (int i = 1; i <= i_tracks_; ++i) { - if (!checkboxes_.value(i - 1)->isChecked()) { - continue; - } - QString transcoded_filename = GetOutputFileName( - ParseFileFormatString(ui_->format_filename->text(), i)); - QString title = track_names_.value(i - 1)->text(); - AddTrack(i, title, transcoded_filename); - } - - // Do nothing if no tracks are selected. - if (tracks_.isEmpty()) - return; - - // Start ripping. - SetWorking(true); - { - QMutexLocker l(&mutex_); - cancel_requested_ = false; - } - QtConcurrent::run(this, &RipCD::ThreadClickedRipButton); -} - -void RipCD::AddTrack(int track_number, const QString& title, - const QString& transcoded_filename) { - TrackInformation track(track_number, title, transcoded_filename); - tracks_.append(track); -} - -void RipCD::TranscodingJobComplete(const QString& input, const QString& output, bool success) { - (*(success ? &finished_success_ : &finished_failed_))++; - emit(SignalUpdateProgress()); -} - -void RipCD::AllTranscodingJobsComplete() { - RemoveTemporaryDirectory(); - - // Save tags. - TranscoderPreset preset = ui_->format->itemData(ui_->format->currentIndex()) - .value(); - AlbumInformation album( - ui_->albumLineEdit->text(), ui_->artistLineEdit->text(), - ui_->genreLineEdit->text(), ui_->yearLineEdit->text().toInt(), - ui_->discLineEdit->text().toInt(), preset.type_); - TagFiles(album, tracks_); -} - -void RipCD::TagFiles(const AlbumInformation& album, - const QList& tracks) { - files_tagged_ = 0; - for (const TrackInformation& track : tracks_) { - Song song; - song.InitFromFilePartial(track.transcoded_filename); - song.set_track(track.track_number); - song.set_title(track.title); - song.set_album(album.album); - song.set_artist(album.artist); - song.set_genre(album.genre); - song.set_year(album.year); - song.set_disc(album.disc); - song.set_filetype(album.type); - - TagReaderReply* reply = - TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song); - NewClosure(reply, SIGNAL(Finished(bool)), this, - SLOT(FileTagged(TagReaderReply*)), reply); - } -} - -void RipCD::FileTagged(TagReaderReply* reply) { - files_tagged_++; - qLog(Debug) << "Tagged" << files_tagged_ << "of" << tracks_.length() - << "files"; - - // Stop working if all files are tagged. - if (files_tagged_ == tracks_.length()) { - qLog(Debug) << "CD ripper finished."; - SetWorking(false); - } - - reply->deleteLater(); -} - -void RipCD::Options() { - TranscoderPreset preset = ui_->format->itemData(ui_->format->currentIndex()) - .value(); - - TranscoderOptionsDialog dialog(preset.type_, this); - if (dialog.is_valid()) { - dialog.exec(); - } -} - -// Adds a folder to the destination box. -void RipCD::AddDestination() { - int index = ui_->destination->currentIndex(); - QString initial_dir = (!ui_->destination->itemData(index).isNull() - ? ui_->destination->itemData(index).toString() - : QDir::homePath()); - QString dir = - QFileDialog::getExistingDirectory(this, tr("Add folder"), initial_dir); - - if (!dir.isEmpty()) { - // Keep only a finite number of items in the box. - while (ui_->destination->count() >= kMaxDestinationItems) { - ui_->destination->removeItem(0); // The oldest item. - } - AddDestinationDirectory(dir); - } -} - -// Adds a directory to the 'destination' combo box. -void RipCD::AddDestinationDirectory(QString dir) { - QIcon icon = IconLoader::Load("folder"); - QVariant data = QVariant::fromValue(dir); - // Do not insert duplicates. - int duplicate_index = ui_->destination->findData(data); - if (duplicate_index == -1) { - ui_->destination->addItem(icon, dir, data); - ui_->destination->setCurrentIndex(ui_->destination->count() - 1); - } else { - ui_->destination->setCurrentIndex(duplicate_index); - } -} - -void RipCD::Cancel() { - { - QMutexLocker l(&mutex_); - cancel_requested_ = true; - } - ui_->progress_bar->setValue(0); - transcoder_->Cancel(); - RemoveTemporaryDirectory(); - SetWorking(false); -} - -bool RipCD::CheckCDIOIsValid() { - if (cdio_) { - cdio_destroy(cdio_); - } - cdio_ = cdio_open(NULL, DRIVER_UNKNOWN); - // Refresh the status of the cd media. This will prevent unnecessary - // rebuilds of the track list table. - cdio_get_media_changed(cdio_); - return cdio_; -} - -void RipCD::SetWorking(bool working) { - rip_button_->setVisible(!working); - cancel_button_->setVisible(working); - close_button_->setVisible(!working); - ui_->input_group->setEnabled(!working); - ui_->output_group->setEnabled(!working); - ui_->progress_group->setVisible(true); -} - -void RipCD::SelectAll() { - for (QCheckBox* checkbox : checkboxes_) { - checkbox->setCheckState(Qt::Checked); - } -} - -void RipCD::SelectNone() { - for (QCheckBox* checkbox : checkboxes_) { - checkbox->setCheckState(Qt::Unchecked); - } -} - -void RipCD::InvertSelection() { - for (QCheckBox* checkbox : checkboxes_) { - if (checkbox->isChecked()) { - checkbox->setCheckState(Qt::Unchecked); - } else { - checkbox->setCheckState(Qt::Checked); - } - } -} - -void RipCD::RemoveTemporaryDirectory() { - if (!temporary_directory_.isEmpty()) - Utilities::RemoveRecursive(temporary_directory_); - temporary_directory_.clear(); -} - -void RipCD::BuildTrackListTable() { - checkboxes_.clear(); - track_names_.clear(); - - i_tracks_ = cdio_get_num_tracks(cdio_); - // Build an empty table if there is an error, e.g. no medium found. - if (i_tracks_ == CDIO_INVALID_TRACK) - i_tracks_ = 0; - - ui_->tableWidget->setRowCount(i_tracks_); - for (int i = 1; i <= i_tracks_; i++) { - QCheckBox* checkbox_i = new QCheckBox(ui_->tableWidget); - checkbox_i->setCheckState(Qt::Checked); - checkboxes_.append(checkbox_i); - ui_->tableWidget->setCellWidget(i - 1, kCheckboxColumn, checkbox_i); - ui_->tableWidget->setCellWidget(i - 1, kTrackNumberColumn, - new QLabel(QString::number(i))); - QString track_title = QString("Track %1").arg(i); - QLineEdit* line_edit_track_title_i = - new QLineEdit(track_title, ui_->tableWidget); - track_names_.append(line_edit_track_title_i); - ui_->tableWidget->setCellWidget(i - 1, kTrackTitleColumn, - line_edit_track_title_i); - } -} - -void RipCD::LogLine(const QString& message) { qLog(Debug) << message; } - -void RipCD::showEvent(QShowEvent* event) { BuildTrackListTable(); }