Yeni "Ham Kayıt Modu" ayarı (send_raw_segments_to_telegram, varsayılan
kapalı): açıksa her ham segment tamamlanır tamamlanmaz sinyal analizi/
STT/9:16 render zincirine hiç girmeden doğrudan Telegram'a gönderiliyor
ve sunucudan siliniyor. Kullanıcı transkript/9:16 istemediğinde ("sen
bana direk videoyu gönder") tüm analiz pipeline'ını atlayıp sadece
kayıt+gönder+sil yapan basit bir mod.
- RawSegmentStatus'a SENT_TO_TELEGRAM eklendi, RawSegment.filePath
nullable yapıldı (aynı ShortVideo.filePath deseni — gönderim sonrası
null, dosya diskten silinmiş demek).
- api-daemon/src/telegram.ts'e sendTelegramVideo eklendi (native fetch+
FormData+Blob ile multipart upload, ~50MB bot-limiti önceden kontrol
ediliyor) — worker/telegram.py'deki aynı fonksiyonun Node karşılığı.
- streamIngest.ts: segment tamamlanınca, mod açıksa signal-detection
kuyruğuna hiç eklemeden doğrudan gönderiyor (kuyruğa eklemek, aşağıda
dosyayı silmenin sinyal analizini bozmasına sebep olurdu).
- Panel: segment listelerinde filePath=null durumunda video/indir yerine
"Telegram'a gönderildi" notu (segments, channels/[id]).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
139 lines
4.2 KiB
Plaintext
139 lines
4.2 KiB
Plaintext
generator client {
|
||
provider = "prisma-client-js"
|
||
}
|
||
|
||
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
}
|
||
|
||
enum RawSegmentStatus {
|
||
PENDING
|
||
PROCESSED
|
||
DISCARDED
|
||
// Analiz zincirine (sinyal analizi/STT/9:16) hiç girmeden ham haliyle
|
||
// doğrudan Telegram'a gönderildi — file_path bu durumda null (dosya
|
||
// gönderim sonrası diskten silindi).
|
||
SENT_TO_TELEGRAM
|
||
}
|
||
|
||
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")
|
||
// null = global varsayılanı (SEGMENT_TIME_SEC env var) kullan
|
||
segmentTimeSec Int? @map("segment_time_sec")
|
||
|
||
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")
|
||
}
|
||
|
||
// Birden fazla YouTube hesabının cookie'si — her kanal (channel.id'den
|
||
// deterministik bir hash ile) bu havuzdaki bir profile sabitlenir, tek bir
|
||
// hesabın çok fazla kanalı taramasından kaynaklanan hesap-seviyesi
|
||
// bot-tespitini havuza yayarak azaltır (bkz. proxyUrlForChannel ile aynı
|
||
// mantık, ytdlpCookies.ts).
|
||
model CookieProfile {
|
||
id String @id @default(cuid())
|
||
label String
|
||
value String
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
@@map("cookie_profiles")
|
||
}
|
||
|
||
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")
|
||
}
|