feat: altyazı otomasyon sistemi MVP'sini ekle

Docker tabanlı mikro servis mimarisi ile altyazı otomasyon sistemi altyapısı kuruldu.

- Core (Node.js): Chokidar dosya izleyici, BullMQ iş kuyrukları, ffprobe medya analizi, MongoDB entegrasyonu ve dosya yazma işlemleri.
- API (Fastify): Mock sağlayıcılar, arşiv güvenliği (zip-slip), altyazı doğrulama, puanlama ve aday seçim motoru.
- UI (React/Vite): İş yönetimi paneli, canlı SSE log akışı, manuel inceleme arayüzü ve sistem ayarları.
- Altyapı: Docker Compose (dev/prod), Redis, Mongo ve çevresel değişken yapılandırmaları.
This commit is contained in:
2026-02-15 23:12:24 +03:00
commit f1a1f093e6
72 changed files with 2882 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
import type { Candidate, SearchParams, SubtitleProvider } from '../types/index.js';
import { generateMockArtifact } from '../lib/mockArtifact.js';
import { hashString, seeded } from '../lib/deterministic.js';
import { env } from '../config/env.js';
export class OpenSubtitlesProvider implements SubtitleProvider {
async search(params: SearchParams): Promise<Candidate[]> {
// TODO(v2): real OpenSubtitles API integration.
const key = `${params.title}|${params.year}|${params.season}|${params.episode}|os`;
const rnd = seeded(hashString(key));
const base = params.title.replace(/\s+/g, '.');
const directForMovie = params.type === 'movie' && rnd() > 0.4;
return [
{
id: `os-${hashString(`${key}-a`)}`,
provider: 'opensubtitles',
displayName: `OS ${base} Official`,
downloadType: directForMovie ? 'direct' : 'archiveZip',
downloadUrl: directForMovie ? `mock://os/${base}/direct.srt` : `mock://os/${base}/archive.zip`,
lang: 'tr',
releaseHints: ['1080p', rnd() > 0.5 ? 'x265' : 'x264', 'flux'],
scoreHints: ['api_match'],
isHI: rnd() > 0.8,
isForced: rnd() > 0.92
},
{
id: `os-${hashString(`${key}-b`)}`,
provider: 'opensubtitles',
displayName: `OS ${base} Backup`,
downloadType: 'archiveZip',
downloadUrl: `mock://os/${base}/backup.zip`,
lang: 'tr',
releaseHints: ['720p', 'x264'],
scoreHints: ['backup'],
isHI: false,
isForced: false
}
];
}
async download(candidate: Candidate, params: SearchParams, jobToken: string) {
const artifact = await generateMockArtifact(candidate, params, jobToken, `${env.tempRoot}/${jobToken}/download`);
return { type: artifact.type, filePath: artifact.filePath, candidateId: candidate.id };
}
}