Import: each row's place is its line number, so blank lines leave a
place empty; a fully numbered text file ("38. Title") or a Rank column
gives places directly, and repeated numbers take the next free place.
Entries are added at those places (existing ones there move down), the
review shows each row's #, and the room check covers the highest place.
Export writes a blank line for every empty place, so line N is #N and a
file round-trips.
Static files now carry an ETag and unversioned ones are revalidated on
every load. Scripts import each other without a ?v= tag, so browsers kept
an hour-old import.js (with the "null" bug) after an update.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Entries now sit at places 1…max with gaps allowed (#28, nothing at #29,
#30). Moving onto an empty place just puts the entry there; onto a taken
place, the entries in between shift one step, stopping at the first
empty place, so lists without gaps reorder exactly as before. Inserts
push down only to the next gap; deleting can close up or leave the place
empty. New endpoint POST /api/lists/{id}/places inserts or removes empty
places or closes every gap; move and delete reply with every entry's
place. The podium shows an empty step for an empty #2 or #3, and a list
can't shrink below its last used place.
The editor loads every entry (no paging) and applies edits instantly
with the same rules (places.js mirrors places.go), queueing saves in the
background and reconciling with the server's reply. Type a place and
press Enter (↑/↓ move between boxes, Alt+↑/↓ moves the entry), drag, or
use the row menu. Empty runs show as rows with Add here and Remove, and
a toolbar shows the gap count, Close gaps and a Go to # box. Clicking an
entry opens it for editing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The image now starts through docker-entrypoint.sh: as root it hands
/data and the backup folder to PUID:PGID (only files owned by someone
else, so restarts stay fast), applies UMASK, and runs Canon as that
user with su-exec. Started with --user, it runs Canon directly. The
image defaults to 1000:1000; the Compose file presets Unraid's 99:100.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The list editor gets an Import button. A .csv/.tsv/.txt file or pasted
text is parsed on the server: header columns (Title, Year, Artist or
Director, Note, Rank), numbered lines and trailing "(1999)" years are
understood, and Canon's own export round-trips.
Each row is then searched with the list's provider and scored. A row is
matched automatically only when one result fits exactly and nothing else
fits as well; a year or artist in the file breaks ties, an exact title
beats a bracketed version like "(Live)", and "Artist - Title" lines are
tried both ways. Custom lists need a year or subtitle to be certain.
Everything else is shown for review with a recommended pick, the other
results, "Add as written", Skip, and a search-again box. Entries are
added in file order after the existing ones, with any notes from the
file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings → Privacy can set a password for the whole public site. Until a
visitor enters it, every public page shows a prompt over a blurred
made-up page, and images, preview clips and exports return 401; nothing
real is sent. Unlocking sets a 90-day cookie derived from the password
hash, so changing the password asks everyone again. The owner is never
asked while signed in. Wrong guesses are rate limited, and a locked site
sends noindex.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Backups are .tar.gz archives of a consistent database snapshot (VACUUM
INTO) plus the images folder. They run on a cron schedule, daily at 3:00
by default, and the newest N scheduled ones are kept (7 by default).
Manual backups are never pruned.
Settings → Backups offers schedule presets or a custom cron expression,
retention, "Back up now", and download/delete for each backup. A small
built-in parser handles standard 5-field cron syntax with names, ranges,
steps and @daily-style shorthands.
Backups go to CANON_BACKUP_DIR (default data/backups). Compose mounts a
separate canon-backups volume at /backups, overridable with
CANON_BACKUP_PATH.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Podium, Spotlight and Grid render smaller on the home page on desktop so
one entry no longer fills the screen; list pages keep full size.
Compose now builds on every deploy (pull_policy: build), stores data in
the canon-data named volume outside the synced folder (CANON_DATA_PATH
overrides), and takes optional bind/port/version settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Every provider can look an entry up again by its ID (TMDB, TheTVDB,
IGDB, Deezer, MusicBrainz, Wikipedia); search and lookup share parsing
- POST /api/items/{id}/refresh re-applies title, subtitle, year,
description, link, details and artwork. Kept: the "why" note, rank,
theater production/venue/cast and a theater entry's year, the Deezer
preview match. Empty source fields don't erase existing values
- New items.image_custom column (migration 4; existing uploads marked)
records artwork the owner chose; refresh keeps it unless asked
- Refused for custom lists and hand-added entries; entries the source
dropped are reported, and a refresh whose title shares no words with
the current one is rejected as a likely bad ID
- Editor: refresh button per row (asks about the owner's own artwork),
and "Refresh all" with a confirm step, replace-my-art checkbox,
progress with Stop, and a summary of problems
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Each list picks a style in List settings (new lists default to Podium);
existing lists keep Grid or List
- Podium: top 3 on medal-colored steps (#1 center and raised), the rest
behind a native <details> "Show N more"; stacks #1 over #2/#3 on phones
- Spotlight: #1 as a feature card (art, why-note or description, play
button), the rest as a numbered list
- Visitors can switch styles on a list page (?style=), preserved across
pages; later pages fall back to a plain list since they don't start at #1
- Entries' detail data now lives on a wrapper per list, so featured and
collapsed entries all open the detail view
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Search
- Albums and songs search Deezer and MusicBrainz at once and merge the
results: duplicates keep the Deezer copy, best word matches first.
Deezer's search misses albums it carries (Death Magnetic) and most game
soundtracks; MusicBrainz finds them
- MusicBrainz secondary types are shown ("Soundtrack", "Live")
- Settings: Deezer + MusicBrainz (default), Deezer only, MusicBrainz only
- Search results show which service they came from
Previews
- Songs and albums get a play button (cards, rows, detail view, editor,
and Deezer search results) for a 30-second Deezer clip; albums play
their most popular track
- Clips are looked up fresh (Deezer URLs expire), cached 10 minutes, and
streamed through Canon with Range support; hidden lists stay private
- Entries from MusicBrainz or added by hand are matched to Deezer on save
so they can preview too
- "Play previews" toggle in Settings → Profile
Tests cover merging and title/artist matching; `-tags live` runs checks
against the real services.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pure #000 background (OLED pixels off), near-black panels with dark red
borders, and a red accent (#e0263a) that passes 4.5:1 as text on black and
behind white button labels. Badges use a brighter red to stay legible on
their tinted background.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New CANON_HOST (default 0.0.0.0; 127.0.0.1 keeps it on one machine)
- On start, log the http:// addresses other devices can use, skipping
Docker/VM bridge interfaces
- docker-compose publishes 0.0.0.0:4242 explicitly
- README: opening Canon from other devices and firewall note
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Descriptions
- Items keep the source's description (TMDB/TheTVDB overview, IGDB
summary, Wikipedia intro) in a new items.description column
(migration 3); editable in the item form
- Deezer/MusicBrainz track counts and album names are now display-only
hints, not saved as descriptions
Why it's #N
- The owner's note is presented as "Why it's #N" in the editor and the
public detail view, above an "About" section with the description
IGDB developers
- IGDB credits internal divisions ("Nintendo EAD", "Nintendo EPD
Production Group No. 8"). A developer whose name shares its first word
with its parent company is credited to the parent ("Nintendo");
independently named studios keep their name (Retro Studios, Naughty
Dog, Landfall Games)
- Fall back to the publisher when IGDB lists no developer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Module github.com/cdnutter/canon, binary/command cmd/canon, Docker
image and compose project "canon"
- Environment variables now use the CANON_ prefix
- Default site title is "<Your name>'s Canon" (or "Canon" with no name);
a Site title set in Settings still overrides it
- Existing topdeck.db (and WAL files) is renamed to canon.db on start
- README: upgrade notes, including moving the old compose volume
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export
- GET /l/<list>/export?format=csv|txt downloads a list's titles in rank
order, one per line, with no header or other fields (CSV gets a UTF-8
BOM for Excel)
- Export menu in the list editor and, for the owner, on the public list
page; hidden lists stay owner-only
Custom lists
- New "Custom" category for anything not built in (sports teams,
drinks…): owner-named, slug from the title, entries added by hand or
from an unfiltered Wikipedia search
- Per-list artwork shape (square or tall), stored in a new lists.art
column (migration 2)
- Art can be uploaded while adding an entry, not only after
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A single Go binary with SQLite that serves a public page of the owner's
ranked lists (movies, TV, albums, songs, video games, theater) and a
login-gated editor.
- Lists of 1–65,535 entries (100 recommended), several per category
- Search-to-add via TMDB, TheTVDB, IGDB, Deezer/MusicBrainz, Wikipedia;
manual entry as a fallback
- Theater entries carry a production (Broadway, tour, …), venue and cast
- Artwork cached locally; URL override and uploads
- Drag, arrow and type-a-rank reordering; paginated for long lists
- 12 theme presets plus custom CSS
- First-run setup code or env-provided admin; bcrypt, rate-limited login,
CSRF-guarded JSON API
- Dockerfile and docker-compose.yml
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>