Lyrics are suggested at submission and never imposed. The conversion worker makes one LRCLIB lookup with whatever metadata exists, and the waiting page has a Hae sanoitukset button that re-queries with whatever title and artist are currently typed — which is the case that matters, since our metadata comes from ID3 tags and YouTube uploaders. Neither path overwrites typed text. - lyrics text on both submissions and songs, copied across at publish. Nothing has launched, so the column goes into 001_init.sql rather than a migration - The lock does not cover lyrics: it freezes what the song claims to be, and nobody reviewed the lyrics. So the submitter can still fix them afterwards, or paste them for an old song a year later - The review strip gained a second pane: lyrics on the left, review on the right, so following the words costs no scrolling. No lyrics means no pane, not an empty one. Below 1024px the panes stack - LRC timestamps are stored but stripped for reading — they belong to the player, not the reader - The lyrics box is capped and scrolls inside itself, so a long song cannot stretch the strip past the screen Fixes a real bug found on the way: saveMetadata cleared any field the request did not carry, so publishing wiped the lyrics the worker had just fetched. Fields absent from a request now keep their stored value. The client identifies itself to LRCLIB as "levyraati" and nothing more. Tests cover cleanLyrics keeping line breaks, and fetchLyrics against a local server: synced beats plain, instrumentals and wrong-length takes are skipped, and a miss is empty with no error.
92 lines
3.1 KiB
SQL
92 lines
3.1 KiB
SQL
create table users (
|
|
id bigserial primary key,
|
|
name text not null,
|
|
email text not null unique,
|
|
password_hash text not null,
|
|
avatar text,
|
|
banned boolean not null default false,
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
create table sessions (
|
|
token text primary key,
|
|
user_id bigint not null references users (id) on delete cascade,
|
|
idle_ttl interval not null,
|
|
expires_at timestamptz not null,
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
create index on sessions (user_id);
|
|
|
|
create table invites (
|
|
id bigserial primary key,
|
|
code text not null unique,
|
|
is_valid boolean not null default true,
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
create table songs (
|
|
id bigserial primary key,
|
|
title text not null,
|
|
artist text not null,
|
|
genre text not null,
|
|
description text,
|
|
-- LRC or plain text, told apart by whether the first line starts with '['. Not covered by the
|
|
-- lock: nobody reviewed the lyrics.
|
|
lyrics text,
|
|
audio_file text not null,
|
|
duration_seconds integer not null,
|
|
source_url text,
|
|
submitted_by bigint not null references users (id),
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
create index on songs (created_at desc);
|
|
|
|
create table submissions (
|
|
id bigserial primary key,
|
|
user_id bigint not null references users (id) on delete cascade,
|
|
status text not null default 'queued',
|
|
status_msg text,
|
|
source_url text,
|
|
tmp_path text,
|
|
title text,
|
|
artist text,
|
|
genre text,
|
|
description text,
|
|
lyrics text,
|
|
created_at timestamptz not null default now(),
|
|
constraint submissions_status check (
|
|
status in ('queued', 'downloading', 'converting', 'ready', 'failed')
|
|
)
|
|
);
|
|
|
|
-- The submission quota (5 per rolling 24h, failures excluded) reads this.
|
|
create index on submissions (user_id, created_at desc);
|
|
|
|
create table reviews (
|
|
id bigserial primary key,
|
|
song_id bigint not null references songs (id) on delete cascade,
|
|
reviewer_id bigint not null references users (id),
|
|
score integer not null check (score between 1 and 100),
|
|
text text not null,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
unique (song_id, reviewer_id)
|
|
);
|
|
|
|
create index on reviews (song_id);
|
|
|
|
-- The queue asks "songs this member has not reviewed" — that lookup is by reviewer.
|
|
create index on reviews (reviewer_id, song_id);
|
|
|
|
create table reports (
|
|
id bigserial primary key,
|
|
user_id bigint not null references users (id) on delete cascade,
|
|
body text not null,
|
|
page text,
|
|
user_agent text,
|
|
resolved_at timestamptz,
|
|
created_at timestamptz not null default now()
|
|
);
|