Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mpris2: send more metadata about current track #165

Merged

Conversation

onegentig
Copy link
Contributor

@onegentig onegentig commented Jul 20, 2024

This PR adds more MPRIS2 metadata to broadcast about the current track to "MPRIS2 Server" plugin.


Compared to players like Supersonic, Rhythmbox, DeaDBeeF (via plugin) or Spotify, Audacious does not provide much metadata about the currently playing track.

Intercepted Examples (using dbus-python)


Audacious: (7)

{
    "xesam:title": "apple_pie.0",
    "xesam:artist": [ "NǽnøĉÿbbŒrğ VbëřřĦōlökäävsŦ" ],
    "xesam:album": "simulated_worlds",
    "xesam:url": "file:///...%20apple_pie.0.mp3",
    "mpris:length": 6074000,
    "mpris:artUrl": "file:///tmp/audacious-temp-XD94Q2",
    "mpris:trackid": "/org/mpris/MediaPlayer2/CurrentTrack"
}

Rhythmbox: (15)

{
    "mpris:trackid": "/org/mpris/MediaPlayer2/Track/737",
    "xesam:url": "file:///...%20apple_pie.0.mp3",
    "xesam:title": "apple_pie.0",
    "xesam:artist": [ "NǽnøĉÿbbŒrğ VbëřřĦōlökäävsŦ" ],
    "xesam:album": "simulated_worlds",
    "xesam:genre": [ "Post-Noise" ],
    "xesam:comment": "i am nanocyborg",
    "xesam:albumArtist": [ "NǽnøĉÿbbŒrğ VbëřřĦōlökäävsŦ" ],
    "xesam:contentCreated": "2045-01-01T00:00:00Z"
    "mpris:length": 6000000,
    "xesam:tracknumber": 20,
    "xesam:discNumber": 1,
    "xesam:useCount": 0,
    "xesam:userRating": 0.0,
    "xesam:artUrl": "file:///home/onegen/.cache/rhythmbox/album-art/./004"
}

Spotify: (11)

{
    "mpris:trackid": "/com/spotify/track/4JS5k...",
    "mpris:length": 79971000,
    "mpris:artUrl": "https://i.scdn.co/image/ab67...",
    "xesam:album": "What a Wonderful World",
    "xesam:albumArtist": [ "Auvic" ],
    "xesam:artist": [ "Auvic" ],
    "xesam:autoRating": 0.11,
    "xesam:discNumber": 1,
    "xesam:title": "Dearly Dearest",
    "xesam:tracknumber": 1,
    "xesam:url": "https://open.spotify.com/track/4JS..."
}

VLC Media Player: (14)

{
    "mpris:trackid": "/org/videolan/vlc/playlist/3",
    "xesam:url": "file:///...%20apple_pie.0.mp3",
    "xesam:title": "apple_pie.0",
    "xesam:artist": [ "NǽnøĉÿbbŒrğ VbëřřĦōlökäävsŦ" ],
    "xesam:album": "simulated_worlds",
    "xesam:tracknumber": 20,
    "vlc:time": 6,
    "mpris:length": 6168000,
    "xesam:genre": [ "Post-Noise" ],
    "xesam:contentCreated": "2045",
    "mpris:artUrl": "file:///home/onegen/.cache/vlc/.../art.png",
    "xesam:comment": "i am nanocyborg",
    "vlc:length": 6168,
    "vlc:publisher": 3
}

As is, Audacious only provides xesam:title, xesam:artist, xesam:album, mpris:length, xesam:artUrl (ex. "audacious-temp-TNGWQ2") and xesam:url (local path to played file).

This PR adds the following fields:

  • xesam:albumArtist
  • xesam:comment
  • xesam:genre
  • xesam:composer
  • xesam:trackNumber
  • xesam:discNumber
