Application: Use shared pointers

Fixes #1239
This commit is contained in:
Jonas Kvinge
2023-07-21 05:55:24 +02:00
parent d6b53f78ab
commit 2e61235403
316 changed files with 2170 additions and 1643 deletions

View File

@@ -19,36 +19,27 @@
#include "config.h"
#include <QMutex>
#include <memory>
#include <QList>
#include <QString>
#include <QSettings>
#include "core/shared_ptr.h"
#include "core/application.h"
#include "core/logging.h"
#include "core/song.h"
#include "settings/settingsdialog.h"
#include "settings/scrobblersettingspage.h"
#include "audioscrobbler.h"
#include "scrobblersettings.h"
#include "scrobblerservice.h"
#include "lastfmscrobbler.h"
#include "librefmscrobbler.h"
#include "listenbrainzscrobbler.h"
#ifdef HAVE_SUBSONIC
# include "subsonicscrobbler.h"
#endif
using std::make_shared;
AudioScrobbler::AudioScrobbler(Application *app, QObject *parent)
: QObject(parent),
app_(app),
enabled_(false),
offline_(false),
scrobble_button_(false),
love_button_(false),
submit_delay_(0),
prefer_albumartist_(false),
show_error_dialog_(false) {
settings_(make_shared<ScrobblerSettings>(app)) {
ReloadSettings();
@@ -57,56 +48,46 @@ AudioScrobbler::AudioScrobbler(Application *app, QObject *parent)
AudioScrobbler::~AudioScrobbler() {
while (!services_.isEmpty()) {
delete services_.take(services_.firstKey());
ScrobblerServicePtr service = services_.first();
RemoveService(service);
}
}
void AudioScrobbler::AddService(ScrobblerService *service) {
void AudioScrobbler::AddService(ScrobblerServicePtr service) {
{
QMutexLocker locker(&mutex_);
services_.insert(service->name(), service);
}
services_.insert(service->name(), service);
QObject::connect(service, &ScrobblerService::ErrorMessage, this, &AudioScrobbler::ErrorReceived);
QObject::connect(&*service, &ScrobblerService::ErrorMessage, this, &AudioScrobbler::ErrorReceived);
qLog(Debug) << "Registered scrobbler service" << service->name();
}
void AudioScrobbler::RemoveService(ScrobblerService *service) {
void AudioScrobbler::RemoveService(ScrobblerServicePtr service) {
if (!service || !services_.contains(service->name())) return;
{
QMutexLocker locker(&mutex_);
services_.remove(service->name());
QObject::disconnect(service, nullptr, this, nullptr);
}
services_.remove(service->name());
QObject::disconnect(&*service, nullptr, this, nullptr);
QObject::disconnect(service, &ScrobblerService::ErrorMessage, this, &AudioScrobbler::ErrorReceived);
QObject::disconnect(&*service, &ScrobblerService::ErrorMessage, this, &AudioScrobbler::ErrorReceived);
qLog(Debug) << "Unregistered scrobbler service" << service->name();
}
int AudioScrobbler::NextId() { return next_id_.fetchAndAddRelaxed(1); }
QList<ScrobblerServicePtr> AudioScrobbler::GetAll() {
QList<ScrobblerService*> AudioScrobbler::GetAll() {
QList<ScrobblerServicePtr> services;
QList<ScrobblerService*> services;
{
QMutexLocker locker(&mutex_);
services = services_.values();
}
return services;
}
ScrobblerService *AudioScrobbler::ServiceByName(const QString &name) {
ScrobblerServicePtr AudioScrobbler::ServiceByName(const QString &name) {
if (services_.contains(name)) return services_.value(name);
return nullptr;
@@ -115,45 +96,10 @@ ScrobblerService *AudioScrobbler::ServiceByName(const QString &name) {
void AudioScrobbler::ReloadSettings() {
QSettings s;
s.beginGroup(ScrobblerSettingsPage::kSettingsGroup);
enabled_ = s.value("enabled", false).toBool();
offline_ = s.value("offline", false).toBool();
scrobble_button_ = s.value("scrobble_button", false).toBool();
love_button_ = s.value("love_button", false).toBool();
submit_delay_ = s.value("submit", 0).toInt();
prefer_albumartist_ = s.value("albumartist", false).toBool();
show_error_dialog_ = s.value("show_error_dialog", true).toBool();
QStringList sources = s.value("sources").toStringList();
s.endGroup();
settings_->ReloadSettings();
sources_.clear();
if (sources.isEmpty()) {
sources_ << Song::Source::Unknown
<< Song::Source::LocalFile
<< Song::Source::Collection
<< Song::Source::CDDA
<< Song::Source::Device
<< Song::Source::Stream
<< Song::Source::Tidal
<< Song::Source::Subsonic
<< Song::Source::Qobuz
<< Song::Source::SomaFM
<< Song::Source::RadioParadise;
}
else {
for (const QString &source : sources) {
sources_ << Song::SourceFromText(source);
}
}
emit ScrobblingEnabledChanged(enabled_);
emit ScrobbleButtonVisibilityChanged(scrobble_button_);
emit LoveButtonVisibilityChanged(love_button_);
QList<ScrobblerService*> services = services_.values();
for (ScrobblerService *service : services) {
QList<ScrobblerServicePtr> services = services_.values();
for (ScrobblerServicePtr service : services) {
service->ReloadSettings();
}
@@ -161,31 +107,17 @@ void AudioScrobbler::ReloadSettings() {
void AudioScrobbler::ToggleScrobbling() {
bool enabled_old_ = enabled_;
enabled_ = !enabled_;
settings_->ToggleScrobbling();
QSettings s;
s.beginGroup(ScrobblerSettingsPage::kSettingsGroup);
s.setValue("enabled", enabled_);
s.endGroup();
if (enabled_ != enabled_old_) emit ScrobblingEnabledChanged(enabled_);
if (enabled_ && !offline_) { Submit(); }
if (settings_->enabled() && !settings_->offline()) { Submit(); }
}
void AudioScrobbler::ToggleOffline() {
bool offline_old_ = offline_;
offline_ = !offline_;
settings_->ToggleOffline();
QSettings s;
s.beginGroup(ScrobblerSettingsPage::kSettingsGroup);
s.setValue("offline", offline_);
s.endGroup();
if (offline_ != offline_old_) { emit ScrobblingOfflineChanged(offline_); }
if (enabled_ && !offline_) { Submit(); }
if (settings_->enabled() && !settings_->offline()) { Submit(); }
}
@@ -195,13 +127,13 @@ void AudioScrobbler::ShowConfig() {
void AudioScrobbler::UpdateNowPlaying(const Song &song) {
if (!sources_.contains(song.source())) return;
if (!settings_->sources().contains(song.source())) return;
qLog(Debug) << "Sending now playing for song" << song.artist() << song.album() << song.title();
QList<ScrobblerService*> services = GetAll();
for (ScrobblerService *service : services) {
if (!service->IsEnabled()) continue;
QList<ScrobblerServicePtr> services = GetAll();
for (ScrobblerServicePtr service : services) {
if (!service->enabled()) continue;
service->UpdateNowPlaying(song);
}
@@ -209,9 +141,9 @@ void AudioScrobbler::UpdateNowPlaying(const Song &song) {
void AudioScrobbler::ClearPlaying() {
QList<ScrobblerService*> services = GetAll();
for (ScrobblerService *service : services) {
if (!service->IsEnabled()) continue;
QList<ScrobblerServicePtr> services = GetAll();
for (ScrobblerServicePtr service : services) {
if (!service->enabled()) continue;
service->ClearPlaying();
}
@@ -219,13 +151,13 @@ void AudioScrobbler::ClearPlaying() {
void AudioScrobbler::Scrobble(const Song &song, const qint64 scrobble_point) {
if (!sources_.contains(song.source())) return;
if (!settings_->sources().contains(song.source())) return;
qLog(Debug) << "Scrobbling song" << song.artist() << song.album() << song.title() << "at" << scrobble_point;
QList<ScrobblerService*> services = GetAll();
for (ScrobblerService *service : services) {
if (!service->IsEnabled()) continue;
QList<ScrobblerServicePtr> services = GetAll();
for (ScrobblerServicePtr service : services) {
if (!service->enabled()) continue;
service->Scrobble(song);
}
@@ -233,9 +165,9 @@ void AudioScrobbler::Scrobble(const Song &song, const qint64 scrobble_point) {
void AudioScrobbler::Love() {
QList<ScrobblerService*> services = GetAll();
for (ScrobblerService *service : services) {
if (!service->IsEnabled() || !service->IsAuthenticated()) continue;
QList<ScrobblerServicePtr> services = GetAll();
for (ScrobblerServicePtr service : services) {
if (!service->enabled() || !service->authenticated()) continue;
service->Love();
}
@@ -243,9 +175,9 @@ void AudioScrobbler::Love() {
void AudioScrobbler::Submit() {
QList<ScrobblerService*> services = GetAll();
for (ScrobblerService *service : services) {
if (!service->IsEnabled() || !service->IsAuthenticated() || service->IsSubmitted()) continue;
QList<ScrobblerServicePtr> services = GetAll();
for (ScrobblerServicePtr service : services) {
if (!service->enabled() || !service->authenticated() || service->submitted()) continue;
service->StartSubmit();
}
@@ -253,9 +185,9 @@ void AudioScrobbler::Submit() {
void AudioScrobbler::WriteCache() {
QList<ScrobblerService*> services = GetAll();
for (ScrobblerService *service : services) {
if (!service->IsEnabled()) continue;
QList<ScrobblerServicePtr> services = GetAll();
for (ScrobblerServicePtr service : services) {
if (!service->enabled()) continue;
service->WriteCache();
}

View File

@@ -22,15 +22,17 @@
#include "config.h"
#include <memory>
#include <QtGlobal>
#include <QObject>
#include <QMutex>
#include <QList>
#include <QMap>
#include <QString>
#include <QAtomicInt>
#include "core/shared_ptr.h"
#include "core/song.h"
#include "scrobblersettings.h"
class Application;
class ScrobblerService;
@@ -43,29 +45,30 @@ class AudioScrobbler : public QObject {
explicit AudioScrobbler(Application *app, QObject *parent = nullptr);
~AudioScrobbler();
void AddService(ScrobblerService *service);
void RemoveService(ScrobblerService *service);
QList<ScrobblerService*> List() const { return services_.values(); }
void AddService(SharedPtr<ScrobblerService> service);
void RemoveService(SharedPtr<ScrobblerService> service);
QList<SharedPtr<ScrobblerService>> List() const { return services_.values(); }
bool HasAnyServices() const { return !services_.isEmpty(); }
int NextId();
QList<ScrobblerService*> GetAll();
ScrobblerService *ServiceByName(const QString &name);
QList<SharedPtr<ScrobblerService>> GetAll();
SharedPtr<ScrobblerService> ServiceByName(const QString &name);
template<typename T>
T *Service() {
return qobject_cast<T*>(ServiceByName(T::kName));
SharedPtr<T> Service() {
return std::static_pointer_cast<T>(ServiceByName(T::kName));
}
void ReloadSettings();
SharedPtr<ScrobblerSettings> settings() { return settings_; }
bool IsEnabled() const { return enabled_; }
bool IsOffline() const { return offline_; }
bool ScrobbleButton() const { return scrobble_button_; }
bool LoveButton() const { return love_button_; }
int SubmitDelay() const { return submit_delay_; }
bool PreferAlbumArtist() const { return prefer_albumartist_; }
bool ShowErrorDialog() const { return show_error_dialog_; }
QList<Song::Source> sources() const { return sources_; }
bool enabled() const { return settings_->enabled(); }
bool offline() const { return settings_->offline(); }
bool scrobble_button() const { return settings_->scrobble_button(); }
bool love_button() const { return settings_->love_button(); }
int submit_delay() const { return settings_->submit_delay(); }
bool prefer_albumartist() const { return settings_->prefer_albumartist(); }
bool ShowErrorDialog() const { return settings_->show_error_dialog(); }
QList<Song::Source> sources() const { return settings_->sources(); }
void ShowConfig();
@@ -83,26 +86,11 @@ class AudioScrobbler : public QObject {
signals:
void ErrorMessage(const QString &error);
void ScrobblingEnabledChanged(const bool value);
void ScrobblingOfflineChanged(const bool value);
void ScrobbleButtonVisibilityChanged(const bool value);
void LoveButtonVisibilityChanged(const bool value);
private:
Application *app_;
QMap<QString, ScrobblerService*> services_;
QMutex mutex_;
QAtomicInt next_id_;
bool enabled_;
bool offline_;
bool scrobble_button_;
bool love_button_;
int submit_delay_;
bool prefer_albumartist_;
bool show_error_dialog_;
QList<Song::Source> sources_;
SharedPtr<ScrobblerSettings> settings_;
QMap<QString, SharedPtr<ScrobblerService>> services_;
Q_DISABLE_COPY(AudioScrobbler)
};

View File

@@ -38,8 +38,9 @@
#include <QJsonArray>
#include <QJsonValue>
#include "core/networkaccessmanager.h"
#include "core/logging.h"
#include "core/shared_ptr.h"
#include "core/networkaccessmanager.h"
#include "lastfmimport.h"
@@ -48,7 +49,7 @@
const int LastFMImport::kRequestsDelay = 2000;
LastFMImport::LastFMImport(NetworkAccessManager *network, QObject *parent)
LastFMImport::LastFMImport(SharedPtr<NetworkAccessManager> network, QObject *parent)
: QObject(parent),
network_(network),
timer_flush_requests_(new QTimer(this)),

View File

@@ -31,6 +31,8 @@
#include <QQueue>
#include <QDateTime>
#include "core/shared_ptr.h"
class QTimer;
class QNetworkReply;
@@ -40,7 +42,7 @@ class LastFMImport : public QObject {
Q_OBJECT
public:
explicit LastFMImport(NetworkAccessManager *network, QObject *parent = nullptr);
explicit LastFMImport(SharedPtr<NetworkAccessManager> network, QObject *parent = nullptr);
~LastFMImport() override;
void ReloadSettings();
@@ -94,7 +96,7 @@ class LastFMImport : public QObject {
private:
static const int kRequestsDelay;
NetworkAccessManager *network_;
SharedPtr<NetworkAccessManager> network_;
QTimer *timer_flush_requests_;
QString username_;

View File

@@ -21,9 +21,10 @@
#include <QObject>
#include "core/shared_ptr.h"
#include "core/networkaccessmanager.h"
#include "audioscrobbler.h"
#include "scrobblersettings.h"
#include "lastfmscrobbler.h"
const char *LastFMScrobbler::kName = "Last.fm";
@@ -32,5 +33,5 @@ const char *LastFMScrobbler::kAuthUrl = "https://www.last.fm/api/auth/";
const char *LastFMScrobbler::kApiUrl = "https://ws.audioscrobbler.com/2.0/";
const char *LastFMScrobbler::kCacheFile = "lastfmscrobbler.cache";
LastFMScrobbler::LastFMScrobbler(AudioScrobbler *scrobbler, NetworkAccessManager *network, QObject *parent)
: ScrobblingAPI20(kName, kSettingsGroup, kAuthUrl, kApiUrl, true, kCacheFile, scrobbler, network, parent) {}
LastFMScrobbler::LastFMScrobbler(SharedPtr<ScrobblerSettings> settings, SharedPtr<NetworkAccessManager> network, QObject *parent)
: ScrobblingAPI20(kName, kSettingsGroup, kAuthUrl, kApiUrl, true, kCacheFile, settings, network, parent) {}

View File

@@ -25,6 +25,7 @@
#include <QtGlobal>
#include <QObject>
#include "core/shared_ptr.h"
#include "scrobblingapi20.h"
class AudioScrobbler;
@@ -34,7 +35,7 @@ class LastFMScrobbler : public ScrobblingAPI20 {
Q_OBJECT
public:
explicit LastFMScrobbler(AudioScrobbler *scrobbler, NetworkAccessManager *network, QObject *parent = nullptr);
explicit LastFMScrobbler(SharedPtr<ScrobblerSettings> settings, SharedPtr<NetworkAccessManager> network, QObject *parent = nullptr);
static const char *kName;
static const char *kSettingsGroup;

View File

@@ -21,9 +21,10 @@
#include <QObject>
#include "core/shared_ptr.h"
#include "core/networkaccessmanager.h"
#include "audioscrobbler.h"
#include "scrobblersettings.h"
#include "scrobblingapi20.h"
#include "librefmscrobbler.h"
@@ -33,5 +34,5 @@ const char *LibreFMScrobbler::kAuthUrl = "https://www.libre.fm/api/auth/";
const char *LibreFMScrobbler::kApiUrl = "https://libre.fm/2.0/";
const char *LibreFMScrobbler::kCacheFile = "librefmscrobbler.cache";
LibreFMScrobbler::LibreFMScrobbler(AudioScrobbler *scrobbler, NetworkAccessManager *network, QObject *parent)
: ScrobblingAPI20(kName, kSettingsGroup, kAuthUrl, kApiUrl, false, kCacheFile, scrobbler, network, parent) {}
LibreFMScrobbler::LibreFMScrobbler(SharedPtr<ScrobblerSettings> settings, SharedPtr<NetworkAccessManager> network, QObject *parent)
: ScrobblingAPI20(kName, kSettingsGroup, kAuthUrl, kApiUrl, false, kCacheFile, settings, network, parent) {}

View File

@@ -25,16 +25,17 @@
#include <QtGlobal>
#include <QObject>
#include "core/shared_ptr.h"
#include "scrobblingapi20.h"
class AudioScrobbler;
class ScrobblerSettings;
class NetworkAccessManager;
class LibreFMScrobbler : public ScrobblingAPI20 {
Q_OBJECT
public:
explicit LibreFMScrobbler(AudioScrobbler *scrobbler, NetworkAccessManager *network, QObject *parent = nullptr);
explicit LibreFMScrobbler(SharedPtr<ScrobblerSettings> settings, SharedPtr<NetworkAccessManager> network, QObject *parent = nullptr);
static const char *kName;
static const char *kSettingsGroup;

View File

@@ -40,6 +40,7 @@
#include <QJsonArray>
#include <QJsonValue>
#include "core/shared_ptr.h"
#include "core/networkaccessmanager.h"
#include "core/song.h"
#include "core/logging.h"
@@ -47,7 +48,7 @@
#include "internet/localredirectserver.h"
#include "settings/scrobblersettingspage.h"
#include "audioscrobbler.h"
#include "scrobblersettings.h"
#include "scrobblerservice.h"
#include "scrobblercache.h"
#include "scrobblercacheitem.h"
@@ -65,9 +66,9 @@ const char *ListenBrainzScrobbler::kClientSecretB64 = "Uk9GZ2hrZVEzRjNvUHlFaHFpe
const char *ListenBrainzScrobbler::kCacheFile = "listenbrainzscrobbler.cache";
const int ListenBrainzScrobbler::kScrobblesPerRequest = 10;
ListenBrainzScrobbler::ListenBrainzScrobbler(AudioScrobbler *scrobbler, NetworkAccessManager *network, QObject *parent)
ListenBrainzScrobbler::ListenBrainzScrobbler(SharedPtr<ScrobblerSettings> settings, SharedPtr<NetworkAccessManager> network, QObject *parent)
: ScrobblerService(kName, parent),
scrobbler_(scrobbler),
settings_(settings),
network_(network),
cache_(new ScrobblerCache(kCacheFile, this)),
server_(nullptr),
@@ -449,7 +450,7 @@ void ListenBrainzScrobbler::UpdateNowPlaying(const Song &song) {
scrobbled_ = false;
timestamp_ = QDateTime::currentDateTime().toSecsSinceEpoch();
if (!song.is_metadata_good() || !IsAuthenticated() || scrobbler_->IsOffline()) return;
if (!song.is_metadata_good() || !authenticated() || settings_->offline()) return;
QJsonObject object_listen;
object_listen.insert("track_metadata", JsonTrackMetadata(ScrobbleMetadata(song)));
@@ -509,7 +510,7 @@ void ListenBrainzScrobbler::Scrobble(const Song &song) {
cache_->Add(song, timestamp_);
if (scrobbler_->IsOffline() || !IsAuthenticated()) return;
if (settings_->offline() || !authenticated()) return;
StartSubmit();
@@ -518,14 +519,14 @@ void ListenBrainzScrobbler::Scrobble(const Song &song) {
void ListenBrainzScrobbler::StartSubmit(const bool initial) {
if (!submitted_ && cache_->Count() > 0) {
if (initial && scrobbler_->SubmitDelay() <= 0 && !submit_error_) {
if (initial && settings_->submit_delay() <= 0 && !submit_error_) {
if (timer_submit_.isActive()) {
timer_submit_.stop();
}
Submit();
}
else if (!timer_submit_.isActive()) {
int submit_delay = static_cast<int>(std::max(scrobbler_->SubmitDelay(), submit_error_ ? 30 : 5) * kMsecPerSec);
int submit_delay = static_cast<int>(std::max(settings_->submit_delay(), submit_error_ ? 30 : 5) * kMsecPerSec);
timer_submit_.setInterval(submit_delay);
timer_submit_.start();
}
@@ -537,7 +538,7 @@ void ListenBrainzScrobbler::Submit() {
qLog(Debug) << "ListenBrainz: Submitting scrobbles.";
if (!IsEnabled() || !IsAuthenticated() || scrobbler_->IsOffline()) return;
if (!enabled() || !authenticated() || settings_->offline()) return;
QJsonArray array;
ScrobblerCacheItemPtrList cache_items_sent;
@@ -620,7 +621,7 @@ void ListenBrainzScrobbler::Love() {
if (!song_playing_.is_valid() || !song_playing_.is_metadata_good()) return;
if (!IsAuthenticated()) scrobbler_->ShowConfig();
if (!authenticated()) settings_->ShowConfig();
if (song_playing_.musicbrainz_recording_id().isEmpty()) {
Error(tr("Missing MusicBrainz recording ID for %1 %2 %3").arg(song_playing_.artist()).arg(song_playing_.album()).arg(song_playing_.title()));
@@ -671,7 +672,7 @@ void ListenBrainzScrobbler::Error(const QString &error, const QVariant &debug) {
qLog(Error) << "ListenBrainz:" << error;
if (debug.isValid()) qLog(Debug) << debug;
if (scrobbler_->ShowErrorDialog()) {
if (settings_->show_error_dialog()) {
emit ErrorMessage(tr("ListenBrainz error: %1").arg(error));
}

View File

@@ -32,6 +32,7 @@
#include <QJsonDocument>
#include <QTimer>
#include "core/shared_ptr.h"
#include "core/song.h"
#include "scrobblerservice.h"
#include "scrobblercache.h"
@@ -39,7 +40,7 @@
class QNetworkReply;
class AudioScrobbler;
class ScrobblerSettings;
class NetworkAccessManager;
class LocalRedirectServer;
@@ -47,7 +48,7 @@ class ListenBrainzScrobbler : public ScrobblerService {
Q_OBJECT
public:
explicit ListenBrainzScrobbler(AudioScrobbler *scrobbler, NetworkAccessManager *network, QObject *parent = nullptr);
explicit ListenBrainzScrobbler(SharedPtr<ScrobblerSettings> settings, SharedPtr<NetworkAccessManager> network, QObject *parent = nullptr);
~ListenBrainzScrobbler() override;
static const char *kName;
@@ -56,10 +57,9 @@ class ListenBrainzScrobbler : public ScrobblerService {
void ReloadSettings() override;
void LoadSession();
bool IsEnabled() const override { return enabled_; }
bool IsAuthenticated() const override { return !access_token_.isEmpty() && !user_token_.isEmpty(); }
bool IsSubmitted() const override { return submitted_; }
void Submitted() override { submitted_ = true; }
bool enabled() const override { return enabled_; }
bool authenticated() const override { return !access_token_.isEmpty() && !user_token_.isEmpty(); }
bool submitted() const override { return submitted_; }
QString user_token() const { return user_token_; }
void Authenticate();
@@ -110,8 +110,8 @@ class ListenBrainzScrobbler : public ScrobblerService {
static const char *kCacheFile;
static const int kScrobblesPerRequest;
AudioScrobbler *scrobbler_;
NetworkAccessManager *network_;
SharedPtr<ScrobblerSettings> settings_;
SharedPtr<NetworkAccessManager> network_;
ScrobblerCache *cache_;
LocalRedirectServer *server_;
bool enabled_;

View File

@@ -19,9 +19,9 @@
#include "config.h"
#include <memory>
#include <functional>
#include <chrono>
#include <memory>
#include <QObject>
#include <QStandardPaths>
@@ -41,6 +41,7 @@
#include "scrobblercache.h"
#include "scrobblercacheitem.h"
using std::make_shared;
using namespace std::chrono_literals;
ScrobblerCache::ScrobblerCache(const QString &filename, QObject *parent)
@@ -182,7 +183,7 @@ void ScrobblerCache::ReadCache() {
metadata.musicbrainz_work_id = json_obj_track["musicbrainz_work_id"].toString();
}
ScrobblerCacheItemPtr cache_item = std::make_shared<ScrobblerCacheItem>(metadata, timestamp);
ScrobblerCacheItemPtr cache_item = make_shared<ScrobblerCacheItem>(metadata, timestamp);
scrobbler_cache_ << cache_item;
}
@@ -248,7 +249,7 @@ void ScrobblerCache::WriteCache() {
ScrobblerCacheItemPtr ScrobblerCache::Add(const Song &song, const quint64 timestamp) {
ScrobblerCacheItemPtr cache_item = std::make_shared<ScrobblerCacheItem>(ScrobbleMetadata(song), timestamp);
ScrobblerCacheItemPtr cache_item = make_shared<ScrobblerCacheItem>(ScrobbleMetadata(song), timestamp);
scrobbler_cache_ << cache_item;

View File

@@ -22,11 +22,10 @@
#include "config.h"
#include <memory>
#include <QtGlobal>
#include <QMetaType>
#include "core/shared_ptr.h"
#include "scrobblemetadata.h"
class ScrobblerCacheItem {
@@ -40,7 +39,7 @@ class ScrobblerCacheItem {
bool error;
};
using ScrobblerCacheItemPtr = std::shared_ptr<ScrobblerCacheItem>;
using ScrobblerCacheItemPtr = SharedPtr<ScrobblerCacheItem>;
using ScrobblerCacheItemPtrList = QList<ScrobblerCacheItemPtr>;
Q_DECLARE_METATYPE(ScrobblerCacheItemPtr)

View File

@@ -30,6 +30,7 @@
#include <QString>
#include <QJsonObject>
#include "core/shared_ptr.h"
#include "core/song.h"
class ScrobblerService : public QObject {
@@ -42,8 +43,8 @@ class ScrobblerService : public QObject {
virtual void ReloadSettings() = 0;
virtual bool IsEnabled() const { return false; }
virtual bool IsAuthenticated() const { return false; }
virtual bool enabled() const { return false; }
virtual bool authenticated() const { return false; }
virtual void UpdateNowPlaying(const Song &song) = 0;
virtual void ClearPlaying() = 0;
@@ -51,8 +52,7 @@ class ScrobblerService : public QObject {
virtual void Love() {}
virtual void StartSubmit(const bool initial = false) = 0;
virtual void Submitted() = 0;
virtual bool IsSubmitted() const { return false; }
virtual bool submitted() const { return false; }
protected:
using Param = QPair<QString, QString>;
@@ -75,4 +75,6 @@ class ScrobblerService : public QObject {
QString name_;
};
using ScrobblerServicePtr = SharedPtr<ScrobblerService>;
#endif // SCROBBLERSERVICE_H

View File

@@ -0,0 +1,122 @@
/*
* Strawberry Music Player
* Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <QList>
#include <QString>
#include <QSettings>
#include "core/application.h"
#include "core/song.h"
#include "settings/settingsdialog.h"
#include "settings/scrobblersettingspage.h"
#include "scrobblersettings.h"
ScrobblerSettings::ScrobblerSettings(Application *app, QObject *parent)
: QObject(parent),
app_(app),
enabled_(false),
offline_(false),
scrobble_button_(false),
love_button_(false),
submit_delay_(0),
prefer_albumartist_(false),
show_error_dialog_(false) {
ReloadSettings();
}
void ScrobblerSettings::ReloadSettings() {
QSettings s;
s.beginGroup(ScrobblerSettingsPage::kSettingsGroup);
enabled_ = s.value("enabled", false).toBool();
offline_ = s.value("offline", false).toBool();
scrobble_button_ = s.value("scrobble_button", false).toBool();
love_button_ = s.value("love_button", false).toBool();
submit_delay_ = s.value("submit", 0).toInt();
prefer_albumartist_ = s.value("albumartist", false).toBool();
show_error_dialog_ = s.value("show_error_dialog", true).toBool();
QStringList sources = s.value("sources").toStringList();
s.endGroup();
sources_.clear();
if (sources.isEmpty()) {
sources_ << Song::Source::Unknown
<< Song::Source::LocalFile
<< Song::Source::Collection
<< Song::Source::CDDA
<< Song::Source::Device
<< Song::Source::Stream
<< Song::Source::Tidal
<< Song::Source::Subsonic
<< Song::Source::Qobuz
<< Song::Source::SomaFM
<< Song::Source::RadioParadise;
}
else {
for (const QString &source : sources) {
sources_ << Song::SourceFromText(source);
}
}
emit ScrobblingEnabledChanged(enabled_);
emit ScrobbleButtonVisibilityChanged(scrobble_button_);
emit LoveButtonVisibilityChanged(love_button_);
}
void ScrobblerSettings::ToggleScrobbling() {
bool enabled_old_ = enabled_;
enabled_ = !enabled_;
QSettings s;
s.beginGroup(ScrobblerSettingsPage::kSettingsGroup);
s.setValue("enabled", enabled_);
s.endGroup();
if (enabled_ != enabled_old_) emit ScrobblingEnabledChanged(enabled_);
}
void ScrobblerSettings::ToggleOffline() {
bool offline_old_ = offline_;
offline_ = !offline_;
QSettings s;
s.beginGroup(ScrobblerSettingsPage::kSettingsGroup);
s.setValue("offline", offline_);
s.endGroup();
if (offline_ != offline_old_) { emit ScrobblingOfflineChanged(offline_); }
}
void ScrobblerSettings::ShowConfig() {
app_->OpenSettingsDialogAtPage(SettingsDialog::Page::Scrobbler);
}
void ScrobblerSettings::ErrorReceived(const QString &error) {
emit ErrorMessage(error);
}

View File

@@ -0,0 +1,81 @@
/*
* Strawberry Music Player
* Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCROBBLERSETTINGS_H
#define SCROBBLERSETTINGS_H
#include "config.h"
#include <QtGlobal>
#include <QObject>
#include <QList>
#include <QString>
#include "core/song.h"
class Application;
class Song;
class ScrobblerSettings : public QObject {
Q_OBJECT
public:
explicit ScrobblerSettings(Application *app, QObject *parent = nullptr);
void ReloadSettings();
bool enabled() const { return enabled_; }
bool offline() const { return offline_; }
bool scrobble_button() const { return scrobble_button_; }
bool love_button() const { return love_button_; }
int submit_delay() const { return submit_delay_; }
bool prefer_albumartist() const { return prefer_albumartist_; }
bool show_error_dialog() const { return show_error_dialog_; }
QList<Song::Source> sources() const { return sources_; }
void ShowConfig();
public slots:
void ToggleScrobbling();
void ToggleOffline();
void ErrorReceived(const QString &error);
signals:
void ErrorMessage(const QString &error);
void ScrobblingEnabledChanged(const bool value);
void ScrobblingOfflineChanged(const bool value);
void ScrobbleButtonVisibilityChanged(const bool value);
void LoveButtonVisibilityChanged(const bool value);
private:
Application *app_;
bool enabled_;
bool offline_;
bool scrobble_button_;
bool love_button_;
int submit_delay_;
bool prefer_albumartist_;
bool show_error_dialog_;
QList<Song::Source> sources_;
Q_DISABLE_COPY(ScrobblerSettings)
};
#endif // SCROBBLERSETTINGS_H

View File

@@ -44,6 +44,7 @@
#include <QJsonValue>
#include <QFlags>
#include "core/shared_ptr.h"
#include "core/networkaccessmanager.h"
#include "core/song.h"
#include "core/logging.h"
@@ -51,7 +52,7 @@
#include "internet/localredirectserver.h"
#include "settings/scrobblersettingspage.h"
#include "audioscrobbler.h"
#include "scrobblersettings.h"
#include "scrobblerservice.h"
#include "scrobblingapi20.h"
#include "scrobblercache.h"
@@ -62,14 +63,14 @@ const char *ScrobblingAPI20::kApiKey = "211990b4c96782c05d1536e7219eb56e";
const char *ScrobblingAPI20::kSecret = "80fd738f49596e9709b1bf9319c444a8";
const int ScrobblingAPI20::kScrobblesPerRequest = 50;
ScrobblingAPI20::ScrobblingAPI20(const QString &name, const QString &settings_group, const QString &auth_url, const QString &api_url, const bool batch, const QString &cache_file, AudioScrobbler *scrobbler, NetworkAccessManager *network, QObject *parent)
ScrobblingAPI20::ScrobblingAPI20(const QString &name, const QString &settings_group, const QString &auth_url, const QString &api_url, const bool batch, const QString &cache_file, SharedPtr<ScrobblerSettings> settings, SharedPtr<NetworkAccessManager> network, QObject *parent)
: ScrobblerService(name, parent),
name_(name),
settings_group_(settings_group),
auth_url_(auth_url),
api_url_(api_url),
batch_(batch),
scrobbler_(scrobbler),
settings_(settings),
network_(network),
cache_(new ScrobblerCache(cache_file, this)),
server_(nullptr),
@@ -394,7 +395,7 @@ void ScrobblingAPI20::UpdateNowPlaying(const Song &song) {
timestamp_ = QDateTime::currentDateTime().toSecsSinceEpoch();
scrobbled_ = false;
if (!IsAuthenticated() || !song.is_metadata_good() || scrobbler_->IsOffline()) return;
if (!authenticated() || !song.is_metadata_good() || settings_->offline()) return;
ParamList params = ParamList()
<< Param("method", "track.updateNowPlaying")
@@ -453,10 +454,10 @@ void ScrobblingAPI20::Scrobble(const Song &song) {
cache_->Add(song, timestamp_);
if (scrobbler_->IsOffline()) return;
if (settings_->offline()) return;
if (!IsAuthenticated()) {
if (scrobbler_->ShowErrorDialog()) {
if (!authenticated()) {
if (settings_->show_error_dialog()) {
emit ErrorMessage(tr("Scrobbler %1 is not authenticated!").arg(name_));
}
return;
@@ -468,14 +469,14 @@ void ScrobblingAPI20::Scrobble(const Song &song) {
void ScrobblingAPI20::StartSubmit(const bool initial) {
if (!submitted_ && cache_->Count() > 0) {
if (initial && (!batch_ || scrobbler_->SubmitDelay() <= 0) && !submit_error_) {
if (initial && (!batch_ || settings_->submit_delay() <= 0) && !submit_error_) {
if (timer_submit_.isActive()) {
timer_submit_.stop();
}
Submit();
}
else if (!timer_submit_.isActive()) {
int submit_delay = static_cast<int>(std::max(scrobbler_->SubmitDelay(), submit_error_ ? 30 : 5) * kMsecPerSec);
int submit_delay = static_cast<int>(std::max(settings_->submit_delay(), submit_error_ ? 30 : 5) * kMsecPerSec);
timer_submit_.setInterval(submit_delay);
timer_submit_.start();
}
@@ -485,7 +486,7 @@ void ScrobblingAPI20::StartSubmit(const bool initial) {
void ScrobblingAPI20::Submit() {
if (!IsEnabled() || !IsAuthenticated() || scrobbler_->IsOffline()) return;
if (!enabled() || !authenticated() || settings_->offline()) return;
qLog(Debug) << name_ << "Submitting scrobbles.";
@@ -823,7 +824,7 @@ void ScrobblingAPI20::Love() {
if (!song_playing_.is_valid() || !song_playing_.is_metadata_good()) return;
if (!IsAuthenticated()) scrobbler_->ShowConfig();
if (!authenticated()) settings_->ShowConfig();
qLog(Debug) << name_ << "Sending love for song" << song_playing_.artist() << song_playing_.album() << song_playing_.title();
@@ -905,7 +906,7 @@ void ScrobblingAPI20::Error(const QString &error, const QVariant &debug) {
qLog(Error) << name_ << error;
if (debug.isValid()) qLog(Debug) << debug;
if (scrobbler_->ShowErrorDialog()) {
if (settings_->show_error_dialog()) {
emit ErrorMessage(tr("Scrobbler %1 error: %2").arg(name_, error));
}
}

View File

@@ -30,6 +30,7 @@
#include <QString>
#include <QTimer>
#include "core/shared_ptr.h"
#include "core/song.h"
#include "scrobblerservice.h"
#include "scrobblercache.h"
@@ -37,7 +38,7 @@
class QNetworkReply;
class AudioScrobbler;
class ScrobblerSettings;
class NetworkAccessManager;
class LocalRedirectServer;
@@ -45,7 +46,7 @@ class ScrobblingAPI20 : public ScrobblerService {
Q_OBJECT
public:
explicit ScrobblingAPI20(const QString &name, const QString &settings_group, const QString &auth_url, const QString &api_url, const bool batch, const QString &cache_file, AudioScrobbler *scrobbler, NetworkAccessManager *network, QObject *parent = nullptr);
explicit ScrobblingAPI20(const QString &name, const QString &settings_group, const QString &auth_url, const QString &api_url, const bool batch, const QString &cache_file, SharedPtr<ScrobblerSettings> settings, SharedPtr<NetworkAccessManager> network, QObject *parent = nullptr);
~ScrobblingAPI20() override;
static const char *kApiKey;
@@ -53,11 +54,10 @@ class ScrobblingAPI20 : public ScrobblerService {
void ReloadSettings() override;
void LoadSession();
bool IsEnabled() const override { return enabled_; }
bool IsAuthenticated() const override { return !username_.isEmpty() && !session_key_.isEmpty(); }
bool IsSubscriber() const { return subscriber_; }
bool IsSubmitted() const override { return submitted_; }
void Submitted() override { submitted_ = true; }
bool enabled() const override { return enabled_; }
bool authenticated() const override { return !username_.isEmpty() && !session_key_.isEmpty(); }
bool subscriber() const { return subscriber_; }
bool submitted() const override { return submitted_; }
QString username() const { return username_; }
void Authenticate();
@@ -141,8 +141,8 @@ class ScrobblingAPI20 : public ScrobblerService {
QString api_url_;
bool batch_;
AudioScrobbler *scrobbler_;
NetworkAccessManager *network_;
SharedPtr<ScrobblerSettings> settings_;
SharedPtr<NetworkAccessManager> network_;
ScrobblerCache *cache_;
LocalRedirectServer *server_;

View File

@@ -20,29 +20,33 @@
#include "config.h"
#include <memory>
#include <QVariant>
#include <QString>
#include <QDateTime>
#include <QTimer>
#include "core/shared_ptr.h"
#include "core/application.h"
#include "core/song.h"
#include "core/logging.h"
#include "utilities/timeconstants.h"
#include "internet/internetservices.h"
#include "settings/subsonicsettingspage.h"
#include "internet/internetservices.h"
#include "subsonic/subsonicservice.h"
#include "audioscrobbler.h"
#include "scrobblersettings.h"
#include "scrobblerservice.h"
#include "subsonicscrobbler.h"
const char *SubsonicScrobbler::kName = "Subsonic";
SubsonicScrobbler::SubsonicScrobbler(AudioScrobbler *scrobbler, SubsonicService *service, QObject *parent)
SubsonicScrobbler::SubsonicScrobbler(SharedPtr<ScrobblerSettings> settings, Application *app, QObject *parent)
: ScrobblerService(kName, parent),
scrobbler_(scrobbler),
service_(service),
settings_(settings),
app_(app),
service_(nullptr),
enabled_(false),
submitted_(false) {
@@ -62,6 +66,16 @@ void SubsonicScrobbler::ReloadSettings() {
}
SubsonicServicePtr SubsonicScrobbler::service() {
if (!service_) {
service_ = app_->internet_services()->Service<SubsonicService>();
}
return service_;
}
void SubsonicScrobbler::UpdateNowPlaying(const Song &song) {
if (song.source() != Song::Source::Subsonic) return;
@@ -69,9 +83,9 @@ void SubsonicScrobbler::UpdateNowPlaying(const Song &song) {
song_playing_ = song;
time_ = QDateTime::currentDateTime();
if (!song.is_metadata_good() || scrobbler_->IsOffline()) return;
if (!song.is_metadata_good() || settings_->offline() || !service()) return;
service_->Scrobble(song.song_id(), false, time_);
service()->Scrobble(song.song_id(), false, time_);
}
@@ -86,15 +100,15 @@ void SubsonicScrobbler::Scrobble(const Song &song) {
if (song.source() != Song::Source::Subsonic || song.id() != song_playing_.id() || song.url() != song_playing_.url() || !song.is_metadata_good()) return;
if (scrobbler_->IsOffline()) return;
if (settings_->offline()) return;
if (!submitted_) {
submitted_ = true;
if (scrobbler_->SubmitDelay() <= 0) {
if (settings_->submit_delay() <= 0) {
Submit();
}
else if (!timer_submit_.isActive()) {
timer_submit_.setInterval(static_cast<int>(scrobbler_->SubmitDelay() * kMsecPerSec));
timer_submit_.setInterval(static_cast<int>(settings_->submit_delay() * kMsecPerSec));
timer_submit_.start();
}
}
@@ -106,8 +120,8 @@ void SubsonicScrobbler::Submit() {
qLog(Debug) << "SubsonicScrobbler: Submitting scrobble for" << song_playing_.artist() << song_playing_.title();
submitted_ = false;
if (scrobbler_->IsOffline()) return;
if (settings_->offline() || !service()) return;
service_->Scrobble(song_playing_.song_id(), true, time_);
service()->Scrobble(song_playing_.song_id(), true, time_);
}

View File

@@ -30,41 +30,44 @@
#include <QString>
#include <QTimer>
#include "core/shared_ptr.h"
#include "core/song.h"
#include "scrobblerservice.h"
class Application;
class AudioScrobbler;
class ScrobblerSettings;
class SubsonicService;
class SubsonicScrobbler : public ScrobblerService {
Q_OBJECT
public:
explicit SubsonicScrobbler(AudioScrobbler *scrobbler, SubsonicService *service, QObject *parent = nullptr);
explicit SubsonicScrobbler(SharedPtr<ScrobblerSettings> settings, Application *app, QObject *parent = nullptr);
static const char *kName;
void ReloadSettings() override;
bool IsEnabled() const override { return enabled_; }
bool IsAuthenticated() const override { return true; }
bool enabled() const override { return enabled_; }
bool authenticated() const override { return true; }
void UpdateNowPlaying(const Song &song) override;
void ClearPlaying() override;
void Scrobble(const Song &song) override;
void StartSubmit(const bool initial = false) override { Q_UNUSED(initial) }
void Submitted() override { submitted_ = true; }
bool IsSubmitted() const override { return submitted_; }
bool submitted() const override { return submitted_; }
SharedPtr<SubsonicService> service();
public slots:
void WriteCache() override {}
void Submit() override;
private:
AudioScrobbler *scrobbler_;
SubsonicService *service_;
SharedPtr<ScrobblerSettings> settings_;
Application *app_;
SharedPtr<SubsonicService> service_;
bool enabled_;
bool submitted_;
Song song_playing_;