118 lines
2.9 KiB
TypeScript
118 lines
2.9 KiB
TypeScript
import {
|
|
index,
|
|
bigint,
|
|
integer,
|
|
jsonb,
|
|
pgTable,
|
|
text,
|
|
timestamp,
|
|
} from "drizzle-orm/pg-core";
|
|
import { uuidv7 } from "uuidv7";
|
|
|
|
export type VideoUploadStatus =
|
|
| "draft"
|
|
| "scheduled"
|
|
| "processing"
|
|
| "completed"
|
|
| "partial"
|
|
| "failed";
|
|
export type VideoPlatform = "youtube" | "tiktok";
|
|
export type VideoJobStatus = "pending" | "processing" | "completed" | "failed";
|
|
|
|
export const videoUploads = pgTable(
|
|
"video_upload",
|
|
{
|
|
id: text("id")
|
|
.primaryKey()
|
|
.$defaultFn(() => uuidv7()),
|
|
title: text("title").notNull(),
|
|
description: text("description").notNull().default(""),
|
|
videoPath: text("video_path").notNull(),
|
|
thumbnailPath: text("thumbnail_path"),
|
|
originalFilename: text("original_filename").notNull(),
|
|
mimeType: text("mime_type").notNull(),
|
|
size: bigint("size", { mode: "number" }).notNull(),
|
|
scheduledAt: timestamp("scheduled_at", {
|
|
mode: "date",
|
|
withTimezone: true,
|
|
}),
|
|
status: text("status")
|
|
.$type<VideoUploadStatus>()
|
|
.notNull()
|
|
.default("draft"),
|
|
youtubeSettings: jsonb("youtube_settings")
|
|
.$type<YoutubeSettings>()
|
|
.notNull()
|
|
.default({}),
|
|
tiktokSettings: jsonb("tiktok_settings")
|
|
.$type<TiktokSettings>()
|
|
.notNull()
|
|
.default({}),
|
|
createdByDiscordId: text("created_by_discord_id").notNull(),
|
|
createdAt: timestamp("created_at", { mode: "date", withTimezone: true })
|
|
.defaultNow()
|
|
.notNull(),
|
|
updatedAt: timestamp("updated_at", { mode: "date", withTimezone: true })
|
|
.defaultNow()
|
|
.notNull(),
|
|
},
|
|
(table) => [
|
|
index("video_upload_status_scheduled_idx").on(
|
|
table.status,
|
|
table.scheduledAt,
|
|
),
|
|
],
|
|
);
|
|
|
|
export const videoUploadJobs = pgTable(
|
|
"video_upload_job",
|
|
{
|
|
id: text("id")
|
|
.primaryKey()
|
|
.$defaultFn(() => uuidv7()),
|
|
uploadId: text("upload_id")
|
|
.notNull()
|
|
.references(() => videoUploads.id, { onDelete: "cascade" }),
|
|
platform: text("platform").$type<VideoPlatform>().notNull(),
|
|
status: text("status").$type<VideoJobStatus>().notNull().default("pending"),
|
|
externalId: text("external_id"),
|
|
externalUrl: text("external_url"),
|
|
error: text("error"),
|
|
attempts: integer("attempts").notNull().default(0),
|
|
startedAt: timestamp("started_at", { mode: "date", withTimezone: true }),
|
|
completedAt: timestamp("completed_at", {
|
|
mode: "date",
|
|
withTimezone: true,
|
|
}),
|
|
createdAt: timestamp("created_at", { mode: "date", withTimezone: true })
|
|
.defaultNow()
|
|
.notNull(),
|
|
},
|
|
(table) => [
|
|
index("video_upload_job_upload_platform_idx").on(
|
|
table.uploadId,
|
|
table.platform,
|
|
),
|
|
],
|
|
);
|
|
|
|
export type YoutubeSettings = {
|
|
tags?: string[];
|
|
categoryId?: string;
|
|
privacyStatus?: "public" | "private" | "unlisted";
|
|
license?: "youtube" | "creativeCommon";
|
|
madeForKids?: boolean;
|
|
containsSyntheticMedia?: boolean;
|
|
};
|
|
|
|
export type TiktokSettings = {
|
|
privacyLevel?: string;
|
|
disableComment?: boolean;
|
|
disableDuet?: boolean;
|
|
disableStitch?: boolean;
|
|
coverTimestampMs?: number;
|
|
brandContentToggle?: boolean;
|
|
brandOrganicToggle?: boolean;
|
|
isAigc?: boolean;
|
|
};
|