Ampelos dashboard: the web face, and the owner of the schema

Split out of the single Ampelos repository. Next.js app, Drizzle schema and
migrations, brand art, planning notes.

What left: scripts/, which was the agent's job library misfiled under web/ and
imported nothing from src/; and deploy/truenas, whose broadcast posts to the
scan listener on :3427 -- an agent script -- so it belongs beside the thing it
talks to.

This repository keeps the schema. The agent speaks raw SQL against the same
tables and holds no copy of it, so a rename here can break it silently where it
used to be one commit. The README says so, and the agent carries a snapshot to
check against.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Ryan
2026-08-15 12:12:08 +02:00
commit acdc25c797
138 changed files with 70946 additions and 0 deletions
@@ -0,0 +1,41 @@
CREATE TYPE "public"."storage_tier" AS ENUM('live', 'backup', 'archive');--> statement-breakpoint
CREATE TABLE "user_identities" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"provider" text NOT NULL,
"external_id" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"display_name" text NOT NULL,
"email" text NOT NULL,
"is_admin" boolean DEFAULT false NOT NULL,
"watching_now_tv_slots" integer DEFAULT 5 NOT NULL,
"watching_now_movie_slots" integer DEFAULT 10 NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "users_email_unique" UNIQUE("email")
);
--> statement-breakpoint
CREATE TABLE "storage_availability" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"tier_id" uuid NOT NULL,
"online" boolean NOT NULL,
"detected_at" timestamp DEFAULT now() NOT NULL,
"resolved_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "storage_tiers" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"tier" "storage_tier" NOT NULL,
"label" text NOT NULL,
"base_path" text NOT NULL,
"always_online" boolean DEFAULT false NOT NULL,
"notes" text,
CONSTRAINT "storage_tiers_tier_unique" UNIQUE("tier")
);
--> statement-breakpoint
ALTER TABLE "user_identities" ADD CONSTRAINT "user_identities_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "storage_availability" ADD CONSTRAINT "storage_availability_tier_id_storage_tiers_id_fk" FOREIGN KEY ("tier_id") REFERENCES "public"."storage_tiers"("id") ON DELETE no action ON UPDATE no action;
+146
View File
@@ -0,0 +1,146 @@
CREATE TYPE "public"."external_id_source" AS ENUM('tvdb', 'tmdb', 'imdb', 'plex');--> statement-breakpoint
CREATE TYPE "public"."media_type" AS ENUM('tv_series', 'movie');--> statement-breakpoint
CREATE TYPE "public"."season_status" AS ENUM('upcoming', 'airing', 'complete');--> statement-breakpoint
CREATE TYPE "public"."series_status" AS ENUM('upcoming', 'continuing', 'ended');--> statement-breakpoint
CREATE TYPE "public"."watching_now_scope" AS ENUM('show', 'season');--> statement-breakpoint
CREATE TYPE "public"."watchlist_source" AS ENUM('plex', 'manual');--> statement-breakpoint
CREATE TYPE "public"."override_type" AS ENUM('force_live', 'force_archive', 'force_quality', 'force_monitor', 'force_ignore', 'temporary_promotion');--> statement-breakpoint
CREATE TYPE "public"."policy_trigger" AS ENUM('scheduled', 'manual', 'demand_change', 'metadata_refresh');--> statement-breakpoint
CREATE TYPE "public"."quality" AS ENUM('sd', '720p', '1080p', '4k');--> statement-breakpoint
CREATE TABLE "episodes" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"season_id" uuid NOT NULL,
"episode_number" integer NOT NULL,
"title" text,
"overview" text,
"air_date" date
);
--> statement-breakpoint
CREATE TABLE "external_ids" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"media_item_id" uuid NOT NULL,
"source" "external_id_source" NOT NULL,
"external_id" text NOT NULL
);
--> statement-breakpoint
CREATE TABLE "media_items" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"media_type" "media_type" NOT NULL,
"title" text NOT NULL,
"sort_title" text,
"overview" text,
"year" integer,
"poster_path" text,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "movies" (
"id" uuid PRIMARY KEY NOT NULL,
"release_date" date,
"runtime_minutes" integer,
"status" text,
"metadata_refreshed_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "seasons" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"series_id" uuid NOT NULL,
"season_number" integer NOT NULL,
"title" text,
"episode_count" integer,
"air_date" date,
"status" "season_status" DEFAULT 'upcoming' NOT NULL
);
--> statement-breakpoint
CREATE TABLE "series" (
"id" uuid PRIMARY KEY NOT NULL,
"status" "series_status" DEFAULT 'upcoming' NOT NULL,
"network" text,
"first_air_date" date,
"last_air_date" date,
"is_currently_relevant" text,
"metadata_refreshed_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "classics" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"media_item_id" uuid NOT NULL,
"note" text,
"added_by" uuid,
"added_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "classics_media_item_id_unique" UNIQUE("media_item_id")
);
--> statement-breakpoint
CREATE TABLE "watching_now_items" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"media_item_id" uuid NOT NULL,
"scope" "watching_now_scope" DEFAULT 'show' NOT NULL,
"season_number" integer,
"added_at" timestamp DEFAULT now() NOT NULL,
"removed_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "watchlist_items" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"media_item_id" uuid NOT NULL,
"source" "watchlist_source" DEFAULT 'plex' NOT NULL,
"added_at" timestamp DEFAULT now() NOT NULL,
"removed_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "admin_overrides" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"media_item_id" uuid NOT NULL,
"season_number" integer,
"episode_number" integer,
"override_type" "override_type" NOT NULL,
"value" jsonb,
"reason" text,
"expires_at" timestamp,
"created_by" uuid,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "desired_states" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"policy_run_id" uuid,
"media_item_id" uuid NOT NULL,
"season_number" integer,
"episode_number" integer,
"wanted" boolean DEFAULT false NOT NULL,
"storage_tier" "storage_tier",
"min_quality" "quality",
"preferred_quality" "quality",
"monitored" boolean DEFAULT false NOT NULL,
"reason_codes" text[] DEFAULT '{}' NOT NULL,
"computed_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "policy_runs" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"trigger" "policy_trigger" NOT NULL,
"started_at" timestamp DEFAULT now() NOT NULL,
"completed_at" timestamp,
"items_evaluated" integer,
"items_changed" integer
);
--> statement-breakpoint
ALTER TABLE "episodes" ADD CONSTRAINT "episodes_season_id_seasons_id_fk" FOREIGN KEY ("season_id") REFERENCES "public"."seasons"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "external_ids" ADD CONSTRAINT "external_ids_media_item_id_media_items_id_fk" FOREIGN KEY ("media_item_id") REFERENCES "public"."media_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "movies" ADD CONSTRAINT "movies_id_media_items_id_fk" FOREIGN KEY ("id") REFERENCES "public"."media_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "seasons" ADD CONSTRAINT "seasons_series_id_series_id_fk" FOREIGN KEY ("series_id") REFERENCES "public"."series"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "series" ADD CONSTRAINT "series_id_media_items_id_fk" FOREIGN KEY ("id") REFERENCES "public"."media_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "classics" ADD CONSTRAINT "classics_media_item_id_media_items_id_fk" FOREIGN KEY ("media_item_id") REFERENCES "public"."media_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "classics" ADD CONSTRAINT "classics_added_by_users_id_fk" FOREIGN KEY ("added_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "watching_now_items" ADD CONSTRAINT "watching_now_items_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "watching_now_items" ADD CONSTRAINT "watching_now_items_media_item_id_media_items_id_fk" FOREIGN KEY ("media_item_id") REFERENCES "public"."media_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "watchlist_items" ADD CONSTRAINT "watchlist_items_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "watchlist_items" ADD CONSTRAINT "watchlist_items_media_item_id_media_items_id_fk" FOREIGN KEY ("media_item_id") REFERENCES "public"."media_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "admin_overrides" ADD CONSTRAINT "admin_overrides_media_item_id_media_items_id_fk" FOREIGN KEY ("media_item_id") REFERENCES "public"."media_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "admin_overrides" ADD CONSTRAINT "admin_overrides_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "desired_states" ADD CONSTRAINT "desired_states_policy_run_id_policy_runs_id_fk" FOREIGN KEY ("policy_run_id") REFERENCES "public"."policy_runs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "desired_states" ADD CONSTRAINT "desired_states_media_item_id_media_items_id_fk" FOREIGN KEY ("media_item_id") REFERENCES "public"."media_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "external_ids_source_external_id_idx" ON "external_ids" USING btree ("source","external_id");
@@ -0,0 +1 @@
ALTER TABLE "watching_now_items" ADD COLUMN "slot_number" integer;
@@ -0,0 +1,21 @@
CREATE TABLE "storage_files" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"tier_id" uuid NOT NULL,
"media_item_id" uuid,
"relative_path" text NOT NULL,
"full_path" text NOT NULL,
"filename" text NOT NULL,
"extension" text NOT NULL,
"size_bytes" bigint NOT NULL,
"modified_at" timestamp NOT NULL,
"observed_at" timestamp DEFAULT now() NOT NULL,
"missing_at" timestamp,
"inferred_title" text,
"inferred_year" text,
"quality" text,
"codec" text
);
--> statement-breakpoint
ALTER TABLE "storage_files" ADD CONSTRAINT "storage_files_tier_id_storage_tiers_id_fk" FOREIGN KEY ("tier_id") REFERENCES "public"."storage_tiers"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "storage_files" ADD CONSTRAINT "storage_files_media_item_id_media_items_id_fk" FOREIGN KEY ("media_item_id") REFERENCES "public"."media_items"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "storage_files_tier_relative_path_idx" ON "storage_files" USING btree ("tier_id","relative_path");
+2
View File
@@ -0,0 +1,2 @@
ALTER TABLE "storage_files" ADD COLUMN "episode_id" uuid;--> statement-breakpoint
ALTER TABLE "storage_files" ADD CONSTRAINT "storage_files_episode_id_episodes_id_fk" FOREIGN KEY ("episode_id") REFERENCES "public"."episodes"("id") ON DELETE set null ON UPDATE no action;
@@ -0,0 +1,9 @@
ALTER TABLE "storage_files" ADD COLUMN "width" integer;--> statement-breakpoint
ALTER TABLE "storage_files" ADD COLUMN "height" integer;--> statement-breakpoint
ALTER TABLE "storage_files" ADD COLUMN "video_codec" text;--> statement-breakpoint
ALTER TABLE "storage_files" ADD COLUMN "audio_codec" text;--> statement-breakpoint
ALTER TABLE "storage_files" ADD COLUMN "audio_channels" integer;--> statement-breakpoint
ALTER TABLE "storage_files" ADD COLUMN "duration_seconds" integer;--> statement-breakpoint
ALTER TABLE "storage_files" ADD COLUMN "bitrate" bigint;--> statement-breakpoint
ALTER TABLE "storage_files" ADD COLUMN "probed_at" timestamp;--> statement-breakpoint
ALTER TABLE "storage_files" ADD COLUMN "probe_error" text;
@@ -0,0 +1,13 @@
CREATE TABLE "agent_heartbeats" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"agent" text NOT NULL,
"hostname" text,
"version" text,
"first_seen_at" timestamp DEFAULT now() NOT NULL,
"last_seen_at" timestamp DEFAULT now() NOT NULL,
"details" jsonb,
CONSTRAINT "agent_heartbeats_agent_unique" UNIQUE("agent")
);
--> statement-breakpoint
ALTER TABLE "series" ALTER COLUMN "is_currently_relevant" SET DATA TYPE boolean USING "is_currently_relevant"::boolean;--> statement-breakpoint
ALTER TABLE "storage_tiers" ADD COLUMN "agent_name" text;
@@ -0,0 +1,2 @@
CREATE UNIQUE INDEX "episodes_season_id_episode_number_idx" ON "episodes" USING btree ("season_id","episode_number");--> statement-breakpoint
CREATE UNIQUE INDEX "seasons_series_id_season_number_idx" ON "seasons" USING btree ("series_id","season_number");
@@ -0,0 +1 @@
ALTER TABLE "storage_files" ADD COLUMN "edition" text;
@@ -0,0 +1,19 @@
CREATE TYPE "public"."corrupt_file_status" AS ENUM('pending', 'approved', 'deleted', 'dismissed', 'failed');--> statement-breakpoint
CREATE TABLE "corrupt_files" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"full_path" text NOT NULL,
"agent" text NOT NULL,
"tier" text,
"reason" text NOT NULL,
"size_bytes" bigint,
"status" "corrupt_file_status" DEFAULT 'pending' NOT NULL,
"first_detected_at" timestamp DEFAULT now() NOT NULL,
"last_detected_at" timestamp DEFAULT now() NOT NULL,
"reviewed_by" uuid,
"reviewed_at" timestamp,
"deleted_at" timestamp,
"delete_error" text,
CONSTRAINT "corrupt_files_full_path_unique" UNIQUE("full_path")
);
--> statement-breakpoint
ALTER TABLE "corrupt_files" ADD CONSTRAINT "corrupt_files_reviewed_by_users_id_fk" FOREIGN KEY ("reviewed_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
@@ -0,0 +1,17 @@
CREATE TYPE "public"."maintenance_job_status" AS ENUM('running', 'succeeded', 'failed', 'skipped');--> statement-breakpoint
CREATE TYPE "public"."maintenance_trigger" AS ENUM('scheduled', 'manual');--> statement-breakpoint
CREATE TABLE "maintenance_jobs" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"job" text NOT NULL,
"trigger" "maintenance_trigger" DEFAULT 'scheduled' NOT NULL,
"status" "maintenance_job_status" DEFAULT 'running' NOT NULL,
"started_at" timestamp DEFAULT now() NOT NULL,
"completed_at" timestamp,
"duration_ms" integer,
"exit_code" integer,
"detail" text,
"output" text,
"summary" jsonb
);
--> statement-breakpoint
CREATE INDEX "maintenance_jobs_job_started_idx" ON "maintenance_jobs" USING btree ("job","started_at");
+70
View File
@@ -0,0 +1,70 @@
CREATE TYPE "public"."grab_source" AS ENUM('auto', 'manual');--> statement-breakpoint
CREATE TYPE "public"."grab_status" AS ENUM('queued', 'downloading', 'completed', 'importing', 'imported', 'failed', 'orphaned');--> statement-breakpoint
CREATE TABLE "grabs" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"media_item_id" uuid NOT NULL,
"season_number" integer,
"episode_number" integer,
"info_hash" text NOT NULL,
"release_title" text NOT NULL,
"size_bytes" bigint,
"magnet_url" text,
"download_url" text,
"indexer_name" text NOT NULL,
"origin" text,
"seeders" integer,
"quality" "quality",
"score" integer,
"score_reasons" jsonb DEFAULT '[]'::jsonb,
"source" "grab_source" NOT NULL,
"requested_by" uuid,
"status" "grab_status" DEFAULT 'queued' NOT NULL,
"status_detail" text,
"progress" integer DEFAULT 0,
"content_path" text,
"imported_path" text,
"imported_at" timestamp,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "release_blocklist" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"info_hash" text NOT NULL,
"release_title" text,
"media_item_id" uuid,
"reason" text,
"permanent" boolean DEFAULT false NOT NULL,
"created_by" uuid,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "search_runs" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"media_item_id" uuid,
"season_number" integer,
"episode_number" integer,
"source" "grab_source" NOT NULL,
"query" text,
"indexers_queried" integer DEFAULT 0 NOT NULL,
"results_found" integer DEFAULT 0 NOT NULL,
"results_accepted" integer DEFAULT 0 NOT NULL,
"skipped" jsonb DEFAULT '[]'::jsonb,
"errors" jsonb DEFAULT '[]'::jsonb,
"grab_id" uuid,
"decision" text,
"started_at" timestamp DEFAULT now() NOT NULL,
"duration_ms" integer
);
--> statement-breakpoint
ALTER TABLE "grabs" ADD CONSTRAINT "grabs_media_item_id_media_items_id_fk" FOREIGN KEY ("media_item_id") REFERENCES "public"."media_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "grabs" ADD CONSTRAINT "grabs_requested_by_users_id_fk" FOREIGN KEY ("requested_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "release_blocklist" ADD CONSTRAINT "release_blocklist_media_item_id_media_items_id_fk" FOREIGN KEY ("media_item_id") REFERENCES "public"."media_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "release_blocklist" ADD CONSTRAINT "release_blocklist_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "search_runs" ADD CONSTRAINT "search_runs_media_item_id_media_items_id_fk" FOREIGN KEY ("media_item_id") REFERENCES "public"."media_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "search_runs" ADD CONSTRAINT "search_runs_grab_id_grabs_id_fk" FOREIGN KEY ("grab_id") REFERENCES "public"."grabs"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "grabs_info_hash_idx" ON "grabs" USING btree ("info_hash");--> statement-breakpoint
CREATE INDEX "grabs_status_idx" ON "grabs" USING btree ("status");--> statement-breakpoint
CREATE INDEX "grabs_media_item_idx" ON "grabs" USING btree ("media_item_id","season_number","episode_number");--> statement-breakpoint
CREATE UNIQUE INDEX "release_blocklist_info_hash_idx" ON "release_blocklist" USING btree ("info_hash");--> statement-breakpoint
CREATE INDEX "search_runs_media_item_idx" ON "search_runs" USING btree ("media_item_id","started_at");
@@ -0,0 +1,17 @@
CREATE TABLE "grab_files" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"grab_id" uuid NOT NULL,
"storage_file_id" uuid,
"imported_path" text NOT NULL,
"season_number" integer,
"episode_number" integer,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "grabs" ADD COLUMN "seed_path" text;--> statement-breakpoint
ALTER TABLE "grabs" ADD COLUMN "seed_released_at" timestamp;--> statement-breakpoint
ALTER TABLE "grabs" ADD COLUMN "seed_release_reason" text;--> statement-breakpoint
ALTER TABLE "grab_files" ADD CONSTRAINT "grab_files_grab_id_grabs_id_fk" FOREIGN KEY ("grab_id") REFERENCES "public"."grabs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "grab_files" ADD CONSTRAINT "grab_files_storage_file_id_storage_files_id_fk" FOREIGN KEY ("storage_file_id") REFERENCES "public"."storage_files"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "grab_files_grab_path_idx" ON "grab_files" USING btree ("grab_id","imported_path");--> statement-breakpoint
CREATE INDEX "grab_files_storage_file_idx" ON "grab_files" USING btree ("storage_file_id");
@@ -0,0 +1,2 @@
ALTER TABLE "release_blocklist" ALTER COLUMN "info_hash" DROP NOT NULL;--> statement-breakpoint
ALTER TABLE "release_blocklist" ADD COLUMN "release_group" text;
@@ -0,0 +1 @@
ALTER TABLE "grabs" ADD COLUMN "progress_changed_at" timestamp;
@@ -0,0 +1 @@
ALTER TYPE "public"."override_type" ADD VALUE 'purge';
@@ -0,0 +1,28 @@
CREATE TABLE "release_size_rules" (
"quality" "quality" PRIMARY KEY NOT NULL,
"min_mb_per_minute" numeric(6, 1) NOT NULL,
"max_mb_per_minute" numeric(6, 1) NOT NULL,
"atmos_max_mb_per_minute" numeric(6, 1),
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
-- Seeded from percentiles over every probed file in this library that has a
-- real duration, so the table starts out describing the collection it governs:
--
-- quality n p05 p50 p90 p95 p99 max
-- sd 3604 6.1 15.2 16.0 17.0 18.5 45.7
-- 720p 9958 3.4 7.7 18.0 19.1 25.5 55.3
-- 1080p 9184 8.3 19.6 48.7 61.9 104.8 237.9
-- 4k 449 30.3 92.2 154.4 184.1 233.4 264.5
--
-- 1080p is capped at 60 rather than the 40 the film numbers alone suggest:
-- television runs hotter than film at 1080p (median 24.1 against 15.9), and 40
-- would have refused 1,602 files already held, 1,569 of them television.
INSERT INTO "release_size_rules"
("quality", "min_mb_per_minute", "max_mb_per_minute", "atmos_max_mb_per_minute")
VALUES
('sd', 2, 20, NULL),
('720p', 2, 20, NULL),
('1080p', 4, 60, NULL),
('4k', 16, 160, 240)
ON CONFLICT ("quality") DO NOTHING;
@@ -0,0 +1,12 @@
ALTER TABLE "release_size_rules" ADD COLUMN "timeless_max_mb_per_minute" numeric(6, 1);--> statement-breakpoint
-- Sized to admit a genuine remux and nothing looser. A remux is the disc's own
-- streams, so its size is a property of the disc rather than a choice: a 1080p
-- one runs 20-30GB for a two-hour film (170-250 MB/min) and a 4K one 50-80GB
-- (420-670 MB/min).
--
-- Only 1080p and 4K carry one. There is no such thing as a 720p remux -- the
-- format is a re-encode by definition, so an allowance there would license
-- bloat with nothing lossless to show for it.
UPDATE "release_size_rules" SET "timeless_max_mb_per_minute" = 250 WHERE "quality" = '1080p';
--> statement-breakpoint
UPDATE "release_size_rules" SET "timeless_max_mb_per_minute" = 650 WHERE "quality" = '4k';
+16
View File
@@ -0,0 +1,16 @@
CREATE TABLE "plex_accounts" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"plex_user_id" text NOT NULL,
"plex_uuid" text,
"plex_username" text NOT NULL,
"plex_email" text,
"libraries_shared_at" timestamp,
"shared_section_ids" text[],
"linked_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "plex_accounts_user_id_unique" UNIQUE("user_id"),
CONSTRAINT "plex_accounts_plex_user_id_unique" UNIQUE("plex_user_id")
);
--> statement-breakpoint
ALTER TABLE "plex_accounts" ADD CONSTRAINT "plex_accounts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
@@ -0,0 +1,3 @@
ALTER TABLE "external_ids" ADD COLUMN "verified_at" timestamp;--> statement-breakpoint
ALTER TABLE "external_ids" ADD COLUMN "verified_by" uuid;--> statement-breakpoint
ALTER TABLE "series" ADD COLUMN "episode_runtime_minutes" integer;
@@ -0,0 +1,2 @@
ALTER TABLE "external_ids" ADD COLUMN "rejected_at" timestamp;--> statement-breakpoint
ALTER TABLE "external_ids" ADD COLUMN "rejected_by" uuid;
@@ -0,0 +1,2 @@
CREATE INDEX "storage_files_media_item_idx" ON "storage_files" USING btree ("media_item_id");--> statement-breakpoint
CREATE INDEX "external_ids_media_item_idx" ON "external_ids" USING btree ("media_item_id");
@@ -0,0 +1,2 @@
ALTER TABLE "external_ids" ADD COLUMN "remote_title" text;--> statement-breakpoint
ALTER TABLE "external_ids" ADD COLUMN "remote_checked_at" timestamp;
@@ -0,0 +1,2 @@
ALTER TABLE "storage_files" ADD COLUMN "replace_requested_at" timestamp;--> statement-breakpoint
ALTER TABLE "storage_files" ADD COLUMN "replace_requested_by" uuid;
+282
View File
@@ -0,0 +1,282 @@
{
"id": "d8b57dfe-223d-4124-8c4a-d1f05c6d62c1",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.user_identities": {
"name": "user_identities",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"provider": {
"name": "provider",
"type": "text",
"primaryKey": false,
"notNull": true
},
"external_id": {
"name": "external_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"user_identities_user_id_users_id_fk": {
"name": "user_identities_user_id_users_id_fk",
"tableFrom": "user_identities",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"display_name": {
"name": "display_name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"is_admin": {
"name": "is_admin",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
},
"watching_now_tv_slots": {
"name": "watching_now_tv_slots",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 5
},
"watching_now_movie_slots": {
"name": "watching_now_movie_slots",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 10
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"users_email_unique": {
"name": "users_email_unique",
"nullsNotDistinct": false,
"columns": [
"email"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.storage_availability": {
"name": "storage_availability",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"tier_id": {
"name": "tier_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"online": {
"name": "online",
"type": "boolean",
"primaryKey": false,
"notNull": true
},
"detected_at": {
"name": "detected_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"resolved_at": {
"name": "resolved_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"storage_availability_tier_id_storage_tiers_id_fk": {
"name": "storage_availability_tier_id_storage_tiers_id_fk",
"tableFrom": "storage_availability",
"tableTo": "storage_tiers",
"columnsFrom": [
"tier_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.storage_tiers": {
"name": "storage_tiers",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"tier": {
"name": "tier",
"type": "storage_tier",
"typeSchema": "public",
"primaryKey": false,
"notNull": true
},
"label": {
"name": "label",
"type": "text",
"primaryKey": false,
"notNull": true
},
"base_path": {
"name": "base_path",
"type": "text",
"primaryKey": false,
"notNull": true
},
"always_online": {
"name": "always_online",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
},
"notes": {
"name": "notes",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"storage_tiers_tier_unique": {
"name": "storage_tiers_tier_unique",
"nullsNotDistinct": false,
"columns": [
"tier"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {
"public.storage_tier": {
"name": "storage_tier",
"schema": "public",
"values": [
"live",
"backup",
"archive"
]
}
},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+174
View File
@@ -0,0 +1,174 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1781818467932,
"tag": "0000_sudden_lila_cheney",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1781820073445,
"tag": "0001_slow_molten_man",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1781864081697,
"tag": "0002_careful_sentinels",
"breakpoints": true
},
{
"idx": 3,
"version": "7",
"when": 1781937141943,
"tag": "0003_yummy_santa_claus",
"breakpoints": true
},
{
"idx": 4,
"version": "7",
"when": 1782289634052,
"tag": "0004_sloppy_gambit",
"breakpoints": true
},
{
"idx": 5,
"version": "7",
"when": 1786270380404,
"tag": "0005_square_sentinels",
"breakpoints": true
},
{
"idx": 6,
"version": "7",
"when": 1786273172759,
"tag": "0006_sleepy_silver_surfer",
"breakpoints": true
},
{
"idx": 7,
"version": "7",
"when": 1786273540007,
"tag": "0007_chemical_virginia_dare",
"breakpoints": true
},
{
"idx": 8,
"version": "7",
"when": 1786282914317,
"tag": "0008_nappy_lord_tyger",
"breakpoints": true
},
{
"idx": 9,
"version": "7",
"when": 1786287535313,
"tag": "0009_nebulous_falcon",
"breakpoints": true
},
{
"idx": 10,
"version": "7",
"when": 1786359123121,
"tag": "0010_friendly_shiver_man",
"breakpoints": true
},
{
"idx": 11,
"version": "7",
"when": 1786383962407,
"tag": "0011_acquisition",
"breakpoints": true
},
{
"idx": 12,
"version": "7",
"when": 1786385181240,
"tag": "0012_grab_seed_tracking",
"breakpoints": true
},
{
"idx": 13,
"version": "7",
"when": 1786385710589,
"tag": "0013_blocklist_groups",
"breakpoints": true
},
{
"idx": 14,
"version": "7",
"when": 1786387139515,
"tag": "0014_grab_progress_stall",
"breakpoints": true
},
{
"idx": 15,
"version": "7",
"when": 1786396608811,
"tag": "0015_purge_override",
"breakpoints": true
},
{
"idx": 16,
"version": "7",
"when": 1786432882168,
"tag": "0016_release_size_rules",
"breakpoints": true
},
{
"idx": 17,
"version": "7",
"when": 1786438564256,
"tag": "0017_timeless_remux_allowance",
"breakpoints": true
},
{
"idx": 18,
"version": "7",
"when": 1786442023657,
"tag": "0018_plex_accounts",
"breakpoints": true
},
{
"idx": 19,
"version": "7",
"when": 1786472234377,
"tag": "0019_verified_tmdb_links",
"breakpoints": true
},
{
"idx": 20,
"version": "7",
"when": 1786543985425,
"tag": "0020_link_review_rejection",
"breakpoints": true
},
{
"idx": 21,
"version": "7",
"when": 1786550874284,
"tag": "0021_media_item_lookup_indexes",
"breakpoints": true
},
{
"idx": 22,
"version": "7",
"when": 1786641926372,
"tag": "0022_external_id_remote_title",
"breakpoints": true
},
{
"idx": 23,
"version": "7",
"when": 1786655149441,
"tag": "0023_replacement_requests",
"breakpoints": true
}
]
}