No updates detected yet. GitHub releases will appear here.
share music with friends
翻訳待ち
Loading the full README.
Read the full README ↗Music between friends, with memory.
A private channel for passing tracks between two to five people. Paste a link from Spotify, Apple Music, YouTube, SoundCloud, Bandcamp, Beatport, Tidal or Deezer; Crate resolves it to one canonical track with artwork, a 30-second preview, links on every platform, an accent color, and (in the browser) BPM, key and energy. The other person catches up on the stage at the top of their playlist and keeps what lands.
Set up where you already listen and the sends mostly stop being something you do: what you play lands in your log and, by each crew's rule, in its playlist. Plays reach Crate three ways. The Mac helper asks the Music, Spotify and SoundCloud apps on a Mac what is on, as it plays. The Chrome extension does the same for SoundCloud, YouTube and most music sites in Chrome on a computer. Connected accounts sync on their own: Spotify's recently-played history brings plays from any device a few minutes behind (so a phone-only person who connects Spotify is covered); YouTube, SoundCloud and TIDAL accounts bring likes, not plays; Last.fm scrobbles count as plays. Apple Music has no now-playing API, so Music app plays come in through the Mac helper. A second helper on a Mac turns music links texted to you in iMessage into cards at the top of your playlist.
Web first: one Next.js codebase, installable on iOS, Android and desktop as a PWA. On a phone the navigation moves to a bottom bar (Playlist, Live, Log, Send) and every panel becomes a bottom sheet; on a desktop it is a wide stage with a queue beside it. Dark is the default; the sun and moon in the top bar flip it, and Settings → Appearance can pin Auto, Light or Dark.
One word per thing, everywhere in the app:
/days, and each day on its own page.There is no "reel" in the product's copy; the catch-up stage at the top of the playlist is where a send lands.
npm install
cp .env.example .env.local # everything in it is optional for local dev
npm run dev # http://localhost:3000
Start a crew, copy the invite link, open it in a private window as the second person, send a link both ways.
libsql:// URL and auth token. (Any libsql/SQLite host works; locally it is a file.)| Variable | Required | What it does |
|---|---|---|
DATABASE_URL | yes | libsql://...turso.io in production; file:./data/crate.db locally |
DATABASE_AUTH_TOKEN | prod | Turso token |
SESSION_SECRET | yes | long random string; signs the session cookie |
NEXT_PUBLIC_APP_URL | yes | public URL, used in invite links and the iOS Shortcut recipe |
ANTHROPIC_API_KEY | no | agent-written notes and playlist names; templated fallback without it |
AGENT_MODEL | no | defaults to claude-sonnet-4-5 |
LASTFM_API_KEY | no | lets members add a Last.fm username; scrobbling is the bridge for platforms with no API (Pandora, Amazon Music, Deezer) |
CRON_SECRET | prod | protects /api/cron/sync (Vercel sends it on its own) and /api/admin/import |
Turso's Vercel Marketplace integration sets TURSO_DATABASE_URL and TURSO_AUTH_TOKEN instead; both spellings work. vercel.json schedules /api/cron/sync once a day, the most a Hobby plan allows (on Pro, */5 * * * * is fine); the open app still syncs itself every two minutes, and the Mac helper and the Chrome extension report plays as they happen. A Dockerfile and fly.toml are there too for a single always-on machine with the SQLite file on a disk; with CRATE_SYNC_LOOP=1 that server runs the sync loop itself.
Moving an existing Crate: POST /api/admin/import with the old SQLite file as the body and Authorization: Bearer $CRON_SECRET copies every table over (rows replaced by id) and answers with a one-day sign-in link per person, since cookies from the old address do not carry. Platforms need the new address added as a redirect: Spotify, Google, SoundCloud and TIDAL each in their own dashboard.
The schema is created on first request; there are no migrations to run.
link ──► detectSource() which platform, any ids in the URL
──► platform hints oEmbed (YouTube, SoundCloud, Spotify), page meta, Apple/Deezer lookups
──► cleanVideoTitle() "Artist - Title (Official Video) [HQ]" → artist + title
──► matchApple() iTunes Search: canonical track, artwork, preview, Apple link
──► matchDeezer() preview fallback (MP3) when Apple has none
──► buildLinks() direct links where known, search links everywhere else
──► accentFromImage() one color sampled from the artwork (sharp)
──► upsertTrack() one Track per recording; the same song from two platforms is one row
──► suggestNote() the agent's one-liner (Claude API, or a templated voice)
This is the beginning of the README. Read the rest at the original. Read the full README ↗