main
Some checks failed
CI / build (push) Has been cancelled

This commit is contained in:
Harun CAN
2026-03-23 02:34:03 +03:00
parent 83b0ae61a8
commit e60b6ea526
8 changed files with 667 additions and 1 deletions

View File

@@ -3,6 +3,7 @@ import { PrismaService } from '../../../database/prisma.service';
import { GeminiService } from '../../gemini/gemini.service';
import { CreateSegmentDto, UpdateSegmentDto } from '../dto';
import { AnalysisService } from './analysis.service';
import { VersionsService } from './versions.service';
import {
buildScriptOutlinePrompt,
buildChapterSegmentPrompt,
@@ -28,6 +29,7 @@ export class ScriptsService {
private readonly prisma: PrismaService,
private readonly gemini: GeminiService,
private readonly analysisService: AnalysisService,
private readonly versionsService: VersionsService,
) {}
/**
@@ -113,6 +115,20 @@ export class ScriptsService {
async generateScript(projectId: string) {
this.logger.log(`Generating script for project: ${projectId}`);
// Auto-snapshot current state before regeneration
await this.versionsService.createSnapshot(
projectId,
'AUTO_SAVE',
undefined,
'Auto-save before script generation',
).catch(() => { /* ignore if no segments yet */ });
// Update status
await this.prisma.scriptProject.update({
where: { id: projectId },
data: { status: 'SCRIPTING' },
});
const project = await this.prisma.scriptProject.findUnique({
where: { id: projectId },
include: {
@@ -364,4 +380,103 @@ export class ScriptsService {
},
});
}
// ========== REGENERATE (Tek segment / Partial) ==========
/**
* Regenerate a single segment with AI.
* Auto-snapshots current state before regeneration.
*
* @param segmentId - Segment ID to regenerate
* @returns Updated segment
*/
async regenerateSegment(segmentId: string) {
const segment = await this.prisma.scriptSegment.findUnique({
where: { id: segmentId },
include: { project: { include: { characters: true } } },
});
if (!segment) {
throw new NotFoundException(`Segment with ID ${segmentId} not found`);
}
// Auto-snapshot before regeneration
await this.versionsService.createSnapshot(
segment.projectId,
'AUTO_SAVE',
undefined,
`Auto-save before regenerating segment #${segment.sortOrder + 1}`,
).catch(() => {});
const characterContext = segment.project.characters
.map((c) => `${c.name} (${c.role}): Values[${c.values}] Traits[${c.traits}]`)
.join('\n');
const chapterPromptData = buildChapterSegmentPrompt({
chapterIndex: segment.sortOrder,
totalChapters: 1,
chapterTitle: segment.segmentType,
chapterFocus: segment.visualDescription || segment.segmentType,
chapterType: segment.segmentType,
speechStyles: segment.project.speechStyle,
targetAudience: segment.project.targetAudience,
characterContext,
language: segment.project.language,
});
const resp = await this.gemini.generateJSON<any[]>(
chapterPromptData.prompt,
chapterPromptData.schema,
{ temperature: chapterPromptData.temperature },
);
const newSeg = resp.data[0];
if (!newSeg) return segment;
const words = newSeg.narratorScript ? newSeg.narratorScript.split(' ').length : 0;
const dur = Math.max(5, Math.ceil(words / (140 / 60)));
return this.prisma.scriptSegment.update({
where: { id: segmentId },
data: {
narratorScript: newSeg.narratorScript,
visualDescription: newSeg.visualDescription,
videoPrompt: newSeg.videoPrompt,
imagePrompt: newSeg.imagePrompt,
onScreenText: newSeg.onScreenText,
audioCues: newSeg.audioCues,
stockQuery: newSeg.stockQuery,
duration: `${dur}s`,
},
});
}
/**
* Regenerate multiple selected segments.
*
* @param projectId - Project ID
* @param segmentIds - Array of segment IDs to regenerate
* @returns Updated segments
*/
async regeneratePartial(projectId: string, segmentIds: string[]) {
// Auto-snapshot
await this.versionsService.createSnapshot(
projectId,
'AUTO_SAVE',
undefined,
`Auto-save before partial regeneration (${segmentIds.length} segments)`,
).catch(() => {});
const results: any[] = [];
for (const segId of segmentIds) {
try {
const result = await this.regenerateSegment(segId);
results.push(result);
} catch (error) {
this.logger.warn(`Failed to regenerate segment ${segId}: ${error}`);
}
}
return results;
}
}