Sync layouts, typography, and watermark studio for self-hosted OSS.

Unlock features without credits/Stripe, point docs to docs.songs2vid.com, and drop leftover billing admin surfaces.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Songs2YT
2026-07-25 03:50:06 +02:00
co-authored by Cursor
parent bcca0a6e6f
commit d951ecb489
53 changed files with 3258 additions and 1977 deletions
+43 -34
View File
@@ -57,36 +57,16 @@ model User {
subscribedAt DateTime?
bonusQuota Int @default(0)
apiRateLimitBonus Int @default(0)
/** Prepaid pay-as-you-go video credits (1 credit = 1 video). Cap: 1000. */
/// Legacy column retained for schema compatibility; unused in OSS (always 0).
videoCredits Int @default(0)
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
@@ -127,25 +107,54 @@ model Job {
}
model JobItem {
id String @id @default(cuid())
id String @id @default(cuid())
jobId String
job Job @relation(fields: [jobId], references: [id], onDelete: Cascade)
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(true)
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(true)
/// Optional per-item cover. Falls back to Job.imagePath when null.
itemImagePath String?
/// none | default | text | logo
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
artist String?
/// COVER_LEFT_TEXT_RIGHT | COVER_TOP_TEXT_BOTTOM | COVER_RIGHT_TEXT_LEFT | CENTERED_COMPACT | null=classic
layoutTemplate String?
/// 0100 Gaussian / boxblur intensity
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)
status JobItemStatus @default(PENDING)
outputPath String?
youtubeVideoId String?
error String?