Checking minimal JSON submission

I’ve written a script to submit listens from cmus. It’s written to only submit a listen if the track is tagged with a MusicBrainz recording ID. It’s been working okay; I just want to check that my minimal JSON is enough to accurately identify tracks:

{
	"listen_type": "single",
	"payload": [
		{
			"listened_at": "$1",
			"track_metadata": {
				"artist_name": "$2",
				"release_name": "$3",
				"track_name": "$4",
				"additional_info": {
					"recording_mbid": "$5"
				}
			}
		}
	]
}
1 Like

Have you checked the docs?

JSON Documentation — ListenBrainz 0.1.0 documentation

I think your data looks ok, however, we encourage users to send as much relevant information as possible, to help us match the tracks. In particular, if you have release_mbid and artist/artist_credit MBIDs in your collection, you should send those as well.

Yes, those can be looked up from the recording_mbid, but it is possible for the metadata in your collection to diverge from the data that is currently in MB. This means that LB might show you the wrong artist or release if we need to look it up from MB.

See what I mean?

1 Like

Thanks @rob! Yep the docs were a great guidance. I would have been totally lost without them.

I do have release_mbid and artist_mbid in my collection, but unfortunately they aren’t exposed by cmus, only the recording_mbid. I guess I could take the file path and query beets but I like the simplicity of the code at the moment.

Okay so I’m trying to wrap my head around the ways in which this could go wrong… If I submit the correct recording_mbid does this not unambiguously identify the track? Or do you mean if my library has the incorrect recording_mbid for what a track actually is?

Or do I have it all wrong and tracks are primarily identified through artist name, release name and title?

Thanks again.

1 Like

Ah – in that case, don’t worry about it. Perhaps ask the maintainers to expose release_mbid and artist_mbid as well?

It does – but the data around it could change, which might lead to LB showing the incorrect (or rather slightly incorrect) data. But, the more I think about it, that is really an edge case that might not ever be noticed. I guess I am mostly interesting in having complete data than anything else, lol. :slight_smile:

Not wrong at all – we’re still working on making all of this a reality, but our end goal is to:

  1. If the user submits MBIDs, we show them to the user when showing their listens.
  2. If the user does not submit MBIDs, we attempt to map the recording based on the textual metadata.

This sounds simple, but there are a lot of details that go into making this all work. Its surprisingly complex. :confused:

2 Likes

Awesome. Love it.

Here’s the script if anyone is interested. Very basic. Currently it just logs offline listens to a TSV file. I’ll have to figure out how to loop through that and build a single JSON blob rather than making multiple API calls. Any feedback welcome!

#!/bin/sh

api_host=https://api.listenbrainz.org
api_path=/1/submit-listens
api_url="$api_host$api_path"
token=$(cat $HOME/.keys/listenbrainz)
offline_db="$XDG_CONFIG_HOME/cmus/offline.tsv"

# print_json(time, artist, album, title, mbid)
# returns: JSON
print_json() {
	cat <<EOF
{
	"listen_type": "single",
	"payload": [
		{
			"listened_at": "$1",
			"track_metadata": {
				"artist_name": "$2",
				"release_name": "$3",
				"track_name": "$4",
				"additional_info": {
					"recording_mbid": "$5"
				}
			}
		}
	]
}
EOF
}

# scrobble(time, artist, album, title, mbid)
# returns: 0
scrobble() {
	print_json "$1" "$2" "$3" "$4" "$5" |
		curl -X POST \
			 -H "Authorization: Token $token" \
			 -H "Content-Type: application/json" \
			 -d "@-" \
			 "$api_url" >> $HOME/.cache/cmus_mb.log
}

# log_offline(time, artist, album, title, mbid)
# returns: 0
log_offline() {
	printf "%s\t%s\t%s\t%s\t%s\n" "$1" "$2" "$3" "$4" "$5" >> "$offline_db"
}

main() {
	cmus_stat=$(cmus-remote -Q)
	starttime=$(echo "$cmus_stat" | grep "^position " | cut -d" " -f2)
	[ "$starttime" -eq 0 ] || exit 0

	mbid=$(echo "$cmus_stat" | grep "^tag musicbrainz_trackid " | cut -d" " -f3)
	[ -n "$mbid" ] || exit 1

	duration=$(echo "$cmus_stat" | grep "^duration " | cut -d" " -f2)
	scrobtime=$(( duration / 2 ))
	[ "$scrobtime" -gt 240 ] && scrobtime=240

	while true; do
		cmus_stat=$(cmus-remote -Q)
		status=$(echo "$cmus_stat" | grep "^status " | cut -d" " -f2)
		[ "$status" == stopped ] && exit 0
		mbid_cur=$(echo "$cmus_stat" | grep "^tag musicbrainz_trackid " | cut -d" " -f3)
		[ "$mbid_cur" == "$mbid" ] || exit 0

		curtime=$(echo "$cmus_stat" | grep "^position " | cut -d" " -f2)

		if [ "$curtime" -ge "$scrobtime" ]; then
			time=$(date +%s)
			artist=$(echo "$cmus_stat" | grep "^tag artist " | cut -d" " -f3-)
			album=$(echo "$cmus_stat" | grep "^tag album " | cut -d" " -f3-)
			title=$(echo "$cmus_stat" | grep "^tag title " | cut -d" " -f3-)
			if ! scrobble "$time" "$artist" "$album" "$title" "$mbid"; then
				log_offline "$time" "$artist" "$album" "$title" "$mbid"
			fi
			exit 0
		fi

		sleep $(( scrobtime - curtime ))
	done
}

main
2 Likes

Neat! If you agree, I’ll “steal” your print_json function for the macOS scrobbler I’ve been working on. I’m quite new to JSON+Bash, and this is interesting!
Also, I’ll add a query for proper MBIDs, which are unsupported now from my much more basic script!

Of course! Quite an interesting project you have.