diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 05a0c7ac..bf1442c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -177,6 +177,7 @@ set(SOURCES lyrics/geniuslyricsprovider.cpp lyrics/musixmatchlyricsprovider.cpp lyrics/chartlyricsprovider.cpp + lyrics/stands4lyricsprovider.cpp providers/musixmatchprovider.cpp @@ -413,6 +414,7 @@ set(HEADERS lyrics/geniuslyricsprovider.h lyrics/musixmatchlyricsprovider.h lyrics/chartlyricsprovider.h + lyrics/stands4lyricsprovider.h settings/settingsdialog.h settings/settingspage.h diff --git a/src/core/application.cpp b/src/core/application.cpp index b2f4c809..a5932b39 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -63,6 +63,7 @@ #include "lyrics/lololyricsprovider.h" #include "lyrics/musixmatchlyricsprovider.h" #include "lyrics/chartlyricsprovider.h" +#include "lyrics/stands4lyricsprovider.h" #include "scrobbler/audioscrobbler.h" #include "scrobbler/lastfmimport.h" @@ -154,6 +155,7 @@ class ApplicationImpl { lyrics_providers->AddProvider(new LoloLyricsProvider(lyrics_providers->network(), app)); lyrics_providers->AddProvider(new MusixmatchLyricsProvider(lyrics_providers->network(), app)); lyrics_providers->AddProvider(new ChartLyricsProvider(lyrics_providers->network(), app)); + lyrics_providers->AddProvider(new Stands4LyricsProvider(lyrics_providers->network(), app)); lyrics_providers->ReloadSettings(); return lyrics_providers; }), diff --git a/src/lyrics/stands4lyricsprovider.cpp b/src/lyrics/stands4lyricsprovider.cpp new file mode 100644 index 00000000..00e5ab43 --- /dev/null +++ b/src/lyrics/stands4lyricsprovider.cpp @@ -0,0 +1,126 @@ +/* + * Strawberry Music Player + * Copyright 2023, 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 +#include +#include +#include +#include +#include +#include +#include + +#include "core/logging.h" +#include "core/networkaccessmanager.h" +#include "utilities/strutils.h" +#include "lyricsfetcher.h" +#include "lyricsprovider.h" +#include "stands4lyricsprovider.h" + +const char *Stands4LyricsProvider::kUrl = "https://www.lyrics.com/lyrics/"; + +Stands4LyricsProvider::Stands4LyricsProvider(NetworkAccessManager *network, QObject *parent) : LyricsProvider("Stands4Lyrics", true, false, network, parent) {} + +Stands4LyricsProvider::~Stands4LyricsProvider() { + + while (!replies_.isEmpty()) { + QNetworkReply *reply = replies_.takeFirst(); + QObject::disconnect(reply, nullptr, this, nullptr); + reply->abort(); + reply->deleteLater(); + } + +} + +bool Stands4LyricsProvider::StartSearch(const QString &artist, const QString &album, const QString &title, const int id) { + + Q_UNUSED(album); + + QUrl url(kUrl + StringFixup(artist) + "/" + StringFixup(title) + ".html"); + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); + QNetworkReply *reply = network_->get(req); + replies_ << reply; + QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, id, artist, title]() { HandleSearchReply(reply, id, artist, title); }); + + qLog(Debug) << "Stands4Lyrics: Sending request for" << url; + + return true; + +} + +void Stands4LyricsProvider::CancelSearch(const int id) { Q_UNUSED(id); } + +void Stands4LyricsProvider::HandleSearchReply(QNetworkReply *reply, const int id, const QString &artist, const QString &title) { + + if (!replies_.contains(reply)) return; + replies_.removeAll(reply); + QObject::disconnect(reply, nullptr, this, nullptr); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + Error(QString("%1 (%2)").arg(reply->errorString()).arg(reply->error())); + emit SearchFinished(id, LyricsSearchResults()); + return; + } + else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { + Error(QString("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt())); + emit SearchFinished(id, LyricsSearchResults()); + return; + } + + const QByteArray data = reply->readAll(); + if (data.isEmpty()) { + Error("Empty reply received from server."); + emit SearchFinished(id, LyricsSearchResults()); + return; + } + + const QString lyrics = ParseLyricsFromHTML(QString::fromUtf8(data), QRegularExpression("]*>"), QRegularExpression("<\\/div>"), QRegularExpression("
]+>"), false); + if (lyrics.isEmpty() || lyrics.contains("Click to search for the Lyrics on Lyrics.com", Qt::CaseInsensitive)) { + qLog(Debug) << "Stands4Lyrics: No lyrics for" << artist << title; + return; + } + + qLog(Debug) << "Stands4Lyrics: Got lyrics for" << artist << title; + + LyricsSearchResult result; + result.lyrics = lyrics; + emit SearchFinished(id, LyricsSearchResults() << result); + +} + +QString Stands4LyricsProvider::StringFixup(QString string) { + + return string.replace('/', '-') + .replace('\'', '-') + .remove(QRegularExpression("[^\\w0-9\\- ]", QRegularExpression::UseUnicodePropertiesOption)) + .simplified() + .replace(' ', '-') + .replace(QRegularExpression("(-)\\1+"), "-") + .toLower(); + +} + +void Stands4LyricsProvider::Error(const QString &error, const QVariant &debug) { + + qLog(Error) << "Stands4Lyrics:" << error; + if (debug.isValid()) qLog(Debug) << debug; + +} diff --git a/src/lyrics/stands4lyricsprovider.h b/src/lyrics/stands4lyricsprovider.h new file mode 100644 index 00000000..46bc567d --- /dev/null +++ b/src/lyrics/stands4lyricsprovider.h @@ -0,0 +1,57 @@ +/* + * Strawberry Music Player + * Copyright 2023, 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 STANDS4LYRICSPROVIDER_H +#define STANDS4LYRICSPROVIDER_H + +#include +#include +#include +#include +#include + +#include "lyricsprovider.h" +#include "lyricsfetcher.h" + +class QNetworkReply; +class NetworkAccessManager; + +class Stands4LyricsProvider : public LyricsProvider { + Q_OBJECT + + public: + explicit Stands4LyricsProvider(NetworkAccessManager *network, QObject *parent = nullptr); + ~Stands4LyricsProvider() override; + + bool StartSearch(const QString &artist, const QString &album, const QString &title, int id) override; + void CancelSearch(const int id) override; + + private: + void Error(const QString &error, const QVariant &debug = QVariant()) override; + static QString StringFixup(QString string); + + private slots: + void HandleSearchReply(QNetworkReply *reply, const int id, const QString &artist, const QString &title); + + private: + static const char *kUrl; + QList replies_; +}; + +#endif // STANDS4LYRICSPROVIDER_H