281 lines
8.2 KiB
C++
281 lines
8.2 KiB
C++
/*
|
|
* Cantata
|
|
*
|
|
* Copyright (c) 2011-2012 Craig Drummond <craig.p.drummond@gmail.com>
|
|
*
|
|
* ----
|
|
*
|
|
* This program 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "songinfoprovider.h"
|
|
#include "lyricspage.h"
|
|
#include "ultimatelyricsprovider.h"
|
|
#include "ultimatelyricsreader.h"
|
|
#include "network.h"
|
|
#include "settings.h"
|
|
#include "mainwindow.h"
|
|
#include <QtCore/QFuture>
|
|
#include <QtCore/QFutureWatcher>
|
|
#include <QtCore/QSettings>
|
|
#include <QtCore/QtConcurrentRun>
|
|
#include <QTextBrowser>
|
|
#include <QtCore/QFile>
|
|
#include <QtCore/QDir>
|
|
#include <QtGui/QIcon>
|
|
#include <QtGui/QToolButton>
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
#include <KDE/KAction>
|
|
#include <KDE/KLocale>
|
|
#include <KDE/KXMLGUIClient>
|
|
#include <KDE/KActionCollection>
|
|
#endif
|
|
|
|
static QString changeExt(const QString &f, const QString &newExt)
|
|
{
|
|
QString newStr(f);
|
|
int dotPos(newStr.lastIndexOf('.'));
|
|
|
|
if (-1==dotPos) {
|
|
newStr+=newExt;
|
|
} else {
|
|
newStr.remove(dotPos, newStr.length());
|
|
newStr+=newExt;
|
|
}
|
|
return newStr;
|
|
}
|
|
|
|
static const QLatin1String constLyricsDir("lyrics/");
|
|
const QLatin1String LyricsPage::constExtension(".lyrics");
|
|
|
|
static QString cacheFile(QString artist, QString title, bool createDir=false)
|
|
{
|
|
title.replace("/", "_");
|
|
artist.replace("/", "_");
|
|
return QDir::toNativeSeparators(Network::cacheDir(constLyricsDir+artist+'/', createDir))+title+LyricsPage::constExtension;
|
|
}
|
|
|
|
typedef QList<UltimateLyricsProvider *> ProviderList;
|
|
|
|
bool CompareLyricProviders(const UltimateLyricsProvider* a, const UltimateLyricsProvider* b) {
|
|
return a->relevance() < b->relevance();
|
|
}
|
|
|
|
LyricsPage::LyricsPage(QWidget *parent)
|
|
: QWidget(parent)
|
|
// , reader(new UltimateLyricsReader(this))
|
|
, currentRequest(0)
|
|
{
|
|
setupUi(this);
|
|
|
|
providers=UltimateLyricsReader().Parse(QString(":lyrics/ultimate_providers.xml"));
|
|
foreach (UltimateLyricsProvider* provider, providers) {
|
|
connect(provider, SIGNAL(InfoReady(int, const QString &)), SLOT(resultReady(int, const QString &)));
|
|
}
|
|
|
|
// Parse the ultimate lyrics xml file in the background
|
|
// QFuture<ProviderList> future = QtConcurrent::run(reader.data(), &UltimateLyricsReader::Parse,
|
|
// QString(":lyrics/ultimate_providers.xml"));
|
|
// QFutureWatcher<ProviderList> *watcher = new QFutureWatcher<ProviderList>(this);
|
|
// watcher->setFuture(future);
|
|
// connect(watcher, SIGNAL(finished()), SLOT(ultimateLyricsParsed()));
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
KXMLGUIClient *client=dynamic_cast<KXMLGUIClient *>(parent);
|
|
if (client) {
|
|
refreshAction = client->actionCollection()->addAction("refreshlyrics");
|
|
} else {
|
|
refreshAction = new KAction(this);
|
|
}
|
|
refreshAction->setText(i18n("Refresh"));
|
|
#else
|
|
refreshAction = new QAction(tr("Refresh"), this);
|
|
#endif
|
|
refreshAction->setIcon(QIcon::fromTheme("view-refresh"));
|
|
connect(refreshAction, SIGNAL(triggered()), SLOT(update()));
|
|
MainWindow::initButton(refresh);
|
|
refresh->setDefaultAction(refreshAction);
|
|
text->setZoom(Settings::self()->lyricsZoom());
|
|
}
|
|
|
|
LyricsPage::~LyricsPage()
|
|
{
|
|
foreach (UltimateLyricsProvider* provider, providers) {
|
|
delete provider;
|
|
}
|
|
}
|
|
|
|
void LyricsPage::save()
|
|
{
|
|
Settings::self()->saveLyricsZoom(text->zoom());
|
|
}
|
|
|
|
void LyricsPage::setEnabledProviders(const QStringList &providerList)
|
|
{
|
|
foreach (UltimateLyricsProvider* provider, providers) {
|
|
provider->set_enabled(false);
|
|
provider->set_relevance(0xFFFF);
|
|
}
|
|
|
|
int relevance=0;
|
|
foreach (const QString &p, providerList) {
|
|
UltimateLyricsProvider *provider=providerByName(p);
|
|
if (provider) {
|
|
provider->set_enabled(true);
|
|
provider->set_relevance(relevance++);
|
|
}
|
|
}
|
|
|
|
qSort(providers.begin(), providers.end(), CompareLyricProviders);
|
|
}
|
|
|
|
void LyricsPage::update()
|
|
{
|
|
update(currentSong, true);
|
|
}
|
|
|
|
void LyricsPage::update(const Song &song, bool force)
|
|
{
|
|
if (force || song.artist!=currentSong.artist || song.title!=currentSong.title) {
|
|
currentRequest++;
|
|
currentSong=song;
|
|
currentProvider=-1;
|
|
if (song.title.isEmpty() || song.artist.isEmpty()) {
|
|
text->setText(QString());
|
|
return;
|
|
}
|
|
|
|
if (!Settings::self()->mpdDir().isEmpty()) {
|
|
// Check for MPD file...
|
|
QString mpdLyrics=changeExt(Settings::self()->mpdDir()+song.file, constExtension);
|
|
|
|
// Stop here if we found lyrics in the mpd dir.
|
|
if (setLyricsFromFile(mpdLyrics)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check for cached file...
|
|
QString file=cacheFile(song.artist, song.title);
|
|
|
|
if (force && QFile::exists(file)) {
|
|
// Delete the cached lyrics file when the user
|
|
// is force-fully re-fetching the lyrics.
|
|
// Afterwards we'll simply do getLyrics() to get
|
|
// the new ones.
|
|
QFile::remove(file);
|
|
} else if (setLyricsFromFile(file)) {
|
|
// We just wanted a normal update without
|
|
// explicit re-fetching. We can return
|
|
// here because we got cached lyrics and
|
|
// we don't want an explicit re-fetch.
|
|
return;
|
|
}
|
|
|
|
getLyrics();
|
|
}
|
|
}
|
|
|
|
void LyricsPage::resultReady(int id, const QString &lyrics)
|
|
{
|
|
if (id != currentRequest)
|
|
return;
|
|
|
|
if (lyrics.isEmpty()) {
|
|
getLyrics();
|
|
} else {
|
|
text->setText(lyrics);
|
|
|
|
QFile f(cacheFile(currentSong.artist, currentSong.title, true));
|
|
|
|
if (f.open(QIODevice::WriteOnly)) {
|
|
QTextStream(&f) << text->toPlainText();
|
|
f.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
UltimateLyricsProvider* LyricsPage::providerByName(const QString &name) const
|
|
{
|
|
foreach (UltimateLyricsProvider* provider, providers) {
|
|
if (provider->name() == name) {
|
|
return provider;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void LyricsPage::getLyrics()
|
|
{
|
|
for(;;) {
|
|
currentProvider++;
|
|
if (currentProvider<providers.count()) {
|
|
UltimateLyricsProvider *prov=providers.at(currentProvider++);
|
|
if (prov && prov->is_enabled()) {
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
text->setText(i18n("Fetching lyrics via %1", prov->name()));
|
|
#else
|
|
text->setText(tr("Fetching lyrics via %1").arg(prov->name()));
|
|
#endif
|
|
prov->FetchInfo(currentRequest, currentSong);
|
|
return;
|
|
}
|
|
} else {
|
|
#ifdef ENABLE_KDE_SUPPORT
|
|
text->setText(i18n("No lyrics found"));
|
|
#else
|
|
text->setText(tr("No lyrics found"));
|
|
#endif
|
|
currentProvider=-1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool LyricsPage::setLyricsFromFile(const QString &filePath) const
|
|
{
|
|
bool success = false;
|
|
|
|
QFile f(filePath);
|
|
|
|
if (f.exists() && f.open(QIODevice::ReadOnly)) {
|
|
// Read the file using a QTextStream so we get
|
|
// automatic UTF8 detection.
|
|
QTextStream inputStream(&f);
|
|
|
|
text->setText(inputStream.readAll());
|
|
f.close();
|
|
|
|
success = true;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
// void LyricsPage::ultimateLyricsParsed()
|
|
// {
|
|
// QFutureWatcher<ProviderList>* watcher = static_cast<QFutureWatcher<ProviderList>*>(sender());
|
|
// QStringList names;
|
|
//
|
|
// foreach (UltimateLyricsProvider* provider, watcher->result()) {
|
|
// providers.append(provider);
|
|
// connect(provider, SIGNAL(InfoReady(int, const QString &)), SLOT(resultReady(int, const QString &)));
|
|
// }
|
|
//
|
|
// watcher->deleteLater();
|
|
// reader.reset();
|
|
// emit providersUpdated();
|
|
// }
|