Commit 4148b456 authored by Andrea Scarpino's avatar Andrea Scarpino

Cache lyrics (fixes #1)

- Fix Genius
- Drop LyricsWiki because it changes its policies
parent 9f05cb55
......@@ -8,7 +8,6 @@ SOURCES += \
src/lyric.cpp \
src/chartlyricsapi.cpp \
src/geniusapi.cpp \
src/lyricswikiapi.cpp \
src/provider.cpp \
src/lyricsmaniaapi.cpp
......@@ -19,7 +18,6 @@ HEADERS += \
src/chartlyricsapi.h \
src/geniusapi.h \
src/geniusapi_secret.h \
src/lyricswikiapi.h \
src/provider.h \
src/lyricsmaniaapi.h
......@@ -41,7 +39,7 @@ TRANSLATIONS += translations/harbour-lyrics-it.ts \
translations/harbour-lyrics-fi.ts \
translations/harbour-lyrics-fr_FR.ts
# LyricsWiki, Genius, LyricsMania
# Genius, LyricsMania
QT += webkitwidgets
# ChartLyrics
......
/*
The MIT License (MIT)
Copyright (c) 2014-2015 Andrea Scarpino <me@andreascarpino.it>
Copyright (c) 2014-2016 Andrea Scarpino <me@andreascarpino.it>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......
......@@ -30,7 +30,8 @@ Page {
allowedOrientations: Orientation.All
Column {
width: parent.width
x: Theme.horizontalPageMargin
width: parent.width - Theme.horizontalPageMargin * 2
PageHeader {
title: qsTr("Settings")
......@@ -54,12 +55,15 @@ Page {
MenuItem {
text: "LyricsMania"
}
MenuItem {
text: "LyricsWiki"
}
}
}
Button {
width: parent.width
text: qsTr("Clear cache")
onClicked: manager.clearCache();
}
}
Component.onCompleted: {
......@@ -68,10 +72,8 @@ Page {
provider.currentIndex = 0;
} else if (api === "Genius") {
provider.currentIndex = 1;
} else if (api === "LyricsMania") {
provider.currentIndex = 2;
} else {
provider.currentIndex = 3;
provider.currentIndex = 2;
}
}
......@@ -80,9 +82,8 @@ Page {
switch (provider.currentIndex) {
case 0: manager.setProvider("ChartLyrics"); break;
case 1: manager.setProvider("Genius"); break;
case 2: manager.setProvider("LyricsMania"); break;
case 3:
default: manager.setProvider("LyricsWiki");
case 2:
default: manager.setProvider("LyricsMania");
}
}
}
......
* Thu Jun 02 2016 Andrea Scarpino <me@andreascarpino.it> 0.5-1
- Add copy to clipboard (#2).
- Cache lyrics (#1).
- Fix Genius.
- Drop LyricsWiki.
* Sat Jul 18 2015 Andrea Scarpino <me@andreascarpino.it> 0.4-1
- Add LyricsMania
- Add LyricsMania.
* Fri Jul 17 2015 Andrea Scarpino <me@andreascarpino.it> 0.3.1-1
- Display current provider in main window.
......
......@@ -13,7 +13,7 @@ Name: harbour-lyrics
%{!?qtc_make:%define qtc_make make}
%{?qtc_builddir:%define _builddir %qtc_builddir}
Summary: Music lyrics application
Version: 0.4
Version: 0.5
Release: 1
Group: Qt/Qt
License: MIT
......@@ -31,7 +31,7 @@ BuildRequires: desktop-file-utils
%description
Music lyrics application.
It does support: ChartLyrics, LiricsWiki, Genius.
It does support: ChartLyrics, LyricsMania, Genius.
%prep
......
Name: harbour-lyrics
Summary: Music lyrics application
Version: 0.4
Version: 0.5
Release: 1
# The contents of the Group field should be one of the groups listed here:
# http://gitorious.org/meego-developer-tools/spectacle/blobs/master/data/GROUPS
......@@ -13,7 +13,7 @@ Sources:
- '%{name}-%{version}.tar.bz2'
Description: |
Music lyrics application.
It does support: ChartLyrics, LiricsWiki, Genius.
It does support: ChartLyrics, LyricsMania, Genius.
Configure: none
# The qtc5 builder inserts macros to allow QtCreator to have fine
# control over qmake/make execution
......
......@@ -34,15 +34,13 @@ class QNetworkAccessManager;
class ChartLyricsAPI : public Provider
{
Q_OBJECT
Q_INTERFACES(Provider)
public:
explicit ChartLyricsAPI(QObject *parent = 0);
virtual ~ChartLyricsAPI();
void getLyric(const QString &artist, const QString &song);
Q_SIGNALS:
void lyricFetched(Lyric *lyric, const bool &found);
private:
void onGetLyricResult();
......
......@@ -37,15 +37,13 @@ class QUrl;
class GeniusAPI : public Provider
{
Q_OBJECT
Q_INTERFACES(Provider)
public:
explicit GeniusAPI(QObject *parent = 0);
virtual ~GeniusAPI();
void getLyric(const QString &artist, const QString &song);
Q_SIGNALS:
void lyricFetched(Lyric *lyric, const bool &found);
private:
void onGetLyricResult();
void onGetLyricPageResult();
......
......@@ -26,12 +26,13 @@
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QSettings>
#include <QStandardPaths>
#include "chartlyricsapi.h"
#include "geniusapi.h"
#include "lyricsmaniaapi.h"
#include "lyricswikiapi.h"
#include "provider.h"
LyricsManager::LyricsManager(QObject *parent) :
......@@ -49,6 +50,12 @@ LyricsManager::~LyricsManager()
delete api;
}
void LyricsManager::clearCache()
{
const bool ret = QDir(getLyricsDir()).removeRecursively();
qDebug() << "Cache cleared:" << ret;
}
QString LyricsManager::getProvider() const
{
QString provider;
......@@ -58,10 +65,8 @@ QString LyricsManager::getProvider() const
provider = "ChartLyrics";
} else if (className.compare(QStringLiteral("GeniusAPI")) == 0) {
provider = "Genius";
} else if (className.compare(QStringLiteral("LyricsManiaAPI")) == 0) {
provider = "LyricsMania";
} else {
provider = "LyricsWiki";
provider = "LyricsMania";
}
qDebug() << "Default provider is" << provider;
......@@ -81,22 +86,57 @@ void LyricsManager::setProvider(const QString &provider)
} else if (provider.compare(QStringLiteral("Genius")) == 0) {
api = new GeniusAPI;
p = QStringLiteral("Genius");
} else if (provider.compare(QStringLiteral("LyricsMania")) == 0) {
} else {
api = new LyricsManiaAPI;
p = QStringLiteral("LyricsMania");
} else {
api = new LyricsWikiAPI;
p = QStringLiteral("LyricsWiki");
}
qDebug() << "Setting default provider to" << p;
settings->setValue("Provider", p);
}
QString LyricsManager::getLyricsDir() const
{
const QDir lyricsDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
if (!lyricsDir.exists()) {
if (!lyricsDir.mkpath(lyricsDir.absolutePath())) {
qCritical() << "Cannot create dir!";
}
}
return QString(lyricsDir.absolutePath() + QDir::separator());
}
void LyricsManager::storeLyric(Lyric *lyric, const bool &found)
{
if (found) {
qDebug() << "Caching lyric by" << lyric->artist() << lyric->song();
QFile f(getLyricsDir() + lyric->artist() + "_" + lyric->song() + ".txt");
f.open(QIODevice::WriteOnly);
f.write(lyric->text().toLatin1());
f.close();
}
}
void LyricsManager::search(const QString &artist, const QString &song)
{
qDebug() << "Querying" << api->metaObject()->className();
api->getLyric(artist, song);
QFile f(getLyricsDir() + artist + "_" + song + ".txt");
if (f.exists()) {
f.open(QIODevice::ReadOnly);
Lyric* lyric = new Lyric();
lyric->setArtist(artist);
lyric->setSong(song);
lyric->setText(f.readAll());
f.close();
connect(api, &Provider::lyricFetched, this, &LyricsManager::searchResult);
Q_EMIT searchResult(lyric, true);
} else {
qDebug() << "Querying" << api->metaObject()->className();
api->getLyric(artist, song);
connect(api, &Provider::lyricFetched, this, &LyricsManager::searchResult);
connect(api, &Provider::lyricFetched, this, &LyricsManager::storeLyric);
}
}
......@@ -40,6 +40,7 @@ public:
explicit LyricsManager(QObject *parent = 0);
virtual ~LyricsManager();
Q_INVOKABLE void clearCache();
Q_INVOKABLE QString getProvider() const;
Q_INVOKABLE void search(const QString &artist, const QString &song);
Q_INVOKABLE void setProvider(const QString &provider);
......@@ -48,6 +49,9 @@ Q_SIGNALS:
void searchResult(Lyric *lyric, const bool &found);
private:
QString getLyricsDir() const;
void storeLyric(Lyric *lyric, const bool &found);
QSettings *settings;
Provider *api;
......
......@@ -32,6 +32,8 @@
#include <QWebElement>
#include <QWebFrame>
#include "lyric.h"
const static QString BASE_URL = QStringLiteral("http://www.lyricsmania.com");
LyricsManiaAPI::LyricsManiaAPI(QObject *parent) :
......
......@@ -36,15 +36,13 @@ class QNetworkReply;
class LyricsManiaAPI : public Provider
{
Q_OBJECT
Q_INTERFACES(Provider)
public:
explicit LyricsManiaAPI(QObject *parent = 0);
virtual ~LyricsManiaAPI();
void getLyric(const QString &artist, const QString &song);
Q_SIGNALS:
void lyricFetched(Lyric *lyric, const bool &found);
private:
void onGetLyricPageResult();
......
/*
The MIT License (MIT)
Copyright (c) 2015 Andrea Scarpino <me@andreascarpino.it>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "lyricswikiapi.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QWebElement>
#include <QWebFrame>
#include <QWebPage>
#include <QUrl>
#include <QUrlQuery>
#include "lyric.h"
const static QString SITE_URL = QStringLiteral("http://lyrics.wikia.com");
LyricsWikiAPI::LyricsWikiAPI(QObject *parent) :
Provider(parent)
, network(new QNetworkAccessManager(this))
{
}
LyricsWikiAPI::~LyricsWikiAPI()
{
qDeleteAll(lyrics.keys());
qDeleteAll(lyrics.values());
delete network;
}
void LyricsWikiAPI::getLyric(const QString &artist, const QString &song)
{
qDebug() << "Requesting lyric for artist" << artist << ", song" << song;
QUrl url(SITE_URL);
QString artNoSpaces(artist);
artNoSpaces.replace(QChar::Space, QChar::fromLatin1('_'));
QString songNoSpaces(song);
songNoSpaces.replace(QChar::Space, QChar::fromLatin1('_'));
url.setPath("/wiki/" + artNoSpaces + QChar::fromLatin1(':') + songNoSpaces);
qDebug() << "Requesting lyric page" << url;
QNetworkRequest req(url);
QNetworkReply* reply = network->get(req);
Lyric* lyric = new Lyric();
lyric->setArtist(artist);
lyric->setSong(song);
lyrics.insert(reply, lyric);
connect(reply, &QNetworkReply::finished, this, &LyricsWikiAPI::onGetLyricResult);
}
void LyricsWikiAPI::onGetLyricResult()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(QObject::sender());
bool found = false;
Lyric* lyric = 0;
if (reply->error() != QNetworkReply::NoError) {
qCritical() << "Cannot fetch lyric";
} else {
QWebPage page;
page.settings()->setAttribute(QWebSettings::AutoLoadImages, false);
page.mainFrame()->setHtml(reply->readAll());
QWebElement lyricbox = page.mainFrame()->findFirstElement("div[class=lyricbox]");
if (lyricbox.isNull()) {
qCritical() << "Cannot find lyric text in HTML page";
} else {
// Remove the <script> tags
Q_FOREACH (QWebElement script, lyricbox.findAll(QStringLiteral("script"))) {
script.removeFromDocument();
}
lyric = lyrics.take(reply);
if (!lyric) {
qCritical() << "Got an invalid lyric object!";
} else {
lyric->setText(lyricbox.toPlainText());
found = true;
}
}
}
qDebug() << "Lyric found:" << found;
Q_EMIT lyricFetched(lyric, found);
reply->deleteLater();
}
/*
The MIT License (MIT)
Copyright (c) 2015 Andrea Scarpino <me@andreascarpino.it>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef LYRICSWIKIAPI_H
#define LYRICSWIKIAPI_H
#include <QMap>
#include <QString>
#include "provider.h"
class QNetworkAccessManager;
class QNetworkReply;
class QUrl;
class LyricsWikiAPI : public Provider
{
Q_OBJECT
public:
explicit LyricsWikiAPI(QObject *parent = 0);
virtual ~LyricsWikiAPI();
void getLyric(const QString &artist, const QString &song);
Q_SIGNALS:
void lyricFetched(Lyric *lyric, const bool &found);
private:
void onGetLyricResult();
QNetworkAccessManager *network;
QMap<QNetworkReply*, Lyric*> lyrics;
};
#endif // LYRICSWIKIAPI_H
/*
The MIT License (MIT)
Copyright (c) 2015 Andrea Scarpino <me@andreascarpino.it>
Copyright (c) 2015-2016 Andrea Scarpino <me@andreascarpino.it>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......@@ -24,11 +24,6 @@
#include "provider.h"
#include <QDebug>
#include <QUrl>
#include "lyric.h"
Provider::Provider(QObject *parent) :
QObject(parent)
{
......
......@@ -28,7 +28,7 @@
#include <QObject>
#include <QString>
#include "lyric.h"
class Lyric;
class Provider : public QObject
{
......@@ -46,4 +46,6 @@ Q_SIGNALS:
};
Q_DECLARE_INTERFACE(Provider, "Provider")
#endif // PROVIDER_H
<?xml version="1.0" ?><!DOCTYPE TS><TS language="ca" version="2.1">
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ca">
<context>
<name>MainPage</name>
<message>
......@@ -12,22 +14,27 @@
<translation>Configuració</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="151"/>
<location filename="../qml/pages/MainPage.qml" line="159"/>
<source>Powered by %1</source>
<translation>Impulsat per %1</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="95"/>
<location filename="../qml/pages/MainPage.qml" line="103"/>
<source>Artist</source>
<translation>Artista</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="104"/>
<location filename="../qml/pages/MainPage.qml" line="75"/>
<source>Copy to clipboard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="112"/>
<source>Song</source>
<translation>Cançó</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="118"/>
<location filename="../qml/pages/MainPage.qml" line="126"/>
<source>Search</source>
<translation>Cerca</translation>
</message>
......@@ -35,14 +42,19 @@
<context>
<name>Settings</name>
<message>
<location filename="../qml/pages/Settings.qml" line="36"/>
<location filename="../qml/pages/Settings.qml" line="37"/>
<source>Settings</source>
<translation>Configuració</translation>
</message>
<message>
<location filename="../qml/pages/Settings.qml" line="42"/>
<location filename="../qml/pages/Settings.qml" line="43"/>
<source>Provider</source>
<translation>Proveïdor</translation>
</message>
<message>
<location filename="../qml/pages/Settings.qml" line="63"/>
<source>Clear cache</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
\ No newline at end of file
</TS>
<?xml version="1.0" ?><!DOCTYPE TS><TS language="cs" version="2.1">
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="cs">
<context>
<name>MainPage</name>
<message>
......@@ -12,22 +14,27 @@
<translation>Nastavení</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="151"/>
<location filename="../qml/pages/MainPage.qml" line="159"/>
<source>Powered by %1</source>
<translation type="unfinished"/>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="95"/>
<location filename="../qml/pages/MainPage.qml" line="103"/>
<source>Artist</source>
<translation>Umělec</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="104"/>
<location filename="../qml/pages/MainPage.qml" line="75"/>
<source>Copy to clipboard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="112"/>
<source>Song</source>
<translation>Píseň</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="118"/>
<location filename="../qml/pages/MainPage.qml" line="126"/>
<source>Search</source>
<translation>Hledat</translation>
</message>
......@@ -35,14 +42,19 @@
<context>
<name>Settings</name>
<message>
<location filename="../qml/pages/Settings.qml" line="36"/>
<location filename="../qml/pages/Settings.qml" line="37"/>
<source>Settings</source>
<translation>Nastavení</translation>
</message>
<message>
<location filename="../qml/pages/Settings.qml" line="42"/>
<location filename="../qml/pages/Settings.qml" line="43"/>
<source>Provider</source>
<translation>Poskytovatel</translation>
</message>
<message>
<location filename="../qml/pages/Settings.qml" line="63"/>
<source>Clear cache</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
\ No newline at end of file
</TS>
<?xml version="1.0" ?><!DOCTYPE TS><TS language="fi" version="2.1">
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fi">
<context>
<name>MainPage</name>
<message>
......@@ -12,22 +14,27 @@
<translation>Asetukset</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="151"/>
<location filename="../qml/pages/MainPage.qml" line="159"/>
<source>Powered by %1</source>
<translation>Powered by %1</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="95"/>
<location filename="../qml/pages/MainPage.qml" line="103"/>
<source>Artist</source>
<translation>Artisti</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="104"/>
<location filename="../qml/pages/MainPage.qml" line="75"/>
<source>Copy to clipboard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="112"/>
<source>Song</source>
<translation>Kappale</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="118"/>
<location filename="../qml/pages/MainPage.qml" line="126"/>
<source>Search</source>
<translation>Etsi</translation>
</message>
......@@ -35,14 +42,19 @@
<context>
<name>Settings</name>
<message>
<location filename="../qml/pages/Settings.qml" line="36"/>
<location filename="../qml/pages/Settings.qml" line="37"/>
<source>Settings</source>
<translation>Asetukset</translation>
</message>
<message>
<location filename="../qml/pages/Settings.qml" line="42"/>
<location filename="../qml/pages/Settings.qml" line="43"/>
<source>Provider</source>
<translation>Toimittaja</translation>
</message>
<message>
<location filename="../qml/pages/Settings.qml" line="63"/>
<source>Clear cache</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
\ No newline at end of file
</TS>
<?xml version="1.0" ?><!DOCTYPE TS><TS language="fr_FR" version="2.1">
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fr_FR">
<context>
<name>MainPage</name>
<message>
......@@ -12,22 +14,27 @@
<translation>Paramètres</translation>
</message>