Go to file
Brian Fertig fa118448c6 Updated PWA 2026-05-02 17:00:03 -06:00
db feat: add playlist top genres and similar playlists 2026-04-19 12:10:39 -06:00
public Updated PWA 2026-05-02 17:00:03 -06:00
src chore: simplify playlist visibility logic and update dependencies 2026-05-02 16:38:09 -06:00
.env.example feat: add configurable site name via SITE_NAME env var 2026-04-11 08:34:11 -06:00
.gitignore Adding env and gitignore 2026-04-09 18:46:37 -06:00
PWA.md feat: implement three-tier visibility system for songs and playlists 2026-04-09 22:40:18 -06:00
README.md feat: add social features, user uploads, AI music generation, and PWA enhancements 2026-04-17 20:12:34 -06:00
package-lock.json chore: simplify playlist visibility logic and update dependencies 2026-05-02 16:38:09 -06:00
package.json chore: simplify playlist visibility logic and update dependencies 2026-05-02 16:38:09 -06:00
server.js first commit 2026-04-09 18:41:01 -06:00

README.md

Tunes

A self-hosted music website: browse and listen to songs and playlists, with user accounts, social features, and AI-assisted music and cover art generation. Everything runs from a single Node.js process with an embedded SQLite database and local media storage — no external services required beyond an optional ComfyUI instance for AI generation.

Features

  • Public browse & listen

    • Home page with featured playlists and recently added songs
    • Paginated, searchable song list (title / artist / album)
    • Playlist grid and detail view with "Play all"
    • Persistent bottom-of-page audio player with queue support, repeat, and visualizer
    • HTTP Range streaming so seeking/scrubbing works correctly
    • PWA-ready with Media Session API: lock-screen controls and background playback on mobile
    • Lightbox for cover art
  • User accounts

    • Email + password registration (argon2id hashing)
    • Optional email verification via SMTP
    • Login / logout with SQLite-backed sessions (survive restarts)
    • Account page: update display name, avatar, and password
    • Public profile pages with song/playlist activity
    • CSRF protection on all state-changing routes
  • Social

    • Like and favorite songs and playlists
    • In-app notifications
    • Profile pages showing liked and favorited content
  • User music

    • Logged-in users can upload their own songs and create playlists
    • Visibility controls: public, logged_in, vip, or private
    • NSFW flag for explicit content
  • AI music generation (requires ComfyUI)

    • Generate songs from style prompts, lyrics, BPM, key, duration, and creativity settings
    • Per-user rate limiting (cooldown + hourly cap)
    • Generated songs can be previewed, published, or deleted
    • Optionally generate album cover art after publishing
  • Admin panel (/admin)

    • User management: disable, enable, verify, grant/revoke VIP, delete
    • ComfyUI integration settings: base URL, music workflow JSON, image workflow JSON, node ID mappings
    • Toggle music generation and image generation on/off site-wide
  • My Music (/mymusic) — for any logged-in user

    • Upload songs with cover art; title/artist/album/genre/year/BPM metadata
    • Duration auto-extracted from the audio file
    • Create and edit playlists, add/remove/reorder tracks
    • Bulk visibility changes

Tech stack

  • Node.js + Express 4 — server and routing
  • SQLite via better-sqlite3 — embedded database, WAL mode
  • EJS + express-ejs-layouts — server-rendered views
  • argon2 — password hashing
  • express-session + better-sqlite3-session-store — persistent sessions
  • multer — file uploads (audio, cover art, avatars)
  • sharp — server-side image resizing and conversion
  • music-metadata — extract duration from uploaded audio
  • nodemailer — email verification
  • csrf-sync, zod, pino — CSRF, validation, logging
  • htmx (vendored) — progressive interactivity, no build step
  • CropperJS (vendored) — client-side avatar cropping

Getting started

Prerequisites

  • Node.js 18+ (tested on 18.19)
  • npm
  • (optional) A running ComfyUI instance for AI generation

Install and run

