Merge branch 'scr1'
Co-Authored-By: Claude Fable 5.1 <[email protected]> # Conflicts: # CHANGELOG.md # docs/plan/00-first-version-plan.md
This commit is contained in:
commit
2e83107f68
45 files changed
+2867
-41
No files matched your search
@@ -0,0 +1,38 @@
|
||||
import type { Kysely } from "kysely";
|
||||
import { execDdl } from "../src/migration-helpers.ts";
|
||||
|
||||
// SCR-1 刮削:每部作品最近一次刮削(往媒体库目录写 NFO 与图片)的状态与逐文件结果。
|
||||
// 一部作品一行,重新刮削覆盖;历史看审计(organize.scrape)与引擎运行记录。
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
|
||||
export async function up(db: Kysely<any>): Promise<void> {
|
||||
await execDdl(
|
||||
db,
|
||||
`create table if not exists media_scrape (
|
||||
media_entity_id uuid primary key references media_entity (id) on delete cascade,
|
||||
status text not null default 'queued' check (status in ('queued', 'running', 'succeeded', 'partial', 'failed')),
|
||||
trigger text not null default 'manual' check (trigger in ('auto', 'manual')),
|
||||
mode text not null default 'missing' check (mode in ('missing', 'overwrite')),
|
||||
record_id uuid references organization_record (id) on delete set null,
|
||||
engine_run_id text,
|
||||
requested_at timestamptz not null default now(),
|
||||
started_at timestamptz,
|
||||
finished_at timestamptz,
|
||||
written integer not null default 0,
|
||||
skipped integer not null default 0,
|
||||
failed integer not null default 0,
|
||||
reason text,
|
||||
files jsonb not null default '[]'::jsonb,
|
||||
created_at timestamptz not null default now(),
|
||||
updated_at timestamptz not null default now()
|
||||
);
|
||||
drop trigger if exists media_scrape_touch_updated_at on media_scrape;
|
||||
create trigger media_scrape_touch_updated_at before update on media_scrape
|
||||
for each row execute function umt_touch_updated_at();`,
|
||||
);
|
||||
}
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: 同上
|
||||
export async function down(db: Kysely<any>): Promise<void> {
|
||||
await execDdl(db, "drop table if exists media_scrape;");
|
||||
}
|
||||
@@ -159,6 +159,9 @@ export type MediaAssetKind = "video" | "bluray_dir" | "subtitle" | "audio" | "nf
|
||||
export type MediaAssetStatus = "active" | "replaced" | "retired" | "missing";
|
||||
export type MediaEntityKind = "movie" | "series" | "variety" | "collection" | "other";
|
||||
export type MediaEntityStatus = "provisional" | "active" | "merged" | "archived";
|
||||
export type MediaScrapeStatus = "queued" | "running" | "succeeded" | "partial" | "failed";
|
||||
export type MediaScrapeTrigger = "auto" | "manual";
|
||||
export type MediaScrapeMode = "missing" | "overwrite";
|
||||
export type MediaServerInstanceKind = "emby" | "jellyfin";
|
||||
export type MediaServerInstanceHealthStatus = "unknown" | "healthy" | "degraded" | "offline";
|
||||
export type MetadataEnrichmentStatus = "suggested" | "applied" | "failed";
|
||||
@@ -915,6 +918,25 @@ export interface MediaEntityTable {
|
||||
naming_override: Json | null;
|
||||
}
|
||||
|
||||
export interface MediaScrapeTable {
|
||||
media_entity_id: string;
|
||||
status: Generated<MediaScrapeStatus>;
|
||||
trigger: Generated<MediaScrapeTrigger>;
|
||||
mode: Generated<MediaScrapeMode>;
|
||||
record_id: string | null;
|
||||
engine_run_id: string | null;
|
||||
requested_at: GeneratedTimestamptz;
|
||||
started_at: Timestamptz | null;
|
||||
finished_at: Timestamptz | null;
|
||||
written: Generated<number>;
|
||||
skipped: Generated<number>;
|
||||
failed: Generated<number>;
|
||||
reason: string | null;
|
||||
files: Generated<Json>;
|
||||
created_at: GeneratedTimestamptz;
|
||||
updated_at: GeneratedTimestamptz;
|
||||
}
|
||||
|
||||
export interface MediaServerInstanceTable {
|
||||
id: Generated<string>;
|
||||
name: string;
|
||||
@@ -1698,6 +1720,7 @@ export interface DB {
|
||||
mcp_server_config: McpServerConfigTable;
|
||||
media_asset: MediaAssetTable;
|
||||
media_entity: MediaEntityTable;
|
||||
media_scrape: MediaScrapeTable;
|
||||
media_server_instance: MediaServerInstanceTable;
|
||||
metadata_enrichment: MetadataEnrichmentTable;
|
||||
metadata_field: MetadataFieldTable;
|
||||
@@ -1858,6 +1881,9 @@ export type MediaAssetUpdate = Updateable<MediaAssetTable>;
|
||||
export type MediaEntityRow = Selectable<MediaEntityTable>;
|
||||
export type NewMediaEntity = Insertable<MediaEntityTable>;
|
||||
export type MediaEntityUpdate = Updateable<MediaEntityTable>;
|
||||
export type MediaScrapeRow = Selectable<MediaScrapeTable>;
|
||||
export type NewMediaScrape = Insertable<MediaScrapeTable>;
|
||||
export type MediaScrapeUpdate = Updateable<MediaScrapeTable>;
|
||||
export type MediaServerInstanceRow = Selectable<MediaServerInstanceTable>;
|
||||
export type NewMediaServerInstance = Insertable<MediaServerInstanceTable>;
|
||||
export type MediaServerInstanceUpdate = Updateable<MediaServerInstanceTable>;
|
||||
|
||||
@@ -26,6 +26,7 @@ const EXPECTED_TABLES = [
|
||||
"path_mapping",
|
||||
// 内容 / 身份
|
||||
"media_entity",
|
||||
"media_scrape",
|
||||
"content_unit",
|
||||
"title_alias",
|
||||
"external_identity",
|
||||
@@ -153,6 +154,8 @@ const NON_ENTITY_TABLES = [
|
||||
"kv_cache",
|
||||
// 站点请求计数:(site_id, hour) 主键
|
||||
"site_request_count",
|
||||
// 作品最近一次刮削:media_entity_id 主键(SCR-1)
|
||||
"media_scrape",
|
||||
];
|
||||
|
||||
describe.skipIf(skip)("schema migrations (real PG)", () => {
|
||||
|
||||
Reference in new issue
Block a user