From c3903a7b350107f6a175567f3116f1e1a36d07e1 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Mon, 12 Aug 2019 18:11:01 +0200 Subject: [PATCH] Add lyrics from lyrics.ovh --- README.md | 2 +- debian/control | 2 +- dist/man/strawberry.1 | 2 +- dist/rpm/strawberry.spec.in | 2 +- dist/unix/org.strawbs.strawberry.appdata.xml | 2 +- src/CMakeLists.txt | 4 + src/core/application.cpp | 2 + src/lyrics/auddlyricsprovider.cpp | 51 +---------- src/lyrics/auddlyricsprovider.h | 5 +- src/lyrics/jsonlyricsprovider.cpp | 78 ++++++++++++++++ src/lyrics/jsonlyricsprovider.h | 45 +++++++++ src/lyrics/ovhlyricsprovider.cpp | 96 ++++++++++++++++++++ src/lyrics/ovhlyricsprovider.h | 58 ++++++++++++ 13 files changed, 293 insertions(+), 56 deletions(-) create mode 100644 src/lyrics/jsonlyricsprovider.cpp create mode 100644 src/lyrics/jsonlyricsprovider.h create mode 100644 src/lyrics/ovhlyricsprovider.cpp create mode 100644 src/lyrics/ovhlyricsprovider.h diff --git a/README.md b/README.md index 4badce0a8..b7d3e3892 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Strawberry is a music player and music collection organizer. It is a fork of Cle * Edit tags on music files * Fetch tags from MusicBrainz * Album cover art from Last.fm, Musicbrainz, Discogs, Deezer and Tidal - * Song lyrics from AudD + * Song lyrics from AudD and lyrics.ovh * Support for multiple backends * Audio analyzer * Audio equalizer diff --git a/debian/control b/debian/control index 2ebd30e76..d164027fa 100644 --- a/debian/control +++ b/debian/control @@ -58,7 +58,7 @@ Description: Audio player and music collection organizer - Edit tags on music files - Fetch tags from MusicBrainz - Album cover art from Lastfm, Musicbrainz, Discogs, Deezer and Tidal - - Song lyrics from AudD + - Song lyrics from AudD and lyrics.ovh - Support for multiple backends - Audio analyzer - Audio equalizer diff --git a/dist/man/strawberry.1 b/dist/man/strawberry.1 index c16ca924b..8d6dfbb73 100644 --- a/dist/man/strawberry.1 +++ b/dist/man/strawberry.1 @@ -27,7 +27,7 @@ Features: .br - Album cover art from Lastfm, Musicbrainz, Discogs, Deezer and Tidal .br -- Song lyrics from AudD +- Song lyrics from AudD and lyrics.ovh .br - Support for multiple backends .br diff --git a/dist/rpm/strawberry.spec.in b/dist/rpm/strawberry.spec.in index dd2cbdd00..c48dac9ea 100644 --- a/dist/rpm/strawberry.spec.in +++ b/dist/rpm/strawberry.spec.in @@ -102,7 +102,7 @@ Features: - Edit tags on music files - Fetch tags from MusicBrainz - Album cover art from Last.fm, Musicbrainz, Discogs, Deezer and Tidal - - Song lyrics from AudD + - Song lyrics from AudD and lyrics.ovh - Support for multiple backends - Audio analyzer - Audio equalizer diff --git a/dist/unix/org.strawbs.strawberry.appdata.xml b/dist/unix/org.strawbs.strawberry.appdata.xml index 145c4b6bc..4e53b5584 100644 --- a/dist/unix/org.strawbs.strawberry.appdata.xml +++ b/dist/unix/org.strawbs.strawberry.appdata.xml @@ -29,7 +29,7 @@
  • Edit tags on music files
  • Fetch tags from MusicBrainz
  • Album cover art from Last.fm, Musicbrainz and Discogs
  • -
  • Song lyrics from AudD
  • +
  • Song lyrics from AudD and lyrics.ovh
  • Support for multiple backends
  • Audio analyzer
  • Audio equalizer
  • diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 40b546077..5d07e604b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -207,7 +207,9 @@ set(SOURCES lyrics/lyricsprovider.cpp lyrics/lyricsfetcher.cpp lyrics/lyricsfetchersearch.cpp + lyrics/jsonlyricsprovider.cpp lyrics/auddlyricsprovider.cpp + lyrics/ovhlyricsprovider.cpp settings/settingsdialog.cpp settings/settingspage.cpp @@ -388,7 +390,9 @@ set(HEADERS lyrics/lyricsprovider.h lyrics/lyricsfetcher.h lyrics/lyricsfetchersearch.h + lyrics/jsonlyricsprovider.h lyrics/auddlyricsprovider.h + lyrics/ovhlyricsprovider.h settings/settingsdialog.h settings/settingspage.h diff --git a/src/core/application.cpp b/src/core/application.cpp index 09969009e..af9d11fe2 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -59,6 +59,7 @@ #include "lyrics/lyricsproviders.h" #include "lyrics/lyricsprovider.h" #include "lyrics/auddlyricsprovider.h" +#include "lyrics/ovhlyricsprovider.h" #include "scrobbler/audioscrobbler.h" @@ -135,6 +136,7 @@ class ApplicationImpl { lyrics_providers_([=]() { LyricsProviders *lyrics_providers = new LyricsProviders(app); lyrics_providers->AddProvider(new AuddLyricsProvider(app)); + lyrics_providers->AddProvider(new OVHLyricsProvider(app)); return lyrics_providers; }), internet_services_([=]() { diff --git a/src/lyrics/auddlyricsprovider.cpp b/src/lyrics/auddlyricsprovider.cpp index 7ad51abf4..cf41232b3 100644 --- a/src/lyrics/auddlyricsprovider.cpp +++ b/src/lyrics/auddlyricsprovider.cpp @@ -43,7 +43,7 @@ #include "core/logging.h" #include "core/network.h" #include "core/utilities.h" -#include "lyricsprovider.h" +#include "jsonlyricsprovider.h" #include "lyricsfetcher.h" #include "auddlyricsprovider.h" @@ -51,7 +51,7 @@ const char *AuddLyricsProvider::kUrlSearch = "https://api.audd.io/findLyrics/"; const char *AuddLyricsProvider::kAPITokenB64 = "ZjA0NjQ4YjgyNDM3ZTc1MjY3YjJlZDI5ZDBlMzQxZjk="; const int AuddLyricsProvider::kMaxLength = 6000; -AuddLyricsProvider::AuddLyricsProvider(QObject *parent) : LyricsProvider("AudD", parent), network_(new NetworkAccessManager(this)) {} +AuddLyricsProvider::AuddLyricsProvider(QObject *parent) : JsonLyricsProvider("AudD", parent), network_(new NetworkAccessManager(this)) {} bool AuddLyricsProvider::StartSearch(const QString &artist, const QString &album, const QString &title, const quint64 id) { @@ -74,8 +74,7 @@ bool AuddLyricsProvider::StartSearch(const QString &artist, const QString &album } -void AuddLyricsProvider::CancelSearch(quint64 id) { -} +void AuddLyricsProvider::CancelSearch(quint64 id) {} void AuddLyricsProvider::HandleSearchReply(QNetworkReply *reply, const quint64 id, const QString &artist, const QString &title) { @@ -127,50 +126,6 @@ void AuddLyricsProvider::HandleSearchReply(QNetworkReply *reply, const quint64 i } -QJsonObject AuddLyricsProvider::ExtractJsonObj(QNetworkReply *reply, const quint64 id) { - - if (reply->error() != QNetworkReply::NoError) { - QString failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); - Error(id, failure_reason); - return QJsonObject(); - } - - if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { - QString failure_reason = QString("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()); - Error(id, failure_reason); - return QJsonObject(); - } - - QByteArray data(reply->readAll()); - - QJsonParseError error; - QJsonDocument json_doc = QJsonDocument::fromJson(data, &error); - - if (error.error != QJsonParseError::NoError) { - Error(id, "Reply from server missing Json data."); - return QJsonObject(); - } - - if (json_doc.isNull() || json_doc.isEmpty()) { - Error(id, "Received empty Json document."); - return QJsonObject(); - } - - if (!json_doc.isObject()) { - Error(id, "Json document is not an object."); - return QJsonObject(); - } - - QJsonObject json_obj = json_doc.object(); - if (json_obj.isEmpty()) { - Error(id, "Received empty Json object."); - return QJsonObject(); - } - - return json_obj; - -} - QJsonArray AuddLyricsProvider::ExtractResult(QNetworkReply *reply, const quint64 id, const QString &artist, const QString &title) { QJsonObject json_obj = ExtractJsonObj(reply, id); diff --git a/src/lyrics/auddlyricsprovider.h b/src/lyrics/auddlyricsprovider.h index 39dbb03a4..9b160b2fd 100644 --- a/src/lyrics/auddlyricsprovider.h +++ b/src/lyrics/auddlyricsprovider.h @@ -31,10 +31,10 @@ #include #include -#include "lyricsprovider.h" +#include "jsonlyricsprovider.h" #include "lyricsfetcher.h" -class AuddLyricsProvider : public LyricsProvider { +class AuddLyricsProvider : public JsonLyricsProvider { Q_OBJECT public: @@ -53,7 +53,6 @@ class AuddLyricsProvider : public LyricsProvider { QNetworkAccessManager *network_; void Error(const quint64 id, const QString &error, QVariant debug = QVariant()); - QJsonObject ExtractJsonObj(QNetworkReply *reply, const quint64 id); QJsonArray ExtractResult(QNetworkReply *reply, const quint64 id, const QString &artist, const QString &title); }; diff --git a/src/lyrics/jsonlyricsprovider.cpp b/src/lyrics/jsonlyricsprovider.cpp new file mode 100644 index 000000000..28d860e2e --- /dev/null +++ b/src/lyrics/jsonlyricsprovider.cpp @@ -0,0 +1,78 @@ +/* + * Strawberry Music Player + * Copyright 2019, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "lyricsprovider.h" +#include "lyricsfetcher.h" +#include "jsonlyricsprovider.h" + +JsonLyricsProvider::JsonLyricsProvider(const QString &name, QObject *parent) : LyricsProvider(name, parent) {} + +QJsonObject JsonLyricsProvider::ExtractJsonObj(QNetworkReply *reply, const quint64 id) { + + if (reply->error() != QNetworkReply::NoError) { + QString failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); + Error(id, failure_reason); + return QJsonObject(); + } + + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { + QString failure_reason = QString("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()); + Error(id, failure_reason); + return QJsonObject(); + } + + QByteArray data = reply->readAll(); + + QJsonParseError error; + QJsonDocument json_doc = QJsonDocument::fromJson(data, &error); + + if (error.error != QJsonParseError::NoError) { + Error(id, "Reply from server missing Json data."); + return QJsonObject(); + } + + if (json_doc.isNull() || json_doc.isEmpty()) { + Error(id, "Received empty Json document."); + return QJsonObject(); + } + + if (!json_doc.isObject()) { + Error(id, "Json document is not an object."); + return QJsonObject(); + } + + QJsonObject json_obj = json_doc.object(); + if (json_obj.isEmpty()) { + Error(id, "Received empty Json object."); + return QJsonObject(); + } + + return json_obj; + +} diff --git a/src/lyrics/jsonlyricsprovider.h b/src/lyrics/jsonlyricsprovider.h new file mode 100644 index 000000000..b7678e3d5 --- /dev/null +++ b/src/lyrics/jsonlyricsprovider.h @@ -0,0 +1,45 @@ +/* + * Strawberry Music Player + * Copyright 2019, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef JSONLYRICSPROVIDER_H +#define JSONLYRICSPROVIDER_H + +#include "config.h" + +#include +#include +#include + +#include "lyricsprovider.h" + +class QNetworkReply; + +class JsonLyricsProvider : public LyricsProvider { + Q_OBJECT + + public: + explicit JsonLyricsProvider(const QString &name, QObject *parent = nullptr); + QJsonObject ExtractJsonObj(QNetworkReply *reply, const quint64 id); + + private: + virtual void Error(const quint64 id, const QString &error, QVariant debug = QVariant()) = 0; + +}; + +#endif // JSONLYRICSPROVIDER_H diff --git a/src/lyrics/ovhlyricsprovider.cpp b/src/lyrics/ovhlyricsprovider.cpp new file mode 100644 index 000000000..12f844296 --- /dev/null +++ b/src/lyrics/ovhlyricsprovider.cpp @@ -0,0 +1,96 @@ +/* + * Strawberry Music Player + * Copyright 2019, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/closure.h" +#include "core/logging.h" +#include "core/network.h" +#include "core/utilities.h" +#include "lyricsprovider.h" +#include "lyricsfetcher.h" +#include "jsonlyricsprovider.h" +#include "ovhlyricsprovider.h" + +const char *OVHLyricsProvider::kUrlSearch = "https://api.lyrics.ovh/v1/"; + +OVHLyricsProvider::OVHLyricsProvider(QObject *parent) : JsonLyricsProvider("Lyrics.ovh", parent), network_(new NetworkAccessManager(this)) {} + +bool OVHLyricsProvider::StartSearch(const QString &artist, const QString &album, const QString &title, const quint64 id) { + + QUrl url(kUrlSearch + QString(QUrl::toPercentEncoding(artist)) + "/" + QString(QUrl::toPercentEncoding(title))); + QNetworkReply *reply = network_->get(QNetworkRequest(url)); + NewClosure(reply, SIGNAL(finished()), this, SLOT(HandleSearchReply(QNetworkReply*, const quint64, const QString&, const QString&)), reply, id, artist, title); + + //qLog(Debug) << "OVHLyrics: Sending request for" << url; + + return true; + +} + +void OVHLyricsProvider::CancelSearch(quint64 id) {} + +void OVHLyricsProvider::HandleSearchReply(QNetworkReply *reply, const quint64 id, const QString &artist, const QString &title) { + + reply->deleteLater(); + + QJsonObject json_obj = ExtractJsonObj(reply, id); + if (json_obj.isEmpty()) { + emit SearchFinished(id, LyricsSearchResults()); + return; + } + + if (json_obj.contains("error")) { + Error(id, json_obj["error"].toString()); + qLog(Debug) << "OVHLyrics: No lyrics for" << artist << title; + emit SearchFinished(id, LyricsSearchResults()); + return; + } + + if (!json_obj.contains("lyrics")) { + emit SearchFinished(id, LyricsSearchResults()); + return; + } + + LyricsSearchResult result; + result.lyrics = json_obj["lyrics"].toString(); + qLog(Debug) << "OVHLyrics: Got lyrics for" << artist << title; + emit SearchFinished(id, LyricsSearchResults() << result); + +} + + +void OVHLyricsProvider::Error(const quint64 id, const QString &error, QVariant debug) { + + qLog(Error) << "OVHLyrics:" << error; + if (debug.isValid()) qLog(Debug) << debug; + emit SearchFinished(id, LyricsSearchResults()); + +} diff --git a/src/lyrics/ovhlyricsprovider.h b/src/lyrics/ovhlyricsprovider.h new file mode 100644 index 000000000..b03bb2319 --- /dev/null +++ b/src/lyrics/ovhlyricsprovider.h @@ -0,0 +1,58 @@ +/* + * Strawberry Music Player + * Copyright 2019, Jonas Kvinge + * + * Strawberry 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. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef OVHLYRICSPROVIDER_H +#define OVHLYRICSPROVIDER_H + +#include "config.h" + +#include + +#include +#include +#include + +#include "lyricsprovider.h" +#include "jsonlyricsprovider.h" +#include "lyricsfetcher.h" + +class QNetworkAccessManager; +class QNetworkReply; + +class OVHLyricsProvider : public JsonLyricsProvider { + Q_OBJECT + + public: + explicit OVHLyricsProvider(QObject *parent = nullptr); + + bool StartSearch(const QString &artist, const QString &album, const QString &title, quint64 id); + void CancelSearch(quint64 id); + + private slots: + void HandleSearchReply(QNetworkReply *reply, const quint64 id, const QString &artist, const QString &title); + + private: + static const char *kUrlSearch; + QNetworkAccessManager *network_; + void Error(const quint64 id, const QString &error, QVariant debug = QVariant()); + +}; + +#endif // OVHLYRICSPROVIDER_H +