File indexing completed on 2024-05-12 05:10:10

0001 /***************************************************************************
0002     Copyright (C) 2023 Robby Stephenson <robby@periapsis.org>
0003  ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or         *
0008  *   modify it under the terms of the GNU General Public License as        *
0009  *   published by the Free Software Foundation; either version 2 of        *
0010  *   the License or (at your option) version 3 or any later version        *
0011  *   accepted by the membership of KDE e.V. (or its successor approved     *
0012  *   by the membership of KDE e.V.), which shall act as a proxy            *
0013  *   defined in Section 14 of version 3 of the license.                    *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful,       *
0016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0018  *   GNU General Public License for more details.                          *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0022  *                                                                         *
0023  ***************************************************************************/
0024 
0025 #include "discogsimporter.h"
0026 #include "../collections/musiccollection.h"
0027 #include "../images/imagefactory.h"
0028 #include "../core/filehandler.h"
0029 #include "../utils/mapvalue.h"
0030 #include "../tellico_debug.h"
0031 
0032 #include <KConfigGroup>
0033 #include <KLocalizedString>
0034 
0035 #include <QLineEdit>
0036 #include <QVBoxLayout>
0037 #include <QFormLayout>
0038 #include <QGroupBox>
0039 #include <QUrlQuery>
0040 #include <QJsonDocument>
0041 #include <QJsonObject>
0042 #include <QFile>
0043 
0044 namespace {
0045   static const char* DISCOGS_API_URL = "https://api.discogs.com";
0046 }
0047 
0048 using Tellico::Import::DiscogsImporter;
0049 
0050 DiscogsImporter::DiscogsImporter() : Import::Importer(), m_widget(nullptr), m_userEdit(nullptr), m_tokenEdit(nullptr) {
0051 }
0052 
0053 void DiscogsImporter::setConfig(KSharedConfig::Ptr config_) {
0054   m_config = config_;
0055 }
0056 
0057 bool DiscogsImporter::canImport(int type) const {
0058   return type == Data::Collection::Album;
0059 }
0060 
0061 Tellico::Data::CollPtr DiscogsImporter::collection() {
0062   if(m_coll) {
0063     return m_coll;
0064   }
0065 
0066   if(!m_config) {
0067     m_config = KSharedConfig::openConfig();
0068   }
0069 
0070   KConfigGroup cg(m_config, QStringLiteral("ImportOptions - Discogs"));
0071   m_user = cg.readEntry("User ID");
0072   m_token = cg.readEntry("API Key"); // same config name as used in discogsfetcher
0073 
0074   if(m_widget) {
0075     m_user = m_userEdit->text().trimmed();
0076     m_token = m_tokenEdit->text().trimmed();
0077   }
0078 
0079   if(m_user.isEmpty()) {
0080     setStatusMessage(i18n("A valid user ID must be entered."));
0081     return Data::CollPtr();
0082   }
0083 
0084   m_coll = new Data::MusicCollection(true);
0085   loadPage(1);
0086   return m_coll;
0087 }
0088 
0089 void DiscogsImporter::loadPage(int page_) {
0090   QUrl u(QString::fromLatin1(DISCOGS_API_URL));
0091   u.setPath(QStringLiteral("/users/%1/collection/folders/0/releases").arg(m_user));
0092   QUrlQuery q;
0093   q.addQueryItem(QLatin1String("page"), QString::number(page_));
0094   if(!m_token.isEmpty()) {
0095     q.addQueryItem(QStringLiteral("token"), m_token);
0096   }
0097   u.setQuery(q);
0098 //  myDebug() << u;
0099   const QByteArray data = FileHandler::readDataFile(u, true /* quiet */);
0100 
0101 #if 0
0102   myWarning() << "Remove output debug from discogsimporter.cpp";
0103   QFile f(QLatin1String("/tmp/discogs.json"));
0104   if(f.open(QIODevice::WriteOnly)) {
0105     QDataStream t(&f);
0106     t << data;
0107   }
0108   f.close();
0109 #endif
0110 
0111   QJsonDocument doc = QJsonDocument::fromJson(data);
0112   const QVariantMap resultMap = doc.object().toVariantMap();
0113   if(resultMap.contains(QStringLiteral("message")) && mapValue(resultMap, "id").isEmpty()) {
0114     const auto& msg = mapValue(resultMap, "message");
0115     myLog() << "DiscogsFetcher -" << msg;
0116   }
0117   const int totalPages = mapValue(resultMap, "pagination", "pages").toInt();
0118 //  myDebug() << "total pages:" << totalPages << "current:" << page_;
0119   foreach(const QVariant& release, resultMap.value(QLatin1String("releases")).toList()) {
0120     Data::EntryPtr entry(new Data::Entry(m_coll));
0121     const auto releaseMap = release.toMap();
0122     populateEntry(entry, releaseMap.value(QLatin1String("basic_information")).toMap());
0123 
0124     const QString rating = mapValue(releaseMap, "rating");
0125     if(!rating.isEmpty() && rating != QLatin1String("0")) {
0126       entry->setField(QStringLiteral("rating"), rating);
0127     }
0128     entry->setField(QStringLiteral("comments"), mapValue(releaseMap, "notes", "value"));
0129     m_coll->addEntries(entry);
0130   }
0131 
0132   if(totalPages > page_) {
0133     loadPage(page_+1);
0134   }
0135 }
0136 
0137 void DiscogsImporter::populateEntry(Data::EntryPtr entry_, const QVariantMap& releaseMap_) {
0138   entry_->setField(QStringLiteral("title"), mapValue(releaseMap_, "title"));
0139   const QString year = mapValue(releaseMap_, "year");
0140   if(year != QLatin1String("0")) {
0141     entry_->setField(QStringLiteral("year"), year);
0142   }
0143   // the styles value seems more like genres than the actual genres value
0144   entry_->setField(QStringLiteral("genre"),  mapValue(releaseMap_, "styles"));
0145 
0146   QStringList labels;
0147   foreach(const QVariant& label, releaseMap_.value(QLatin1String("labels")).toList()) {
0148     labels << mapValue(label.toMap(), "name");
0149   }
0150   entry_->setField(QStringLiteral("label"), labels.join(FieldFormat::delimiterString()));
0151 
0152   QStringList artists;
0153   foreach(const QVariant& artist, releaseMap_.value(QLatin1String("artists")).toList()) {
0154     artists << mapValue(artist.toMap(), "name");
0155   }
0156   artists.removeDuplicates(); // sometimes the same value is repeated
0157   entry_->setField(QStringLiteral("artist"), artists.join(FieldFormat::delimiterString()));
0158 
0159   foreach(const QVariant& format, releaseMap_.value(QLatin1String("formats")).toList()) {
0160     const QString formatName = mapValue(format.toMap(), "name");
0161     if(formatName == QLatin1String("CD")) {
0162       entry_->setField(QStringLiteral("medium"), i18n("Compact Disc"));
0163     } else if(formatName == QLatin1String("Vinyl")) {
0164       entry_->setField(QStringLiteral("medium"), i18n("Vinyl"));
0165     } else if(formatName == QLatin1String("Cassette")) {
0166       entry_->setField(QStringLiteral("medium"), i18n("Cassette"));
0167     } else if(formatName == QLatin1String("DVD")) {
0168       // sometimes a CD and DVD both are included. If we're using the CD, ignore the DVD
0169       entry_->setField(QStringLiteral("medium"), i18n("DVD"));
0170     }
0171   }
0172 
0173   QString coverUrl = mapValue(releaseMap_, "cover_image");
0174   if(coverUrl.isEmpty()) {
0175     coverUrl = mapValue(releaseMap_, "thumb");
0176   }
0177   if(!coverUrl.isEmpty()) {
0178     const QString id = ImageFactory::addImage(QUrl::fromUserInput(coverUrl), true /* quiet */);
0179     // empty image ID is ok
0180     entry_->setField(QStringLiteral("cover"), id);
0181   }
0182 }
0183 
0184 QWidget* DiscogsImporter::widget(QWidget* parent_) {
0185   if(m_widget) {
0186     return m_widget;
0187   }
0188   m_widget = new QWidget(parent_);
0189   QVBoxLayout* l = new QVBoxLayout(m_widget);
0190 
0191   QGroupBox* gbox = new QGroupBox(i18n("Discogs Options"), m_widget);
0192   QFormLayout* lay = new QFormLayout(gbox);
0193 
0194   m_userEdit = new QLineEdit(gbox);
0195   m_userEdit->setText(m_user);
0196   m_tokenEdit = new QLineEdit(gbox);
0197   m_tokenEdit->setText(m_token);
0198 
0199   lay->addRow(i18n("User ID:"), m_userEdit);
0200   lay->addRow(i18n("User token:"), m_tokenEdit);
0201 
0202   l->addWidget(gbox);
0203   l->addStretch(1);
0204 
0205   return m_widget;
0206 }