ListenBrainzScrobbler: Report more info to ListenBrainz
Report music service, URL, and Spotify ID to ListenBrainz. ListenBrainz accepts the music service in listen reports, in both a canonical domain format and a human-readable display name format. This commit makes Strawberry report both, for maximum flexibility. I've also set it up to report a shareable track URL for supported streaming services. I am already using this data in my homepage's "Now Playing" widget. Fixes #1768
This commit is contained in:
committed by
Jonas Kvinge
parent
e0d61223a4
commit
a5f94b608b
@@ -1182,6 +1182,22 @@ QIcon Song::IconForSource(const Source source) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert a source to a music service domain name, for ListenBrainz.
|
||||||
|
// See the "Music service names" note on https://listenbrainz.readthedocs.io/en/latest/users/json.html.
|
||||||
|
|
||||||
|
QString Song::DomainForSource(const Source source) {
|
||||||
|
|
||||||
|
switch (source) {
|
||||||
|
case Song::Source::Tidal: return u"tidal.com"_s;
|
||||||
|
case Song::Source::Qobuz: return u"qobuz.com"_s;
|
||||||
|
case Song::Source::SomaFM: return u"somafm.com"_s;
|
||||||
|
case Song::Source::RadioParadise: return u"radioparadise.com"_s;
|
||||||
|
case Song::Source::Spotify: return u"spotify.com"_s;
|
||||||
|
default: return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
QString Song::TextForFiletype(const FileType filetype) {
|
QString Song::TextForFiletype(const FileType filetype) {
|
||||||
|
|
||||||
switch (filetype) {
|
switch (filetype) {
|
||||||
@@ -1282,6 +1298,23 @@ QIcon Song::IconForFiletype(const FileType filetype) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get a URL usable for sharing this song with another user.
|
||||||
|
// This is only applicable when streaming from a streaming service, since we can't link to local content.
|
||||||
|
// Returns a web URL which points to the current streaming track or live stream, or an empty string if that is not applicable.
|
||||||
|
|
||||||
|
QString Song::ShareURL() const {
|
||||||
|
|
||||||
|
switch (source()) {
|
||||||
|
case Song::Source::Stream:
|
||||||
|
case Song::Source::SomaFM: return url().toString();
|
||||||
|
case Song::Source::Tidal: return "https://tidal.com/track/%1"_L1.arg(song_id());
|
||||||
|
case Song::Source::Qobuz: return "https://open.qobuz.com/track/%1"_L1.arg(song_id());
|
||||||
|
case Song::Source::Spotify: return "https://open.spotify.com/track/%1"_L1.arg(song_id());
|
||||||
|
default: return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool Song::IsFileLossless() const {
|
bool Song::IsFileLossless() const {
|
||||||
|
|
||||||
switch (filetype()) {
|
switch (filetype()) {
|
||||||
|
|||||||
@@ -453,6 +453,7 @@ class Song {
|
|||||||
static QString DescriptionForSource(const Source source);
|
static QString DescriptionForSource(const Source source);
|
||||||
static Source SourceFromText(const QString &source);
|
static Source SourceFromText(const QString &source);
|
||||||
static QIcon IconForSource(const Source source);
|
static QIcon IconForSource(const Source source);
|
||||||
|
static QString DomainForSource(const Source source);
|
||||||
static QString TextForFiletype(const FileType filetype);
|
static QString TextForFiletype(const FileType filetype);
|
||||||
static QString ExtensionForFiletype(const FileType filetype);
|
static QString ExtensionForFiletype(const FileType filetype);
|
||||||
static QIcon IconForFiletype(const FileType filetype);
|
static QIcon IconForFiletype(const FileType filetype);
|
||||||
@@ -460,9 +461,12 @@ class Song {
|
|||||||
QString TextForSource() const { return TextForSource(source()); }
|
QString TextForSource() const { return TextForSource(source()); }
|
||||||
QString DescriptionForSource() const { return DescriptionForSource(source()); }
|
QString DescriptionForSource() const { return DescriptionForSource(source()); }
|
||||||
QIcon IconForSource() const { return IconForSource(source()); }
|
QIcon IconForSource() const { return IconForSource(source()); }
|
||||||
|
QString DomainForSource() const { return DomainForSource(source()); }
|
||||||
QString TextForFiletype() const { return TextForFiletype(filetype()); }
|
QString TextForFiletype() const { return TextForFiletype(filetype()); }
|
||||||
QIcon IconForFiletype() const { return IconForFiletype(filetype()); }
|
QIcon IconForFiletype() const { return IconForFiletype(filetype()); }
|
||||||
|
|
||||||
|
QString ShareURL() const;
|
||||||
|
|
||||||
bool IsFileLossless() const;
|
bool IsFileLossless() const;
|
||||||
static FileType FiletypeByMimetype(const QString &mimetype);
|
static FileType FiletypeByMimetype(const QString &mimetype);
|
||||||
static FileType FiletypeByDescription(const QString &text);
|
static FileType FiletypeByDescription(const QString &text);
|
||||||
|
|||||||
@@ -235,6 +235,21 @@ QJsonObject ListenBrainzScrobbler::JsonTrackMetadata(const ScrobbleMetadata &met
|
|||||||
object_additional_info.insert("work_mbids"_L1, array_musicbrainz_work_id);
|
object_additional_info.insert("work_mbids"_L1, array_musicbrainz_work_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!metadata.music_service.isEmpty()) {
|
||||||
|
object_additional_info.insert("music_service"_L1, metadata.music_service);
|
||||||
|
}
|
||||||
|
if (!metadata.music_service_name.isEmpty()) {
|
||||||
|
object_additional_info.insert("music_service_name"_L1, metadata.music_service_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metadata.share_url.isEmpty()) {
|
||||||
|
object_additional_info.insert("origin_url"_L1, metadata.share_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metadata.spotify_id.isEmpty()) {
|
||||||
|
object_additional_info.insert("spotify_id"_L1, metadata.spotify_id);
|
||||||
|
}
|
||||||
|
|
||||||
object_track_metadata.insert("additional_info"_L1, object_additional_info);
|
object_track_metadata.insert("additional_info"_L1, object_additional_info);
|
||||||
|
|
||||||
return object_track_metadata;
|
return object_track_metadata;
|
||||||
|
|||||||
@@ -38,4 +38,8 @@ ScrobbleMetadata::ScrobbleMetadata(const Song &song)
|
|||||||
musicbrainz_disc_id(song.musicbrainz_disc_id()),
|
musicbrainz_disc_id(song.musicbrainz_disc_id()),
|
||||||
musicbrainz_release_group_id(song.musicbrainz_release_group_id()),
|
musicbrainz_release_group_id(song.musicbrainz_release_group_id()),
|
||||||
musicbrainz_work_id(song.musicbrainz_work_id()),
|
musicbrainz_work_id(song.musicbrainz_work_id()),
|
||||||
|
music_service(song.is_stream() ? song.DomainForSource() : QString()),
|
||||||
|
music_service_name(song.is_stream() ? song.DescriptionForSource() : QString()),
|
||||||
|
share_url(song.ShareURL()),
|
||||||
|
spotify_id(song.source() == Song::Source::Spotify ? song.song_id() : QString()),
|
||||||
length_nanosec(song.length_nanosec()) {}
|
length_nanosec(song.length_nanosec()) {}
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ class ScrobbleMetadata {
|
|||||||
QString musicbrainz_disc_id;
|
QString musicbrainz_disc_id;
|
||||||
QString musicbrainz_release_group_id; // release_group_mbid
|
QString musicbrainz_release_group_id; // release_group_mbid
|
||||||
QString musicbrainz_work_id; // work_mbids
|
QString musicbrainz_work_id; // work_mbids
|
||||||
|
QString music_service;
|
||||||
|
QString music_service_name;
|
||||||
|
QString share_url;
|
||||||
|
QString spotify_id;
|
||||||
qint64 length_nanosec;
|
qint64 length_nanosec;
|
||||||
|
|
||||||
QString effective_albumartist() const { return albumartist.isEmpty() ? artist : albumartist; }
|
QString effective_albumartist() const { return albumartist.isEmpty() ? artist : albumartist; }
|
||||||
|
|||||||
@@ -180,6 +180,18 @@ void ScrobblerCache::ReadCache() {
|
|||||||
if (json_obj_track.contains("musicbrainz_work_id"_L1)) {
|
if (json_obj_track.contains("musicbrainz_work_id"_L1)) {
|
||||||
metadata.musicbrainz_work_id = json_obj_track["musicbrainz_work_id"_L1].toString();
|
metadata.musicbrainz_work_id = json_obj_track["musicbrainz_work_id"_L1].toString();
|
||||||
}
|
}
|
||||||
|
if (json_obj_track.contains("music_service"_L1)) {
|
||||||
|
metadata.music_service = json_obj_track["music_service"_L1].toString();
|
||||||
|
}
|
||||||
|
if (json_obj_track.contains("music_service_name"_L1)) {
|
||||||
|
metadata.music_service_name = json_obj_track["music_service_name"_L1].toString();
|
||||||
|
}
|
||||||
|
if (json_obj_track.contains("share_url"_L1)) {
|
||||||
|
metadata.share_url = json_obj_track["share_url"_L1].toString();
|
||||||
|
}
|
||||||
|
if (json_obj_track.contains("spotify_id"_L1)) {
|
||||||
|
metadata.spotify_id = json_obj_track["spotify_id"_L1].toString();
|
||||||
|
}
|
||||||
|
|
||||||
ScrobblerCacheItemPtr cache_item = make_shared<ScrobblerCacheItem>(metadata, timestamp);
|
ScrobblerCacheItemPtr cache_item = make_shared<ScrobblerCacheItem>(metadata, timestamp);
|
||||||
scrobbler_cache_ << cache_item;
|
scrobbler_cache_ << cache_item;
|
||||||
@@ -220,6 +232,10 @@ void ScrobblerCache::WriteCache() {
|
|||||||
object.insert("musicbrainz_disc_id"_L1, QJsonValue::fromVariant(cache_item->metadata.musicbrainz_disc_id));
|
object.insert("musicbrainz_disc_id"_L1, QJsonValue::fromVariant(cache_item->metadata.musicbrainz_disc_id));
|
||||||
object.insert("musicbrainz_release_group_id"_L1, QJsonValue::fromVariant(cache_item->metadata.musicbrainz_release_group_id));
|
object.insert("musicbrainz_release_group_id"_L1, QJsonValue::fromVariant(cache_item->metadata.musicbrainz_release_group_id));
|
||||||
object.insert("musicbrainz_work_id"_L1, QJsonValue::fromVariant(cache_item->metadata.musicbrainz_work_id));
|
object.insert("musicbrainz_work_id"_L1, QJsonValue::fromVariant(cache_item->metadata.musicbrainz_work_id));
|
||||||
|
object.insert("music_service"_L1, QJsonValue::fromVariant(cache_item->metadata.music_service));
|
||||||
|
object.insert("music_service_name"_L1, QJsonValue::fromVariant(cache_item->metadata.music_service_name));
|
||||||
|
object.insert("share_url"_L1, QJsonValue::fromVariant(cache_item->metadata.share_url));
|
||||||
|
object.insert("spotify_id"_L1, QJsonValue::fromVariant(cache_item->metadata.spotify_id));
|
||||||
object.insert("length_nanosec"_L1, QJsonValue::fromVariant(cache_item->metadata.length_nanosec));
|
object.insert("length_nanosec"_L1, QJsonValue::fromVariant(cache_item->metadata.length_nanosec));
|
||||||
array.append(QJsonValue::fromVariant(object));
|
array.append(QJsonValue::fromVariant(object));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user