Command line app to manage your ListenBrainz listens and process listen dumps.
If you want to give it a try to see what you get, you can safely execute it with Deno. Run the following command to see its integrated help, Deno will prompt you for the necessary permissions:
deno run jsr:@kellnerd/elbisaur
If you don’t want to have to remember the URL and grant permissions every time, you can install it with the permissions specified as arguments:
deno install --global --allow-env=LB_USER,LB_TOKEN,ELBISAUR_LISTEN_TEMPLATE --allow-net=jsr.io,api.listenbrainz.org,musicbrainz.org --allow-read --allow-write jsr:@kellnerd/elbisaur
Now you can simply run the app by executing elbisaur
, which should show you the help and complain about a missing required environment variable LB_TOKEN
.
This is your ListenBrainz user token which is required to submit listens and can be obtained from your ListenBrainz settings page.
Running elbisaur
automatically tries to load environment variables from a .env
file in your working directory, so you can comfortably safe your LB user token inside there:
# example token below, insert your own
LB_TOKEN = "3b851ecc-474d-44eb-a4d0-db3bbb5ef8b8"
# user name is optional, but it sometimes saves an API request if it is specified
LB_USER = "Your LB user name here"
elbisaur is a modular command line app with multiple subcommands which you can execute with elbisaur <command>
.
The following commands are available:
Command | Description |
---|---|
history |
Show the listening history of yourself or another user |
delete |
Delete listens in the given JSON file from your history |
import |
Import listens from the given JSON file |
listen |
Submit listens for selected tracks from a release (given by its URL) |
parse |
Parse listens from a file and write them into a JSONL file |
statistics |
Show statistics for the given JSON file |
transform |
Modify listens from a JSON input file and write them into a JSONL file |
You can view the integrated help of each command with elbisaur <command> --help
.
All commands which accept JSON files as input also accept JSONL files (one JSON object per line).
These should contain (one or multiple) serialized Listen
objects as included in a ListenBrainz listening history export (and as shown by the ListenBrainz “Inspect listen” dialog).
JSONL files which are written by elbisaur also follow this format.
The following formats can be parsed by elbisaur (in addition to native JSON and JSONL listens):
- .scrobbler.log (
*.log
): Log file which is generated by some portable music players for later submission to Last.fm. - Spotify (
*.json
): JSON files from an Extended Streaming History download.
See the parser documentation for implementation details.
Note
The parsers perform no filtering of listens, so you have to detect potential duplicates and skipped listens yourself.
View your most recent listening history using the history
command:
elbisaur history
In order to see the listening history of another user, specify their name with the -u, --user
option:
elbisaur history --user listenbrainz
You can filter listens by listening time (-a, --after
and -b, --before
, happens on the server) and metadata (-f, --filter
, happens locally).
If you want to apply metadata filters (locally), ensure that the API returns enough results by specifying a higher count with -c, --count
.
Downloaded results can be stored as JSONL file by specifying an output path with -o, --output
.
If the file already exists, the new listens will be appended to it.
Download your listens for all tracks by Jane Doe to which you listened on 17th January 2024:
elbisaur history -f "artist_name==Jane Doe" -a 2024-01-17 -b 2024-01-18 -c 200 -o jane.jsonl
Importing listens is straightforward if you have a JSON file which contains one or multiple listens. Such a file can be a ListenBrainz listening history export, the result of another elbisaur command or the content from a ListenBrainz “Inspect listen” dialog.
Before running the actual import
command itself, it is recommended to run the same command with the -p, --preview
flag to see which listens would be imported:
elbisaur import listens.json --preview
Only if you are satisfied with what you see, you should proceed:
elbisaur import listens.json
You can also use elbisaur
to submit listens for selected tracks from a release manually.
If you want to quickly submit a single listen with track title (Love Song) and artist (John Doe), you can use:
elbisaur listen "John Doe - Love Song" --at "2024-03-17 15:24:36"
Or quickly submit a playing now notification for that track:
elbisaur listen "John Doe - Love Song" --now
Alternatively you can also submit multiple listens using the MusicBrainz URL of a release. Say you started listening to side A of a vinyl record today at 12:34:56 and want to submit listens for these tracks. The app automatically calculates the listening timestamps for all tracks based on the track lengths.
elbisaur listen https://musicbrainz.org/release/d6010be3-98f8-422c-a6c9-787e2e491e58 A --at 12:34:56
Of course you can also specify the time when you finished listening (to the last track):
elbisaur listen https://musicbrainz.org/release/d6010be3-98f8-422c-a6c9-787e2e491e58 A --until 12:59:52
And if you finished listening to side B right now (this second), you can simply omit the time option:
elbisaur listen https://musicbrainz.org/release/d6010be3-98f8-422c-a6c9-787e2e491e58 B
Instead of specifying the track number prefix (here: the side number A
), you could also explicitly type the track range A1-A6
.
For releases with multiple media, you can additionally specify the medium number (2:1-5
), or even just a medium number (2:
, since simply 2
would be interpreted as track number prefix).
If you want to delete some of your listens, you need to obtain a JSON file which contains the bad listens.
You can either use a filtered listening history export for this or create the file using elbisaur history
.
The file not only contains the necessary data to send deletion requests to the API, it can also serve as a backup in case you accidentally delete the wrong listens.
Before running the actual delete
command itself, it is recommended to run the same command with the -p, --preview
flag to see which listens would be deleted:
elbisaur delete bad-listens.jsonl --preview
Only if you are satisfied with what you see, you should proceed:
elbisaur delete bad-listens.jsonl
Please note that this only marks listens for deletion currently and that it takes until the full hour before the deleted listens finally disappear from ListenBrainz.
Parse a .scrobbler.log
file (for example from a Rockbox player) and discard all scrobbles which are marked as skipped:
elbisaur parse .scrobbler.log --filter skipped!=1
You can also run the parser again and write skipped scrobbles to a separate file for manual review:
elbisaur parse .scrobbler.log --filter skipped==1 skipped-listens.jsonl
Note
Timestamps are automatically converted from your local timezone to UTC as Rockbox players are usually not timezone-aware.
If you have requested your Extended Streaming History from Spotify, you receive one or more Streaming_History_Audio_*.json
files.
These files can be parsed with elbisaur, but they also contain skipped streams which are sometimes not marked as such.
Parse Spotify history file and only keep streams which were not skipped and were played for at least 30 seconds:
elbisaur parse Streaming_History_Audio_2024.json --filter "skipped!=1&&ms_played>=30e3"
While some skipped listens can be detected by their reason_end
properties, bad listens can also have a reason of trackdone
although Spotify failed to play them (playback duration is only a second or two).
You might have to experiment with the filter options a bit or do multiple passes to get optimal results.
Limiting the output by specifying a time range (-a, --after
and -b, --before
) makes reviewing the results (using the -p, --preview
option) a lot more comfortable.
Note
This parser calculates the correct listen (start) timestamp from stream end time and duration.
In some cases this time is completely inaccurate because the logged end timestamp is wrong and does not indicate when the track stopped playing. It appears to be the next time when Spotify was opened again after the app or the web player had been closed unexpectedly.
In those cases the parser uses the so called “offline” timestamp which, despite its name, is not exclusively used to track offline playback. While it seems to be a few seconds off in general, it is pretty accurate for those cases where the logged end time is bogus.
You can specify the -d, --debug
flag to include all possible timestamp data in the additional_info
properties of each parsed listen.
Correct a typo in the release name property of a few listens from a JSON file:
elbisaur transform input.jsonl -f "release_name==Exmaple" -e "release_name=Example" fixed.jsonl
Inject (or correct) the release MBID for all listens with a given release name:
elbisaur transform input.jsonl -f "release_name==Example" -e "release_mbid=bf9e91ea-8029-4a04-a26a-224e00a83266" output.jsonl
Compensate a wrong listen time offset of an hour for all listens (by adding 3600 seconds):
elbisaur transform wrong-time.jsonl -t 3600 correct-time.jsonl