/*************************************************************************** copyright : (C) 2006 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 "yahoofetcher.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 #include #include #include #include #include #include #include namespace { static const int YAHOO_MAX_RETURNS_TOTAL = 20; static const char* YAHOO_BASE_URL = "http://search.yahooapis.com/AudioSearchService/V1/albumSearch"; static const char* YAHOO_APP_ID = "tellico-robby"; } using Tellico::Fetch::YahooFetcher; YahooFetcher::YahooFetcher(TQObject* parent_, const char* name_) : Fetcher(parent_, name_), m_xsltHandler(0), m_limit(YAHOO_MAX_RETURNS_TOTAL), m_job(0), m_started(false) { } YahooFetcher::~YahooFetcher() { delete m_xsltHandler; m_xsltHandler = 0; } TQString YahooFetcher::defaultName() { return i18n("Yahoo! Audio Search"); } TQString YahooFetcher::source() const { return m_name.isEmpty() ? defaultName() : m_name; } bool YahooFetcher::canFetch(int type) const { return type == Data::Collection::Album; } void YahooFetcher::readConfigHook(const TDEConfigGroup& config_) { Q_UNUSED(config_); } void YahooFetcher::search(FetchKey key_, const TQString& value_) { m_key = key_; m_value = value_; m_started = true; m_start = 1; m_total = -1; doSearch(); } void YahooFetcher::continueSearch() { m_started = true; doSearch(); } void YahooFetcher::doSearch() { // myDebug() << "YahooFetcher::search() - value = " << value_ << endl; KURL u(TQString::fromLatin1(YAHOO_BASE_URL)); u.addQueryItem(TQString::fromLatin1("appid"), TQString::fromLatin1(YAHOO_APP_ID)); u.addQueryItem(TQString::fromLatin1("type"), TQString::fromLatin1("all")); u.addQueryItem(TQString::fromLatin1("output"), TQString::fromLatin1("xml")); u.addQueryItem(TQString::fromLatin1("start"), TQString::number(m_start)); u.addQueryItem(TQString::fromLatin1("results"), TQString::number(YAHOO_MAX_RETURNS_TOTAL)); 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(TQString::fromLatin1("album"), m_value); break; case Person: u.addQueryItem(TQString::fromLatin1("artist"), m_value); break; // raw is used for the entry updates case Raw: // u.removeQueryItem(TQString::fromLatin1("type")); // u.addQueryItem(TQString::fromLatin1("type"), TQString::fromLatin1("phrase")); u.setQuery(u.query() + '&' + m_value); break; default: kdWarning() << "YahooFetcher::search() - key not recognized: " << m_key << endl; stop(); return; } // myDebug() << "YahooFetcher::search() - url: " << u.url() << endl; m_job = TDEIO::get(u, false, false); connect(m_job, TQ_SIGNAL(data(TDEIO::Job*, const TQByteArray&)), TQ_SLOT(slotData(TDEIO::Job*, const TQByteArray&))); connect(m_job, TQ_SIGNAL(result(TDEIO::Job*)), TQ_SLOT(slotComplete(TDEIO::Job*))); } void YahooFetcher::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 YahooFetcher::slotData(TDEIO::Job*, const TQByteArray& data_) { TQDataStream stream(m_data, IO_WriteOnly | IO_Append); stream.writeRawBytes(data_.data(), data_.size()); } void YahooFetcher::slotComplete(TDEIO::Job* job_) { // myDebug() << "YahooFetcher::slotComplete()" << endl; // since the fetch is done, don't worry about holding the job pointer m_job = 0; if(job_->error()) { job_->showErrorDialog(Kernel::self()->widget()); stop(); return; } if(m_data.isEmpty()) { myDebug() << "YahooFetcher::slotComplete() - no data" << endl; stop(); return; } #if 0 kdWarning() << "Remove debug from yahoofetcher.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() << "YahooFetcher::slotComplete() - server did not return valid XML." << endl; return; } // total is top level element, with attribute totalResultsAvailable TQDomElement e = dom.documentElement(); if(!e.isNull()) { m_total = e.attribute(TQString::fromLatin1("totalResultsAvailable")).toInt(); } } // assume yahoo 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() << "YahooFetcher::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")) + TQChar('/') + entry->field(TQString::fromLatin1("year")); SearchResult* r = new SearchResult(this, entry->title(), desc, entry->field(TQString::fromLatin1("isbn"))); m_entries.insert(r->uid, Data::EntryPtr(entry)); emit signalResultFound(r); } m_start = m_entries.count() + 1; m_hasMoreResults = m_start <= m_total; stop(); // required } Tellico::Data::EntryPtr YahooFetcher::fetchEntry(uint uid_) { Data::EntryPtr entry = m_entries[uid_]; if(!entry) { kdWarning() << "YahooFetcher::fetchEntry() - no entry in dict" << endl; return 0; } KURL imageURL = entry->field(TQString::fromLatin1("image")); if(!imageURL.isEmpty()) { TQString id = ImageFactory::addImage(imageURL, true); if(id.isEmpty()) { // rich text causes layout issues // emit signalStatus(i18n("The cover image for %1 could not be loaded.").arg( // entry->field(TQString::fromLatin1("title")))); message(i18n("The cover image could not be loaded."), MessageHandler::Warning); } else { entry->setField(TQString::fromLatin1("cover"), id); } } getTracks(entry); // don't want to show image urls in the fetch dialog entry->setField(TQString::fromLatin1("image"), TQString()); // no need for album id now ? entry->setField(TQString::fromLatin1("yahoo"), TQString()); return entry; } void YahooFetcher::initXSLTHandler() { TQString xsltfile = locate("appdata", TQString::fromLatin1("yahoo2tellico.xsl")); if(xsltfile.isEmpty()) { kdWarning() << "YahooFetcher::initXSLTHandler() - can not locate yahoo2tellico.xsl." << endl; return; } KURL u; u.setPath(xsltfile); delete m_xsltHandler; m_xsltHandler = new XSLTHandler(u); if(!m_xsltHandler->isValid()) { kdWarning() << "YahooFetcher::initXSLTHandler() - error in yahoo2tellico.xsl." << endl; delete m_xsltHandler; m_xsltHandler = 0; return; } } void YahooFetcher::getTracks(Data::EntryPtr entry_) { // get album id if(!entry_ || entry_->field(TQString::fromLatin1("yahoo")).isEmpty()) { return; } const TQString albumid = entry_->field(TQString::fromLatin1("yahoo")); KURL u(TQString::fromLatin1(YAHOO_BASE_URL)); u.setFileName(TQString::fromLatin1("songSearch")); u.addQueryItem(TQString::fromLatin1("appid"), TQString::fromLatin1(YAHOO_APP_ID)); u.addQueryItem(TQString::fromLatin1("type"), TQString::fromLatin1("all")); u.addQueryItem(TQString::fromLatin1("output"), TQString::fromLatin1("xml")); // go ahesad and ask for all results, since there might well be more than 10 songs on the CD u.addQueryItem(TQString::fromLatin1("results"), TQString::number(50)); u.addQueryItem(TQString::fromLatin1("albumid"), albumid); // myDebug() << "YahooFetcher::getTracks() - url: " << u.url() << endl; TQDomDocument dom = FileHandler::readXMLFile(u, false /*no namespace*/, true /*quiet*/); if(dom.isNull()) { myDebug() << "YahooFetcher::getTracks() - null dom returned" << endl; return; } #if 0 kdWarning() << "Remove debug from yahoofetcher.cpp" << endl; TQFile f(TQString::fromLatin1("/tmp/test.xml")); if(f.open(IO_WriteOnly)) { TQTextStream t(&f); t.setEncoding(TQTextStream::UnicodeUTF8); t << dom.toString(); } f.close(); #endif const TQString track = TQString::fromLatin1("track"); TQDomNodeList nodes = dom.documentElement().childNodes(); for(uint i = 0; i < nodes.count(); ++i) { TQDomElement e = nodes.item(i).toElement(); if(e.isNull()) { continue; } TQString t = e.namedItem(TQString::fromLatin1("Title")).toElement().text(); TQString n = e.namedItem(TQString::fromLatin1("Track")).toElement().text(); bool ok; int trackNum = Tellico::toUInt(n, &ok); // trackNum might be 0 if(t.isEmpty() || !ok || trackNum < 1) { continue; } TQString a = e.namedItem(TQString::fromLatin1("Artist")).toElement().text(); TQString l = e.namedItem(TQString::fromLatin1("Length")).toElement().text(); int len = Tellico::toUInt(l, &ok); TQString value = t + "::" + a; if(ok && len > 0) { value += + "::" + Tellico::minutes(len); } entry_->setField(track, insertValue(entry_->field(track), value, trackNum)); } } // not zero-based TQString YahooFetcher::insertValue(const TQString& str_, const TQString& value_, uint pos_) { TQStringList list = Data::Field::split(str_, true); for(uint i = list.count(); i < pos_; ++i) { list += TQString(); } bool write = true; if(!list[pos_-1].isNull()) { // for some reason, some songs are repeated from yahoo, with 0 length, don't overwrite that if(value_.contains(TQString::fromLatin1("::")) < 2) { // means no length value write = false; } } if(!value_.isEmpty() && write) { list[pos_-1] = value_; } return list.join(TQString::fromLatin1("; ")); } void YahooFetcher::updateEntry(Data::EntryPtr entry_) { // myDebug() << "YahooFetcher::updateEntry()" << endl; // limit to top 5 results m_limit = 5; TQString value; TQString title = entry_->field(TQString::fromLatin1("title")); if(!title.isEmpty()) { value += TQString::fromLatin1("album=") + title; } TQString artist = entry_->field(TQString::fromLatin1("artist")); if(!artist.isEmpty()) { if(!value.isEmpty()) { value += '&'; } value += TQString::fromLatin1("artist=") + artist; } if(!value.isEmpty()) { search(Fetch::Raw, value); return; } myDebug() << "YahooFetcher::updateEntry() - insufficient info to search" << endl; emit signalDone(this); // always need to emit this if not continuing with the search } Tellico::Fetch::ConfigWidget* YahooFetcher::configWidget(TQWidget* parent_) const { return new YahooFetcher::ConfigWidget(parent_, this); } YahooFetcher::ConfigWidget::ConfigWidget(TQWidget* parent_, const YahooFetcher*/*=0*/) : Fetch::ConfigWidget(parent_) { TQVBoxLayout* l = new TQVBoxLayout(optionsWidget()); l->addWidget(new TQLabel(i18n("This source has no options."), optionsWidget())); l->addStretch(); } TQString YahooFetcher::ConfigWidget::preferredName() const { return YahooFetcher::defaultName(); } #include "yahoofetcher.moc"