feat: implement admin video upload system with platform integration for YouTube and TikTok
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
export * from "./schema/account";
|
||||
export * from "./schema/form";
|
||||
export * from "./schema/video";
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
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;
|
||||
};
|
||||
Reference in New Issue
Block a user