Her aday klip transkribe olduktan hemen sonra otomatik olarak video-render kuyruğuna giriyor. Yeni Python worker (video_render.py): - Klibi ffmpeg ile keser - OpenCV + MediaPipe Face Detector (Tasks API — yeni mediapipe sürümlerinde eski solutions API'si kalktı, .tflite model dosyası Docker build'de indiriliyor) ile örneklenmiş karelerde yüz merkezi tespit eder, yumuşatır (moving average); hiç yüz bulunamazsa sabit merkez crop'a düşer, hata saymaz - Kırpılmış kareleri OpenCV'den ffmpeg'e pipe'layıp tek geçişte orijinal sesi map'ler ve transcript_json'dan ürettiği .ass altyazıyı yakar - Sonucu /shared-media/shorts/<candidate_id>.mp4'e yazar Yeni ShortVideo modeli (PENDING/RENDERING/READY/FAILED). Panelde (segments + kanal detay sayfaları) render durumu + READY olunca video önizleme/indirme. VIDEO_RENDER_CONCURRENCY ayrı ve düşük (varsayılan 2) — bu iş diğerlerinden çok daha ağır. Kapsam: sadece tekil konuşmacı şablonu (çoklu konuşmacı/split-screen sonraki bir iterasyona bırakıldı — aktif-konuşmacı tespiti gerektiriyor). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
118 lines
3.3 KiB
Plaintext
118 lines
3.3 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
enum RawSegmentStatus {
|
|
PENDING
|
|
PROCESSED
|
|
DISCARDED
|
|
}
|
|
|
|
enum CandidateSegmentStatus {
|
|
PENDING_STT
|
|
TRANSCRIBED
|
|
FAILED
|
|
}
|
|
|
|
enum ShortVideoStatus {
|
|
PENDING
|
|
RENDERING
|
|
READY
|
|
FAILED
|
|
}
|
|
|
|
model Channel {
|
|
id String @id @default(cuid())
|
|
name String
|
|
youtubeHandle String @map("youtube_handle")
|
|
channelId String @unique @map("channel_id")
|
|
isActive Boolean @default(true) @map("is_active")
|
|
lastCheckedAt DateTime? @map("last_checked_at")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
sessions StreamSession[]
|
|
|
|
@@map("channels")
|
|
}
|
|
|
|
model StreamSession {
|
|
id String @id @default(cuid())
|
|
channelId String @map("channel_id")
|
|
channel Channel @relation(fields: [channelId], references: [id])
|
|
liveVideoId String? @map("live_video_id")
|
|
startedAt DateTime @default(now()) @map("started_at")
|
|
endedAt DateTime? @map("ended_at")
|
|
totalSegments Int @default(0) @map("total_segments")
|
|
|
|
segments RawSegment[]
|
|
|
|
@@map("stream_sessions")
|
|
}
|
|
|
|
model RawSegment {
|
|
id String @id @default(cuid())
|
|
sessionId String @map("session_id")
|
|
session StreamSession @relation(fields: [sessionId], references: [id])
|
|
filePath String @map("file_path")
|
|
duration Int
|
|
startedAt DateTime @map("started_at")
|
|
status RawSegmentStatus @default(PENDING)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
candidates CandidateSegment[]
|
|
|
|
@@map("raw_segments")
|
|
}
|
|
|
|
model CandidateSegment {
|
|
id String @id @default(cuid())
|
|
rawSegmentId String @map("raw_segment_id")
|
|
rawSegment RawSegment @relation(fields: [rawSegmentId], references: [id])
|
|
startSec Int @map("start_sec")
|
|
endSec Int @map("end_sec")
|
|
audioPeakScore Float? @map("audio_peak_score")
|
|
chatVelocityScore Float? @map("chat_velocity_score")
|
|
transcriptJson Json? @map("transcript_json")
|
|
status CandidateSegmentStatus @default(PENDING_STT)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
short ShortVideo?
|
|
|
|
@@map("candidate_segments")
|
|
}
|
|
|
|
model ShortVideo {
|
|
id String @id @default(cuid())
|
|
candidateId String @unique @map("candidate_id")
|
|
candidate CandidateSegment @relation(fields: [candidateId], references: [id])
|
|
filePath String? @map("file_path")
|
|
status ShortVideoStatus @default(PENDING)
|
|
errorMessage String? @map("error_message")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
@@map("short_videos")
|
|
}
|
|
|
|
model AppSetting {
|
|
key String @id
|
|
value String
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
@@map("app_settings")
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
username String @unique
|
|
passwordHash String @map("password_hash")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
@@map("users")
|
|
}
|