generated from fahricansecer/boilerplate-be
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Project" ADD COLUMN "cinematicReference" VARCHAR(200);
|
||||||
@@ -264,6 +264,7 @@ model Project {
|
|||||||
language String @default("tr") @db.VarChar(5) // ISO 639-1
|
language String @default("tr") @db.VarChar(5) // ISO 639-1
|
||||||
aspectRatio AspectRatio @default(PORTRAIT_9_16)
|
aspectRatio AspectRatio @default(PORTRAIT_9_16)
|
||||||
videoStyle VideoStyle @default(CINEMATIC)
|
videoStyle VideoStyle @default(CINEMATIC)
|
||||||
|
cinematicReference String? @db.VarChar(200)
|
||||||
targetDuration Int @default(60) // saniye
|
targetDuration Int @default(60) // saniye
|
||||||
|
|
||||||
// SEO & Social Content (skill-enhanced)
|
// SEO & Social Content (skill-enhanced)
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ export class CreateProjectDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
videoStyle?: VideoStyleDto;
|
videoStyle?: VideoStyleDto;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Sinematik stil referansı (örn: Wes Anderson, Matrix)' })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@MaxLength(200)
|
||||||
|
cinematicReference?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: 'Hedef video süresi (saniye)',
|
description: 'Hedef video süresi (saniye)',
|
||||||
example: 60,
|
example: 60,
|
||||||
@@ -138,6 +144,12 @@ export class UpdateProjectDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
videoStyle?: VideoStyleDto;
|
videoStyle?: VideoStyleDto;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Sinematik stil referansı (örn: Wes Anderson, Matrix)' })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@MaxLength(200)
|
||||||
|
cinematicReference?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Hedef video süresi (saniye)' })
|
@ApiPropertyOptional({ description: 'Hedef video süresi (saniye)' })
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -189,6 +201,12 @@ export class CreateFromTweetDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
videoStyle?: VideoStyleDto;
|
videoStyle?: VideoStyleDto;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Sinematik stil referansı (örn: Wes Anderson, Matrix)' })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@MaxLength(200)
|
||||||
|
cinematicReference?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Hedef video süresi (saniye)', default: 60 })
|
@ApiPropertyOptional({ description: 'Hedef video süresi (saniye)', default: 60 })
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ export class ProjectsService {
|
|||||||
targetDurationSeconds: project.targetDuration,
|
targetDurationSeconds: project.targetDuration,
|
||||||
language: project.language,
|
language: project.language,
|
||||||
videoStyle: project.videoStyle,
|
videoStyle: project.videoStyle,
|
||||||
|
cinematicReference: (project as any).cinematicReference || undefined,
|
||||||
seoKeywords: (project as any).seoKeywords || [],
|
seoKeywords: (project as any).seoKeywords || [],
|
||||||
referenceUrl: (project as any).referenceUrl || undefined,
|
referenceUrl: (project as any).referenceUrl || undefined,
|
||||||
});
|
});
|
||||||
@@ -404,6 +405,7 @@ export class ProjectsService {
|
|||||||
targetDurationSeconds: project.targetDuration,
|
targetDurationSeconds: project.targetDuration,
|
||||||
language: project.language,
|
language: project.language,
|
||||||
videoStyle: project.videoStyle,
|
videoStyle: project.videoStyle,
|
||||||
|
cinematicReference: (project as any).cinematicReference || undefined,
|
||||||
seoKeywords: [],
|
seoKeywords: [],
|
||||||
referenceUrl: dto.tweetUrl,
|
referenceUrl: dto.tweetUrl,
|
||||||
sourceTweet: {
|
sourceTweet: {
|
||||||
@@ -580,7 +582,7 @@ Sadece bu tek sahneyi üret. JSON formatında:
|
|||||||
}
|
}
|
||||||
|
|
||||||
async generateSceneImage(userId: string, projectId: string, sceneId: string, customPrompt?: string) {
|
async generateSceneImage(userId: string, projectId: string, sceneId: string, customPrompt?: string) {
|
||||||
const project = await this.findOne(projectId, userId);
|
const project = await this.findOne(userId, projectId);
|
||||||
const scene = project.scenes.find((s) => s.id === sceneId);
|
const scene = project.scenes.find((s) => s.id === sceneId);
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
throw new NotFoundException('Sahne bulunamadı');
|
throw new NotFoundException('Sahne bulunamadı');
|
||||||
@@ -602,9 +604,8 @@ Sadece bu tek sahneyi üret. JSON formatında:
|
|||||||
const mappedRatio = aspectRatioMap[project.aspectRatio] || '9:16';
|
const mappedRatio = aspectRatioMap[project.aspectRatio] || '9:16';
|
||||||
|
|
||||||
// Görüntüyü üret
|
// Görüntüyü üret
|
||||||
const imageResult = await this.geminiService.generateImageForScene(
|
const imageResult = await this.geminiService.generateImage(
|
||||||
scene.visualPrompt,
|
`${scene.visualPrompt}. Style: ${project.videoStyle}`,
|
||||||
project.videoStyle,
|
|
||||||
mappedRatio,
|
mappedRatio,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -648,7 +649,7 @@ Sadece bu tek sahneyi üret. JSON formatında:
|
|||||||
}
|
}
|
||||||
|
|
||||||
async upscaleSceneImage(userId: string, projectId: string, sceneId: string) {
|
async upscaleSceneImage(userId: string, projectId: string, sceneId: string) {
|
||||||
const project = await this.findOne(projectId, userId);
|
const project = await this.findOne(userId, projectId);
|
||||||
const scene = project.scenes.find((s) => s.id === sceneId);
|
const scene = project.scenes.find((s) => s.id === sceneId);
|
||||||
if (!scene) {
|
if (!scene) {
|
||||||
throw new NotFoundException('Sahne bulunamadı');
|
throw new NotFoundException('Sahne bulunamadı');
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export interface ScriptGenerationInput {
|
|||||||
targetDurationSeconds: number;
|
targetDurationSeconds: number;
|
||||||
language: string;
|
language: string;
|
||||||
videoStyle: string;
|
videoStyle: string;
|
||||||
|
cinematicReference?: string;
|
||||||
aspectRatio?: string; // PORTRAIT_9_16 | LANDSCAPE_16_9 | SQUARE_1_1
|
aspectRatio?: string; // PORTRAIT_9_16 | LANDSCAPE_16_9 | SQUARE_1_1
|
||||||
referenceUrl?: string;
|
referenceUrl?: string;
|
||||||
seoKeywords?: string[];
|
seoKeywords?: string[];
|
||||||
@@ -595,7 +596,7 @@ export class VideoAiService {
|
|||||||
|
|
||||||
const script = this.parseAndValidateScript(rawText);
|
const script = this.parseAndValidateScript(rawText);
|
||||||
const humanizedScript = this.applyHumanizerPass(script);
|
const humanizedScript = this.applyHumanizerPass(script);
|
||||||
const enrichedScript = this.enrichVisualPrompts(humanizedScript, input.videoStyle, input.aspectRatio);
|
const enrichedScript = this.enrichVisualPrompts(humanizedScript, input.videoStyle, input.cinematicReference, input.aspectRatio);
|
||||||
|
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`✅ Senaryo üretildi — "${enrichedScript.metadata.title}", ` +
|
`✅ Senaryo üretildi — "${enrichedScript.metadata.title}", ` +
|
||||||
@@ -791,9 +792,10 @@ export class VideoAiService {
|
|||||||
private enrichVisualPrompts(
|
private enrichVisualPrompts(
|
||||||
script: GeneratedScript,
|
script: GeneratedScript,
|
||||||
videoStyle: string,
|
videoStyle: string,
|
||||||
|
cinematicReference?: string,
|
||||||
aspectRatio?: string,
|
aspectRatio?: string,
|
||||||
): GeneratedScript {
|
): GeneratedScript {
|
||||||
const styleDNA = this.getStyleDNA(videoStyle);
|
const styleDNA = this.getStyleDNA(videoStyle, cinematicReference);
|
||||||
const defaultNegative = 'Avoid: text overlays, watermarks, brand logos, recognizable celebrity faces, distorted anatomy, extra fingers, blurry faces, stock photo aesthetic, oversaturated CGI plastic look, generic clip art, UI elements';
|
const defaultNegative = 'Avoid: text overlays, watermarks, brand logos, recognizable celebrity faces, distorted anatomy, extra fingers, blurry faces, stock photo aesthetic, oversaturated CGI plastic look, generic clip art, UI elements';
|
||||||
|
|
||||||
for (let i = 0; i < script.scenes.length; i++) {
|
for (let i = 0; i < script.scenes.length; i++) {
|
||||||
@@ -885,14 +887,14 @@ export class VideoAiService {
|
|||||||
/**
|
/**
|
||||||
* Video stiline göre varsayılan görsel DNA değerlerini döndürür.
|
* Video stiline göre varsayılan görsel DNA değerlerini döndürür.
|
||||||
*/
|
*/
|
||||||
private getStyleDNA(videoStyle: string): StyleDNA {
|
private getStyleDNA(videoStyle: string, cinematicReference?: string): StyleDNA {
|
||||||
const dnaMap: Record<string, StyleDNA> = {
|
const dnaMap: Record<string, StyleDNA> = {
|
||||||
CINEMATIC: {
|
CINEMATIC: {
|
||||||
reference: 'Denis Villeneuve and Roger Deakins cinematography',
|
reference: cinematicReference ? `${cinematicReference} visual style and cinematography` : 'Denis Villeneuve and Roger Deakins cinematography',
|
||||||
lighting: 'Dramatic key-and-fill lighting with a single strong motivated source casting deep sculpted shadows.',
|
lighting: cinematicReference ? `Iconic lighting setup matching the ${cinematicReference} cinematic style` : 'Dramatic key-and-fill lighting with a single strong motivated source casting deep sculpted shadows.',
|
||||||
lens: 'Shot on 35mm anamorphic lens with shallow depth of field f/2.0 and characteristic oval bokeh.',
|
lens: cinematicReference ? `Signature camera lens choice and depth of field suitable for ${cinematicReference}` : 'Shot on 35mm anamorphic lens with shallow depth of field f/2.0 and characteristic oval bokeh.',
|
||||||
color: 'Teal-and-orange blockbuster color grade with desaturated midtones and crushed blacks.',
|
color: cinematicReference ? `Color grading and palette uniquely associated with ${cinematicReference}` : 'Teal-and-orange blockbuster color grade with desaturated midtones and crushed blacks.',
|
||||||
texture: 'Subtle Kodak Vision3 film grain, anamorphic horizontal lens flare, slight vignette darkening corners.',
|
texture: cinematicReference ? `Film grain and visual texture evoking the ${cinematicReference} experience` : 'Subtle Kodak Vision3 film grain, anamorphic horizontal lens flare, slight vignette darkening corners.',
|
||||||
},
|
},
|
||||||
DOCUMENTARY: {
|
DOCUMENTARY: {
|
||||||
reference: 'National Geographic and Planet Earth II',
|
reference: 'National Geographic and Planet Earth II',
|
||||||
|
|||||||
26
test-gemini-image.ts
Normal file
26
test-gemini-image.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { GoogleGenAI } from '@google/genai';
|
||||||
|
import * as dotenv from 'dotenv';
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
|
||||||
|
|
||||||
|
async function listModels() {
|
||||||
|
console.log('Listing models...');
|
||||||
|
try {
|
||||||
|
let hasImagen = false;
|
||||||
|
let hasPreview = false;
|
||||||
|
// Iterate over pagination using valid SDK method if possible, but let's just use REST if not found.
|
||||||
|
// However, the new `@google/genai` has ai.models.list()
|
||||||
|
const response = await ai.models.list();
|
||||||
|
for await (const m of response) {
|
||||||
|
if (m.name.includes('image') || m.name.includes('imagen')) {
|
||||||
|
console.log(m.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error listing models:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listModels();
|
||||||
Reference in New Issue
Block a user