You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
414 lines
13 KiB
414 lines
13 KiB
/***************************************************************************
|
|
copyright : (C) 2008 by Robby Stephenson
|
|
email : robby@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; *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#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 "../tellico_debug.h"
|
|
|
|
#include <tdelocale.h>
|
|
#include <kstandarddirs.h>
|
|
#include <tdeconfig.h>
|
|
#include <tdeio/job.h>
|
|
|
|
#include <tqlabel.h>
|
|
#include <tqlayout.h>
|
|
#include <tqfile.h>
|
|
#include <tqwhatsthis.h>
|
|
|
|
//#define DISCOGS_TEST
|
|
|
|
namespace {
|
|
static const int DISCOGS_MAX_RETURNS_TOTAL = 20;
|
|
static const char* DISCOGS_API_URL = "http://www.discogs.com";
|
|
static const char* DISCOGS_API_KEY = "de6cb96534";
|
|
}
|
|
|
|
using Tellico::Fetch::DiscogsFetcher;
|
|
|
|
DiscogsFetcher::DiscogsFetcher(TQObject* parent_, const char* name_)
|
|
: Fetcher(parent_, name_), m_xsltHandler(0),
|
|
m_limit(DISCOGS_MAX_RETURNS_TOTAL), m_job(0), m_started(false),
|
|
m_apiKey(TQString::fromLatin1(DISCOGS_API_KEY)) {
|
|
}
|
|
|
|
DiscogsFetcher::~DiscogsFetcher() {
|
|
delete m_xsltHandler;
|
|
m_xsltHandler = 0;
|
|
}
|
|
|
|
TQString DiscogsFetcher::defaultName() {
|
|
return i18n("Discogs Audio Search");
|
|
}
|
|
|
|
TQString 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 TDEConfigGroup& config_) {
|
|
TQString 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 TQString& 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(TQString::fromLatin1(DISCOGS_API_URL));
|
|
u.addQueryItem(TQString::fromLatin1("f"), TQString::fromLatin1("xml"));
|
|
u.addQueryItem(TQString::fromLatin1("api_key"), m_apiKey);
|
|
|
|
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.setPath(TQString::fromLatin1("/search"));
|
|
u.addQueryItem(TQString::fromLatin1("q"), m_value);
|
|
u.addQueryItem(TQString::fromLatin1("type"), TQString::fromLatin1("release"));
|
|
break;
|
|
|
|
case Person:
|
|
u.setPath(TQString::fromLatin1("/artist/%1").arg(m_value));
|
|
break;
|
|
|
|
case Keyword:
|
|
u.setPath(TQString::fromLatin1("/search"));
|
|
u.addQueryItem(TQString::fromLatin1("q"), m_value);
|
|
u.addQueryItem(TQString::fromLatin1("type"), TQString::fromLatin1("all"));
|
|
break;
|
|
|
|
default:
|
|
kdWarning() << "DiscogsFetcher::search() - key not recognized: " << m_key << endl;
|
|
stop();
|
|
return;
|
|
}
|
|
|
|
#ifdef DISCOGS_TEST
|
|
u = KURL(TQString::fromLatin1("/home/robby/discogs-results.xml"));
|
|
#endif
|
|
// myDebug() << "DiscogsFetcher::search() - url: " << u.url() << endl;
|
|
|
|
m_job = TDEIO::get(u, false, false);
|
|
connect(m_job, TQT_SIGNAL(data(TDEIO::Job*, const TQByteArray&)),
|
|
TQT_SLOT(slotData(TDEIO::Job*, const TQByteArray&)));
|
|
connect(m_job, TQT_SIGNAL(result(TDEIO::Job*)),
|
|
TQT_SLOT(slotComplete(TDEIO::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(TDEIO::Job*, const TQByteArray& data_) {
|
|
TQDataStream stream(m_data, IO_WriteOnly | IO_Append);
|
|
stream.writeRawBytes(data_.data(), data_.size());
|
|
}
|
|
|
|
void DiscogsFetcher::slotComplete(TDEIO::Job* job_) {
|
|
// myDebug() << "DiscogsFetcher::slotComplete()" << endl;
|
|
if(job_->error()) {
|
|
job_->showErrorDialog(Kernel::self()->widget());
|
|
stop();
|
|
return;
|
|
}
|
|
|
|
if(m_data.isEmpty()) {
|
|
myDebug() << "DiscogsFetcher::slotComplete() - no data" << endl;
|
|
stop();
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
kdWarning() << "Remove debug from discogsfetcher.cpp" << endl;
|
|
TQFile f(TQString::fromLatin1("/tmp/test.xml"));
|
|
if(f.open(IO_WriteOnly)) {
|
|
TQTextStream t(&f);
|
|
t.setEncoding(TQTextStream::UnicodeUTF8);
|
|
t << TQCString(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;
|
|
}
|
|
}
|
|
|
|
if(m_total == -1) {
|
|
TQDomDocument dom;
|
|
if(!dom.setContent(m_data, false)) {
|
|
kdWarning() << "DiscogsFetcher::slotComplete() - server did not return valid XML." << endl;
|
|
return;
|
|
}
|
|
// total is /resp/searchresults/@numResults
|
|
TQDomNode n = dom.documentElement().namedItem(TQString::fromLatin1("resp"))
|
|
.namedItem(TQString::fromLatin1("searchresults"));
|
|
TQDomElement e = n.toElement();
|
|
if(!e.isNull()) {
|
|
m_total = e.attribute(TQString::fromLatin1("numResults")).toInt();
|
|
myDebug() << "total = " << m_total;
|
|
}
|
|
}
|
|
|
|
// assume discogs is always utf-8
|
|
TQString str = m_xsltHandler->applyStylesheet(TQString::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;
|
|
}
|
|
TQString desc = entry->field(TQString::fromLatin1("artist"))
|
|
+ TQChar('/')
|
|
+ entry->field(TQString::fromLatin1("label"));
|
|
|
|
SearchResult* r = new SearchResult(this, entry->title(), desc, TQString());
|
|
m_entries.insert(r->uid, Data::EntryPtr(entry));
|
|
emit signalResultFound(r);
|
|
}
|
|
m_start = m_entries.count() + 1;
|
|
// not sure how tospecify start in the REST url
|
|
// m_hasMoreResults = m_start <= m_total;
|
|
|
|
stop(); // required
|
|
}
|
|
|
|
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;
|
|
}
|
|
// one way we tell if this entry has been fully initialized is to
|
|
// check for a cover image
|
|
if(!entry->field(TQString::fromLatin1("cover")).isEmpty()) {
|
|
myLog() << "DiscogsFetcher::fetchEntry() - already downloaded " << entry->title() << endl;
|
|
return entry;
|
|
}
|
|
|
|
TQString release = entry->field(TQString::fromLatin1("discogs-id"));
|
|
if(release.isEmpty()) {
|
|
myDebug() << "DiscogsFetcher::fetchEntry() - no discogs release found" << endl;
|
|
return entry;
|
|
}
|
|
|
|
#ifdef DISCOGS_TEST
|
|
KURL u(TQString::fromLatin1("/home/robby/discogs-release.xml"));
|
|
#else
|
|
KURL u(TQString::fromLatin1(DISCOGS_API_URL));
|
|
u.setPath(TQString::fromLatin1("/release/%1").arg(release));
|
|
u.addQueryItem(TQString::fromLatin1("f"), TQString::fromLatin1("xml"));
|
|
u.addQueryItem(TQString::fromLatin1("api_key"), m_apiKey);
|
|
#endif
|
|
// myDebug() << "DiscogsFetcher::fetchEntry() - url: " << u << endl;
|
|
|
|
// quiet, utf8, allowCompressed
|
|
TQString output = FileHandler::readTextFile(u, true, true, true);
|
|
#if 0
|
|
kdWarning() << "Remove output debug from discogsfetcher.cpp" << endl;
|
|
TQFile f(TQString::fromLatin1("/tmp/test.xml"));
|
|
if(f.open(IO_WriteOnly)) {
|
|
TQTextStream t(&f);
|
|
t.setEncoding(TQTextStream::UnicodeUTF8);
|
|
t << output;
|
|
}
|
|
f.close();
|
|
#endif
|
|
|
|
Import::TellicoImporter imp(m_xsltHandler->applyStylesheet(output));
|
|
Data::CollPtr coll = imp.collection();
|
|
// getTracks(entry);
|
|
if(!coll) {
|
|
kdWarning() << "DiscogsFetcher::fetchEntry() - no collection pointer" << endl;
|
|
return entry;
|
|
}
|
|
|
|
if(coll->entryCount() > 1) {
|
|
myDebug() << "DiscogsFetcher::fetchEntry() - weird, more than one entry found" << endl;
|
|
}
|
|
|
|
const StringMap customFields = this->customFields();
|
|
for(StringMap::ConstIterator it = customFields.begin(); it != customFields.end(); ++it) {
|
|
if(!m_fields.contains(it.key())) {
|
|
coll->removeField(it.key());
|
|
}
|
|
}
|
|
|
|
// don't want to include id
|
|
coll->removeField(TQString::fromLatin1("discogs-id"));
|
|
|
|
entry = coll->entries().front();
|
|
m_entries.replace(uid_, entry);
|
|
return entry;
|
|
}
|
|
|
|
void DiscogsFetcher::initXSLTHandler() {
|
|
TQString xsltfile = locate("appdata", TQString::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;
|
|
}
|
|
}
|
|
|
|
void DiscogsFetcher::updateEntry(Data::EntryPtr entry_) {
|
|
// myDebug() << "DiscogsFetcher::updateEntry()" << endl;
|
|
|
|
TQString value;
|
|
TQString title = entry_->field(TQString::fromLatin1("title"));
|
|
if(!title.isEmpty()) {
|
|
search(Title, value);
|
|
return;
|
|
}
|
|
|
|
TQString artist = entry_->field(TQString::fromLatin1("artist"));
|
|
if(!artist.isEmpty()) {
|
|
search(Person, artist);
|
|
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(TQWidget* parent_) const {
|
|
return new DiscogsFetcher::ConfigWidget(parent_, this);
|
|
}
|
|
|
|
DiscogsFetcher::ConfigWidget::ConfigWidget(TQWidget* parent_, const DiscogsFetcher* fetcher_)
|
|
: Fetch::ConfigWidget(parent_) {
|
|
TQGridLayout* l = new TQGridLayout(optionsWidget(), 2, 2);
|
|
l->setSpacing(4);
|
|
l->setColStretch(1, 10);
|
|
|
|
int row = -1;
|
|
TQLabel* label = new TQLabel(i18n("API &key: "), optionsWidget());
|
|
l->addWidget(label, ++row, 0);
|
|
|
|
m_apiKeyEdit = new KLineEdit(optionsWidget());
|
|
connect(m_apiKeyEdit, TQT_SIGNAL(textChanged(const TQString&)), TQT_SLOT(slotSetModified()));
|
|
l->addWidget(m_apiKeyEdit, row, 1);
|
|
TQString 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).");
|
|
TQWhatsThis::add(label, w);
|
|
TQWhatsThis::add(m_apiKeyEdit, w);
|
|
label->setBuddy(m_apiKeyEdit);
|
|
|
|
m_fetchImageCheck = new TQCheckBox(i18n("Download cover &image"), optionsWidget());
|
|
connect(m_fetchImageCheck, TQT_SIGNAL(clicked()), TQT_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.");
|
|
TQWhatsThis::add(m_fetchImageCheck, w);
|
|
|
|
l->setRowStretch(++row, 10);
|
|
|
|
// now add additional fields widget
|
|
addFieldsWidget(DiscogsFetcher::customFields(), fetcher_ ? fetcher_->m_fields : TQStringList());
|
|
|
|
if(fetcher_) {
|
|
m_apiKeyEdit->setText(fetcher_->m_apiKey);
|
|
m_fetchImageCheck->setChecked(fetcher_->m_fetchImages);
|
|
} else {
|
|
m_apiKeyEdit->setText(TQString::fromLatin1(DISCOGS_API_KEY));
|
|
m_fetchImageCheck->setChecked(true);
|
|
}
|
|
}
|
|
|
|
void DiscogsFetcher::ConfigWidget::saveConfig(TDEConfigGroup& config_) {
|
|
TQString apiKey = m_apiKeyEdit->text().stripWhiteSpace();
|
|
if(!apiKey.isEmpty()) {
|
|
config_.writeEntry("API Key", apiKey);
|
|
}
|
|
config_.writeEntry("Fetch Images", m_fetchImageCheck->isChecked());
|
|
|
|
saveFieldsConfig(config_);
|
|
slotSetModified(false);
|
|
}
|
|
|
|
TQString DiscogsFetcher::ConfigWidget::preferredName() const {
|
|
return DiscogsFetcher::defaultName();
|
|
}
|
|
|
|
Tellico::StringMap DiscogsFetcher::customFields() {
|
|
StringMap map;
|
|
map[TQString::fromLatin1("producer")] = i18n("Producer");
|
|
map[TQString::fromLatin1("nationality")] = i18n("Nationality");
|
|
map[TQString::fromLatin1("discogs")] = i18n("Discogs Link");
|
|
return map;
|
|
}
|
|
|
|
#include "discogsfetcher.moc"
|