diff --git a/api/drizzle/0011_join_rename.sql b/api/drizzle/0011_join_rename.sql new file mode 100644 index 00000000..a806f70a --- /dev/null +++ b/api/drizzle/0011_join_rename.sql @@ -0,0 +1,20 @@ +ALTER TABLE "kyoo"."show_studio_join" RENAME COLUMN "show" TO "show_pk";--> statement-breakpoint +ALTER TABLE "kyoo"."show_studio_join" RENAME COLUMN "studio" TO "studio_pk";--> statement-breakpoint +ALTER TABLE "kyoo"."entry_video_join" RENAME COLUMN "entry" TO "entry_pk";--> statement-breakpoint +ALTER TABLE "kyoo"."entry_video_join" RENAME COLUMN "video" TO "video_pk";--> statement-breakpoint +ALTER TABLE "kyoo"."show_studio_join" DROP CONSTRAINT "show_studio_join_show_shows_pk_fk"; +--> statement-breakpoint +ALTER TABLE "kyoo"."show_studio_join" DROP CONSTRAINT "show_studio_join_studio_studios_pk_fk"; +--> statement-breakpoint +ALTER TABLE "kyoo"."entry_video_join" DROP CONSTRAINT "entry_video_join_entry_entries_pk_fk"; +--> statement-breakpoint +ALTER TABLE "kyoo"."entry_video_join" DROP CONSTRAINT "entry_video_join_video_videos_pk_fk"; +--> statement-breakpoint +ALTER TABLE "kyoo"."show_studio_join" DROP CONSTRAINT "show_studio_join_show_studio_pk";--> statement-breakpoint +ALTER TABLE "kyoo"."entry_video_join" DROP CONSTRAINT "entry_video_join_entry_video_pk";--> statement-breakpoint +ALTER TABLE "kyoo"."show_studio_join" ADD CONSTRAINT "show_studio_join_show_pk_studio_pk_pk" PRIMARY KEY("show_pk","studio_pk");--> statement-breakpoint +ALTER TABLE "kyoo"."entry_video_join" ADD CONSTRAINT "entry_video_join_entry_pk_video_pk_pk" PRIMARY KEY("entry_pk","video_pk");--> statement-breakpoint +ALTER TABLE "kyoo"."show_studio_join" ADD CONSTRAINT "show_studio_join_show_pk_shows_pk_fk" FOREIGN KEY ("show_pk") REFERENCES "kyoo"."shows"("pk") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "kyoo"."show_studio_join" ADD CONSTRAINT "show_studio_join_studio_pk_studios_pk_fk" FOREIGN KEY ("studio_pk") REFERENCES "kyoo"."studios"("pk") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "kyoo"."entry_video_join" ADD CONSTRAINT "entry_video_join_entry_pk_entries_pk_fk" FOREIGN KEY ("entry_pk") REFERENCES "kyoo"."entries"("pk") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "kyoo"."entry_video_join" ADD CONSTRAINT "entry_video_join_video_pk_videos_pk_fk" FOREIGN KEY ("video_pk") REFERENCES "kyoo"."videos"("pk") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/api/drizzle/meta/0011_snapshot.json b/api/drizzle/meta/0011_snapshot.json new file mode 100644 index 00000000..414d7975 --- /dev/null +++ b/api/drizzle/meta/0011_snapshot.json @@ -0,0 +1,1265 @@ +{ + "id": "6237922f-c7f2-4e4f-9206-acccdd2009a7", + "prevId": "8aedfd6c-095c-496f-a2fb-369b4c7d1911", + "version": "7", + "dialect": "postgresql", + "tables": { + "kyoo.entries": { + "name": "entries", + "schema": "kyoo", + "columns": { + "pk": { + "name": "pk", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "entries_pk_seq", + "schema": "kyoo", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "show_pk": { + "name": "show_pk", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "order": { + "name": "order", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "season_number": { + "name": "season_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "episode_number": { + "name": "episode_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "entry_type", + "typeSchema": "kyoo", + "primaryKey": false, + "notNull": true + }, + "extra_kind": { + "name": "extra_kind", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "air_date": { + "name": "air_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "runtime": { + "name": "runtime", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "thumbnail": { + "name": "thumbnail", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "next_refresh": { + "name": "next_refresh", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "entry_kind": { + "name": "entry_kind", + "columns": [ + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hash", + "with": {} + }, + "entry_order": { + "name": "entry_order", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "entries_show_pk_shows_pk_fk": { + "name": "entries_show_pk_shows_pk_fk", + "tableFrom": "entries", + "tableTo": "shows", + "schemaTo": "kyoo", + "columnsFrom": ["show_pk"], + "columnsTo": ["pk"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entries_id_unique": { + "name": "entries_id_unique", + "nullsNotDistinct": false, + "columns": ["id"] + }, + "entries_slug_unique": { + "name": "entries_slug_unique", + "nullsNotDistinct": false, + "columns": ["slug"] + }, + "entries_showPk_seasonNumber_episodeNumber_unique": { + "name": "entries_showPk_seasonNumber_episodeNumber_unique", + "nullsNotDistinct": false, + "columns": ["show_pk", "season_number", "episode_number"] + } + }, + "policies": {}, + "checkConstraints": { + "order_positive": { + "name": "order_positive", + "value": "\"kyoo\".\"entries\".\"order\" >= 0" + } + }, + "isRLSEnabled": false + }, + "kyoo.entry_translations": { + "name": "entry_translations", + "schema": "kyoo", + "columns": { + "pk": { + "name": "pk", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tagline": { + "name": "tagline", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "poster": { + "name": "poster", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "entry_name_trgm": { + "name": "entry_name_trgm", + "columns": [ + { + "expression": "\"name\" gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "entry_translations_pk_entries_pk_fk": { + "name": "entry_translations_pk_entries_pk_fk", + "tableFrom": "entry_translations", + "tableTo": "entries", + "schemaTo": "kyoo", + "columnsFrom": ["pk"], + "columnsTo": ["pk"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "entry_translations_pk_language_pk": { + "name": "entry_translations_pk_language_pk", + "columns": ["pk", "language"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "kyoo.season_translations": { + "name": "season_translations", + "schema": "kyoo", + "columns": { + "pk": { + "name": "pk", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "poster": { + "name": "poster", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "thumbnail": { + "name": "thumbnail", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "banner": { + "name": "banner", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "season_name_trgm": { + "name": "season_name_trgm", + "columns": [ + { + "expression": "\"name\" gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "season_translations_pk_seasons_pk_fk": { + "name": "season_translations_pk_seasons_pk_fk", + "tableFrom": "season_translations", + "tableTo": "seasons", + "schemaTo": "kyoo", + "columnsFrom": ["pk"], + "columnsTo": ["pk"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "season_translations_pk_language_pk": { + "name": "season_translations_pk_language_pk", + "columns": ["pk", "language"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "kyoo.seasons": { + "name": "seasons", + "schema": "kyoo", + "columns": { + "pk": { + "name": "pk", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "seasons_pk_seq", + "schema": "kyoo", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "show_pk": { + "name": "show_pk", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "season_number": { + "name": "season_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "start_air": { + "name": "start_air", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "end_air": { + "name": "end_air", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "next_refresh": { + "name": "next_refresh", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "show_fk": { + "name": "show_fk", + "columns": [ + { + "expression": "show_pk", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hash", + "with": {} + }, + "season_nbr": { + "name": "season_nbr", + "columns": [ + { + "expression": "season_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "seasons_show_pk_shows_pk_fk": { + "name": "seasons_show_pk_shows_pk_fk", + "tableFrom": "seasons", + "tableTo": "shows", + "schemaTo": "kyoo", + "columnsFrom": ["show_pk"], + "columnsTo": ["pk"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "seasons_id_unique": { + "name": "seasons_id_unique", + "nullsNotDistinct": false, + "columns": ["id"] + }, + "seasons_slug_unique": { + "name": "seasons_slug_unique", + "nullsNotDistinct": false, + "columns": ["slug"] + }, + "seasons_showPk_seasonNumber_unique": { + "name": "seasons_showPk_seasonNumber_unique", + "nullsNotDistinct": false, + "columns": ["show_pk", "season_number"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "kyoo.show_translations": { + "name": "show_translations", + "schema": "kyoo", + "columns": { + "pk": { + "name": "pk", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tagline": { + "name": "tagline", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "aliases": { + "name": "aliases", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "poster": { + "name": "poster", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "thumbnail": { + "name": "thumbnail", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "banner": { + "name": "banner", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "trailer_url": { + "name": "trailer_url", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "name_trgm": { + "name": "name_trgm", + "columns": [ + { + "expression": "\"name\" gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "tags": { + "name": "tags", + "columns": [ + { + "expression": "tags", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "show_translations_pk_shows_pk_fk": { + "name": "show_translations_pk_shows_pk_fk", + "tableFrom": "show_translations", + "tableTo": "shows", + "schemaTo": "kyoo", + "columnsFrom": ["pk"], + "columnsTo": ["pk"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "show_translations_pk_language_pk": { + "name": "show_translations_pk_language_pk", + "columns": ["pk", "language"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "kyoo.shows": { + "name": "shows", + "schema": "kyoo", + "columns": { + "pk": { + "name": "pk", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "shows_pk_seq", + "schema": "kyoo", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "show_kind", + "typeSchema": "kyoo", + "primaryKey": false, + "notNull": true + }, + "genres": { + "name": "genres", + "type": "genres[]", + "primaryKey": false, + "notNull": true + }, + "rating": { + "name": "rating", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "runtime": { + "name": "runtime", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "show_status", + "typeSchema": "kyoo", + "primaryKey": false, + "notNull": true + }, + "start_air": { + "name": "start_air", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "end_air": { + "name": "end_air", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "original_language": { + "name": "original_language", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "collection_pk": { + "name": "collection_pk", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "next_refresh": { + "name": "next_refresh", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "kind": { + "name": "kind", + "columns": [ + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hash", + "with": {} + }, + "rating": { + "name": "rating", + "columns": [ + { + "expression": "rating", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "startAir": { + "name": "startAir", + "columns": [ + { + "expression": "start_air", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "shows_collection_pk_shows_pk_fk": { + "name": "shows_collection_pk_shows_pk_fk", + "tableFrom": "shows", + "tableTo": "shows", + "schemaTo": "kyoo", + "columnsFrom": ["collection_pk"], + "columnsTo": ["pk"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "shows_id_unique": { + "name": "shows_id_unique", + "nullsNotDistinct": false, + "columns": ["id"] + }, + "shows_slug_unique": { + "name": "shows_slug_unique", + "nullsNotDistinct": false, + "columns": ["slug"] + } + }, + "policies": {}, + "checkConstraints": { + "rating_valid": { + "name": "rating_valid", + "value": "\"kyoo\".\"shows\".\"rating\" between 0 and 100" + }, + "runtime_valid": { + "name": "runtime_valid", + "value": "\"kyoo\".\"shows\".\"runtime\" >= 0" + } + }, + "isRLSEnabled": false + }, + "kyoo.show_studio_join": { + "name": "show_studio_join", + "schema": "kyoo", + "columns": { + "show_pk": { + "name": "show_pk", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "studio_pk": { + "name": "studio_pk", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "show_studio_join_show_pk_shows_pk_fk": { + "name": "show_studio_join_show_pk_shows_pk_fk", + "tableFrom": "show_studio_join", + "tableTo": "shows", + "schemaTo": "kyoo", + "columnsFrom": ["show_pk"], + "columnsTo": ["pk"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "show_studio_join_studio_pk_studios_pk_fk": { + "name": "show_studio_join_studio_pk_studios_pk_fk", + "tableFrom": "show_studio_join", + "tableTo": "studios", + "schemaTo": "kyoo", + "columnsFrom": ["studio_pk"], + "columnsTo": ["pk"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "show_studio_join_show_pk_studio_pk_pk": { + "name": "show_studio_join_show_pk_studio_pk_pk", + "columns": ["show_pk", "studio_pk"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "kyoo.studio_translations": { + "name": "studio_translations", + "schema": "kyoo", + "columns": { + "pk": { + "name": "pk", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "studio_name_trgm": { + "name": "studio_name_trgm", + "columns": [ + { + "expression": "\"name\" gin_trgm_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "studio_translations_pk_studios_pk_fk": { + "name": "studio_translations_pk_studios_pk_fk", + "tableFrom": "studio_translations", + "tableTo": "studios", + "schemaTo": "kyoo", + "columnsFrom": ["pk"], + "columnsTo": ["pk"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "studio_translations_pk_language_pk": { + "name": "studio_translations_pk_language_pk", + "columns": ["pk", "language"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "kyoo.studios": { + "name": "studios", + "schema": "kyoo", + "columns": { + "pk": { + "name": "pk", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "studios_pk_seq", + "schema": "kyoo", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "studios_id_unique": { + "name": "studios_id_unique", + "nullsNotDistinct": false, + "columns": ["id"] + }, + "studios_slug_unique": { + "name": "studios_slug_unique", + "nullsNotDistinct": false, + "columns": ["slug"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "kyoo.entry_video_join": { + "name": "entry_video_join", + "schema": "kyoo", + "columns": { + "entry_pk": { + "name": "entry_pk", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "video_pk": { + "name": "video_pk", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "entry_video_join_entry_pk_entries_pk_fk": { + "name": "entry_video_join_entry_pk_entries_pk_fk", + "tableFrom": "entry_video_join", + "tableTo": "entries", + "schemaTo": "kyoo", + "columnsFrom": ["entry_pk"], + "columnsTo": ["pk"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entry_video_join_video_pk_videos_pk_fk": { + "name": "entry_video_join_video_pk_videos_pk_fk", + "tableFrom": "entry_video_join", + "tableTo": "videos", + "schemaTo": "kyoo", + "columnsFrom": ["video_pk"], + "columnsTo": ["pk"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "entry_video_join_entry_pk_video_pk_pk": { + "name": "entry_video_join_entry_pk_video_pk_pk", + "columns": ["entry_pk", "video_pk"] + } + }, + "uniqueConstraints": { + "entry_video_join_slug_unique": { + "name": "entry_video_join_slug_unique", + "nullsNotDistinct": false, + "columns": ["slug"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "kyoo.videos": { + "name": "videos", + "schema": "kyoo", + "columns": { + "pk": { + "name": "pk", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "videos_pk_seq", + "schema": "kyoo", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rendering": { + "name": "rendering", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "part": { + "name": "part", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "guess": { + "name": "guess", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "videos_id_unique": { + "name": "videos_id_unique", + "nullsNotDistinct": false, + "columns": ["id"] + }, + "videos_path_unique": { + "name": "videos_path_unique", + "nullsNotDistinct": false, + "columns": ["path"] + } + }, + "policies": {}, + "checkConstraints": { + "part_pos": { + "name": "part_pos", + "value": "\"kyoo\".\"videos\".\"part\" >= 0" + }, + "version_pos": { + "name": "version_pos", + "value": "\"kyoo\".\"videos\".\"version\" >= 0" + } + }, + "isRLSEnabled": false + } + }, + "enums": { + "kyoo.entry_type": { + "name": "entry_type", + "schema": "kyoo", + "values": ["unknown", "episode", "movie", "special", "extra"] + }, + "kyoo.genres": { + "name": "genres", + "schema": "kyoo", + "values": [ + "action", + "adventure", + "animation", + "comedy", + "crime", + "documentary", + "drama", + "family", + "fantasy", + "history", + "horror", + "music", + "mystery", + "romance", + "science-fiction", + "thriller", + "war", + "western", + "kids", + "reality", + "politics", + "soap", + "talk" + ] + }, + "kyoo.show_kind": { + "name": "show_kind", + "schema": "kyoo", + "values": ["serie", "movie", "collection"] + }, + "kyoo.show_status": { + "name": "show_status", + "schema": "kyoo", + "values": ["unknown", "finished", "airing", "planned"] + } + }, + "schemas": { + "kyoo": "kyoo" + }, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/api/drizzle/meta/_journal.json b/api/drizzle/meta/_journal.json index 0c941c9c..91c1bbb1 100644 --- a/api/drizzle/meta/_journal.json +++ b/api/drizzle/meta/_journal.json @@ -78,6 +78,13 @@ "when": 1740950531468, "tag": "0010_studios", "breakpoints": true + }, + { + "idx": 11, + "version": "7", + "when": 1741014917375, + "tag": "0011_join_rename", + "breakpoints": true } ] } diff --git a/api/src/controllers/seed/insert/entries.ts b/api/src/controllers/seed/insert/entries.ts index 4ee855e5..a3100cbf 100644 --- a/api/src/controllers/seed/insert/entries.ts +++ b/api/src/controllers/seed/insert/entries.ts @@ -141,8 +141,8 @@ export const insertEntries = async ( .select( db .select({ - entry: sql`vids.entryPk::integer`.as("entry"), - video: sql`${videos.pk}`.as("video"), + entryPk: sql`vids.entryPk::integer`.as("entry"), + videoPk: sql`${videos.pk}`.as("video"), slug: computeVideoSlug( sql`${show.slug}::text`, sql`vids.needRendering::boolean`, @@ -154,7 +154,7 @@ export const insertEntries = async ( .onConflictDoNothing() .returning({ slug: entryVideoJoin.slug, - entryPk: entryVideoJoin.entry, + entryPk: entryVideoJoin.entryPk, }); return retEntries.map((entry) => ({ diff --git a/api/src/controllers/seed/insert/studios.ts b/api/src/controllers/seed/insert/studios.ts index 53ed9fbc..291801d7 100644 --- a/api/src/controllers/seed/insert/studios.ts +++ b/api/src/controllers/seed/insert/studios.ts @@ -48,7 +48,7 @@ export const insertStudios = async (seed: SeedStudio[], showPk: number) => { await tx .insert(showStudioJoin) - .values(ret.map((studio) => ({ show: showPk, studio: studio.pk }))) + .values(ret.map((studio) => ({ showPk: showPk, studioPk: studio.pk }))) .onConflictDoNothing(); return ret; }); diff --git a/api/src/controllers/shows/logic.ts b/api/src/controllers/shows/logic.ts index 75353bf9..d2f0a840 100644 --- a/api/src/controllers/shows/logic.ts +++ b/api/src/controllers/shows/logic.ts @@ -1,7 +1,7 @@ import type { StaticDecode } from "@sinclair/typebox"; import { type SQL, and, eq, sql } from "drizzle-orm"; import { db } from "~/db"; -import { showTranslations, shows } from "~/db/schema"; +import { showTranslations, shows, studioTranslations } from "~/db/schema"; import { getColumns, sqlarr } from "~/db/utils"; import type { MovieStatus } from "~/models/movie"; import { SerieStatus } from "~/models/serie"; @@ -12,6 +12,7 @@ import { Sort, isUuid, keysetPaginate, + selectTranslationQuery, sortToSql, } from "~/models/utils"; @@ -130,7 +131,7 @@ export async function getShow( }: { languages: string[]; preferOriginal: boolean | undefined; - relations: ("translations" | "videos")[]; + relations: ("translations" | "studios" | "videos")[]; filters: SQL | undefined; }, ) { @@ -141,18 +142,7 @@ export async function getShow( }, where: and(isUuid(id) ? eq(shows.id, id) : eq(shows.slug, id), filters), with: { - selectedTranslation: { - columns: { - pk: false, - }, - where: !languages.includes("*") - ? eq(showTranslations.language, sql`any(${sqlarr(languages)})`) - : undefined, - orderBy: [ - sql`array_position(${sqlarr(languages)}, ${showTranslations.language})`, - ], - limit: 1, - }, + selectedTranslation: selectTranslationQuery(showTranslations, languages), originalTranslation: { columns: { poster: true, @@ -175,6 +165,23 @@ export async function getShow( }, }, }), + ...(relations.includes("studios") && { + studios: { + with: { + studio: { + columns: { + pk: false, + }, + with: { + selectedTranslation: selectTranslationQuery( + studioTranslations, + languages, + ), + }, + }, + }, + }, + }), }, }); if (!ret) return null; @@ -198,6 +205,12 @@ export async function getShow( ), ), }), + ...(ret.studios && { + studios: ret.studios.map((x: any) => ({ + ...x.studio, + ...x.studio.selectedTranslation[0], + })), + }), }; return { show, language: translation.language }; } diff --git a/api/src/controllers/shows/movies.ts b/api/src/controllers/shows/movies.ts index 7f023f30..44e0c0f4 100644 --- a/api/src/controllers/shows/movies.ts +++ b/api/src/controllers/shows/movies.ts @@ -65,7 +65,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] }) preferOriginal: t.Optional( t.Boolean({ description: desc.preferOriginal }), ), - with: t.Array(t.UnionEnum(["translations", "videos"]), { + with: t.Array(t.UnionEnum(["translations", "studios", "videos"]), { default: [], description: "Include related resources in the response.", }), diff --git a/api/src/controllers/shows/series.ts b/api/src/controllers/shows/series.ts index 3c3a371b..f84a4d5a 100644 --- a/api/src/controllers/shows/series.ts +++ b/api/src/controllers/shows/series.ts @@ -65,7 +65,7 @@ export const series = new Elysia({ prefix: "/series", tags: ["series"] }) preferOriginal: t.Optional( t.Boolean({ description: desc.preferOriginal }), ), - with: t.Array(t.UnionEnum(["translations"]), { + with: t.Array(t.UnionEnum(["translations", "studios"]), { default: [], description: "Include related resources in the response.", }), diff --git a/api/src/controllers/studios.ts b/api/src/controllers/studios.ts index 4330708e..a9e9580c 100644 --- a/api/src/controllers/studios.ts +++ b/api/src/controllers/studios.ts @@ -22,6 +22,7 @@ import { isUuid, keysetPaginate, processLanguages, + selectTranslationQuery, sortToSql, } from "~/models/utils"; import { desc } from "~/models/utils/descriptions"; @@ -47,16 +48,10 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] }) const ret = await db.query.studios.findFirst({ where: isUuid(id) ? eq(studios.id, id) : eq(studios.slug, id), with: { - selectedTranslation: { - columns: { pk: false }, - where: !languages.includes("*") - ? eq(studioTranslations.language, sql`any(${sqlarr(langs)})`) - : undefined, - orderBy: [ - sql`array_position(${sqlarr(langs)}, ${studioTranslations.language})`, - ], - limit: 1, - }, + selectedTranslation: selectTranslationQuery( + studioTranslations, + langs, + ), ...(relations.includes("translations") && { translations: { columns: { @@ -150,7 +145,7 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] }) .get( "", async ({ - query: { limit, after, query, sort, filter }, + query: { limit, after, query, sort }, headers: { "accept-language": languages }, request: { url }, }) => { @@ -271,8 +266,8 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] }) .from(showStudioJoin) .where( and( - eq(showStudioJoin.studio, studio.pk), - eq(showStudioJoin.show, shows.pk), + eq(showStudioJoin.studioPk, studio.pk), + eq(showStudioJoin.showPk, shows.pk), ), ), ), @@ -331,8 +326,8 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] }) .from(showStudioJoin) .where( and( - eq(showStudioJoin.studio, studio.pk), - eq(showStudioJoin.show, shows.pk), + eq(showStudioJoin.studioPk, studio.pk), + eq(showStudioJoin.showPk, shows.pk), ), ), ), @@ -391,8 +386,8 @@ export const studiosH = new Elysia({ prefix: "/studios", tags: ["studios"] }) .from(showStudioJoin) .where( and( - eq(showStudioJoin.studio, studio.pk), - eq(showStudioJoin.show, shows.pk), + eq(showStudioJoin.studioPk, studio.pk), + eq(showStudioJoin.showPk, shows.pk), ), ), ), diff --git a/api/src/controllers/videos.ts b/api/src/controllers/videos.ts index 5462d3ae..6a19f23b 100644 --- a/api/src/controllers/videos.ts +++ b/api/src/controllers/videos.ts @@ -42,78 +42,77 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] }) return error(201, oldRet); // TODO: this is a huge untested wip - // biome-ignore lint/correctness/noUnreachable: leave me alone - const vidsI = db.$with("vidsI").as( - db.insert(videos).values(body).onConflictDoNothing().returning({ - pk: videos.pk, - id: videos.id, - path: videos.path, - guess: videos.guess, - }), - ); - - const findEntriesQ = db - .select({ - guess: videos.guess, - entryPk: entries.pk, - showSlug: shows.slug, - // TODO: handle extras here - // guessit can't know if an episode is a special or not. treat specials like a normal episode. - kind: sql` - case when ${entries.kind} = 'movie' then 'movie' else 'episode' end - `.as("kind"), - season: entries.seasonNumber, - episode: entries.episodeNumber, - }) - .from(entries) - .leftJoin(entryVideoJoin, eq(entryVideoJoin.entry, entries.pk)) - .leftJoin(videos, eq(videos.pk, entryVideoJoin.video)) - .leftJoin(shows, eq(shows.pk, entries.showPk)) - .as("find_entries"); - - const hasRenderingQ = db - .select() - .from(entryVideoJoin) - .where(eq(entryVideoJoin.entry, findEntriesQ.entryPk)); - - const ret = await db - .with(vidsI) - .insert(entryVideoJoin) - .select( - db - .select({ - entry: findEntriesQ.entryPk, - video: vidsI.pk, - slug: computeVideoSlug( - findEntriesQ.showSlug, - sql`exists(${hasRenderingQ})`, - ), - }) - .from(vidsI) - .leftJoin( - findEntriesQ, - and( - eq( - sql`${findEntriesQ.guess}->'title'`, - sql`${vidsI.guess}->'title'`, - ), - // TODO: find if @> with a jsonb created on the fly is - // better than multiples checks - sql`${vidsI.guess} @> {"kind": }::jsonb`, - inArray(findEntriesQ.kind, sql`${vidsI.guess}->'type'`), - inArray(findEntriesQ.episode, sql`${vidsI.guess}->'episode'`), - inArray(findEntriesQ.season, sql`${vidsI.guess}->'season'`), - ), - ), - ) - .onConflictDoNothing() - .returning({ - slug: entryVideoJoin.slug, - entryPk: entryVideoJoin.entry, - id: vidsI.id, - path: vidsI.path, - }); - return error(201, ret as any); + // const vidsI = db.$with("vidsI").as( + // db.insert(videos).values(body).onConflictDoNothing().returning({ + // pk: videos.pk, + // id: videos.id, + // path: videos.path, + // guess: videos.guess, + // }), + // ); + // + // const findEntriesQ = db + // .select({ + // guess: videos.guess, + // entryPk: entries.pk, + // showSlug: shows.slug, + // // TODO: handle extras here + // // guessit can't know if an episode is a special or not. treat specials like a normal episode. + // kind: sql` + // case when ${entries.kind} = 'movie' then 'movie' else 'episode' end + // `.as("kind"), + // season: entries.seasonNumber, + // episode: entries.episodeNumber, + // }) + // .from(entries) + // .leftJoin(entryVideoJoin, eq(entryVideoJoin.entry, entries.pk)) + // .leftJoin(videos, eq(videos.pk, entryVideoJoin.video)) + // .leftJoin(shows, eq(shows.pk, entries.showPk)) + // .as("find_entries"); + // + // const hasRenderingQ = db + // .select() + // .from(entryVideoJoin) + // .where(eq(entryVideoJoin.entry, findEntriesQ.entryPk)); + // + // const ret = await db + // .with(vidsI) + // .insert(entryVideoJoin) + // .select( + // db + // .select({ + // entry: findEntriesQ.entryPk, + // video: vidsI.pk, + // slug: computeVideoSlug( + // findEntriesQ.showSlug, + // sql`exists(${hasRenderingQ})`, + // ), + // }) + // .from(vidsI) + // .leftJoin( + // findEntriesQ, + // and( + // eq( + // sql`${findEntriesQ.guess}->'title'`, + // sql`${vidsI.guess}->'title'`, + // ), + // // TODO: find if @> with a jsonb created on the fly is + // // better than multiples checks + // sql`${vidsI.guess} @> {"kind": }::jsonb`, + // inArray(findEntriesQ.kind, sql`${vidsI.guess}->'type'`), + // inArray(findEntriesQ.episode, sql`${vidsI.guess}->'episode'`), + // inArray(findEntriesQ.season, sql`${vidsI.guess}->'season'`), + // ), + // ), + // ) + // .onConflictDoNothing() + // .returning({ + // slug: entryVideoJoin.slug, + // entryPk: entryVideoJoin.entry, + // id: vidsI.id, + // path: vidsI.path, + // }); + // return error(201, ret as any); }, { body: t.Array(SeedVideo), diff --git a/api/src/db/schema/shows.ts b/api/src/db/schema/shows.ts index 872f7af1..5b7459ed 100644 --- a/api/src/db/schema/shows.ts +++ b/api/src/db/schema/shows.ts @@ -5,7 +5,6 @@ import { date, index, integer, - jsonb, primaryKey, smallint, text, @@ -15,7 +14,8 @@ import { } from "drizzle-orm/pg-core"; import { entries } from "./entries"; import { seasons } from "./seasons"; -import { image, language, schema } from "./utils"; +import { showStudioJoin } from "./studios"; +import { externalid, image, language, schema } from "./utils"; export const showKind = schema.enum("show_kind", [ "serie", @@ -54,20 +54,6 @@ export const genres = schema.enum("genres", [ "talk", ]); -export const externalid = () => - jsonb() - .$type< - Record< - string, - { - dataId: string; - link: string | null; - } - > - >() - .notNull() - .default({}); - export const shows = schema.table( "shows", { @@ -144,6 +130,7 @@ export const showsRelations = relations(shows, ({ many, one }) => ({ }), entries: many(entries, { relationName: "show_entries" }), seasons: many(seasons, { relationName: "show_seasons" }), + studios: many(showStudioJoin, { relationName: "ssj_show" }), })); export const showsTrRelations = relations(showTranslations, ({ one }) => ({ show: one(shows, { diff --git a/api/src/db/schema/studios.ts b/api/src/db/schema/studios.ts index 70549b8d..1c772ee6 100644 --- a/api/src/db/schema/studios.ts +++ b/api/src/db/schema/studios.ts @@ -8,8 +8,8 @@ import { uuid, varchar, } from "drizzle-orm/pg-core"; -import { externalid, shows } from "./shows"; -import { image, language, schema } from "./utils"; +import { shows } from "./shows"; +import { externalid, image, language, schema } from "./utils"; export const studios = schema.table("studios", { pk: integer().primaryKey().generatedAlwaysAsIdentity(), @@ -44,14 +44,14 @@ export const studioTranslations = schema.table( export const showStudioJoin = schema.table( "show_studio_join", { - show: integer() + showPk: integer() .notNull() .references(() => shows.pk, { onDelete: "cascade" }), - studio: integer() + studioPk: integer() .notNull() .references(() => studios.pk, { onDelete: "cascade" }), }, - (t) => [primaryKey({ columns: [t.show, t.studio] })], + (t) => [primaryKey({ columns: [t.showPk, t.studioPk] })], ); export const studioRelations = relations(studios, ({ many }) => ({ @@ -61,7 +61,7 @@ export const studioRelations = relations(studios, ({ many }) => ({ selectedTranslation: many(studioTranslations, { relationName: "studio_selected_translation", }), - showsJoin: many(showStudioJoin, { relationName: "show_studios" }), + showsJoin: many(showStudioJoin, { relationName: "ssj_studio" }), })); export const studioTrRelations = relations(studioTranslations, ({ one }) => ({ studio: one(studios, { @@ -78,12 +78,12 @@ export const studioTrRelations = relations(studioTranslations, ({ one }) => ({ export const ssjRelations = relations(showStudioJoin, ({ one }) => ({ show: one(shows, { relationName: "ssj_show", - fields: [showStudioJoin.show], + fields: [showStudioJoin.showPk], references: [shows.pk], }), studio: one(studios, { relationName: "ssj_studio", - fields: [showStudioJoin.studio], + fields: [showStudioJoin.studioPk], references: [studios.pk], }), })); diff --git a/api/src/db/schema/utils.ts b/api/src/db/schema/utils.ts index 24e49251..de882ff1 100644 --- a/api/src/db/schema/utils.ts +++ b/api/src/db/schema/utils.ts @@ -6,3 +6,18 @@ export const language = () => varchar({ length: 255 }); export const image = () => jsonb().$type<{ id: string; source: string; blurhash: string }>(); + +export const externalid = () => + jsonb() + .$type< + Record< + string, + { + dataId: string; + link: string | null; + } + > + >() + .notNull() + .default({}); + diff --git a/api/src/db/schema/videos.ts b/api/src/db/schema/videos.ts index 66d5291c..877f3c9d 100644 --- a/api/src/db/schema/videos.ts +++ b/api/src/db/schema/videos.ts @@ -39,15 +39,15 @@ export const videos = schema.table( export const entryVideoJoin = schema.table( "entry_video_join", { - entry: integer() + entryPk: integer() .notNull() .references(() => entries.pk, { onDelete: "cascade" }), - video: integer() + videoPk: integer() .notNull() .references(() => videos.pk, { onDelete: "cascade" }), slug: varchar({ length: 255 }).notNull().unique(), }, - (t) => [primaryKey({ columns: [t.entry, t.video] })], + (t) => [primaryKey({ columns: [t.entryPk, t.videoPk] })], ); export const videosRelations = relations(videos, ({ many }) => ({ @@ -59,12 +59,12 @@ export const videosRelations = relations(videos, ({ many }) => ({ export const evjRelations = relations(entryVideoJoin, ({ one }) => ({ video: one(videos, { relationName: "evj_video", - fields: [entryVideoJoin.video], + fields: [entryVideoJoin.videoPk], references: [videos.pk], }), entry: one(entries, { relationName: "evj_entry", - fields: [entryVideoJoin.entry], + fields: [entryVideoJoin.entryPk], references: [entries.pk], }), })); diff --git a/api/src/models/movie.ts b/api/src/models/movie.ts index a94528cb..4f3f7089 100644 --- a/api/src/models/movie.ts +++ b/api/src/models/movie.ts @@ -2,7 +2,7 @@ import { t } from "elysia"; import type { Prettify } from "~/utils"; import { SeedCollection } from "./collections"; import { bubble, bubbleImages, registerExamples } from "./examples"; -import { SeedStudio } from "./studio"; +import { SeedStudio, Studio } from "./studio"; import { DbMetadata, ExternalId, @@ -68,6 +68,7 @@ export const FullMovie = t.Intersect([ t.Object({ translations: t.Optional(TranslationRecord(MovieTranslation)), videos: t.Optional(t.Array(Video)), + studios: t.Optional(t.Array(Studio)), }), ]); export type FullMovie = Prettify; diff --git a/api/src/models/serie.ts b/api/src/models/serie.ts index c83d8433..a20c8795 100644 --- a/api/src/models/serie.ts +++ b/api/src/models/serie.ts @@ -4,7 +4,7 @@ import { SeedCollection } from "./collections"; import { SeedEntry, SeedExtra } from "./entry"; import { bubbleImages, madeInAbyss, registerExamples } from "./examples"; import { SeedSeason } from "./season"; -import { SeedStudio } from "./studio"; +import { SeedStudio, Studio } from "./studio"; import { DbMetadata, ExternalId, @@ -76,6 +76,7 @@ export const FullSerie = t.Intersect([ Serie, t.Object({ translations: t.Optional(TranslationRecord(SerieTranslation)), + studios: t.Optional(t.Array(Studio)), }), ]); export type FullMovie = Prettify; diff --git a/api/src/models/utils/language.ts b/api/src/models/utils/language.ts index 76f141e3..b1d0bcf3 100644 --- a/api/src/models/utils/language.ts +++ b/api/src/models/utils/language.ts @@ -4,7 +4,9 @@ import { type TSchema, type TString, } from "@sinclair/typebox"; +import { type Column, type Table, eq, sql } from "drizzle-orm"; import { t } from "elysia"; +import { sqlarr } from "~/db/utils"; import { comment } from "../../utils"; import { KErrorT } from "../error"; @@ -106,3 +108,19 @@ export const AcceptLanguage = ({ ` : ""), }); + +export const selectTranslationQuery = ( + translationTable: Table & { language: Column }, + languages: string[], +) => ({ + columns: { + pk: false, + } as const, + where: !languages.includes("*") + ? eq(translationTable.language, sql`any(${sqlarr(languages)})`) + : undefined, + orderBy: [ + sql`array_position(${sqlarr(languages)}, ${translationTable.language})`, + ], + limit: 1, +});