- JavaScript 45.7%
- Nunjucks 38.6%
- SCSS 13.5%
- Dockerfile 2.2%
Renamed coolify-hourly.yml and vercel-hourly.yml from .github/workflows to .forgejo/workflows to update workflow configuration location. |
||
|---|---|---|
| .forgejo/workflows | ||
| lib | ||
| src | ||
| .dockerignore | ||
| .eleventy.js | ||
| .env.example | ||
| .gitignore | ||
| Dockerfile | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| vercel.json.example | ||
Musiclisten · Eleventy × Last.fm
A small Eleventy site that compiles a bespoke Bootstrap theme, fetches your recent Last.fm scrobbles at build time, and displays them in a responsive grid. Responses are cached locally so builds stay fast and API limits remain happy.
Requirements
- Node.js 18+ (relies on the global
fetchimplementation) - A Last.fm account and API key
Getting started
- Install dependencies
npm install - Copy the environment template and fill in your secrets
cp .env.example .env - Build once (or run the dev server) to generate the site
npm run build # or npm run serve
Docker / Coolify deployment
Use the provided Dockerfile to build a static image that serves the dist/ folder with NGINX. Because Eleventy fetches data during the build, you must pass the required Last.fm variables as build arguments (Coolify exposes a UI for this).
docker build \
--build-arg LASTFM_API_KEY=yourkey \
--build-arg LASTFM_USERNAME=yourname \
--build-arg LASTFM_CACHE_MINUTES=60 \
--build-arg LASTFM_HISTORY_PAGES=5 \
--build-arg SITE_TITLE="Musiclisten" \
-t musiclisten .
docker run -p 8080:80 musiclisten
Rebuild the image whenever you want to refresh cached data. If you prefer not to bake secrets into the image, use a BuildKit secret or trigger the build inside Coolify where the args remain server-side.
Hourly rebuilds on Vercel (GitHub Actions)
Vercel’s cron feature is unavailable on the Hobby plan, so the repo ships with .github/workflows/vercel-hourly.yml. It triggers every hour (UTC) or on demand and simply calls a Vercel Deploy Hook.
- In your Vercel project, create a Deploy Hook (Settings → Git → Deploy Hooks). Copy the generated URL.
- In GitHub, go to Settings → Secrets and variables → Actions for this repository and add a new secret named
VERCEL_DEPLOY_HOOKwith that URL. - The workflow will now run hourly, hit the hook via
curl, and Vercel will rebuild the project using the environment variables defined in the Vercel dashboard.
If you need a different cadence, adjust the cron expression in vercel-hourly.yml. The workflow also includes workflow_dispatch, so you can trigger it manually from the Actions tab whenever you want a fresh deploy outside the hourly window.
Hourly rebuilds on Coolify (GitHub Actions)
Coolify deploy hooks require an authenticated request, so .github/workflows/coolify-hourly.yml makes the same hourly call but adds a Bearer token header.
- In Coolify, open the service → Deployments → Deploy Hooks, generate a hook URL, and note the accompanying API token (or create a PAT with deploy permission).
- In GitHub Actions secrets, add
COOLIFY_DEPLOY_HOOK(the hook URL) andCOOLIFY_DEPLOY_TOKEN(the bearer token). - The workflow will run on the same
0 * * * *schedule and POST to the hook withAuthorization: Bearer <token>. Adjust the cron or trigger manually viaworkflow_dispatchas needed.
Environment variables
| Key | Description |
|---|---|
SITE_TITLE |
Title of your page |
LASTFM_API_KEY |
API key you receive from the Last.fm developer console. |
LASTFM_USERNAME |
The Last.fm username (profile slug) whose listening history should be fetched. |
LASTFM_CACHE_MINUTES |
Optional override for the cache TTL. Defaults to 15 minutes. |
LASTFM_HISTORY_PAGES |
Number of Last.fm pages (200 tracks each) to fetch for the history archive. Defaults to 1. |
Getting your Last.fm API key & username
- Sign in (or register) at Last.fm.
- Go to the API account page and create an application.
- Once the app is approved instantly, copy the
API Keyvalue intoLASTFM_API_KEYinside.env. - Your
LASTFM_USERNAMEis simply your profile URL slug — e.g.https://www.last.fm/user/<username>.
Keep the .env file out of version control (already covered via .gitignore).
Scripts
| Command | Purpose |
|---|---|
npm run build:css |
Compile src/styles/main.scss (Bootstrap + theme overrides) to src/assets/css/main.css. |
npm run watch:css |
Watch Sass files for changes during local development. Run alongside npm run serve. |
npm run build:site |
Run Eleventy without rebuilding CSS. |
npm run build |
Compile CSS then run Eleventy to produce dist/. |
npm run serve |
Compile CSS once and start Eleventy’s dev server at http://localhost:8080. |
npm run clean |
Remove dist/ and .cache/. |
Data flow & caching
_data/lastfm.jsruns on every Eleventy build, loads.env, and checks.cache/lastfm.jsonfor the last successful fetch.- If the cache is fresher than
LASTFM_CACHE_MINUTES, the cached payload is used to avoid another HTTP request. - When the cache is stale (or missing) the build fetches
user.getrecenttracksfrom the Last.fm REST API, normalizes the response, saves it to.cache/lastfm.json, and exposes it to templates. - Any API failures fall back to the last good cache (if present) and emit a warning banner in the UI.
_data/history.jsperforms a multi-page fetch (up toLASTFM_HISTORY_PAGES× 200 tracks) to build the/history/archive, storing the expanded payload in.cache/history.jsonwith the same TTL behavior._data/topAlbums.jshitsuser.gettopalbumswith a1monthperiod and caches the top 10 albums in.cache/top-albums.jsonso the leaderboard page stays responsive.
History page
src/history.njkrenders a table-style archive that can include up to 1,000 tracks out of the box (5 pages × 200). IncreaseLASTFM_HISTORY_PAGESif you want to go deeper, keeping in mind the trade-off between build time and API rate limits.- The navigation in
layouts/base.njknow links to the new page so you can jump between the live dashboard and the archive.
Top albums page
src/top-albums.njkshowcases the 10 most played albums from the previous month (per Last.fm’s1monthwindow) with artwork, play counts, and profile links.- You can tweak the TTL via
LASTFM_CACHE_MINUTESif you’d like to refresh the leaderboard more or less frequently.
Custom Bootstrap build
src/styles/main.scssimports Bootstrap’s Sass entrypoint with custom color, typography, and layout tweaks.- Output CSS lives in
src/assets/css/main.cssso Eleventy can pass it through directly todist/assets/css/main.css. - Feel free to extend the theme by editing the Sass file or adding additional partials. Run
npm run watch:cssto see changes instantly.
Troubleshooting
- “Missing Last.fm configuration” alert: Ensure
.envcontains bothLASTFM_API_KEYandLASTFM_USERNAME, then rebuild. - API errors or rate limiting: Increase
LASTFM_CACHE_MINUTESor delete.cache/lastfm.jsonto force a refetch after resolving issues. - Stale styling: Delete
src/assets/css/main.cssand rerunnpm run build:cssif the compiled file falls out of sync.
License
MIT © 2025