From 8e226302abde278542801f6d3a588b5a91ab9337 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Wed, 20 Nov 2019 21:30:41 +0100 Subject: [PATCH] Allow scrobbling songs without album Fixes #309 --- src/core/song.cpp | 3 +- src/core/song.h | 2 ++ src/scrobbler/listenbrainzscrobbler.cpp | 16 ++++++--- src/scrobbler/scrobblercacheitem.h | 4 ++- src/scrobbler/scrobblingapi20.cpp | 46 +++++++++++++++++-------- 5 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/core/song.cpp b/src/core/song.cpp index 992373c5d..3ca852020 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -152,6 +152,7 @@ const QString Song::kEmbeddedCover = "(embedded)"; const QRegExp Song::kAlbumRemoveDisc(" ?-? ((\\(|\\[)?)(Disc|CD) ?([0-9]{1,2})((\\)|\\])?)$"); const QRegExp Song::kAlbumRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|([0-9]{1,4}) *Remaster) ?((\\)|\\])?)$"); const QRegExp Song::kTitleRemoveMisc(" ?-? ((\\(|\\[)?)(Remastered|Live|Remastered Version|([0-9]{1,4}) *Remaster) ?((\\)|\\])?)$"); +const QString Song::kVariousArtists("various artists"); const QStringList Song::kArticles = QStringList() << "the " << "a " << "an "; @@ -351,7 +352,7 @@ const QString &Song::cue_path() const { return d->cue_path_; } bool Song::has_cue() const { return !d->cue_path_.isEmpty(); } bool Song::is_collection_song() const { return d->source_ == Source_Collection; } -bool Song::is_metadata_good() const { return !d->title_.isEmpty() && !d->album_.isEmpty() && !d->artist_.isEmpty() && !d->url_.isEmpty() && d->end_ > 0; } +bool Song::is_metadata_good() const { return !d->title_.isEmpty() && !d->artist_.isEmpty() && !d->url_.isEmpty() && d->end_ > 0; } bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal || d->source_ == Source_Subsonic || d->source_ == Source_Qobuz; } bool Song::is_cdda() const { return d->source_ == Source_CDDA; } bool Song::is_compilation() const { return (d->compilation_ || d->compilation_detected_ || d->compilation_on_) && !d->compilation_off_; } diff --git a/src/core/song.h b/src/core/song.h index d512c94e1..c41707f1f 100644 --- a/src/core/song.h +++ b/src/core/song.h @@ -126,6 +126,8 @@ class Song { static const QRegExp kTitleRemoveMisc; static const QRegExp kFilenameRemoveNonFatChars; + static const QString kVariousArtists; + static const QStringList kArticles; static QString JoinSpec(const QString &table); diff --git a/src/scrobbler/listenbrainzscrobbler.cpp b/src/scrobbler/listenbrainzscrobbler.cpp index bf49c82f4..57597837c 100644 --- a/src/scrobbler/listenbrainzscrobbler.cpp +++ b/src/scrobbler/listenbrainzscrobbler.cpp @@ -364,13 +364,16 @@ void ListenBrainzScrobbler::UpdateNowPlaying(const Song &song) { album = album.remove(Song::kAlbumRemoveMisc); QJsonObject object_track_metadata; - if (song.albumartist().isEmpty() || song.albumartist().toLower() == "various artists") { + if (song.albumartist().isEmpty() || song.albumartist().toLower() == Song::kVariousArtists) { object_track_metadata.insert("artist_name", QJsonValue::fromVariant(song.artist())); } else { object_track_metadata.insert("artist_name", QJsonValue::fromVariant(song.albumartist())); } - object_track_metadata.insert("release_name", QJsonValue::fromVariant(album)); + + if (!album.isEmpty()) + object_track_metadata.insert("release_name", QJsonValue::fromVariant(album)); + object_track_metadata.insert("track_name", QJsonValue::fromVariant(song.title())); QJsonObject object_listen; @@ -478,16 +481,19 @@ void ListenBrainzScrobbler::Submit() { for (ScrobblerCacheItem *item : cache_->List()) { if (item->sent_) continue; item->sent_ = true; - i++; + ++i; list << item->timestamp_; QJsonObject object_listen; object_listen.insert("listened_at", QJsonValue::fromVariant(item->timestamp_)); QJsonObject object_track_metadata; - if (item->albumartist_.isEmpty() || item->albumartist_.toLower() == "various artists") + if (item->albumartist_.isEmpty() || item->albumartist_.toLower() == Song::kVariousArtists) object_track_metadata.insert("artist_name", QJsonValue::fromVariant(item->artist_)); else object_track_metadata.insert("artist_name", QJsonValue::fromVariant(item->albumartist_)); - object_track_metadata.insert("release_name", QJsonValue::fromVariant(item->album_)); + + if (!item->album_.isEmpty()) + object_track_metadata.insert("release_name", QJsonValue::fromVariant(item->album_)); + object_track_metadata.insert("track_name", QJsonValue::fromVariant(item->song_)); object_listen.insert("track_metadata", object_track_metadata); array.append(QJsonValue::fromVariant(object_listen)); diff --git a/src/scrobbler/scrobblercacheitem.h b/src/scrobbler/scrobblercacheitem.h index a019c0efb..d8752e5eb 100644 --- a/src/scrobbler/scrobblercacheitem.h +++ b/src/scrobbler/scrobblercacheitem.h @@ -28,6 +28,8 @@ #include #include +#include "core/song.h" + class ScrobblerCacheItem : public QObject { Q_OBJECT @@ -35,7 +37,7 @@ class ScrobblerCacheItem : public QObject { explicit ScrobblerCacheItem(const QString &artist, const QString &album, const QString &song, const QString &albumartist, const int track, const qint64 duration, const quint64 ×tamp); ~ScrobblerCacheItem(); - QString effective_albumartist() const { return albumartist_.isEmpty() ? artist_ : albumartist_; } + QString effective_albumartist() const { return albumartist_.isEmpty() || albumartist_.toLower() == Song::kVariousArtists ? artist_ : albumartist_; } public: QString artist_; diff --git a/src/scrobbler/scrobblingapi20.cpp b/src/scrobbler/scrobblingapi20.cpp index 20e10d66b..131661978 100644 --- a/src/scrobbler/scrobblingapi20.cpp +++ b/src/scrobbler/scrobblingapi20.cpp @@ -436,11 +436,14 @@ void ScrobblingAPI20::UpdateNowPlaying(const Song &song) { ParamList params = ParamList() << Param("method", "track.updateNowPlaying") - << Param("artist", prefer_albumartist_ ? song.effective_albumartist() : song.artist()) - << Param("track", song.title()) - << Param("album", album); + << Param("artist", prefer_albumartist_ && song.effective_albumartist() != Song::kVariousArtists ? song.effective_albumartist() : song.artist()) + << Param("track", song.title()); - if (!prefer_albumartist_ && !song.albumartist().isEmpty()) params << Param("albumArtist", song.albumartist()); + if (!album.isEmpty()) + params << Param("album", album); + + if (!prefer_albumartist_ && !song.albumartist().isEmpty() && song.albumartist().toLower() != Song::kVariousArtists) + params << Param("albumArtist", song.albumartist()); QNetworkReply *reply = CreateRequest(params); NewClosure(reply, SIGNAL(finished()), this, SLOT(UpdateNowPlayingRequestFinished(QNetworkReply*)), reply); @@ -532,16 +535,22 @@ void ScrobblingAPI20::Submit() { for (ScrobblerCacheItem *item : cache()->List()) { if (item->sent_) continue; item->sent_ = true; - if (!batch_) { SendSingleScrobble(item); continue; } + if (!batch_) { + SendSingleScrobble(item); + continue; + } i++; list << item->timestamp_; params << Param(QString("%1[%2]").arg("artist").arg(i), prefer_albumartist_ ? item->effective_albumartist() : item->artist_); - params << Param(QString("%1[%2]").arg("album").arg(i), item->album_); params << Param(QString("%1[%2]").arg("track").arg(i), item->song_); params << Param(QString("%1[%2]").arg("timestamp").arg(i), QString::number(item->timestamp_)); params << Param(QString("%1[%2]").arg("duration").arg(i), QString::number(item->duration_ / kNsecPerSec)); - if (!prefer_albumartist_ && !item->albumartist_.isEmpty()) params << Param(QString("%1[%2]").arg("albumArtist").arg(i), item->albumartist_); - if (item->track_ > 0) params << Param(QString("%1[%2]").arg(i).arg("trackNumber"), QString::number(item->track_)); + if (!item->album_.isEmpty()) + params << Param(QString("%1[%2]").arg("album").arg(i), item->album_); + if (!prefer_albumartist_ && !item->albumartist_.isEmpty() && item->albumartist_.toLower() != Song::kVariousArtists) + params << Param(QString("%1[%2]").arg("albumArtist").arg(i), item->albumartist_); + if (item->track_ > 0) + params << Param(QString("%1[%2]").arg(i).arg("trackNumber"), QString::number(item->track_)); if (i >= kScrobblesPerRequest) break; } @@ -713,13 +722,16 @@ void ScrobblingAPI20::SendSingleScrobble(ScrobblerCacheItem *item) { ParamList params = ParamList() << Param("method", "track.scrobble") << Param("artist", prefer_albumartist_ ? item->effective_albumartist() : item->artist_) - << Param("album", item->album_) << Param("track", item->song_) << Param("timestamp", QString::number(item->timestamp_)) << Param("duration", QString::number(item->duration_ / kNsecPerSec)); - if (!prefer_albumartist_ && !item->albumartist_.isEmpty()) params << Param("albumArtist", item->albumartist_); - if (item->track_ > 0) params << Param("trackNumber", QString::number(item->track_)); + if (!item->album_.isEmpty()) + params << Param("album", item->album_); + if (!prefer_albumartist_ && !item->albumartist_.isEmpty() && item->albumartist_.toLower() != Song::kVariousArtists) + params << Param("albumArtist", item->albumartist_); + if (item->track_ > 0) + params << Param("trackNumber", QString::number(item->track_)); QNetworkReply *reply = CreateRequest(params); NewClosure(reply, SIGNAL(finished()), this, SLOT(SingleScrobbleRequestFinished(QNetworkReply*, quint64)), reply, item->timestamp_); @@ -860,10 +872,14 @@ void ScrobblingAPI20::Love() { ParamList params = ParamList() << Param("method", "track.love") - << Param("artist", song_playing_.artist()) - << Param("track", song_playing_.title()) - << Param("album", song_playing_.album()); - if (!song_playing_.albumartist().isEmpty()) params << Param("albumArtist", song_playing_.albumartist()); + << Param("artist", prefer_albumartist_ && song_playing_.effective_albumartist() != Song::kVariousArtists ? song_playing_.effective_albumartist() : song_playing_.artist()) + << Param("track", song_playing_.title()); + + if (!song_playing_.album().isEmpty()) + params << Param("album", song_playing_.album()); + + if (!prefer_albumartist_ && !song_playing_.albumartist().isEmpty() && song_playing_.albumartist().toLower() != Song::kVariousArtists) + params << Param("albumArtist", song_playing_.albumartist()); QNetworkReply *reply = CreateRequest(params); NewClosure(reply, SIGNAL(finished()), this, SLOT(LoveRequestFinished(QNetworkReply*)), reply);