Align preview typography with FFmpeg output, add video/song title split, and ship OSS updates.

Separate YouTube video titles from on-video song/artist fields with Pro gating, serve curated fonts and watermark assets for 1:1 preview parity, and include billing/API/docs/deploy stack for self-hosted release.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Atakan Doğan Özban
2026-07-27 16:26:19 +02:00
co-authored by Cursor
parent 682020ff5a
commit c8015937f9
206 changed files with 37078 additions and 638 deletions
+108 -10
View File
@@ -34,17 +34,82 @@ enum JobItemStatus {
FAILED
}
enum QuotaExtensionRequestStatus {
PENDING
APPROVED
REJECTED
}
enum QuotaExtensionKind {
VIDEO_QUOTA
API_RATE_LIMIT
}
model User {
id String @id @default(cuid())
email String @unique
name String?
image String?
plan Plan @default(FREE)
videosUsed Int @default(0)
quotaResetAt DateTime
youtubeConnection YouTubeConnection?
jobs Job[]
createdAt DateTime @default(now())
id String @id @default(cuid())
email String @unique
name String?
image String?
/// FREE = free tier; PREMIUM = Pro (€5/mo, formerly starter_5eur)
plan Plan @default(FREE)
/// Credits consumed in the current billing cycle (credits_used)
videosUsed Int @default(0)
/// Monthly allocation for the current cycle (10 free / 50 pro)
monthlyCredits Int @default(10)
quotaResetAt DateTime
cardLast4 String?
subscribedAt DateTime?
bonusQuota Int @default(0)
apiRateLimitBonus Int @default(0)
/// Purchased extras that never expire (maps to legacy "videoCredits" column)
extraCredits Int @default(0) @map("videoCredits")
/// Free-tier one-time +15 top-up already purchased
freeTopUpPurchased Boolean @default(false)
stripeCustomerId String?
stripeSubscriptionId String?
apiKeyHash String? @unique
apiKeyPrefix String?
youtubeConnection YouTubeConnection?
jobs Job[]
quotaExtensionRequests QuotaExtensionRequest[]
creditPurchases CreditPurchase[]
createdAt DateTime @default(now())
}
enum CreditPurchaseStatus {
PENDING
COMPLETED
FAILED
}
model CreditPurchase {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
credits Int
amountCents Int
status CreditPurchaseStatus @default(PENDING)
stripeSessionId String? @unique
stripePaymentIntentId String?
createdAt DateTime @default(now())
completedAt DateTime?
}
enum JobItemBilling {
QUOTA
CREDIT
}
model QuotaExtensionRequest {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
kind QuotaExtensionKind @default(VIDEO_QUOTA)
status QuotaExtensionRequestStatus @default(PENDING)
message String @default("")
requestedAt DateTime @default(now())
processedAt DateTime?
adminNote String?
}
model YouTubeConnection {
@@ -86,6 +151,39 @@ model JobItem {
embeddable Boolean @default(true)
creativeCommons Boolean @default(false)
includeWatermark Boolean @default(true)
/// Optional per-item cover (Pro). Falls back to Job.imagePath when null.
itemImagePath String?
/// none | default | text | logo custom modes require Pro
watermarkMode String @default("default")
watermarkText String?
watermarkLogoPath String?
/// curated key | custom | system
watermarkFontKey String?
/// uploaded .ttf/.otf when fontKey=custom
watermarkFontPath String?
/// top-left | top-right | bottom-left | bottom-right | center
watermarkPosition String @default("bottom-right")
watermarkOffsetX Int @default(20)
watermarkOffsetY Int @default(20)
/// Optional artist line for art-track layouts (Pro, on-video only)
artist String?
/// Song title burned into art-track layouts (Pro, on-video only)
songTitle String?
/// COVER_LEFT_TEXT_RIGHT | COVER_TOP_TEXT_BOTTOM | COVER_RIGHT_TEXT_LEFT | CENTERED_COMPACT | null=classic
layoutTemplate String?
/// 0100 Gaussian / boxblur intensity (Pro art-track)
blurAmount Int @default(55)
/// 0100 visibility of blurred fill vs black
blurOpacity Int @default(100)
/// Padding around cover + text in art-track layouts
textPadding Int @default(48)
/// Extra gap between title and artist lines (px)
titleArtistGap Int @default(10)
/// Fine-tune text block within template (-120..120)
textOffsetX Int @default(0)
textOffsetY Int @default(0)
playlistId String?
billingSource JobItemBilling @default(QUOTA)
status JobItemStatus @default(PENDING)
outputPath String?
youtubeVideoId String?