Discogs.com API patch revision 2
Discogs.com fetcher revision 2:
- Removed bogus #include <boost/lexical_cast.hpp> which i used for some debugging tweaks..
- Added configuration dialog for setting the API key
- Cover images
- Artist info within track list
- Duration within track list
- Cleanup, removal of std::cerr debugging messages
diff -ruN tellico-branch/src/fetch/discogsfetcher.cpp tellico-1.3.x/src/fetch/discogsfetcher.cpp
--- tellico-branch/src/fetch/discogsfetcher.cpp 1970-01-01 01:00:00.000000000 +0100
+++ tellico-1.3.x/src/fetch/discogsfetcher.cpp 2007-12-01 22:34:55.000000000 +0100
@@ -0,0 +1,722 @@
+/***************************************************************************
+ copyright : (C) 2007 by Robby Stephenson
+ email : robby <at> periapsis.org
+
+ discogs fetcher contributed by Roman L. Senn <roman.l.senn <at> gmail.com>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of version 2 of the GNU General Public License as *
+ * published by the Free Software Foundation; *
+ * *
+ ***************************************************************************/
+
+#include "discogsfetcher.h"
+#include "messagehandler.h"
+#include "../translators/xslthandler.h"
+#include "../translators/tellicoimporter.h"
+#include "../imagefactory.h"
+#include "../tellico_kernel.h"
+#include "../tellico_utils.h"
+#include "../collection.h"
+#include "../entry.h"
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <kio/job.h>
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qfile.h>
+#include <qwhatsthis.h>
+
+#include <iostream>
+
+namespace {
+ static const char* DISCOGS_API_URL = "http://www.discogs.com";
+ static const int DISCOGS_MAX_RETURNS_TOTAL = 20;
+// static const char* DISCOGS_API_KEY = "aa507659db";
+// static const char* DISCOGS_APP_ID = "tellico-robby";
+}
+
+using Tellico::Fetch::DiscogsFetcher;
+
+DiscogsFetcher::DiscogsFetcher(QObject* parent_, const char* name_)
+ : Fetcher(parent_, name_), m_xsltHandler(0),
+ m_limit(DISCOGS_MAX_RETURNS_TOTAL), m_job(0), m_started(false) {
+}
+
+DiscogsFetcher::~DiscogsFetcher() {
+ delete m_xsltHandler;
+ m_xsltHandler = 0;
+}
+
+QString DiscogsFetcher::defaultName() {
+ return i18n("Discogs Audio Search");
+}
+
+QString DiscogsFetcher::source() const {
+ return m_name.isEmpty() ? defaultName() : m_name;
+}
+
+bool DiscogsFetcher::canFetch(int type) const {
+ return type == Data::Collection::Album;
+}
+
+void DiscogsFetcher::readConfigHook(const KConfigGroup& config_) {
+ QString k = config_.readEntry("API key");
+ if(!k.isEmpty()) {
+ m_apiKey = k;
+ }
+ m_fetchImages = config_.readBoolEntry("Fetch Images", true);
+ m_fields = config_.readListEntry("Custom Fields");
+}
+
+void DiscogsFetcher::search(FetchKey key_, const QString& value_) {
+ m_key = key_;
+ m_value = value_;
+ m_started = true;
+ m_start = 1;
+ m_total = -1;
+ doSearch();
+}
+
+void DiscogsFetcher::continueSearch() {
+ m_started = true;
+ doSearch();
+}
+
+void DiscogsFetcher::doSearch() {
+
+ KURL u(QString::fromLatin1(DISCOGS_API_URL));
+ u.setFileName(QString::fromLatin1("search"));
+ u.addQueryItem(QString::fromLatin1("api_key"), m_apiKey);
+ u.addQueryItem(QString::fromLatin1("type"), QString::fromLatin1("release"));
+ u.addQueryItem(QString::fromLatin1("f"), QString::fromLatin1("xml"));
+ u.addQueryItem(QString::fromLatin1("q"), m_value);
+
+ if(!canFetch(Kernel::self()->collectionType())) {
+ message(i18n("%1 does not allow searching for this collection type.").arg(source()), MessageHandler::Warning);
+ stop();
+ return;
+ }
+
+/* switch(m_key) {
+ case Title:
+ u.addQueryItem(QString::fromLatin1("album"), m_value);
+ break;
+
+ case Person:
+ u.addQueryItem(QString::fromLatin1("artist"), m_value);
+ break;
+
+ // raw is used for the entry updates
+ case Raw:
+// u.removeQueryItem(QString::fromLatin1("type"));
+// u.addQueryItem(QString::fromLatin1("type"), QString::fromLatin1("phrase"));
+ u.setQuery(u.query() + '&' + m_value);
+ break;
+
+ default:
+ kdWarning() << "DiscogsFetcher::search() - key not recognized: " << m_key << endl;
+ stop();
+ return;
+ }*/
+
+ m_job = KIO::get(u, false, false);
+ connect(m_job, SIGNAL(data(KIO::Job*, const QByteArray&)),
+ SLOT(slotData(KIO::Job*, const QByteArray&)));
+ connect(m_job, SIGNAL(result(KIO::Job*)),
+ SLOT(slotComplete(KIO::Job*)));
+}
+
+void DiscogsFetcher::stop() {
+ if(!m_started) {
+ return;
+ }
+ if(m_job) {
+ m_job->kill();
+ m_job = 0;
+ }
+ m_data.truncate(0);
+ m_started = false;
+ emit signalDone(this);
+}
+
+void DiscogsFetcher::slotData(KIO::Job*, const QByteArray& data_) {
+ QDataStream stream(m_data, IO_WriteOnly | IO_Append);
+ stream.writeRawBytes(data_.data(), data_.size());
+}
+
+void DiscogsFetcher::slotComplete(KIO::Job* job_) {
+// myDebug() << "DiscogsFetcher::slotComplete()" << endl;
+//
+ QDomDocument dom;
+
+ if(job_->error()) {
+ job_->showErrorDialog(Kernel::self()->widget());
+ stop();
+ return;
+ }
+
+ if(m_data.isEmpty()) {
+ myDebug() << "DiscogsFetcher::slotComplete() - no data" << endl;
+ stop();
+ return;
+ }
+
+#if 1
+ kdWarning() << "Remove debug from discogsfetcher.cpp" << endl;
+ QFile f(QString::fromLatin1(m_jobentries.contains(job_)?"/tmp/release.xml":"/tmp/result.xml"));
+ if(f.open(IO_WriteOnly)) {
+ QTextStream t(&f);
+ t.setEncoding(QTextStream::UnicodeUTF8);
+ t << QCString(m_data, m_data.size()+1);
+ }
+ f.close();
+#endif
+
+ if(!m_xsltHandler) {
+ initXSLTHandler();
+ if(!m_xsltHandler) { // probably an error somewhere in the stylesheet loading
+ stop();
+ return;
+ }
+ }
+
+ /* Process search results */
+ if(job_ == m_job)
+ {
+ // since the fetch is done, don't worry about holding the job pointer
+ m_job = 0;
+
+
+ if(m_total == -1) {
+ if(!dom.setContent(m_data, false)) {
+ kdWarning() << "DiscogsFetcher::slotComplete() - server did not return valid XML." << endl;
+ return;
+ }
+ // total is top level element, with attribute totalResultsAvailable
+ QDomElement e = dom.documentElement();
+ if(!e.isNull()) {
+ m_total = e.attribute(QString::fromLatin1("totalResultsAvailable")).toInt();
+ }
+ }
+
+ // assume discogs is always utf-8
+ QString str = m_xsltHandler->applyStylesheet(QString::fromUtf8(m_data, m_data.size()));
+ Import::TellicoImporter imp(str);
+ Data::CollPtr coll = imp.collection();
+ if(!coll) {
+ myDebug() << "DiscogsFetcher::slotComplete() - no collection pointer" << endl;
+ stop();
+ return;
+ }
+
+ int count = 0;
+ Data::EntryVec entries = coll->entries();
+ for(Data::EntryVec::Iterator entry = entries.begin(); count < m_limit && entry != entries.end();
++entry, ++count) {
+ if(!m_started) {
+ // might get aborted
+ break;
+ }
+ QString desc
+ = entry->field(QString::fromLatin1("artist"))
+ + QChar('/')
+ + entry->field(QString::fromLatin1("album"))
+ + QChar('/')
+ + entry->field(QString::fromLatin1("label"));
+
+ SearchResult* r = new SearchResult(this, entry->title(), desc, entry->field(QString::fromLatin1("discogs")));
+ m_entries.insert(r->uid, Data::EntryPtr(entry));
+ emit signalResultFound(r);
+ }
+ m_start = m_entries.count() + 1;
+ m_hasMoreResults = m_start <= m_total;
+ }
+
+ /* Process Discogs Release details... */
+ if(m_jobentries.contains(job_))
+ {
+ Data::EntryPtr entry = m_jobentries[job_];
+
+ if(!dom.setContent(m_data, false)) {
+ kdWarning() << "DiscogsFetcher::slotComplete() - server did not return valid XML." << endl;
+ return;
+ }
+
+ QDomElement doc = dom.documentElement();
+ QDomElement rel = doc.firstChild().toElement();
+ QString year;
+
+ for(uint k = 0; k < rel.childNodes().length(); ++k)
+ {
+ QDomElement child = rel.childNodes().item(k).toElement();
+
+ if(child.nodeName() == "released")
+ {
+ year = child.text();
+
+ if(year[4] == '-')
+ year = year.left(4);
+
+ entry->setField(QString::fromLatin1("year"), year);
+ continue;
+
+ } else if(child.nodeName() == "country") {
+
+ entry->setField(QString::fromLatin1("country"), child.text());
+
+ } else if(child.nodeName() == "notes") {
+
+ entry->setField(QString::fromLatin1("notes"), child.text());
+
+ } else if(child.nodeName() == "images") {
+
+ doCover(entry, child);
+
+ }
+
+// std::cerr << "elm: " << child.nodeName() << std::endl;
+ }
+
+
+ // Get genre
+ QDomNodeList nodes = dom.elementsByTagName(QString::fromLatin1("genre"));
+ QString genre;
+
+ for(uint i = 0; i < nodes.count(); ++i) {
+ if(genre.length() != 0)
+ genre += ", ";
+
+ genre += nodes.item(i).toElement().text();
+ }
+
+ if(genre.length())
+ entry->setField(QString::fromLatin1("genre"), genre);
+
+ // Get style
+ nodes = dom.elementsByTagName(QString::fromLatin1("style"));
+ QString style;
+
+ for(uint i = 0; i < nodes.count(); ++i) {
+ if(style.length() != 0)
+ style += ", ";
+
+ style += nodes.item(i).toElement().text();
+ }
+
+ if(style.length())
+ {
+ if(style.length())
+ entry->setField(QString::fromLatin1("style"), style);
+
+ genre += QString(" (") + style + ")";
+ }
+
+ // Get label
+ nodes = dom.elementsByTagName(QString::fromLatin1("label"));
+ QString label;
+
+ for(uint i = 0; i < nodes.count(); ++i) {
+ if(label.length() != 0)
+ label += ", ";
+
+ label += nodes.item(i).toElement().attribute("name");
+ }
+
+ if(label.length()) {
+ entry->setField(QString::fromLatin1("label"), label);
+ }
+
+ // Get format
+ nodes = dom.elementsByTagName(QString::fromLatin1("format"));
+ QString format;
+ QString medium;
+
+ for(uint i = 0; i < nodes.count(); ++i) {
+ if(format.length() != 0)
+ format += ", ";
+
+ format += nodes.item(i).toElement().attribute("name");
+
+ if(!medium.length())
+ medium = format;
+
+ QDomNodeList dnodes = nodes.item(i).toElement().elementsByTagName("description");
+
+ for(uint j = 0; j < dnodes.count(); ++j) {
+ format += ", ";
+ format += dnodes.item(j).toElement().text();
+ }
+
+ int q = nodes.item(i).toElement().attribute("qty").toInt();
+
+ if(q > 1) {
+ format += " (";
+ format += QString::number(q) + " discs)";
+ }
+ }
+
+ if(medium.length()) {
+ if(medium.left(2) == QString::fromLatin1("CD")) {
+ medium = "Compact Disc";
+ }
+
+ entry->setField(QString::fromLatin1("medium"), medium);
+ }
+
+ // get the tracks
+ QString track = QString::fromLatin1("track");
+ nodes = dom.elementsByTagName(track);
+ int n = 1;
+
+ for(uint i = 0; i < nodes.count(); ++i) {
+ QDomElement e = nodes.item(i).toElement();
+ if(e.isNull()) {
+ continue;
+ }
+
+ QString pos = e.namedItem(QString::fromLatin1("position")).toElement().text();
+ QString title = e.namedItem(QString::fromLatin1("title")).toElement().text();
+ QString dur = e.namedItem(QString::fromLatin1("duration")).toElement().text();
+
+ bool ok;
+ int trackNum = Tellico::toUInt(pos, &ok);
+ if(!ok) trackNum = Tellico::toUInt(pos.mid(1), &ok);
+ if(!ok) trackNum = Tellico::toUInt(pos.mid(2), &ok);
+
+ // trackNum might be 0
+ if(title.isEmpty() || !ok || trackNum < 1) {
+ continue;
+ }
+
+ if(trackNum != n || QString::number(trackNum) != pos)
+ title = pos + " - " + title;
+
+ QDomNodeList artists = e.elementsByTagName(QString::fromLatin1("artist"));
+ QString addArtist;
+
+ for(uint j = 0; j < artists.count(); ++j) {
+ e = artists.item(j).toElement();
+ QString name = e.namedItem(QString::fromLatin1("name")).toElement().text();
+ QString role = e.namedItem(QString::fromLatin1("role")).toElement().text();
+
+ if(name == entry->field(QString::fromLatin1("artist"))) {
+ name = "";
+ }
+ if(name.right(5) == ", The") {
+ name = QString("The ") + name.left(name.length() - 5);
+ }
+ if(addArtist.length() && (name.length() || role.length())) {
+ addArtist += ", ";
+ }
+ if(role == QString::fromLatin1("Featuring")) {
+ addArtist += "feat. " + name;
+ } else if(role.length()) {
+ if(name.length() && role != QString::fromLatin1("Remix")) {
+ addArtist += role + " by " + name;
+ } else {
+ if(name.left(4) == "The ") {
+ name = name.mid(4);
+ }
+ addArtist += name + " Mix";
+ }
+ } else {
+ addArtist += name;
+ }
+ }
+
+ title += QString::fromLatin1("::");
+
+ if(addArtist.length() && !title.contains(addArtist)) {
+ title += /*QString(" (") +*/ addArtist /*+ ")"*/;
+ }
+ if(!dur.isEmpty()) {
+ title += QString::fromLatin1("::") + dur;
+ }
+
+ entry->setField(track, insertValue(entry->field(track), title, trackNum));
+ ++n;
+ }
+
+ m_jobentries.erase(job_);
+ }
+
+ stop(); // required
+}
+
+void DiscogsFetcher::getTracks(Data::EntryPtr entry_) {
+ // get album id
+ if(!entry_ || entry_->field(QString::fromLatin1("discogs")).isEmpty()) {
+ return;
+ }
+
+ /* For the detailed track listing and more specific infos about the record
+ * we are requesting the Discogs Release page by another async request. */
+ const QString releaseURL = entry_->field(QString::fromLatin1("discogs"));
+ KURL u(releaseURL);
+
+ u.addQueryItem(QString::fromLatin1("key"), m_apiKey);
+ u.addQueryItem(QString::fromLatin1("f"), QString::fromLatin1("xml"));
+
+ KIO::Job *job;
+ m_jobentries[(job = KIO::get(u, false, false))] = entry_;
+ connect(job, SIGNAL(data(KIO::Job*, const QByteArray&)),
+ SLOT(slotData(KIO::Job*, const QByteArray&)));
+ connect(job, SIGNAL(result(KIO::Job*)),
+ SLOT(slotComplete(KIO::Job*)));
+
+ m_started = true;
+
+ return;
+
+ QDomDocument dom = FileHandler::readXMLFile(u, false, false);
+ if(dom.isNull()) {
+// std::cerr << "null dom" << std::endl;
+ myDebug() << "DiscogsFetcher::getTracks() - null dom returned" << endl;
+ return;
+ }
+
+// myDebug() << "DiscogsFetcher::getTracks() - url: " << u.url() << endl;
+
+#if 0
+ kdWarning() << "Remove debug from discogsfetcher.cpp" << endl;
+ QFile f(QString::fromLatin1("/tmp/test.xml"));
+ if(f.open(IO_WriteOnly)) {
+ QTextStream t(&f);
+ t.setEncoding(QTextStream::UnicodeUTF8);
+ t << dom.toString();
+ }
+ f.close();
+#endif
+
+ const QString track = QString::fromLatin1("track");
+
+ QDomNodeList nodes = dom.documentElement().childNodes();
+ for(uint i = 0; i < nodes.count(); ++i) {
+ QDomElement e = nodes.item(i).toElement();
+ if(e.isNull()) {
+ continue;
+ }
+ QString t = e.namedItem(QString::fromLatin1("Title")).toElement().text();
+ QString n = e.namedItem(QString::fromLatin1("Track")).toElement().text();
+ bool ok;
+ int trackNum = Tellico::toUInt(n, &ok);
+ // trackNum might be 0
+ if(t.isEmpty() || !ok || trackNum < 1) {
+ continue;
+ }
+ QString a = e.namedItem(QString::fromLatin1("Artist")).toElement().text();
+ QString l = e.namedItem(QString::fromLatin1("Length")).toElement().text();
+
+ int len = Tellico::toUInt(l, &ok);
+ QString value = t + "::" + a;
+ if(ok && len > 0) {
+ value += + "::" + Tellico::minutes(len);
+ }
+ entry_->setField(track, insertValue(entry_->field(track), value, trackNum));
+ }
+}
+
+Tellico::Data::EntryPtr DiscogsFetcher::fetchEntry(uint uid_) {
+ Data::EntryPtr entry = m_entries[uid_];
+ if(!entry) {
+ kdWarning() << "DiscogsFetcher::fetchEntry() - no entry in dict" << endl;
+ return 0;
+ }
+
+ KURL imageURL = entry->field(QString::fromLatin1("image"));
+ if(!imageURL.isEmpty()) {
+ QString id = ImageFactory::addImage(imageURL, true);
+ if(id.isEmpty()) {
+ // rich text causes layout issues
+// emit signalStatus(i18n("<qt>The cover image for <i>%1</i> could not be loaded.</qt>").arg(
+// entry->field(QString::fromLatin1("title"))));
+ message(i18n("The cover image could not be loaded."), MessageHandler::Warning);
+ } else {
+ entry->setField(QString::fromLatin1("cover"), id);
+ }
+ }
+
+ getTracks(entry);
+
+ // don't want to show image urls in the fetch dialog
+ entry->setField(QString::fromLatin1("image"), QString::null);
+ return entry;
+}
+
+void DiscogsFetcher::initXSLTHandler() {
+ QString xsltfile = locate("appdata", QString::fromLatin1("discogs2tellico.xsl"));
+ if(xsltfile.isEmpty()) {
+ kdWarning() << "DiscogsFetcher::initXSLTHandler() - can not locate discogs2tellico.xsl." << endl;
+ return;
+ }
+
+ KURL u;
+ u.setPath(xsltfile);
+
+ delete m_xsltHandler;
+ m_xsltHandler = new XSLTHandler(u);
+ if(!m_xsltHandler->isValid()) {
+ kdWarning() << "DiscogsFetcher::initXSLTHandler() - error in discogs2tellico.xsl." << endl;
+ delete m_xsltHandler;
+ m_xsltHandler = 0;
+ return;
+ }
+}
+
+// not zero-based
+QString DiscogsFetcher::insertValue(const QString& str_, const QString& value_, uint pos_) {
+ QStringList list = Data::Field::split(str_, true);
+ for(uint i = list.count(); i < pos_; ++i) {
+ list += QString::null;
+ }
+ bool write = true;
+ if(!list[pos_-1].isNull()) {
+ // for some reason, some songs are repeated from discogs, with 0 length, don't overwrite that
+ if(value_.contains(QString::fromLatin1("::")) < 2) { // means no length value
+ write = false;
+ }
+ }
+ if(!value_.isEmpty() && write) {
+ list[pos_-1] = value_;
+ }
+ return list.join(QString::fromLatin1("; "));
+}
+
+/* The Discogs Release page (in XML format) can contain several (cover-)images
+ * in the form:
+ *
+ * <images>
+ * <image type="[primary/secondary]" width="[width]" height="[height]" uri="[uri]">
+ * ...
+ * </images>
+ *
+ * A detailed description of these records can be found at
+ * http://www.discogs.com/help/api in the section "About Images".
+ */
+void DiscogsFetcher::doCover(Data::EntryPtr entry_, const QDomElement& elm_) {
+ const QString cover = QString::fromLatin1("cover");
+ const QDomNodeList images = elm_.elementsByTagName(QString::fromLatin1("image"));
+ long maxSize = 0.0;
+ bool havePrimary = false;
+ KURL u;
+
+ /* We're after the biggest primary image... */
+ for(uint i = 0; i < images.count(); ++i) {
+ QDomElement imgElem = images.item(i).toElement();
+ QString imageURL = imgElem.attribute(QString::fromLatin1("uri"));
+ long size = imgElem.attribute(QString::fromLatin1("width")).toDouble() *
+ imgElem.attribute(QString::fromLatin1("height")).toDouble();
+ bool pri = (imgElem.attribute(QString::fromLatin1("type")) == QString::fromLatin1("primary"));
+ if(!imageURL.isEmpty() && size >= maxSize && pri >= havePrimary) {
+ u = imageURL;
+ maxSize = size;
+ havePrimary = pri;
+ }
+ }
+
+ QString id = ImageFactory::addImage(u, true);
+ if(!id.isEmpty()) {
+// std::cerr << "Cover image: " << u.url() << std::endl;
+ entry_->setField(cover, id);
+ entry_->setField(QString::fromLatin1("image"), u.url());
+ }
+}
+
+void DiscogsFetcher::updateEntry(Data::EntryPtr entry_) {
+// myDebug() << "DiscogsFetcher::updateEntry()" << endl;
+ // limit to top 5 results
+ m_limit = 5;
+
+ QString value;
+ QString title = entry_->field(QString::fromLatin1("title"));
+ if(!title.isEmpty()) {
+ value += /*QString::fromLatin1("album=") +*/ title;
+ }
+ QString artist = entry_->field(QString::fromLatin1("artist"));
+ if(!artist.isEmpty()) {
+ if(!value.isEmpty()) {
+ value += ' ';
+ }
+ value += /*QString::fromLatin1("artist=") +*/ artist;
+ }
+ if(!value.isEmpty()) {
+ search(Fetch::Raw, value);
+ return;
+ }
+
+ myDebug() << "DiscogsFetcher::updateEntry() - insufficient info to search" << endl;
+ emit signalDone(this); // always need to emit this if not continuing with the search
+}
+
+Tellico::Fetch::ConfigWidget* DiscogsFetcher::configWidget(QWidget* parent_) const {
+ return new DiscogsFetcher::ConfigWidget(parent_, this);
+}
+
+DiscogsFetcher::ConfigWidget::ConfigWidget(QWidget* parent_, const DiscogsFetcher* fetcher_)
+ : Fetch::ConfigWidget(parent_) {
+ QGridLayout* l = new QGridLayout(optionsWidget(), 2, 2);
+ l->setSpacing(4);
+ l->setColStretch(1, 10);
+
+ int row = -1;
+ QLabel* label = new QLabel(i18n("API &key: "), optionsWidget());
+ l->addWidget(label, ++row, 0);
+
+ m_apiKeyEdit = new KLineEdit(optionsWidget());
+ connect(m_apiKeyEdit, SIGNAL(textChanged(const QString&)), SLOT(slotSetModified()));
+ l->addWidget(m_apiKeyEdit, row, 1);
+ QString w = i18n("With your discogs.com account you receive an API key for the usage of their XML-based
interface (See http://www.discogs.com/help/api).");
+ QWhatsThis::add(label, w);
+ QWhatsThis::add(m_apiKeyEdit, w);
+ label->setBuddy(m_apiKeyEdit);
+
+ m_fetchImageCheck = new QCheckBox(i18n("Download cover &image"), optionsWidget());
+ connect(m_fetchImageCheck, SIGNAL(clicked()), SLOT(slotSetModified()));
+ ++row;
+ l->addMultiCellWidget(m_fetchImageCheck, row, row, 0, 1);
+ w = i18n("The cover image may be downloaded as well. However, too many large images in the "
+ "collection may degrade performance.");
+ QWhatsThis::add(m_fetchImageCheck, w);
+
+ l->setRowStretch(++row, 10);
+
+ // now add additional fields widget
+ addFieldsWidget(DiscogsFetcher::customFields(), fetcher_ ? fetcher_->m_fields : QStringList());
+
+ if(fetcher_) {
+ m_apiKeyEdit->setText(fetcher_->m_apiKey);
+ m_fetchImageCheck->setChecked(fetcher_->m_fetchImages);
+ } else {
+ m_apiKeyEdit->setText(QString::fromLatin1(""));
+ m_fetchImageCheck->setChecked(true);
+ }
+}
+
+void DiscogsFetcher::ConfigWidget::saveConfig(KConfigGroup& config_) {
+ QString apiKey = m_apiKeyEdit->text().stripWhiteSpace();
+ if(!apiKey.isEmpty()) {
+ config_.writeEntry("API key", apiKey);
+ }
+ config_.writeEntry("Fetch Images", m_fetchImageCheck->isChecked());
+
+ saveFieldsConfig(config_);
+ slotSetModified(false);
+}
+
+QString DiscogsFetcher::ConfigWidget::preferredName() const {
+ return DiscogsFetcher::defaultName();
+}
+
+Tellico::StringMap DiscogsFetcher::customFields() {
+ StringMap map;
+ map[QString::fromLatin1("discogs")] = i18n("Discogs Link");
+ return map;
+}
+
+#include "discogsfetcher.moc"
diff -ruN tellico-branch/src/fetch/discogsfetcher.h tellico-1.3.x/src/fetch/discogsfetcher.h
--- tellico-branch/src/fetch/discogsfetcher.h 1970-01-01 01:00:00.000000000 +0100
+++ tellico-1.3.x/src/fetch/discogsfetcher.h 2007-12-01 03:50:32.000000000 +0100
@@ -0,0 +1,123 @@
+/***************************************************************************
+ copyright : (C) 2006 by Robby Stephenson
+ email : robby <at> periapsis.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of version 2 of the GNU General Public License as *
+ * published by the Free Software Foundation; *
+ * *
+ ***************************************************************************/
+
+#ifndef DISCOGSFETCHER_H
+#define DISCOGSFETCHER_H
+
+namespace Tellico {
+ class XSLTHandler;
+}
+
+#include "fetcher.h"
+#include "configwidget.h"
+#include "../datavectors.h"
+#include <klineedit.h>
+
+#include <qdom.h>
+#include <qcstring.h> // for QByteArray
+#include <qguardedptr.h>
+
+namespace KIO {
+ class Job;
+}
+
+namespace Tellico {
+ namespace Fetch {
+
+/**
+ * A fetcher for Amazon.com.
+ *
+ * @author Robby Stephenson
+ */
+class DiscogsFetcher : public Fetcher {
+Q_OBJECT
+
+public:
+ /**
+ */
+ DiscogsFetcher(QObject* parent, const char* name = 0);
+ /**
+ */
+ virtual ~DiscogsFetcher();
+
+ /**
+ */
+ virtual QString source() const;
+ virtual bool isSearching() const { return m_started; }
+ virtual void search(FetchKey key, const QString& value);
+ virtual void continueSearch();
+ // amazon can search title or person
+ virtual bool canSearch(FetchKey k) const { return k == Title || k == Person; }
+ virtual void stop();
+ virtual Data::EntryPtr fetchEntry(uint uid);
+ virtual Type type() const { return Discogs; }
+ virtual bool canFetch(int type) const;
+ virtual void readConfigHook(const KConfigGroup& config);
+
+ virtual void updateEntry(Data::EntryPtr entry);
+
+ /**
+ * Returns a widget for modifying the fetcher's config.
+ */
+ virtual Fetch::ConfigWidget* configWidget(QWidget* parent) const;
+
+ static StringMap customFields();
+
+ class ConfigWidget : public Fetch::ConfigWidget {
+ public:
+ ConfigWidget(QWidget* parent_, const DiscogsFetcher* fetcher = 0);
+ virtual void saveConfig(KConfigGroup&);
+ virtual QString preferredName() const;
+ private:
+ KLineEdit *m_apiKeyEdit;
+ QCheckBox* m_fetchImageCheck;
+ };
+ friend class ConfigWidget;
+
+ static QString defaultName();
+
+private slots:
+ void slotData(KIO::Job* job, const QByteArray& data);
+ void slotComplete(KIO::Job* job);
+
+private:
+ void initXSLTHandler();
+ void doSearch();
+ void getTracks(Data::EntryPtr entry);
+ QString insertValue(const QString& str, const QString& value, uint pos);
+ void doCover(Data::EntryPtr entry, const QDomElement &elm);
+
+ XSLTHandler* m_xsltHandler;
+ int m_limit;
+ int m_start;
+ int m_total;
+
+ void startQuery(Data::EntryPtr entry);
+
+ QByteArray m_data;
+ QMap<int, Data::EntryPtr> m_entries; // they get modified after collection is created, so can't be const
+ QGuardedPtr<KIO::Job> m_job;
+ QMap< KIO::Job *, Data::EntryPtr > m_jobentries;
+
+ FetchKey m_key;
+ QString m_value;
+ bool m_started;
+
+ bool m_fetchImages;
+ QString m_apiKey;
+ QStringList m_fields;
+};
+
+ } // end namespace
+} // end namespace
+#endif
diff -ruN tellico-branch/src/fetch/fetch.h tellico-1.3.x/src/fetch/fetch.h
--- tellico-branch/src/fetch/fetch.h 2007-11-18 08:28:14.000000000 +0100
+++ tellico-1.3.x/src/fetch/fetch.h 2007-11-15 20:55:05.000000000 +0100
@@ -52,7 +52,8 @@
CrossRef,
Citebase,
Arxiv,
- Bibsonomy
+ Bibsonomy,
+ Discogs
};
}
diff -ruN tellico-branch/src/fetch/fetchmanager.cpp tellico-1.3.x/src/fetch/fetchmanager.cpp
--- tellico-branch/src/fetch/fetchmanager.cpp 2007-12-01 02:29:48.000000000 +0100
+++ tellico-1.3.x/src/fetch/fetchmanager.cpp 2007-12-01 22:27:10.000000000 +0100
@@ -34,6 +34,7 @@
#include "entrezfetcher.h"
#include "execexternalfetcher.h"
#include "yahoofetcher.h"
+#include "discogsfetcher.h"
#include "animenfofetcher.h"
#include "ibsfetcher.h"
#include "isbndbfetcher.h"
@@ -284,6 +285,10 @@
f = new YahooFetcher(this);
break;
+ case Discogs:
+ f = new DiscogsFetcher(this);
+ break;
+
case AnimeNfo:
f = new AnimeNfoFetcher(this);
break;
@@ -338,6 +343,7 @@
vec.append(SRUFetcher::libraryOfCongress(this));
vec.append(new ISBNdbFetcher(this));
vec.append(new YahooFetcher(this));
+ vec.append(new DiscogsFetcher(this));
vec.append(new AnimeNfoFetcher(this));
vec.append(new ArxivFetcher(this));
// only add IBS if user includes italian
@@ -410,6 +416,7 @@
list.append(TypePair(EntrezFetcher::defaultName(), Entrez));
list.append(TypePair(ExecExternalFetcher::defaultName(), ExecExternal));
list.append(TypePair(YahooFetcher::defaultName(), Yahoo));
+ list.append(TypePair(DiscogsFetcher::defaultName(), Discogs));
list.append(TypePair(AnimeNfoFetcher::defaultName(), AnimeNfo));
list.append(TypePair(IBSFetcher::defaultName(), IBS));
list.append(TypePair(ISBNdbFetcher::defaultName(), ISBNdb));
@@ -494,6 +501,9 @@
case Yahoo:
w = new YahooFetcher::ConfigWidget(parent_);
break;
+ case Discogs:
+ w = new DiscogsFetcher::ConfigWidget(parent_);
+ break;
case AnimeNfo:
w = new AnimeNfoFetcher::ConfigWidget(parent_);
break;
@@ -540,6 +550,7 @@
case Entrez: return EntrezFetcher::defaultName();
case ExecExternal: return ExecExternalFetcher::defaultName();
case Yahoo: return YahooFetcher::defaultName();
+ case Discogs: return DiscogsFetcher::defaultName();
case AnimeNfo: return AnimeNfoFetcher::defaultName();
case IBS: return IBSFetcher::defaultName();
case ISBNdb: return ISBNdbFetcher::defaultName();
@@ -609,6 +620,8 @@
name = QString::fromLatin1("exec"); break;
case Yahoo:
name = favIcon("http://yahoo.com"); break;
+ case Discogs:
+ name = favIcon("http://discogs.com"); break;
case AnimeNfo:
name = favIcon("http://animenfo.com"); break;
case IBS:
diff -ruN tellico-branch/src/fetch/Makefile.am tellico-1.3.x/src/fetch/Makefile.am
--- tellico-branch/src/fetch/Makefile.am 2007-11-18 08:28:14.000000000 +0100
+++ tellico-1.3.x/src/fetch/Makefile.am 2007-11-15 19:57:34.000000000 +0100
@@ -7,7 +7,7 @@
libfetch_a_SOURCES = amazonfetcher.cpp animenfofetcher.cpp arxivfetcher.cpp \
bibsonomyfetcher.cpp citebasefetcher.cpp configwidget.cpp crossreffetcher.cpp \
- entrezfetcher.cpp execexternalfetcher.cpp fetcher.cpp fetchmanager.cpp \
+ discogsfetcher.cpp entrezfetcher.cpp execexternalfetcher.cpp fetcher.cpp fetchmanager.cpp \
gcstarpluginfetcher.cpp ibsfetcher.cpp imdbfetcher.cpp isbndbfetcher.cpp messagehandler.cpp \
srufetcher.cpp yahoofetcher.cpp z3950connection.cpp z3950fetcher.cpp
@@ -23,7 +23,7 @@
fetcher.h fetcher.cpp fetchmanager.h fetchmanager.cpp \
amazonfetcher.h amazonfetcher.cpp z3950fetcher.h z3950fetcher.cpp \
imdbfetcher.h imdbfetcher.cpp fetch.h configwidget.h configwidget.cpp \
-entrezfetcher.h entrezfetcher.cpp \
+discogsfetcher.h entrezfetcher.h entrezfetcher.cpp \
execexternalfetcher.h execexternalfetcher.cpp \
messagehandler.h messagehandler.cpp \
z3950connection.h z3950connection.cpp \
diff -ruN tellico-branch/xslt/discogs2tellico.xsl tellico-1.3.x/xslt/discogs2tellico.xsl
--- tellico-branch/xslt/discogs2tellico.xsl 1970-01-01 01:00:00.000000000 +0100
+++ tellico-1.3.x/xslt/discogs2tellico.xsl 2007-12-01 22:29:50.000000000 +0100
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns="http://periapsis.org/tellico/"
+ xmlns:exsl="http://exslt.org/common"
+ extension-element-prefixes="exsl"
+ version="1.0">
+
+<!--
+ ===================================================================
+ Tellico XSLT file - used for importing Discogs release search data.
+
+ Copyright (C) 2004-2006 Robby Stephenson - robby <at> periapsis.org
+
+ This XSLT stylesheet is designed to be used with the 'Tellico'
+ application, which can be found at http://www.periapsis.org/tellico/
+
+ ===================================================================
+-->
+
+<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"
+ doctype-public="-//Robby Stephenson/DTD Tellico V10.0//EN"
+ doctype-system="http://periapsis.org/tellico/dtd/v10/tellico.dtd"/>
+
+<xsl:template match="/">
+ <tellico syntaxVersion="10">
+ <collection title="Discogs API" type="4"> <!-- 4 is music -->
+ <fields>
+ <field name="_default"/>
+ <!-- the importer will actually download the image and ignore this field -->
+ <field flags="0" title="Discogs Release" category="General" format="4" type="7" name="discogs"/>
+ <field flags="0" title="Image" category="Images" format="4" type="7" name="image"/>
+ </fields>
+ <xsl:for-each select="resp/searchresults/result[@type='release']">
+ <xsl:apply-templates select="."/>
+ </xsl:for-each>
+ </collection>
+ </tellico>
+</xsl:template>
+
+<xsl:template match="resp/searchresults/result">
+ <entry>
+
+ <title>
+ <xsl:value-of select="title"/>
+ </title>
+
+ <uri>
+ <xsl:value-of select="uri"/>
+ </uri>
+
+ <artists>
+ <artist>
+ <xsl:value-of select="substring-before(title,' - ')"/>
+ </artist>
+ </artists>
+
+ <album>
+ <xsl:value-of select="substring-after(title,' - ')"/>
+ </album>
+
+ <labels>
+ <label>
+ <xsl:value-of select="substring-before(substring-after(normalize-space(summary),'Label:
'),' Catalog#:')"/>
+ </label>
+ </labels>
+
+ <discogs>
+ <xsl:value-of select="uri"/>
+ </discogs>
+
+ </entry>
+
+</xsl:template>
+
+<xsl:template name="year">
+ <xsl:param name="value"/>
+ <!-- assume that discogs always puts the year first -->
+ <xsl:value-of select="substring($value, 0, 5)"/>
+</xsl:template>
+
+</xsl:stylesheet>
diff -ruN tellico-branch/xslt/Makefile.am tellico-1.3.x/xslt/Makefile.am
--- tellico-branch/xslt/Makefile.am 2007-11-18 08:28:23.000000000 +0100
+++ tellico-1.3.x/xslt/Makefile.am 2007-11-15 22:47:46.000000000 +0100
@@ -4,7 +4,7 @@
vhs-logo.png tellico-common.xsl mods2tellico.xsl \
amazon2tellico.xsl MARC21slim2MODS3.xsl MARC21slimUtils.xsl \
pubmed2tellico.xsl tellico2onix.xsl UNIMARC2MODS3.xsl \
- tellico2html.js yahoo2tellico.xsl isbndb2tellico.xsl \
+ tellico2html.js yahoo2tellico.xsl discogs2tellico.xsl isbndb2tellico.xsl \
bluray-logo.png hddvd-logo.png gcstar2tellico.xsl \
xmp2tellico.xsl crossref2tellico.xsl arxiv2tellico.xsl \
referencer2tellico.xsl welcome.html
@@ -15,7 +15,7 @@
dvd-logo.png record-logo.png vhs-logo.png tellico-common.xsl \
mods2tellico.xsl amazon2tellico.xsl MARC21slim2MODS3.xsl \
MARC21slimUtils.xsl pubmed2tellico.xsl tellico2onix.xsl \
- UNIMARC2MODS3.xsl tellico2html.js yahoo2tellico.xsl \
+ UNIMARC2MODS3.xsl tellico2html.js yahoo2tellico.xsl discogs2tellico.xsl \
isbndb2tellico.xsl bluray-logo.png hddvd-logo.png gcstar2tellico.xsl \
xmp2tellico.xsl crossref2tellico.xsl arxiv2tellico.xsl \
referencer2tellico.xsl welcome.html
_______________________________________________
tellico-users mailing list
tellico-users@...
http://forge.novell.com/mailman/listinfo/tellico-users