From b0704331d7cb6b6238490b95a3b25ea81e713783 Mon Sep 17 00:00:00 2001 From: Lukas Prediger Date: Sun, 22 Aug 2021 15:04:43 +0300 Subject: [PATCH] Integrate file name format options into RipCDDialog for consistency with OrganiseDialog and reducing code duplication --- src/core/organiseformat.cpp | 15 ++++- src/core/organiseformat.h | 8 ++- src/ripper/ripcddialog.cpp | 100 +++++++++++++++------------- src/ripper/ripcddialog.h | 16 ++--- src/ripper/ripcddialog.ui | 76 ++++++++++----------- src/ripper/ripper.cpp | 7 +- src/ripper/ripper.h | 8 ++- src/widgets/filenameformatwidget.ui | 6 ++ 8 files changed, 129 insertions(+), 107 deletions(-) diff --git a/src/core/organiseformat.cpp b/src/core/organiseformat.cpp index 7fa83ec0a..f11f1f650 100644 --- a/src/core/organiseformat.cpp +++ b/src/core/organiseformat.cpp @@ -31,6 +31,7 @@ #include "core/arraysize.h" #include "core/timeconstants.h" #include "core/utilities.h" +#include "transcoder/transcoder.h" const char* OrganiseFormat::kTagPattern = "\\%([a-zA-Z]*)"; const char* OrganiseFormat::kBlockPattern = "\\{([^{}]+)\\}"; @@ -97,7 +98,8 @@ bool OrganiseFormat::IsValid() const { return v.validate(format_copy, pos) == QValidator::Acceptable; } -QString OrganiseFormat::GetFilenameForSong(const Song& song) const { +QString OrganiseFormat::GetFilenameForSong(const Song& song, + QString prefix_path) const { QString filename = ParseBlock(format_, song); if (QFileInfo(filename).completeBaseName().isEmpty()) { @@ -141,9 +143,20 @@ QString OrganiseFormat::GetFilenameForSong(const Song& song) const { } } + if (!prefix_path.isEmpty()) parts.insert(0, prefix_path); + return parts.join("/"); } +QString OrganiseFormat::GetFilenameForSong( + const Song& song, const TranscoderPreset& transcoder_preset, + QString prefix_path) const { + OrganiseFormat format(*this); + format.add_tag_override("extension", transcoder_preset.extension_); + + return format.GetFilenameForSong(song, prefix_path); +} + QStringList OrganiseFormat::GetFilenamesForSongs(const SongList& songs) const { // Check if we will have multiple files with the same name. // If so, they will erase each other if the overwrite flag is set. diff --git a/src/core/organiseformat.h b/src/core/organiseformat.h index 3cad35f0b..17e6d3078 100644 --- a/src/core/organiseformat.h +++ b/src/core/organiseformat.h @@ -27,9 +27,12 @@ #include "core/song.h" +class TranscoderPreset; + class OrganiseFormat { public: explicit OrganiseFormat(const QString& format = QString()); + OrganiseFormat(const OrganiseFormat& format) = default; static const char* kTagPattern; static const char* kBlockPattern; @@ -55,7 +58,10 @@ class OrganiseFormat { void reset_tag_overrides() { tag_overrides_.clear(); } bool IsValid() const; - QString GetFilenameForSong(const Song& song) const; + QString GetFilenameForSong(const Song& song, QString prefix_path = "") const; + QString GetFilenameForSong(const Song& song, + const TranscoderPreset& transcoder_preset, + QString prefix_path = "") const; QStringList GetFilenamesForSongs(const SongList& songs) const; class Validator : public QValidator { diff --git a/src/ripper/ripcddialog.cpp b/src/ripper/ripcddialog.cpp index 0157bd7c4..8f21da3d5 100644 --- a/src/ripper/ripcddialog.cpp +++ b/src/ripper/ripcddialog.cpp @@ -30,6 +30,7 @@ #include "config.h" #include "core/logging.h" +#include "core/organiseformat.h" #include "core/tagreaderclient.h" #include "devices/cddadevice.h" #include "devices/cddasongloader.h" @@ -85,8 +86,9 @@ RipCDDialog::RipCDDialog(DeviceManager* device_manager, QWidget* parent) cancel_button_->hide(); ui_->progress_group->hide(); - rip_button_->setEnabled(false); // will be enabled by DeviceSelected if a - // valid device is selected + rip_button_->setEnabled( + false); // will be enabled by signal handlers if a valid device is + // selected by user and a list of tracks is loaded InitializeDevices(); @@ -100,6 +102,9 @@ RipCDDialog::RipCDDialog(DeviceManager* device_manager, QWidget* parent) connect(ui_->options, SIGNAL(clicked()), SLOT(Options())); connect(ui_->select, SIGNAL(clicked()), SLOT(AddDestination())); + connect(ui_->naming_group, SIGNAL(FormatStringChanged()), + SLOT(FormatStringUpdated())); + setWindowTitle(tr("Rip CD")); AddDestinationDirectory(QDir::homePath()); @@ -176,6 +181,14 @@ void RipCDDialog::InitializeDevices() { void RipCDDialog::ClickedRipButton() { Q_ASSERT(cdda_device_); + OrganiseFormat format = ui_->naming_group->format(); + Q_ASSERT(format.IsValid()); + + ui_->naming_group->StoreSettings(); + + QFileInfo path( + ui_->destination->itemData(ui_->destination->currentIndex()).toString()); + // create and connect Ripper instance for this task Ripper* ripper = new Ripper(cdda_device_->raw_cdio(), this); connect(cancel_button_, SIGNAL(clicked()), ripper, SLOT(Cancel())); @@ -196,11 +209,13 @@ void RipCDDialog::ClickedRipButton() { 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(); - ripper->AddTrack(i, title, transcoded_filename, preset); + Song& song = songs_[i - 1]; + QString transcoded_filename = format.GetFilenameForSong( + song, preset, /*prefix_path=*/path.filePath()); + ripper->AddTrack(i, song.title(), transcoded_filename, preset, + ui_->naming_group->overwrite_existing()); } + ripper->SetAlbumInformation( ui_->albumLineEdit->text(), ui_->artistLineEdit->text(), ui_->genreLineEdit->text(), ui_->yearLineEdit->text().toInt(), @@ -275,6 +290,7 @@ void RipCDDialog::DeviceSelected(int device_index) { if (cdda_device_) disconnect(cdda_device_.get(), nullptr, this, nullptr); ResetDialog(); + EnableIfPossible(); if (device_index < 0) return; // Invalid selection, probably no devices around @@ -299,15 +315,14 @@ void RipCDDialog::DeviceSelected(int device_index) { Q_ASSERT(loader_); connect(loader_, SIGNAL(SongsDurationLoaded(SongList)), - SLOT(BuildTrackListTable(SongList))); + SLOT(UpdateTrackList(SongList))); connect(loader_, SIGNAL(SongsMetadataLoaded(SongList)), - SLOT(UpdateTrackListTable(SongList))); + SLOT(UpdateTrackList(SongList))); connect(loader_, SIGNAL(SongsMetadataLoaded(SongList)), SLOT(AddAlbumMetadataFromMusicBrainz(SongList))); // load songs from new SongLoader loader_->LoadSongs(); - rip_button_->setEnabled(true); } void RipCDDialog::Finished(Ripper* ripper) { @@ -328,13 +343,24 @@ void RipCDDialog::UpdateProgressBar(int progress) { ui_->progress_bar->setValue(progress); } -void RipCDDialog::BuildTrackListTable(const SongList& songs) { - checkboxes_.clear(); - track_names_.clear(); +void RipCDDialog::UpdateTrackList(const SongList& songs) { + if (songs_.isEmpty() || songs_.length() == songs.length()) { + songs_ = songs; + UpdateTrackListTable(); + } else { + qLog(Error) << "Number of tracks in metadata does not match number of " + "songs on disc!"; + } + EnableIfPossible(); +} - ui_->tableWidget->setRowCount(songs.length()); +void RipCDDialog::UpdateTrackListTable() { + checkboxes_.clear(); + + ui_->tableWidget->clear(); + ui_->tableWidget->setRowCount(songs_.length()); int current_row = 0; - for (const Song& song : songs) { + for (const Song& song : songs_) { QCheckBox* checkbox = new QCheckBox(ui_->tableWidget); checkbox->setCheckState(Qt::Checked); checkboxes_.append(checkbox); @@ -343,7 +369,10 @@ void RipCDDialog::BuildTrackListTable(const SongList& songs) { new QLabel(QString::number(song.track()))); QLineEdit* line_edit_track_title = new QLineEdit(song.title(), ui_->tableWidget); - track_names_.append(line_edit_track_title); + connect(line_edit_track_title, &QLineEdit::textChanged, + [this, current_row](const QString& text) { + songs_[current_row].set_title(text); + }); ui_->tableWidget->setCellWidget(current_row, kTrackTitleColumn, line_edit_track_title); ui_->tableWidget->setCellWidget(current_row, kTrackDurationColumn, @@ -352,15 +381,6 @@ void RipCDDialog::BuildTrackListTable(const SongList& songs) { } } -void RipCDDialog::UpdateTrackListTable(const SongList& songs) { - if (track_names_.length() == songs.length()) { - BuildTrackListTable(songs); - } else { - qLog(Error) << "Number of tracks in metadata does not match number of " - "songs on disc!"; - } -} - void RipCDDialog::AddAlbumMetadataFromMusicBrainz(const SongList& songs) { Q_ASSERT(songs.length() > 0); @@ -382,31 +402,8 @@ void RipCDDialog::SetWorking(bool working) { ui_->progress_group->setVisible(true); } -QString RipCDDialog::GetOutputFileName(const QString& basename) const { - QFileInfo path( - ui_->destination->itemData(ui_->destination->currentIndex()).toString()); - QString extension = ui_->format->itemData(ui_->format->currentIndex()) - .value() - .extension_; - return path.filePath() + '/' + basename + '.' + extension; -} - -QString RipCDDialog::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("%disc"), ui_->discLineEdit->text()); - to_return.replace(QString("%genre"), ui_->genreLineEdit->text()); - to_return.replace(QString("%year"), ui_->yearLineEdit->text()); - to_return.replace(QString("%title"), - track_names_.value(track_no - 1)->text()); - to_return.replace(QString("%track"), QString::number(track_no)); - - return to_return; -} - void RipCDDialog::ResetDialog() { + songs_.clear(); ui_->tableWidget->setRowCount(0); ui_->albumLineEdit->clear(); ui_->artistLineEdit->clear(); @@ -414,3 +411,10 @@ void RipCDDialog::ResetDialog() { ui_->yearLineEdit->clear(); ui_->discLineEdit->clear(); } + +void RipCDDialog::FormatStringUpdated() { EnableIfPossible(); } + +void RipCDDialog::EnableIfPossible() { + rip_button_->setEnabled(!songs_.isEmpty() && + ui_->naming_group->format().IsValid()); +} diff --git a/src/ripper/ripcddialog.h b/src/ripper/ripcddialog.h index 6e8cd9aed..df94400ca 100644 --- a/src/ripper/ripcddialog.h +++ b/src/ripper/ripcddialog.h @@ -60,31 +60,24 @@ class RipCDDialog : public QDialog { void Cancelled(Ripper* ripper); void SetupProgressBarLimits(int min, int max); void UpdateProgressBar(int progress); - // Initializes track list table based on preliminary song list with durations - // but without metadata. - void BuildTrackListTable(const SongList& songs); - // Update track list based on metadata. - void UpdateTrackListTable(const SongList& songs); + void UpdateTrackList(const SongList& songs); // Update album information with metadata. void AddAlbumMetadataFromMusicBrainz(const SongList& songs); void DiscChanged(); + void FormatStringUpdated(); private: static const char* kSettingsGroup; static const int kMaxDestinationItems; - // Constructs a filename from the given base name with a path taken - // from the ui dialog and an extension that corresponds to the audio - // format chosen in the ui. void AddDestinationDirectory(QString dir); - QString GetOutputFileName(const QString& basename) const; - QString ParseFileFormatString(const QString& file_format, int track_no) const; void SetWorking(bool working); void ResetDialog(); void InitializeDevices(); + void EnableIfPossible(); + void UpdateTrackListTable(); QList checkboxes_; - QList track_names_; QString last_add_dir_; QPushButton* cancel_button_; QPushButton* close_button_; @@ -95,5 +88,6 @@ class RipCDDialog : public QDialog { bool working_; std::shared_ptr cdda_device_; CddaSongLoader* loader_; + SongList songs_; }; #endif // SRC_RIPPER_RIPCDDIALOG_H_ diff --git a/src/ripper/ripcddialog.ui b/src/ripper/ripcddialog.ui index e67478c71..704401e9a 100644 --- a/src/ripper/ripcddialog.ui +++ b/src/ripper/ripcddialog.ui @@ -10,7 +10,7 @@ 0 0 522 - 575 + 800 @@ -122,7 +122,7 @@ 4 - true + false 10 @@ -224,51 +224,30 @@ + + + Output options - - - - Select... - - - - - - - File Format - - - - - - - %track - %artist - %title - - - - + Destination - - - - - 0 - 0 - + + + + Audio format - + true @@ -281,17 +260,27 @@ - - - - Options... + + + + + 0 + 0 + - - + + - Audio format + Select... + + + + + + + Options... @@ -322,6 +311,14 @@ + + + FileNameFormatWidget + QWidget +
widgets/filenameformatwidget.h
+ 1 +
+
tableWidget select_all_button @@ -332,7 +329,6 @@ genreLineEdit yearLineEdit discLineEdit - format_filename format options destination diff --git a/src/ripper/ripper.cpp b/src/ripper/ripper.cpp index 2c9539d3c..1333c0469 100644 --- a/src/ripper/ripper.cpp +++ b/src/ripper/ripper.cpp @@ -58,12 +58,13 @@ Ripper::~Ripper() {} void Ripper::AddTrack(int track_number, const QString& title, const QString& transcoded_filename, - const TranscoderPreset& preset) { + const TranscoderPreset& preset, bool overwrite_existing) { if (track_number < 1 || track_number > TracksOnDisc()) { qLog(Warning) << "Invalid track number:" << track_number << "Ignoring"; return; } - TrackInformation track(track_number, title, transcoded_filename, preset); + TrackInformation track(track_number, title, transcoded_filename, preset, + overwrite_existing); tracks_.append(track); } @@ -232,7 +233,7 @@ void Ripper::Rip() { it->temporary_filename = filename; transcoder_->AddJob(it->temporary_filename, it->preset, - it->transcoded_filename); + it->transcoded_filename, it->overwrite_existing); } transcoder_->Start(); emit RippingComplete(); diff --git a/src/ripper/ripper.h b/src/ripper/ripper.h index 3bf8dd98f..a4f8d8af8 100644 --- a/src/ripper/ripper.h +++ b/src/ripper/ripper.h @@ -48,7 +48,7 @@ class Ripper : public QObject { // chosen TranscoderPreset. void AddTrack(int track_number, const QString& title, const QString& transcoded_filename, - const TranscoderPreset& preset); + const TranscoderPreset& preset, bool overwrite_existing); // Sets album metadata. This information is used when tagging the // final files. void SetAlbumInformation(const QString& album, const QString& artist, @@ -83,17 +83,19 @@ class Ripper : public QObject { struct TrackInformation { TrackInformation(int track_number, const QString& title, const QString& transcoded_filename, - const TranscoderPreset& preset) + const TranscoderPreset& preset, bool overwrite_existing) : track_number(track_number), title(title), transcoded_filename(transcoded_filename), - preset(preset) {} + preset(preset), + overwrite_existing(overwrite_existing) {} int track_number; QString title; QString transcoded_filename; TranscoderPreset preset; QString temporary_filename; + bool overwrite_existing; }; struct AlbumInformation { diff --git a/src/widgets/filenameformatwidget.ui b/src/widgets/filenameformatwidget.ui index 5f24d0c06..f1df90530 100644 --- a/src/widgets/filenameformatwidget.ui +++ b/src/widgets/filenameformatwidget.ui @@ -21,6 +21,12 @@ + + + 0 + 0 + + <p>Tokens start with %, for example: %artist %album %title </p>