generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum Privacy { PUBLIC PRIVATE UNLISTED } enum JobStatus { PENDING PROCESSING COMPLETED FAILED PARTIAL } enum JobItemStatus { PENDING ENCODING UPLOADING COMPLETED FAILED } model User { id String @id @default(cuid()) email String @unique name String? image String? createdVideoCount Int @default(0) apiKeyHash String? @unique apiKeyPrefix String? youtubeConnection YouTubeConnection? jobs Job[] createdAt DateTime @default(now()) } model YouTubeConnection { id String @id @default(cuid()) userId String @unique user User @relation(fields: [userId], references: [id], onDelete: Cascade) accessToken String refreshToken String expiresAt DateTime channelId String channelTitle String } model Job { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) status JobStatus @default(PENDING) imagePath String /// Optional absolute http(s) URL for n8n / automation callbacks webhookUrl String? items JobItem[] createdAt DateTime @default(now()) completedAt DateTime? } model JobItem { id String @id @default(cuid()) jobId String job Job @relation(fields: [jobId], references: [id], onDelete: Cascade) audioPath String audioFilename String title String description String @default("") tags String @default("") privacy Privacy @default(PUBLIC) categoryId String @default("10") resolution String @default("1280x720") notifySubscribers Boolean @default(true) madeForKids Boolean @default(false) embeddable Boolean @default(true) creativeCommons Boolean @default(false) includeWatermark Boolean @default(false) itemImagePath String? /// Optional full-frame background for lower-corner layouts (blur fill). Cover stays foreground. backgroundImagePath String? watermarkMode String @default("none") watermarkText String? watermarkLogoPath String? watermarkFontKey String? watermarkFontPath String? watermarkPosition String @default("bottom-right") watermarkOffsetX Int @default(20) watermarkOffsetY Int @default(20) artist String? songTitle String? layoutTemplate String? /// Classic letterbox: fill black bars with blurred cover blurFill Boolean @default(false) blurAmount Int @default(55) blurOpacity Int @default(100) textPadding Int @default(48) titleArtistGap Int @default(10) titleBold Boolean @default(true) textOffsetX Int @default(0) textOffsetY Int @default(0) playlistId String? status JobItemStatus @default(PENDING) outputPath String? youtubeVideoId String? error String? }