git clone <your-repo-url> tunes
cd tunes
npm install
cp .env.example .env
# Edit .env — at minimum set SESSION_SECRET and ADMIN_BOOTSTRAP_EMAIL
node server.js

The server starts at http://localhost:3000 and runs database migrations automatically against tunes.db in the project root.

For auto-reload during development:

npm run dev

Creating the first admin

  1. Open http://localhost:3000/register
  2. Register with the email you set as ADMIN_BOOTSTRAP_EMAIL in .env. That first matching registration is automatically given the admin role.
  3. Visit /admin to manage users and configure generation settings.
  4. Visit /mymusic/songs to upload audio and /mymusic/playlists to create playlists.

Any subsequent registrations are normal users.

Adding music

  1. Go to /mymusic/songs/new
  2. Fill in title, artist, and (optionally) album/genre/year
  3. Choose an audio file (mp3, m4a, ogg, flac, wav, opus, webm — up to 100 MB)
  4. Optionally add a cover image
  5. Set visibility to Public so visitors can see and play it, then click Upload

Uploaded files live under media/audio/ and media/covers/. The database stores relative paths, so you can move the whole project folder freely as long as media/ and tunes.db stay together.

AI generation setup

  1. Stand up a ComfyUI instance accessible from the server.
  2. In /admin, set the ComfyUI Base URL (e.g. http://localhost:8188).
  3. Upload a Music Workflow JSON and fill in the node ID mappings for your workflow.
  4. Optionally upload an Image Workflow JSON and set the prompt node ID for cover art.
  5. Check Enable Song Generation (and Enable Album Cover Generation if desired).
  6. VIP users (and admins) can now visit /generate to produce songs.

Configuration

All settings come from environment variables (loaded from .env via dotenv):

Variable Default Purpose
PORT 3000 HTTP port
SESSION_SECRET (insecure default) Session cookie signing key — set this in production
ADMIN_BOOTSTRAP_EMAIL (unset) Email auto-promoted to admin on first registration
DB_PATH ./tunes.db SQLite database file path
MEDIA_DIR ./media Root directory for all uploaded and generated files
SITE_NAME Bri-Tunes Site name shown in the header and page titles
APP_BASE_URL http://localhost:3000 Full URL used in email verification links
SMTP_HOST (unset) SMTP server hostname (leave blank to print verify URLs to console)
SMTP_PORT 587 SMTP port
SMTP_SECURE false Use TLS (true) or STARTTLS (false)
SMTP_USER (unset) SMTP username
SMTP_PASS (unset) SMTP password
SMTP_FROM (unset) From address for outgoing email

Project layout

tunes/
├── server.js               # entry point: loads env, runs migrations, starts app
├── db/
│   ├── index.js            # SQLite connection + migration runner
│   └── migrations/*.sql    # numbered schema migrations
├── src/
│   ├── app.js              # Express wiring (sessions, CSRF, views, routes)
│   ├── middleware/auth.js  # requireUser / requireAdmin / requireVip
│   ├── routes/
│   │   ├── public.js       # /, /songs, /playlists, /profiles, /stream/:id
│   │   ├── auth.js         # /register, /login, /logout, /verify-email
│   │   ├── account.js      # /account — profile and password settings
│   │   ├── admin.js        # /mymusic — user song and playlist management
│   │   ├── admin-panel.js  # /admin — site administration
│   │   ├── generate.js     # /generate — AI music + cover art generation
│   │   └── social.js       # /api — likes, favorites
│   ├── services/           # users, songs, playlists, social, generation, settings
│   └── views/              # EJS templates
├── public/
│   ├── css/app.css         # main stylesheet
│   └── js/                 # player, visualizer, social, lightbox, etc.
└── media/                  # runtime uploads and generated files (git-ignored)

Data and backups

Everything you care about lives in two places:

  • tunes.db — users, songs, playlists, sessions, notifications, generation records
  • media/ — audio files, cover art, avatars, and AI-generated files

Back up both together to preserve the full state of the site.