尚未收集儲存庫資訊。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
Liner: an e-ink frame that hangs one track from 320kbps.club at a time, like a work in a museum. Raspberry Pi Zero 2 W + Pimoroni Inky Impression Spectra 6. A LesWWW project.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗An e-ink frame that hangs one track at a time from 320kbps.club, like a work in a museum. A LesWWW project · liner.leswww.com
The cover and its liner notes sit on a museum label, laid over a map of where the artist comes from. A new track every morning and evening, and a small web page to change it from your phone.
A selection of fifteen tracks, as Liner drew them. Click one to listen on 320kbps.club; more pictures, with their 4K wallpaper and black and white versions, are on liner.leswww.com/gallery.
Listen to more on 320kbps.club →
A page on your phone (liner.leswww.com/control, behind a password) to see what is on the wall and whether the frame is online, change the track, follow another feed, and download every picture.
| Computer | Raspberry Pi Zero 2 WH*, Raspberry Pi OS Bookworm (Lite is enough) |
| Screen | Pimoroni Inky Impression 7.3" Spectra 6* (800 × 480; black, white, yellow, red, blue, green) |
| Frame | Any photo frame with a mat. The mat hides the edges of the panel, so the layout stays inside the visible area |
Parts cost about €100 with the deals, €120 otherwise. Two panels are sold as "Inky Impression 7.3"": this code targets the 2025 Spectra 6 (no orange ink).
* Affiliate links: buying through them supports the project at no extra cost to you.
phone Vercel Raspberry Pi
┌───────────┐ request ┌────────────────┐ GET /api/pi ┌──────────────────┐
│ web page │ ──────► │ command.json │ ◄───────────── │ liner.py --sync │ every minute, 7:00–midnight
└─────┬─────┘ │ (Vercel Blob) │ └────────┬─────────┘
│ ◄──────────── │ status.json │ ◄── POST ───────────────┤ after each redraw,
│ picture, PNG │ pictures/*.png │ ◄── PUT ───────────────┤ with the picture
│ └───────┬────────┘ │
│ │ GET /api/map ◄──────────────────┤ map of the artist's origin
│ ▼ ┌────────┴─────────┐
│ MusicBrainz, Nominatim │ liner.py │ 7:00 and 19:00
│ Natural Earth outlines └──────────────────┘
/api/map, SVG rasterised by resvg, cached
by the CDN for good), so outlines, geocoding and MusicBrainz stay off the Pi Zero.liner.env), the frame still runs on its own, twice a day, without maps.app/ web (Next.js), in the LesWWW charter
page.tsx public landing page
control/ control room, behind a password
NowPlaying.tsx request tracking, flash when the frame is done
Frame.tsx the frame mockup (wood, mat, visible area)
api/pi/ the frame: poll for requests, report a redraw, upload its picture
api/map/ duotone map of an artist's origin, as a PNG in the panel's inks
api/status/ polled by the page while a request is on its way
lib/
feed.ts 320kbps.club RSS feed
map.ts origin lookup (MusicBrainz, Nominatim) and map drawing
store.ts requests, status and pictures in Vercel Blob
frame.ts panel size and visible area, shared with the mockup
countries.json country outlines (Natural Earth 1:50m, simplified)
pi/
liner.py the frame: data, layout, display, sync
fonts/ IBM Plex Sans (variable) and IBM Plex Mono
systemd/ liner.timer (7:00, 19:00) and liner-sync.timer (every minute)
install.sh installs dependencies and services
public/ avatar, Open Graph image, sample pictures for the landing page
docs/ sample pictures
The full guide, from parts to calibration, is in docs/BUILD.md. In short:
git clone https://github.com/kxrz/leswww-liner.git ~/liner
cd ~/liner/pi
cp liner.env.example liner.env # then fill in LINER_TOKEN
./install.sh
Enable SPI and I2C first (sudo raspi-config › Interface Options). Update with
cd ~/liner && git pull && pi/install.sh.
| Next track now | sudo systemctl start liner |
| What happened | journalctl -u liner -u liner-sync -n 30 |
| Next runs | systemctl list-timers 'liner*' |
| Try a layout without touching the screen | python3 liner.py --preview, then open preview.png |
| Fill the gallery with the newest tracks | python3 liner.py --seed 15 (screen untouched) |
| Show the calibration grid | python3 calibrate.py |
state.json remembers the tracks already shown and the last request handled; delete it to start again from the
newest track.
The Vercel project (leswww-liner, served at liner.leswww.com) is linked to this repository: every push to main deploys. Connect a Blob store, then set:
| Variable | |
|---|---|
BLOB_READ_WRITE_TOKEN | Set by Vercel when the Blob store is connected |
SITE_PASSWORD | Password for the page. Without it, the page is open to anyone |
DEVICE_TOKEN | The frame's token, same as LINER_TOKEN in pi/liner.env. Without it, /api/pi and /api/map are open |
NEXT_PUBLIC_LINER_VISIBLE | Your frame's visible area for the mockup, as in LINER_VISIBLE (default 52,35,748,480) |
On a phone, "Add to Home Screen" turns the page into a small app. Locally: npm install && npm run dev, with a
.env.local (see .env.example).
fontmode = "1"). Grey edge pixels would snap to the nearest ink, often blue,
and every letter would get a coloured fringe.SATURATED_PALETTE and DESATURATED_PALETTE, as set_image() does by default. The
final image goes to the driver as a six-colour palette image, so it does not dither again.shape-rendering="crispEdges" in pure inks, mapped back to the palette without
dithering on the Pi./control (password) shows the frame and its health (online, silent or asleep; temperature, uptime, code
version), searches the feed (ten tracks at a time) and sends one to the screen, follows the request step by step
(no new request until the frame is done, downloads still work), picks the feed to follow
without typing its address (the whole site, the track of the day, each curator and their playlists, every
playlist and genre, read from 320kbps.club's own index pages, with a preview of each feed before following it;
any other RSS feed by its address), and offers the three downloads for each track./gallery lists what has hung on the wall: click a picture to hang it in the frame mockup, download the 4K
wallpaper, or listen on 320kbps.club./ is the public landing page: today's picture in a frame mockup, a before/after slider (HD vs e-ink), the black
and white version, how it works, hardware, credits. It is rebuilt at most every ten minutes.robots.txt, sitemap.xml and llms.txt.The 320kbps.club feed gives the place an artist is based (MusicBrainz "area"). Liner looks for where they were born or formed ("begin-area"):
The mat hides part of the panel. Measured with a test grid: x 52 → 748, y 35 → 480. Everything is laid out inside
VISIBLE (top of pi/liner.py, mirrored in lib/frame.ts for the mockup). If the Pi moves in its frame, that is
the only value to change.
Liner is designed and built by LesWWW, the studio behind 320kbps.club.
MIT © LesWWW