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 }