Build and publish container / build (push) Failing after 2m16s
First stage of a curation tool for the music library that music-mirror mirrors. Before anything can build playlists or decide what has gone cold, there has to be a local, queryable record of what is actually played; an API call per question does not scale to a library-sized analysis. Ingest is in two halves. A catch-up fetches everything scrobbled since the newest scrobble held, and a backfill walks the history backwards until it runs out. Both take their bounds from the database rather than from a saved cursor, so an interrupted run resumes from what it actually has, and both windows are bounded at each end so paging cannot shift under the fetch while new scrobbles arrive mid-run. Scrobbles carry no identifier, so the primary key is timestamp, artist and track. Two plays of one track in the same second collapse into a single row: they are indistinguishable in the data, and a surrogate key would make re-ingest non-idempotent, which is the worse trade. Three API behaviours are handled explicitly because each fails silently: the currently-playing track arrives with no timestamp and would be re-ingested on every pass; a lone result is returned as a bare object rather than a one-item list; and MBIDs are empty strings rather than absent when unknown, which would later look like a usable join key. Retries cover the rate limit and the transient backend errors with an exponential backoff. An invalid or suspended key fails immediately. The report exists to surface one number before the next stage is built: the share of scrobbles carrying a MusicBrainz recording id. Lidarr exposes the same identifier per track, so those can be joined exactly and the rest must go through name matching. That percentage bounds how far the matcher can be trusted. No runtime dependencies, and the tests run against a fake transport that reproduces the service's paging and response shapes, so they need neither network nor credentials.
31 lines
1.4 KiB
YAML
31 lines
1.4 KiB
YAML
# TrueNAS Scale "Custom App" definition.
|
|
#
|
|
# Adjust the host path and the user to match your pool. The Last.fm API key is
|
|
# a credential: set it through the app's environment in the TrueNAS UI rather
|
|
# than committing it here.
|
|
services:
|
|
music-curator:
|
|
image: code.emmathe.dev/lyrathorpe/music-curator:latest
|
|
container_name: music-curator
|
|
restart: unless-stopped
|
|
# The dataset owner, so the store is not written as root. `id apps` or the
|
|
# ownership of the dataset will tell you the right numbers.
|
|
user: "568:568"
|
|
environment:
|
|
MUSIC_CURATOR_LASTFM_USER: your-lastfm-username
|
|
# Get one at https://www.last.fm/api/account/create -- a key alone is
|
|
# enough, none of the endpoints used here authenticate a user.
|
|
MUSIC_CURATOR_LASTFM_API_KEY: set-me-in-the-truenas-ui
|
|
MUSIC_CURATOR_DB: /data/curator.db
|
|
# How long to wait between passes. Each one catches up on new scrobbles
|
|
# and continues the backfill if it has not finished.
|
|
MUSIC_CURATOR_INTERVAL: 6h
|
|
# Seconds between API requests. Last.fm asks for no more than five a
|
|
# second; the default leaves plenty of room.
|
|
# MUSIC_CURATOR_REQUEST_DELAY: "0.25"
|
|
# Cap the backfill at this many requests per pass. Unlimited by default,
|
|
# which finishes a long history in one go.
|
|
# MUSIC_CURATOR_BACKFILL_LIMIT: "0"
|
|
volumes:
|
|
- /mnt/tank/apps/music-curator:/data
|