/* * Cantata * * Copyright (c) 2011-2013 Craig Drummond * */ /* This file is part of Clementine. Copyright 2010, David Sansome Clementine is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Clementine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Clementine. If not, see . */ #include "ultimatelyricsprovider.h" #include "networkaccessmanager.h" #include "song.h" #include #include static const int constRedirectLimit=5; static QString extract(const QString &source, const QString &begin, const QString &end) { int beginIdx = source.indexOf(begin); if (-1==beginIdx) { return QString(); } beginIdx += begin.length(); int endIdx = source.indexOf(end, beginIdx); if (-1==endIdx) { return QString(); } return source.mid(beginIdx, endIdx - beginIdx - 1); } static QString extractXmlTag(const QString &source, const QString &tag) { QRegExp re("<(\\w+).*>"); // ಠ_ಠ if (-1==re.indexIn(tag)) { return QString(); } return extract(source, tag, ""); } static QString exclude(const QString &source, const QString &begin, const QString &end) { int beginIdx = source.indexOf(begin); if (-1==beginIdx) { return source; } int endIdx = source.indexOf(end, beginIdx + begin.length()); if (-1==endIdx) { return source; } return source.left(beginIdx) + source.right(source.length() - endIdx - end.length()); } static QString excludeXmlTag(const QString &source, const QString &tag) { QRegExp re("<(\\w+).*>"); // ಠ_ಠ if (-1==re.indexIn(tag)) { return source; } return exclude(source, tag, ""); } static void applyExtractRule(const UltimateLyricsProvider::Rule &rule, QString *content) { foreach (const UltimateLyricsProvider::RuleItem& item, rule) { if (item.second.isNull()) { *content = extractXmlTag(*content, item.first); } else { *content = extract(*content, item.first, item.second); } } } static void applyExcludeRule(const UltimateLyricsProvider::Rule &rule, QString *content) { foreach (const UltimateLyricsProvider::RuleItem& item, rule) { if (item.second.isNull()) { *content = excludeXmlTag(*content, item.first); } else { *content = exclude(*content, item.first, item.second); } } } static QString noSpace(const QString &text) { QString ret(text); ret.remove(' '); return ret; } static QString firstChar(const QString &text) { return text.isEmpty() ? text : text[0].toLower(); } static QString titleCase(const QString &text) { if (0==text.length()) { return QString(); } if (1==text.length()) { return text[0].toUpper(); } return text[0].toUpper() + text.right(text.length() - 1).toLower(); } UltimateLyricsProvider::UltimateLyricsProvider() : enabled(true) , relevance(0) , redirectCount(0) { } void UltimateLyricsProvider::fetchInfo(int id, const Song &metadata) { #if QT_VERSION < 0x050000 const QTextCodec *codec = QTextCodec::codecForName(charset.toAscii().constData()); #else const QTextCodec *codec = QTextCodec::codecForName(charset.toLatin1().constData()); #endif if (!codec) { emit lyricsReady(id, QString()); return; } QString artistFixed=metadata.basicArtist(); // Fill in fields in the URL QString urlText(url); doUrlReplace("{artist}", artistFixed.toLower(), urlText); doUrlReplace("{artist2}", noSpace(artistFixed.toLower()), urlText); doUrlReplace("{album}", metadata.album.toLower(), urlText); doUrlReplace("{album2}", noSpace(metadata.album.toLower()), urlText); doUrlReplace("{title}", metadata.title.toLower(), urlText); doUrlReplace("{Artist}", artistFixed, urlText); doUrlReplace("{Album}", metadata.album, urlText); doUrlReplace("{ARTIST}", artistFixed.toUpper(), urlText); doUrlReplace("{year}", QString::number(metadata.year), urlText); doUrlReplace("{Title}", metadata.title, urlText); doUrlReplace("{Title2}", titleCase(metadata.title), urlText); doUrlReplace("{a}", firstChar(artistFixed), urlText); doUrlReplace("{track}", QString::number(metadata.track), urlText); // Fetch the URL, follow redirects redirectCount = 0; QNetworkReply *reply = NetworkAccessManager::self()->get(QNetworkRequest(QUrl(urlText))); requests[reply] = id; connect(reply, SIGNAL(finished()), SLOT(lyricsFetched())); } void UltimateLyricsProvider::lyricsFetched() { QNetworkReply *reply = qobject_cast(sender()); if (!reply) { return; } int id = requests.take(reply); reply->deleteLater(); if (QNetworkReply::NoError!=reply->error()) { //emit Finished(id); emit lyricsReady(id, QString()); return; } // Handle redirects QVariant redirect_target = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); if (redirect_target.isValid()) { if (redirectCount >= constRedirectLimit) { //emit Finished(id); emit lyricsReady(id, QString()); return; } QUrl target = redirect_target.toUrl(); if (target.scheme().isEmpty() || target.host().isEmpty()) { QString path = target.path(); target = reply->url(); target.setPath(path); } redirectCount ++; QNetworkReply* reply = NetworkAccessManager::self()->get(QNetworkRequest(target)); requests[reply] = id; connect(reply, SIGNAL(finished()), SLOT(lyricsFetched())); return; } #if QT_VERSION < 0x050000 const QTextCodec *codec = QTextCodec::codecForName(charset.toAscii().constData()); #else const QTextCodec *codec = QTextCodec::codecForName(charset.toLatin1().constData()); #endif const QString original_content = codec->toUnicode(reply->readAll()); // Check for invalid indicators foreach (const QString &indicator, invalidIndicators) { if (original_content.contains(indicator)) { //emit Finished(id); emit lyricsReady(id, QString()); return; } } QString lyrics; // Apply extract rules foreach (const Rule& rule, extractRules) { QString content = original_content; applyExtractRule(rule, &content); if (!content.isEmpty()) { lyrics = content; } } // Apply exclude rules foreach (const Rule& rule, excludeRules) { applyExcludeRule(rule, &lyrics); } lyrics=lyrics.trimmed(); emit lyricsReady(id, lyrics); } void UltimateLyricsProvider::doUrlReplace(const QString &tag, const QString &value, QString &u) const { if (!u.contains(tag)) { return; } // Apply URL character replacement QString valueCopy(value); foreach (const UltimateLyricsProvider::UrlFormat& format, urlFormats) { QRegExp re("[" + QRegExp::escape(format.first) + "]"); valueCopy.replace(re, format.second); } u.replace(tag, valueCopy, Qt::CaseInsensitive); }