Example
{
    "xesam:title": "apple_pie.0",
    "xesam:artist": [ "NǽnøĉÿbbŒrğ VbëřřĦōlökäävsŦ" ],
    "xesam:album": "simulated_worlds",
    "xesam:albumArtist": [ "NǽnøĉÿbbŒrğ VbëřřĦōlökäävsŦ" ],
    "xesam:comment": "i am nanocyborg",
    "xesam:genre": [ "Post-Noise" ],
    "xesam:url": "file:///...%20apple_pie.0.mp3",
    "xesam:tracknumber": 20,
    "mpris:length": 6074000,
    "xesam:discNumber": 1,
    "mpris:artUrl": "file:///tmp/audacious-temp-XD94Q2",
    "mpris:trackid": "/org/mpris/MediaPlayer2/CurrentTrack"
}

Motivation

Providing richer metadata supports better interoperability and potentially user experience when using other tools alongside Audacious. MPRIS2 metadata is commonly used be player controllers or other music-related apps, like Last.FM/ListenBrainz scrobblers (ex. mpris-scrobbler or rescrobbled – needs as much data as it can for profile display and recording detection), Discord rich presence (multiple – the pretty fields don’t fill themselves) and others. I was working on my own ListenBrainz scrobbler, and found the amount of data lacking. For some tracks, I had to manually link the track to MusicBrainz – it didn’t have enough data to link it itself.
Another potential applicability are voice assistants and smart devices – a prompt "Play track 5 from disc 2 of simulated_worlds album" would only be possible with this additional metadata.

However, I recognise this may be of minor interest to Audacious and the downsides of added file size and performance may outweigh the positives. It is entirely to you all to decide, if this is a desirable addition.

Notes

Codebase Changes

MPRIS2 server source code was refactored to simplify adding new fields and somewhat modernise working with metadata.

  • Metadata is now stored in a new MPRIS2Metadata struct
  • elems, storage of DBus values to broadcast, was changed from an array to std::vector<GVariant *>
    • new import <vector>!
  • Added helper methods to insert GValues to elems (multiple for different value types)

Remaining Metadata

There are some metadata defined in MPRIS2 specification that I wasn’t able to add, as they were not present in the metadata tuple.

  • xesam:audioBPM
  • xesam:autoRating (automatically-generated rating)
  • xesam:firstUsed (datetime of first listen)
  • xesam:lastUsed (datetime of last listen)
  • xesam:useCount (number of times listened)
  • xesam:userRating

There are two fields I could add, but chose not to without consultation:

  • xesam:contentCreated (release date) – it would be possible to set this to a year by converting Year to a string, though this would require another new import – <string> – and I was unsure if I can get away with that after already adding <vector>. As for month and date, I was unsure if I can obtain it… Maybe Date? What format is it in? Seems like a lot of validation required, but idk..
  • xesam:asText (lyrics) – very simple to add, but I was not initially sure if it’s worth it. This is potentially a very long string and most tools usually fetch the lyrics themselves. I am unsure of the weight this might have, maybe I am just paranoid.

I implemented both lyrics and release year on separate branches, see following commits:

I would like to hear your thoughts on them before including them.


I will appreciate any feedback or suggestions on how to make this work.

Thank you for your time and consideration,
– onegen

@radioactiveman
Copy link
Member

This is how a proper merge request should look like. 👍 Thanks also for the great and detailed description.

However, I recognise this may be of minor interest to Audacious and the downsides of added file size and performance may outweigh the positives. It is entirely to you all to decide, if this is a desirable addition.

I don't see why it should impact file size and performance noticeably. So I am in favor of merging this.

Remarks from my side:

  • Use the coding style from the current code. Function calls for example leave a space before the parentheses.
  • Use Index from <libaudcore/index.h> instead of <vector>. This should work as well, right?
  • For xesam:contentCreated you can replace <string> with <libaudcore/audstrings.h>. The field expects an ISO 8601 DateTime but passing only the year should be fine if I understand the description correctly. And your VLC example shows the same approach.
  • Regarding xesam:asText: I wouldn't bother about string length here. Therefore +1 for including it too.

