File indexing completed on 2025-02-23 04:34:18

0001 /**
0002  * \file serverimportdialog.cpp
0003  * Generic dialog to import from a server.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 09 Oct 2006
0008  *
0009  * Copyright (C) 2006-2024  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #include "serverimportdialog.h"
0028 #include <QLayout>
0029 #include <QPushButton>
0030 #include <QLineEdit>
0031 #include <QComboBox>
0032 #include <QCheckBox>
0033 #include <QLabel>
0034 #include <QStatusBar>
0035 #include <QVBoxLayout>
0036 #include <QHBoxLayout>
0037 #include <QListView>
0038 #include <QCoreApplication>
0039 #include "serverimporter.h"
0040 #include "serverimporterconfig.h"
0041 #include "contexthelp.h"
0042 #include "trackdata.h"
0043 
0044 /**
0045  * Constructor.
0046  *
0047  * @param parent  parent widget
0048  */
0049 ServerImportDialog::ServerImportDialog(QWidget* parent) : QDialog(parent),
0050     m_serverComboBox(nullptr), m_cgiLineEdit(nullptr), m_tokenLineEdit(nullptr),
0051     m_standardTagsCheckBox(nullptr), m_additionalTagsCheckBox(nullptr),
0052     m_coverArtCheckBox(nullptr), m_source(nullptr)
0053 {
0054   setObjectName(QLatin1String("ServerImportDialog"));
0055 
0056   auto vlayout = new QVBoxLayout(this);
0057 
0058   auto findLayout = new QHBoxLayout;
0059   m_artistLineEdit = new QComboBox(this);
0060   m_albumLineEdit = new QComboBox(this);
0061   m_findButton = new QPushButton(tr("&Find"), this);
0062   m_artistLineEdit->setEditable(true);
0063   m_artistLineEdit->setDuplicatesEnabled(false);
0064   m_albumLineEdit->setEditable(true);
0065   m_albumLineEdit->setDuplicatesEnabled(false);
0066   m_artistLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
0067   m_albumLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
0068   findLayout->addWidget(m_artistLineEdit);
0069   findLayout->addWidget(m_albumLineEdit);
0070   findLayout->addWidget(m_findButton);
0071   connect(m_findButton, &QAbstractButton::clicked, this, &ServerImportDialog::slotFind);
0072   vlayout->addLayout(findLayout);
0073 
0074   auto serverLayout = new QHBoxLayout;
0075   m_serverLabel = new QLabel(tr("&Server:"), this);
0076   m_serverComboBox = new QComboBox(this);
0077   m_serverComboBox->setEditable(true);
0078   m_cgiLabel = new QLabel(tr("C&GI Path:"), this);
0079   m_cgiLineEdit = new QLineEdit(this);
0080   serverLayout->addWidget(m_serverLabel);
0081   serverLayout->addWidget(m_serverComboBox);
0082   m_serverLabel->setBuddy(m_serverComboBox);
0083   serverLayout->addWidget(m_cgiLabel);
0084   serverLayout->addWidget(m_cgiLineEdit);
0085   m_cgiLabel->setBuddy(m_cgiLineEdit);
0086   vlayout->addLayout(serverLayout);
0087 
0088   auto tokenLayout = new QHBoxLayout;
0089   m_tokenLabel = new QLabel(tr("&Token:"), this);
0090   m_tokenLineEdit = new QLineEdit(this);
0091   tokenLayout->addWidget(m_tokenLabel);
0092   tokenLayout->addWidget(m_tokenLineEdit);
0093   m_tokenLabel->setBuddy(m_tokenLineEdit);
0094   vlayout->addLayout(tokenLayout);
0095 
0096   auto hlayout = new QHBoxLayout;
0097   m_standardTagsCheckBox = new QCheckBox(tr("&Standard Tags"), this);
0098   m_additionalTagsCheckBox = new QCheckBox(tr("&Additional Tags"), this);
0099   m_coverArtCheckBox = new QCheckBox(tr("C&over Art"), this);
0100   hlayout->addWidget(m_standardTagsCheckBox);
0101   hlayout->addWidget(m_additionalTagsCheckBox);
0102   hlayout->addWidget(m_coverArtCheckBox);
0103   vlayout->addLayout(hlayout);
0104 
0105   m_albumListBox = new QListView(this);
0106   m_albumListBox->setEditTriggers(QAbstractItemView::NoEditTriggers);
0107   vlayout->addWidget(m_albumListBox);
0108   connect(m_albumListBox, &QAbstractItemView::activated,
0109       this, static_cast<void (ServerImportDialog::*)(const QModelIndex&)>(
0110             &ServerImportDialog::requestTrackList));
0111 
0112   auto buttonLayout = new QHBoxLayout;
0113   m_helpButton = new QPushButton(tr("&Help"), this);
0114   m_helpButton->setAutoDefault(false);
0115   m_saveButton = new QPushButton(tr("&Save Settings"), this);
0116   m_saveButton->setAutoDefault(false);
0117   auto closeButton = new QPushButton(tr("&Close"), this);
0118   closeButton->setAutoDefault(false);
0119   buttonLayout->addWidget(m_helpButton);
0120   connect(m_helpButton, &QAbstractButton::clicked, this, &ServerImportDialog::showHelp);
0121   buttonLayout->addWidget(m_saveButton);
0122   connect(m_saveButton, &QAbstractButton::clicked, this, &ServerImportDialog::saveConfig);
0123   auto hspacer = new QSpacerItem(16, 0, QSizePolicy::Expanding,
0124                                          QSizePolicy::Minimum);
0125   buttonLayout->addItem(hspacer);
0126   buttonLayout->addWidget(closeButton);
0127   connect(closeButton, &QAbstractButton::clicked, this, &QDialog::accept);
0128   vlayout->addLayout(buttonLayout);
0129 
0130   m_statusBar = new QStatusBar(this);
0131   vlayout->addWidget(m_statusBar);
0132   showStatusMessage(tr("Ready."));
0133 }
0134 
0135 /**
0136  * Set importer to be used.
0137  *
0138  * @param source  import source to use
0139  */
0140 void ServerImportDialog::setImportSource(ServerImporter* source)
0141 {
0142   if (m_source) {
0143     disconnect(m_source, &HttpClient::progress,
0144         this, &ServerImportDialog::showStatusMessage);
0145     disconnect(m_source, &ImportClient::findFinished,
0146         this, &ServerImportDialog::slotFindFinished);
0147     disconnect(m_source, &ImportClient::albumFinished,
0148         this, &ServerImportDialog::slotAlbumFinished);
0149   }
0150   m_source = source;
0151 
0152   if (m_source) {
0153     connect(m_source, &HttpClient::progress,
0154         this, &ServerImportDialog::showStatusMessage);
0155     connect(m_source, &ImportClient::findFinished,
0156         this, &ServerImportDialog::slotFindFinished);
0157     connect(m_source, &ImportClient::albumFinished,
0158         this, &ServerImportDialog::slotAlbumFinished);
0159 
0160     setWindowTitle(QCoreApplication::translate("@default", m_source->name()));
0161     if (m_source->defaultServer()) {
0162       m_serverLabel->show();
0163       m_serverComboBox->show();
0164       if (m_source->defaultCgiPath()) {
0165         m_cgiLabel->show();
0166         m_cgiLineEdit->show();
0167       } else {
0168         m_cgiLabel->hide();
0169         m_cgiLineEdit->hide();
0170       }
0171       if (m_source->serverList()) {
0172         QStringList strList;
0173         for (const char** sl = m_source->serverList(); *sl != nullptr; ++sl) {
0174           strList += QString::fromLatin1(*sl); // clazy:exclude=reserve-candidates
0175         }
0176         m_serverComboBox->clear();
0177         m_serverComboBox->addItems(strList);
0178       }
0179     } else {
0180       m_serverLabel->hide();
0181       m_serverComboBox->hide();
0182       m_cgiLabel->hide();
0183       m_cgiLineEdit->hide();
0184     }
0185     if (qstrcmp(m_source->name(), "Discogs") == 0) {
0186       m_tokenLabel->show();
0187       m_tokenLineEdit->show();
0188     } else {
0189       m_tokenLabel->hide();
0190       m_tokenLineEdit->hide();
0191     }
0192     if (m_source->additionalTags()) {
0193       m_standardTagsCheckBox->show();
0194       m_additionalTagsCheckBox->show();
0195       m_coverArtCheckBox->show();
0196     } else {
0197       m_standardTagsCheckBox->hide();
0198       m_additionalTagsCheckBox->hide();
0199       m_coverArtCheckBox->hide();
0200     }
0201 
0202     m_albumListBox->setModel(m_source->getAlbumListModel());
0203 
0204     if (m_source->helpAnchor()) {
0205       m_helpButton->show();
0206     } else {
0207       m_helpButton->hide();
0208     }
0209     if (m_source->config()) {
0210       m_saveButton->show();
0211     } else {
0212       m_saveButton->hide();
0213     }
0214 
0215     // Pressing Enter will trigger the Find button.
0216     m_findButton->setDefault(true);
0217   }
0218 }
0219 
0220 /**
0221  * Display message in status bar.
0222  *
0223  * @param msg status message
0224  */
0225 void ServerImportDialog::showStatusMessage(const QString& msg)
0226 {
0227   m_statusBar->showMessage(msg);
0228 }
0229 
0230 /**
0231  * Get string with server and port.
0232  *
0233  * @return "servername:port".
0234  */
0235 QString ServerImportDialog::getServer() const
0236 {
0237   if (m_serverComboBox) {
0238     QString server(m_serverComboBox->currentText());
0239     if (server.isEmpty() && m_source) {
0240       server = QString::fromLatin1(m_source->defaultServer());
0241     }
0242     return server;
0243   }
0244   return QString();
0245 }
0246 
0247 /**
0248  * Set string with server and port.
0249  *
0250  * @param srv "servername:port"
0251  */
0252 void ServerImportDialog::setServer(const QString& srv)
0253 {
0254   if (m_serverComboBox) {
0255     if (int idx = m_serverComboBox->findText(srv); idx >= 0) {
0256       m_serverComboBox->setCurrentIndex(idx);
0257     } else {
0258       m_serverComboBox->addItem(srv);
0259       m_serverComboBox->setCurrentIndex(m_serverComboBox->count() - 1);
0260     }
0261   }
0262 }
0263 
0264 /**
0265  * Get string with CGI path.
0266  *
0267  * @return CGI path, e.g. "/~cddb/cddb.cgi".
0268  */
0269 QString ServerImportDialog::getCgiPath() const
0270 {
0271   if (m_cgiLineEdit) {
0272     QString cgi(m_cgiLineEdit->text());
0273     if (cgi.isEmpty() && m_source) {
0274       cgi = QString::fromLatin1(m_source->defaultCgiPath());
0275     }
0276     return cgi;
0277   }
0278   return QString();
0279 }
0280 
0281 /**
0282  * Set string with CGI path.
0283  *
0284  * @param cgi CGI path, e.g. "/~cddb/cddb.cgi".
0285  */
0286 void ServerImportDialog::setCgiPath(const QString& cgi)
0287 {
0288   if (m_cgiLineEdit) {
0289     m_cgiLineEdit->setText(cgi);
0290   }
0291 }
0292 
0293 /**
0294  * Get token to access API server.
0295  * @return token.
0296  */
0297 QString ServerImportDialog::getToken() const
0298 {
0299   return m_tokenLineEdit ? m_tokenLineEdit->text() : QString();
0300 }
0301 
0302 /**
0303  * Set token to access API server.
0304  * @param token access token
0305  */
0306 void ServerImportDialog::setToken(const QString& token)
0307 {
0308   if (m_tokenLineEdit) {
0309     m_tokenLineEdit->setText(token);
0310   }
0311 }
0312 
0313 /**
0314  * Get standard tags option.
0315  *
0316  * @return true if standard tags are enabled.
0317  */
0318 bool ServerImportDialog::getStandardTags() const
0319 {
0320   return m_standardTagsCheckBox ?
0321     m_standardTagsCheckBox->checkState() == Qt::Checked
0322     : false;
0323 }
0324 
0325 /**
0326  * Set standard tags option.
0327  *
0328  * @param enable true if standard tags are enabled
0329  */
0330 void ServerImportDialog::setStandardTags(bool enable)
0331 {
0332   if (m_standardTagsCheckBox) {
0333     m_standardTagsCheckBox->setCheckState(
0334       enable ? Qt::Checked : Qt::Unchecked);
0335   }
0336 }
0337 
0338 /**
0339  * Get additional tags option.
0340  *
0341  * @return true if additional tags are enabled.
0342  */
0343 bool ServerImportDialog::getAdditionalTags() const
0344 {
0345   return m_additionalTagsCheckBox ?
0346     m_additionalTagsCheckBox->checkState() == Qt::Checked
0347     : false;
0348 }
0349 
0350 /**
0351  * Set additional tags option.
0352  *
0353  * @param enable true if additional tags are enabled
0354  */
0355 void ServerImportDialog::setAdditionalTags(bool enable)
0356 {
0357   if (m_additionalTagsCheckBox) {
0358     m_additionalTagsCheckBox->setCheckState(
0359       enable ? Qt::Checked : Qt::Unchecked);
0360   }
0361 }
0362 
0363 /**
0364  * Get cover art option.
0365  *
0366  * @return true if cover art are enabled.
0367  */
0368 bool ServerImportDialog::getCoverArt() const
0369 {
0370   return m_coverArtCheckBox ?
0371     m_coverArtCheckBox->checkState() == Qt::Checked
0372     : false;
0373 }
0374 
0375 /**
0376  * Set cover art option.
0377  *
0378  * @param enable true if cover art are enabled
0379  */
0380 void ServerImportDialog::setCoverArt(bool enable)
0381 {
0382   if (m_coverArtCheckBox) {
0383     m_coverArtCheckBox->setCheckState(
0384       enable ? Qt::Checked : Qt::Unchecked);
0385   }
0386 }
0387 
0388 /**
0389  * Get the local configuration.
0390  *
0391  * @param cfg configuration
0392  */
0393 void ServerImportDialog::getImportSourceConfig(ServerImporterConfig* cfg) const
0394 {
0395   cfg->setServer(getServer());
0396   cfg->setCgiPath(getCgiPath());
0397   cfg->setStandardTags(getStandardTags());
0398   cfg->setAdditionalTags(getAdditionalTags());
0399   cfg->setCoverArt(getCoverArt());
0400   cfg->setWindowGeometry(saveGeometry());
0401 
0402   if (QString token = getToken();
0403       !token.isEmpty() || cfg->property("token").isValid()) {
0404     cfg->setProperty("token", token);
0405   }
0406 }
0407 
0408 /**
0409  * Save the local settings to the configuration.
0410  */
0411 void ServerImportDialog::saveConfig()
0412 {
0413   if (m_source && m_source->config()) {
0414     getImportSourceConfig(m_source->config());
0415   }
0416 }
0417 
0418 /**
0419  * Set a find string from artist and album information.
0420  *
0421  * @param artist artist
0422  * @param album  album
0423  */
0424 void ServerImportDialog::setArtistAlbum(const QString& artist, const QString& album)
0425 {
0426   if (m_source && m_source->config()) {
0427     ServerImporterConfig* cf = m_source->config();
0428     setServer(cf->server());
0429     setCgiPath(cf->cgiPath());
0430     setStandardTags(cf->standardTags());
0431     setAdditionalTags(cf->additionalTags());
0432     setCoverArt(cf->coverArt());
0433     if (!cf->windowGeometry().isEmpty()) {
0434       restoreGeometry(cf->windowGeometry());
0435     }
0436 
0437     setToken(cf->property("token").toString());
0438   }
0439 
0440   if (!(artist.isEmpty() && album.isEmpty())) {
0441     int idx = m_artistLineEdit->findText(artist);
0442     if (idx >= 0) {
0443       m_artistLineEdit->setCurrentIndex(idx);
0444     } else {
0445       m_artistLineEdit->addItem(artist);
0446       m_artistLineEdit->setCurrentIndex(m_artistLineEdit->count() - 1);
0447     }
0448     idx = m_albumLineEdit->findText(album);
0449     if (idx >= 0) {
0450       m_albumLineEdit->setCurrentIndex(idx);
0451     } else {
0452       m_albumLineEdit->addItem(album);
0453       m_albumLineEdit->setCurrentIndex(m_albumLineEdit->count() - 1);
0454     }
0455     if (QLineEdit* lineEdit = m_artistLineEdit->lineEdit()) {
0456       lineEdit->selectAll();
0457     }
0458     m_artistLineEdit->setFocus();
0459   }
0460 }
0461 
0462 /**
0463  * Query a search for a keyword from the server.
0464  */
0465 void ServerImportDialog::slotFind()
0466 {
0467   ServerImporterConfig cfg;
0468   getImportSourceConfig(&cfg);
0469   if (m_source) {
0470     m_source->find(&cfg, m_artistLineEdit->currentText(),
0471                    m_albumLineEdit->currentText());
0472     // Pressing Enter will activate the selected album result and no longer
0473     // the Find button.
0474     m_findButton->setDefault(false);
0475   }
0476 }
0477 
0478 /**
0479  * Process finished find request.
0480  *
0481  * @param searchStr search data received
0482  */
0483 void ServerImportDialog::slotFindFinished(const QByteArray& searchStr)
0484 {
0485   if (m_source)
0486     m_source->parseFindResults(searchStr);
0487   m_albumListBox->setFocus();
0488   if (QItemSelectionModel* selModel = m_albumListBox->selectionModel()) {
0489     if (QAbstractItemModel* model = m_albumListBox->model()) {
0490       if (model->rowCount() > 0) {
0491         selModel->select(model->index(0, 0),
0492             QItemSelectionModel::Select | QItemSelectionModel::Rows);
0493       }
0494     }
0495   }
0496 }
0497 
0498 /**
0499  * Process finished album data.
0500  *
0501  * @param albumStr album track data received
0502  */
0503 void ServerImportDialog::slotAlbumFinished(const QByteArray& albumStr)
0504 {
0505   if (m_source) {
0506     m_source->setStandardTags(getStandardTags());
0507     m_source->setAdditionalTags(getAdditionalTags());
0508     m_source->setCoverArt(getCoverArt());
0509     m_source->parseAlbumResults(albumStr);
0510   }
0511   emit trackDataUpdated();
0512 }
0513 
0514 /**
0515  * Request track list from server.
0516  *
0517  * @param category category, e.g. "release"
0518  * @param id internal ID
0519  */
0520 void ServerImportDialog::requestTrackList(const QString& category,
0521                                           const QString& id)
0522 {
0523   ServerImporterConfig cfg;
0524   getImportSourceConfig(&cfg);
0525   if (m_source)
0526     m_source->getTrackList(&cfg, category, id);
0527 }
0528 
0529 /**
0530  * Request track list from server.
0531  *
0532  * @param index model index of list containing an AlbumListItem
0533  */
0534 void ServerImportDialog::requestTrackList(const QModelIndex& index)
0535 {
0536   if (m_source) {
0537     QString text, category, id;
0538     m_source->getAlbumListModel()->getItem(index.row(), text, category, id);
0539     if (!id.isEmpty()) {
0540       requestTrackList(category, id);
0541     }
0542   }
0543 }
0544 
0545 /**
0546  * Show help.
0547  */
0548 void ServerImportDialog::showHelp()
0549 {
0550   if (m_source && m_source->helpAnchor()) {
0551     ContextHelp::displayHelp(QString::fromLatin1(m_source->helpAnchor()));
0552   }
0553 }