generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" url = env("DATABASE_URL") } model Account { id String @id @default(uuid()) userId String type String provider String providerAccountId String refresh_token String? @db.Text access_token String? @db.Text expires_at Int? token_type String? scope String? id_token String? @db.Text session_state String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) @@index([userId]) @@map("account") } model CharacterAppearance { id String @id @default(uuid()) characterId String appearanceIndex Int changeReason String description String? @db.Text descriptions String? @db.Text imageUrl String? @db.Text imageUrls String? @db.Text selectedIndex Int? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt previousImageUrl String? @db.Text previousImageUrls String? @db.Text previousDescription String? @db.Text // 上一次的描述词(用于撤回) previousDescriptions String? @db.Text // 上一次的描述词数组(用于撤回) imageMediaId String? imageMedia MediaObject? @relation("CharacterAppearanceImageMedia", fields: [imageMediaId], references: [id], onDelete: SetNull) character NovelPromotionCharacter @relation(fields: [characterId], references: [id], onDelete: Cascade) @@unique([characterId, appearanceIndex]) @@index([characterId]) @@index([imageMediaId]) @@map("character_appearances") } model LocationImage { id String @id @default(uuid()) locationId String imageIndex Int description String? @db.Text imageUrl String? @db.Text isSelected Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt previousImageUrl String? @db.Text previousDescription String? @db.Text // 上一次的描述词(用于撤回) imageMediaId String? imageMedia MediaObject? @relation("LocationImageMedia", fields: [imageMediaId], references: [id], onDelete: SetNull) location NovelPromotionLocation @relation("LocationImages", fields: [locationId], references: [id], onDelete: Cascade) selectedByLocations NovelPromotionLocation[] @relation("SelectedLocationImage") @@unique([locationId, imageIndex]) @@index([locationId]) @@index([imageMediaId]) @@map("location_images") } model NovelPromotionCharacter { id String @id @default(uuid()) novelPromotionProjectId String name String aliases String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt customVoiceUrl String? @db.Text customVoiceMediaId String? customVoiceMedia MediaObject? @relation("NovelPromotionCharacterVoiceMedia", fields: [customVoiceMediaId], references: [id], onDelete: SetNull) voiceId String? voiceType String? profileData String? @db.Text profileConfirmed Boolean @default(false) introduction String? @db.Text // 角色介绍(身份、关系、称呼映射,如"我"对应此角色) sourceGlobalCharacterId String? // 🆕 来源全局角色ID(复制时记录) appearances CharacterAppearance[] novelPromotionProject NovelPromotionProject @relation(fields: [novelPromotionProjectId], references: [id], onDelete: Cascade) @@index([novelPromotionProjectId]) @@index([customVoiceMediaId]) @@map("novel_promotion_characters") } model NovelPromotionLocation { id String @id @default(uuid()) novelPromotionProjectId String name String summary String? @db.Text // 场景简要描述(用途/人物关联) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt sourceGlobalLocationId String? // 🆕 来源全局场景ID(复制时记录) selectedImageId String? selectedImage LocationImage? @relation("SelectedLocationImage", fields: [selectedImageId], references: [id], onDelete: SetNull) images LocationImage[] @relation("LocationImages") novelPromotionProject NovelPromotionProject @relation(fields: [novelPromotionProjectId], references: [id], onDelete: Cascade) @@index([novelPromotionProjectId]) @@map("novel_promotion_locations") } model NovelPromotionEpisode { id String @id @default(uuid()) novelPromotionProjectId String episodeNumber Int name String description String? @db.Text novelText String? @db.Text audioUrl String? @db.Text audioMediaId String? audioMedia MediaObject? @relation("NovelPromotionEpisodeAudioMedia", fields: [audioMediaId], references: [id], onDelete: SetNull) srtContent String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt speakerVoices String? @db.Text clips NovelPromotionClip[] novelPromotionProject NovelPromotionProject @relation(fields: [novelPromotionProjectId], references: [id], onDelete: Cascade) shots NovelPromotionShot[] storyboards NovelPromotionStoryboard[] voiceLines NovelPromotionVoiceLine[] editorProject VideoEditorProject? @@unique([novelPromotionProjectId, episodeNumber]) @@index([novelPromotionProjectId]) @@index([audioMediaId]) @@map("novel_promotion_episodes") } // 视频编辑器项目 - 存储剪辑数据 model VideoEditorProject { id String @id @default(uuid()) episodeId String @unique projectData String @db.Text // JSON 存储编辑项目数据 renderStatus String? // pending | rendering | completed | failed renderTaskId String? outputUrl String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt episode NovelPromotionEpisode @relation(fields: [episodeId], references: [id], onDelete: Cascade) @@map("video_editor_projects") } model NovelPromotionClip { id String @id @default(uuid()) episodeId String start Int? end Int? duration Int? summary String @db.Text location String? @db.Text content String @db.Text createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt characters String? @db.Text endText String? @db.Text shotCount Int? startText String? @db.Text screenplay String? @db.Text episode NovelPromotionEpisode @relation(fields: [episodeId], references: [id], onDelete: Cascade) shots NovelPromotionShot[] storyboard NovelPromotionStoryboard? @@index([episodeId]) @@map("novel_promotion_clips") } model NovelPromotionPanel { id String @id @default(uuid()) storyboardId String panelIndex Int panelNumber Int? shotType String? @db.Text cameraMove String? @db.Text description String? @db.Text location String? @db.Text characters String? @db.Text srtSegment String? @db.Text srtStart Float? srtEnd Float? duration Float? imagePrompt String? @db.Text imageUrl String? @db.Text imageMediaId String? imageMedia MediaObject? @relation("NovelPromotionPanelImageMedia", fields: [imageMediaId], references: [id], onDelete: SetNull) imageHistory String? @db.Text videoPrompt String? @db.Text firstLastFramePrompt String? @db.Text videoUrl String? @db.Text videoGenerationMode String? @db.Text // 视频生成方式:normal | firstlastframe videoMediaId String? videoMedia MediaObject? @relation("NovelPromotionPanelVideoMedia", fields: [videoMediaId], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt sceneType String? candidateImages String? @db.Text linkedToNextPanel Boolean @default(false) lipSyncTaskId String? lipSyncVideoUrl String? lipSyncVideoMediaId String? lipSyncVideoMedia MediaObject? @relation("NovelPromotionPanelLipSyncVideoMedia", fields: [lipSyncVideoMediaId], references: [id], onDelete: SetNull) sketchImageUrl String? @db.Text sketchImageMediaId String? sketchImageMedia MediaObject? @relation("NovelPromotionPanelSketchMedia", fields: [sketchImageMediaId], references: [id], onDelete: SetNull) photographyRules String? @db.Text actingNotes String? @db.Text // 演技指导数据 JSON previousImageUrl String? @db.Text previousImageMediaId String? previousImageMedia MediaObject? @relation("NovelPromotionPanelPreviousImageMedia", fields: [previousImageMediaId], references: [id], onDelete: SetNull) storyboard NovelPromotionStoryboard @relation(fields: [storyboardId], references: [id], onDelete: Cascade) matchedVoiceLines NovelPromotionVoiceLine[] @@unique([storyboardId, panelIndex]) @@index([storyboardId]) @@index([imageMediaId]) @@index([videoMediaId]) @@index([lipSyncVideoMediaId]) @@index([sketchImageMediaId]) @@index([previousImageMediaId]) @@map("novel_promotion_panels") } model NovelPromotionProject { id String @id @default(uuid()) projectId String @unique createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt analysisModel String? // 用户配置的分析模型(nullable,必须配置后才能使用) imageModel String? // 用户配置的图片模型 videoModel String? // 用户配置的视频模型 videoRatio String @default("9:16") ttsRate String @default("+50%") globalAssetText String? @db.Text artStyle String @default("american-comic") artStylePrompt String? @db.Text characterModel String? // 用户配置的角色图片模型 locationModel String? // 用户配置的场景图片模型 storyboardModel String? // 用户配置的分镜图片模型 editModel String? // 用户配置的修图/编辑模型 videoResolution String @default("720p") capabilityOverrides String? @db.Text workflowMode String @default("srt") lastEpisodeId String? imageResolution String @default("2K") importStatus String? characters NovelPromotionCharacter[] episodes NovelPromotionEpisode[] locations NovelPromotionLocation[] project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) @@map("novel_promotion_projects") } model NovelPromotionShot { id String @id @default(uuid()) episodeId String clipId String? shotId String srtStart Int srtEnd Int srtDuration Float sequence String? @db.Text locations String? @db.Text characters String? @db.Text plot String? @db.Text imagePrompt String? @db.Text scale String? @db.Text module String? @db.Text focus String? @db.Text zhSummarize String? @db.Text imageUrl String? @db.Text imageMediaId String? imageMedia MediaObject? @relation("NovelPromotionShotImageMedia", fields: [imageMediaId], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt pov String? @db.Text clip NovelPromotionClip? @relation(fields: [clipId], references: [id], onDelete: Cascade) episode NovelPromotionEpisode @relation(fields: [episodeId], references: [id], onDelete: Cascade) @@index([clipId]) @@index([episodeId]) @@index([shotId]) @@index([imageMediaId]) @@map("novel_promotion_shots") } model NovelPromotionStoryboard { id String @id @default(uuid()) episodeId String clipId String @unique storyboardImageUrl String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt panelCount Int @default(9) storyboardTextJson String? @db.Text imageHistory String? @db.Text candidateImages String? @db.Text lastError String? photographyPlan String? @db.Text panels NovelPromotionPanel[] clip NovelPromotionClip @relation(fields: [clipId], references: [id], onDelete: Cascade) episode NovelPromotionEpisode @relation(fields: [episodeId], references: [id], onDelete: Cascade) supplementaryPanels SupplementaryPanel[] @@index([clipId]) @@index([episodeId]) @@map("novel_promotion_storyboards") } model SupplementaryPanel { id String @id @default(uuid()) storyboardId String sourceType String sourcePanelId String? description String? @db.Text imagePrompt String? @db.Text imageUrl String? @db.Text imageMediaId String? imageMedia MediaObject? @relation("SupplementaryPanelImageMedia", fields: [imageMediaId], references: [id], onDelete: SetNull) characters String? @db.Text location String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt storyboard NovelPromotionStoryboard @relation(fields: [storyboardId], references: [id], onDelete: Cascade) @@index([storyboardId]) @@index([imageMediaId]) @@map("supplementary_panels") } model Project { id String @id @default(uuid()) name String description String? @db.Text mode String @default("novel-promotion") userId String createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt lastAccessedAt DateTime? novelPromotionData NovelPromotionProject? user User @relation(fields: [userId], references: [id], onDelete: Cascade) usageCosts UsageCost[] @@index([userId]) @@map("projects") } model Session { id String @id @default(uuid()) sessionToken String @unique(map: "Session_sessionToken_key") userId String expires DateTime user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@map("session") } model UsageCost { id String @id @default(uuid()) projectId String userId String apiType String model String action String quantity Int unit String cost Decimal @db.Decimal(18, 6) metadata String? @db.Text createdAt DateTime @default(now()) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([apiType]) @@index([createdAt]) @@index([projectId]) @@index([userId]) @@map("usage_costs") } model User { id String @id @default(uuid()) name String @unique(map: "User_name_key") email String? emailVerified DateTime? image String? password String? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt accounts Account[] projects Project[] sessions Session[] usageCosts UsageCost[] balance UserBalance? preferences UserPreference? // 资产中心 globalAssetFolders GlobalAssetFolder[] globalCharacters GlobalCharacter[] globalLocations GlobalLocation[] globalVoices GlobalVoice[] tasks Task[] taskEvents TaskEvent[] @@map("user") } model UserPreference { id String @id @default(uuid()) userId String @unique analysisModel String? // 用户配置的分析模型(nullable,必须配置后才能使用) characterModel String? // 用户配置的角色图片模型 locationModel String? // 用户配置的场景图片模型 storyboardModel String? // 用户配置的分镜图片模型 editModel String? // 用户配置的修图模型 videoModel String? // 用户配置的视频模型 lipSyncModel String? // 用户配置的口型同步模型 videoRatio String @default("9:16") videoResolution String @default("720p") artStyle String @default("american-comic") ttsRate String @default("+50%") createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt imageResolution String @default("2K") capabilityDefaults String? @db.Text // API Key 配置(极简版) llmBaseUrl String? @default("https://openrouter.ai/api/v1") llmApiKey String? @db.Text // 加密存储 falApiKey String? @db.Text // FAL(图片+视频+语音) googleAiKey String? @db.Text // Google AI(Gemini 图片) arkApiKey String? @db.Text // 火山引擎(Seedream+Seedance) qwenApiKey String? @db.Text // 阿里百炼(声音设计) // 自定义模型列表 + 价格(JSON) customModels String? @db.Text // 自定义 OpenAI 兼容提供商列表(JSON,包含加密的 API Key) customProviders String? @db.Text user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("user_preferences") } model VerificationToken { identifier String token String @unique(map: "VerificationToken_token_key") expires DateTime @@unique([identifier, token]) @@map("verificationtoken") } model NovelPromotionVoiceLine { id String @id @default(uuid()) episodeId String lineIndex Int speaker String content String @db.Text voicePresetId String? audioUrl String? @db.Text audioMediaId String? audioMedia MediaObject? @relation("NovelPromotionVoiceLineAudioMedia", fields: [audioMediaId], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt emotionPrompt String? @db.Text emotionStrength Float? @default(0.4) matchedPanelIndex Int? matchedStoryboardId String? audioDuration Int? matchedPanelId String? episode NovelPromotionEpisode @relation(fields: [episodeId], references: [id], onDelete: Cascade) matchedPanel NovelPromotionPanel? @relation(fields: [matchedPanelId], references: [id]) @@unique([episodeId, lineIndex]) @@index([episodeId]) @@index([matchedPanelId]) @@index([audioMediaId]) @@map("novel_promotion_voice_lines") } model VoicePreset { id String @id @default(uuid()) name String audioUrl String @db.Text audioMediaId String? audioMedia MediaObject? @relation("VoicePresetAudioMedia", fields: [audioMediaId], references: [id], onDelete: SetNull) description String? @db.Text gender String? isSystem Boolean @default(true) createdAt DateTime @default(now()) @@index([audioMediaId]) @@map("voice_presets") } model UserBalance { id String @id @default(uuid()) userId String @unique balance Decimal @default(0) @db.Decimal(18, 6) frozenAmount Decimal @default(0) @db.Decimal(18, 6) totalSpent Decimal @default(0) @db.Decimal(18, 6) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("user_balances") } model BalanceFreeze { id String @id @default(uuid()) userId String amount Decimal @db.Decimal(18, 6) status String @default("pending") source String? @db.VarChar(64) taskId String? requestId String? idempotencyKey String? @unique metadata String? @db.Text expiresAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@index([userId]) @@index([status]) @@index([taskId]) @@map("balance_freezes") } model BalanceTransaction { id String @id @default(uuid()) userId String type String amount Decimal @db.Decimal(18, 6) balanceAfter Decimal @db.Decimal(18, 6) description String? @db.Text relatedId String? freezeId String? operatorId String? @db.VarChar(64) externalOrderId String? @db.VarChar(128) idempotencyKey String? @db.VarChar(128) projectId String? @db.VarChar(128) // 关联项目 ID,用于流水展示项目名 episodeId String? @db.VarChar(128) // 关联集数 ID,用于流水展示集数 taskType String? @db.VarChar(64) // 任务类型 key(与 action 一致),用于前端 i18n billingMeta String? @db.Text // 计费详情 JSON: { quantity, unit, model, resolution, duration, tokens... } createdAt DateTime @default(now()) @@index([userId]) @@index([type]) @@index([createdAt]) @@index([freezeId]) @@index([externalOrderId]) @@index([projectId]) @@unique([userId, type, idempotencyKey]) @@map("balance_transactions") } model Task { id String @id @default(uuid()) userId String projectId String episodeId String? type String targetType String targetId String status String @default("queued") progress Int @default(0) attempt Int @default(0) maxAttempts Int @default(5) priority Int @default(0) dedupeKey String? @unique externalId String? payload Json? result Json? errorCode String? errorMessage String? @db.Text billingInfo Json? billedAt DateTime? queuedAt DateTime @default(now()) startedAt DateTime? finishedAt DateTime? heartbeatAt DateTime? enqueuedAt DateTime? enqueueAttempts Int @default(0) lastEnqueueError String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) events TaskEvent[] @@index([status]) @@index([type]) @@index([targetType, targetId]) @@index([projectId]) @@index([userId]) @@index([heartbeatAt]) @@map("tasks") } model TaskEvent { id Int @id @default(autoincrement()) taskId String projectId String userId String eventType String payload Json? createdAt DateTime @default(now()) task Task @relation(fields: [taskId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([projectId, id]) @@index([taskId]) @@index([userId]) @@map("task_events") } // ==================== 资产中心 ==================== // 资产文件夹(一层,不支持嵌套) model GlobalAssetFolder { id String @id @default(uuid()) userId String name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) characters GlobalCharacter[] locations GlobalLocation[] voices GlobalVoice[] @@index([userId]) @@map("global_asset_folders") } // 全局角色(结构与 NovelPromotionCharacter 一致) model GlobalCharacter { id String @id @default(uuid()) userId String folderId String? name String aliases String? @db.Text profileData String? @db.Text profileConfirmed Boolean @default(false) voiceId String? voiceType String? customVoiceUrl String? @db.Text customVoiceMediaId String? customVoiceMedia MediaObject? @relation("GlobalCharacterVoiceMedia", fields: [customVoiceMediaId], references: [id], onDelete: SetNull) globalVoiceId String? // 绑定的全局音色 ID createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) folder GlobalAssetFolder? @relation(fields: [folderId], references: [id], onDelete: SetNull) appearances GlobalCharacterAppearance[] @@index([userId]) @@index([folderId]) @@index([customVoiceMediaId]) @@map("global_characters") } // 全局角色形象(结构与 CharacterAppearance 一致) model GlobalCharacterAppearance { id String @id @default(uuid()) characterId String appearanceIndex Int changeReason String @default("default") description String? @db.Text descriptions String? @db.Text imageUrl String? @db.Text imageMediaId String? imageMedia MediaObject? @relation("GlobalCharacterAppearanceImageMedia", fields: [imageMediaId], references: [id], onDelete: SetNull) imageUrls String? @db.Text selectedIndex Int? previousImageUrl String? @db.Text previousImageMediaId String? previousImageMedia MediaObject? @relation("GlobalCharacterAppearancePreviousImageMedia", fields: [previousImageMediaId], references: [id], onDelete: SetNull) previousImageUrls String? @db.Text previousDescription String? @db.Text // 上一次的描述词(用于撤回) previousDescriptions String? @db.Text // 上一次的描述词数组(用于撤回) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt character GlobalCharacter @relation(fields: [characterId], references: [id], onDelete: Cascade) @@unique([characterId, appearanceIndex]) @@index([characterId]) @@index([imageMediaId]) @@index([previousImageMediaId]) @@map("global_character_appearances") } // 全局场景(结构与 NovelPromotionLocation 一致) model GlobalLocation { id String @id @default(uuid()) userId String folderId String? name String summary String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) folder GlobalAssetFolder? @relation(fields: [folderId], references: [id], onDelete: SetNull) images GlobalLocationImage[] @@index([userId]) @@index([folderId]) @@map("global_locations") } // 全局场景图片(结构与 LocationImage 一致) model GlobalLocationImage { id String @id @default(uuid()) locationId String imageIndex Int description String? @db.Text imageUrl String? @db.Text imageMediaId String? imageMedia MediaObject? @relation("GlobalLocationImageMedia", fields: [imageMediaId], references: [id], onDelete: SetNull) isSelected Boolean @default(false) previousImageUrl String? @db.Text previousImageMediaId String? previousImageMedia MediaObject? @relation("GlobalLocationImagePreviousImageMedia", fields: [previousImageMediaId], references: [id], onDelete: SetNull) previousDescription String? @db.Text // 上一次的描述词(用于撤回) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt location GlobalLocation @relation(fields: [locationId], references: [id], onDelete: Cascade) @@unique([locationId, imageIndex]) @@index([locationId]) @@index([imageMediaId]) @@index([previousImageMediaId]) @@map("global_location_images") } // 全局音色库 model GlobalVoice { id String @id @default(uuid()) userId String folderId String? name String // 音色名称 description String? @db.Text // 详细描述 voiceId String? // qwen-tts-vd 的 voice ID voiceType String @default("qwen-designed") // qwen-designed | custom customVoiceUrl String? @db.Text // 上传的音频 URL(预览用) customVoiceMediaId String? customVoiceMedia MediaObject? @relation("GlobalVoiceCustomVoiceMedia", fields: [customVoiceMediaId], references: [id], onDelete: SetNull) voicePrompt String? @db.Text // AI 设计时的提示词 gender String? // male | female | neutral language String @default("zh") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) folder GlobalAssetFolder? @relation(fields: [folderId], references: [id], onDelete: SetNull) @@index([userId]) @@index([folderId]) @@index([customVoiceMediaId]) @@map("global_voices") } model MediaObject { id String @id @default(uuid()) publicId String @unique storageKey String @unique @db.VarChar(512) sha256 String? mimeType String? sizeBytes BigInt? width Int? height Int? durationMs Int? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt characterAppearanceImages CharacterAppearance[] @relation("CharacterAppearanceImageMedia") locationImages LocationImage[] @relation("LocationImageMedia") novelPromotionCharacterVoices NovelPromotionCharacter[] @relation("NovelPromotionCharacterVoiceMedia") novelPromotionEpisodeAudios NovelPromotionEpisode[] @relation("NovelPromotionEpisodeAudioMedia") novelPromotionPanelImages NovelPromotionPanel[] @relation("NovelPromotionPanelImageMedia") novelPromotionPanelVideos NovelPromotionPanel[] @relation("NovelPromotionPanelVideoMedia") novelPromotionPanelLipSyncVideos NovelPromotionPanel[] @relation("NovelPromotionPanelLipSyncVideoMedia") novelPromotionPanelSketchImages NovelPromotionPanel[] @relation("NovelPromotionPanelSketchMedia") novelPromotionPanelPreviousImages NovelPromotionPanel[] @relation("NovelPromotionPanelPreviousImageMedia") novelPromotionShotImages NovelPromotionShot[] @relation("NovelPromotionShotImageMedia") supplementaryPanelImages SupplementaryPanel[] @relation("SupplementaryPanelImageMedia") novelPromotionVoiceLineAudios NovelPromotionVoiceLine[] @relation("NovelPromotionVoiceLineAudioMedia") voicePresetAudios VoicePreset[] @relation("VoicePresetAudioMedia") globalCharacterVoices GlobalCharacter[] @relation("GlobalCharacterVoiceMedia") globalCharacterAppearanceImages GlobalCharacterAppearance[] @relation("GlobalCharacterAppearanceImageMedia") globalCharacterAppearancePreviousImgs GlobalCharacterAppearance[] @relation("GlobalCharacterAppearancePreviousImageMedia") globalLocationImageImages GlobalLocationImage[] @relation("GlobalLocationImageMedia") globalLocationImagePreviousImages GlobalLocationImage[] @relation("GlobalLocationImagePreviousImageMedia") globalVoiceCustomVoices GlobalVoice[] @relation("GlobalVoiceCustomVoiceMedia") @@index([createdAt]) @@map("media_objects") } model LegacyMediaRefBackup { id String @id @default(uuid()) runId String tableName String rowId String fieldName String legacyValue String @db.Text checksum String createdAt DateTime @default(now()) @@index([runId]) @@index([tableName, fieldName]) @@map("legacy_media_refs_backup") }