@jlindgren90: Could you also review this since the MPRIS plugin was mostly written by you? Thanks.

@jlindgren90
Copy link
Member

This looks good to me. I also don't mind using std types if it's more convenient. There were good reasons to use Index at one point, but for new code that isn't in a hot path, those probably don't matter.

@jlindgren90
Copy link
Member

It would be nice to squash the commits when merging.

@onegentig
Copy link
Contributor Author

onegentig commented Jul 20, 2024

Many thanks for the swift review and your kind words!

I adjusted the coding style to fit the rest of the code (9e9726b).

Regarding Index, it is possible to use it instead of std::vector without any issues. The init of a single-member Index<String> is a bit awkward, as I wanted to avoid turning one line to three, but it all works well.

if (meta.artist)
    add_g_variant_arr_str ("xesam:artist", {meta.artist}, elems);
if (meta.artist)
    add_g_variant_arr_str ("xesam:artist",
                          str_list_to_index (meta.artist, ""), elems);

I kept this change on a separate branch for your consideration: https://github.com/onegentig/audacious-plugins/commit/e545acf0a034302a1cdbe786d0bdea9da3859fe6.

For xesam:contentCreated, using just the year works fine and without the headaches of Tuple::Date validation and format. Added zero padding (as year must have 4 characters even if <1000) and swapped <string> for <libaudcore/audstrings.h> without any issues (ea90af7).

And I will also merge xesam:asText (lyrics) to this PR. I often work in low-level, so please pardon my paranoia about long strings.
I have to rebase it first, so give me a minute or two. (35fefae)

@radioactiveman
Copy link
Member

@onegentig: Thanks for the update and sorry for the late reply. But it's vacation time after all. :)

@jlindgren90: Could you please check commit https://github.com/onegentig/audacious-plugins/commit/e545acf0a034302a1cdbe786d0bdea9da3859fe6? Is there a simpler way for the initialization?

@onegentig
Copy link
Contributor Author

onegentig commented Jul 30, 2024

Perfectly understandable, there’s no rush anywhere. 🙂👍

While str_list_to_index works (as used in https://github.com/onegentig/audacious-plugins/commit/e545acf0a034302a1cdbe786d0bdea9da3859fe6), it probably executes unnecessary operations (scanning the entire string for a separator that will never be matched). If libaudcore lacks a simple one-line index init, creating a minor helper function could work:

Index<String> str_to_idx(const char * str) {
    Index<String> idx;
    if (str)
        idx.append(str);
    return idx;
}

…employed as follows:

if (meta.artist)
    add_g_variant_arr_str ("xesam:artist",
                          str_to_idx (meta.artist), elems);

@jlindgren90
Copy link
Member

I would avoid constructing an Index to pass literal arrays/lists in the first place, and use ArrayRef instead. Example: jlindgren90@770b5a3

@onegentig
Copy link
Contributor Author

onegentig commented Jul 30, 2024

I have tested it now and can confirm that ArrayRef does the job well, thank you! I was not aware of the type prior.
I have rebased jlindgren90@770b5a3 onto my refactor/mpris2-index-vector branch for further refinement and consideration (https://github.com/onegentig/audacious-plugins/commit/770b5a3fd78fc02b802b62610db7f303109cb853).

Given the efficacy of ArrayRef and Index in place of vectors here, should I merge that branch into this PR?

EDIT: Squash-merged (62a9935).

Co-authored-by: John Lindgren <john@jlindgren.net>
@radioactiveman radioactiveman merged commit 0137b7f into audacious-media-player:master Jul 31, 2024
10 checks passed
@radioactiveman
Copy link
Member

Merged now, thanks again. 👍

@onegentig
Copy link
Contributor Author

Thank you for the merge and the guidance. Glad to contribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants