Skip to content

DevMatei/make-a-wrapped

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

93 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🎧 Make a Wrapped

Spotify Wrapped-style recap generator for ListenBrainz, Last.fm, and Navidrome. Built with Flask.

Important

Rebranded to Make a Wrapped (was ListenBrainz Wrapped). Site's back up β€” if something's broken, open a GitHub issue.

New 2025 templates are out!

Make a Wrapped β€” shareable stats for your scrobbles

🌐 website

https://wrapped.devmatei.com/

πŸ’‘ why it's cool

  • pulls from ListenBrainz, Last.fm, Navidrome, MusicBrainz, Cover Art Archive, and Wikidata. all public, no tokens needed (except the optional Last.fm key you'd already have for artwork anyway)
  • artist art tries Last.fm first, falls back to MusicBrainz/Wikidata, and if all else fails there's a built-in editor to upload/zoom/position your own image (saved in local storage, or temporarily on the server for 1 hour)
  • rate limits are in so your server doesn't get nuked
  • there's a live counter of total wraps ever generated (idk it seemed cool)
  • officially listed on the ListenBrainz Enabled Applications page :D

wanna make me slightly richer? (i'm broke lol)

ko-fi

⚑ quickstart

python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

copy .env.example to .env and fill in the basics:

FLASK_ENV=production
SECRET_KEY=<something>
LASTFM_API_KEY=<your-lastfm-key>
# set HTTP_PROXY / HTTPS_PROXY if you tunnel through a proxy

run it:

gunicorn -w 4 -b 0.0.0.0:8000 wrapped-fm:app

or locally:

./start.sh <host> <port>

or with Docker (change the port in the compose file first):

sudo docker compose up -d

βš™οΈ config

core

LISTENBRAINZ_API=https://api.listenbrainz.org/1 MUSICBRAINZ_API, COVER_ART_API LISTENBRAINZ_RANGE=this_year AVERAGE_TRACK_LENGTH_MINUTES, COVER_ART_LOOKUP_LIMIT

integrations

LASTFM_API_KEY - required for Last.fm stats + better artist images LASTFM_API, LASTFM_USER_AGENT

performance

HTTP_TIMEOUT, HTTP_POOL_MAXSIZE, LISTENBRAINZ_CACHE_TTL, LISTENBRAINZ_CACHE_SIZE APP_RATE_LIMIT, APP_STATS_RATE_LIMIT, APP_IMAGE_RATE_LIMIT, APP_MAX_TOP_RESULTS APP_IMAGE_CONCURRENCY, APP_IMAGE_QUEUE_LIMIT, APP_IMAGE_QUEUE_TIMEOUT TEMP_ARTWORK_TTL_SECONDS, TEMP_ARTWORK_MAX_BYTES WRAPPED_COUNT_FILE (defaults to data/wrapped-count.txt) WRAPPED_COUNT_SINCE - label for when you started counting wraps (ISO date string) APP_RATE_LIMIT_SALT, APP_TRUST_PROXY_HEADERS

why it exists

I self-host my music library on Navidrome and don't use Spotify, but all my friends post their Wrapped every year and everyone's like "wait what's that?" when you show them a ListenBrainz stats page. so I built this. same vibe, works with open music platforms.

go flex your scrobbles at wrapped.devmatei.com :)

about me

I'm Matei (DevMatei) - full-stack dev from Moldova, I build random web tools, run a homelab full of self-hosted stuff, and care way too much about my music library. hit me up on socials or via the email on my site if you wanna talk projects or self-hosting.

🀝 contributing

See CONTRIBUTING.yml for setup, style notes, and the PR checklist. TL;DR: keep PRs focused, run python -m py_compile wrapped-fm.py, and attach screenshots for any UI changes.

🧩 to-do

  • security improvements
  • faster rendering (capped around 33s by API speeds, not much I can do)
  • modular, readable code (kind of)

originally made for Last.fm only by jeff parla <3

πŸ“œ license

AGPL-3.0 - share alike

not affiliated with Spotify, ListenBrainz, Last.fm, Navidrome, or MusicBrainz. just a fan thing built for fun.

About

A Spotify Wrapped clone using ListenBrainz instead as a source!

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Sponsor this project

Contributors