Nota: Clique em Salvar no editor Flatlogic para pe

This commit is contained in:
Flatlogic Bot 2026-02-16 01:19:44 +00:00
parent cfc1f5d70a
commit a195d0853c
15 changed files with 380 additions and 81 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from .models import Project, PipelineStep, CgiAsset from .models import Project, PipelineStep, CgiAsset, Scene, StudioConfig
class PipelineStepInline(admin.TabularInline): class PipelineStepInline(admin.TabularInline):
model = PipelineStep model = PipelineStep
@ -9,13 +9,17 @@ class CgiAssetInline(admin.TabularInline):
model = CgiAsset model = CgiAsset
extra = 1 extra = 1
class SceneInline(admin.TabularInline):
model = Scene
extra = 1
@admin.register(Project) @admin.register(Project)
class ProjectAdmin(admin.ModelAdmin): class ProjectAdmin(admin.ModelAdmin):
list_display = ('title', 'project_type', 'status', 'created_at') list_display = ('title', 'project_type', 'category', 'status', 'is_ai_generated', 'created_at')
list_filter = ('project_type', 'status') list_filter = ('project_type', 'status', 'is_ai_generated')
search_fields = ('title', 'description') search_fields = ('title', 'description')
prepopulated_fields = {'slug': ('title',)} prepopulated_fields = {'slug': ('title',)}
inlines = [PipelineStepInline, CgiAssetInline] inlines = [PipelineStepInline, CgiAssetInline, SceneInline]
@admin.register(PipelineStep) @admin.register(PipelineStep)
class PipelineStepAdmin(admin.ModelAdmin): class PipelineStepAdmin(admin.ModelAdmin):
@ -26,3 +30,12 @@ class PipelineStepAdmin(admin.ModelAdmin):
class CgiAssetAdmin(admin.ModelAdmin): class CgiAssetAdmin(admin.ModelAdmin):
list_display = ('name', 'project', 'asset_type', 'is_realistic') list_display = ('name', 'project', 'asset_type', 'is_realistic')
list_filter = ('asset_type', 'is_realistic') list_filter = ('asset_type', 'is_realistic')
@admin.register(Scene)
class SceneAdmin(admin.ModelAdmin):
list_display = ('project', 'number', 'title')
list_filter = ('project',)
@admin.register(StudioConfig)
class StudioConfigAdmin(admin.ModelAdmin):
list_display = ('id', 'admin_access_key', 'is_setup')

View File

@ -0,0 +1,48 @@
# Generated by Django 5.2.7 on 2026-02-16 01:12
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0003_studioconfig_cgiasset_assigned_artist_and_more'),
]
operations = [
migrations.AddField(
model_name='cgiasset',
name='physical_description',
field=models.TextField(blank=True, help_text='Detailed physical appearance based on real humans'),
),
migrations.AddField(
model_name='project',
name='category',
field=models.CharField(default='Sci-Fi', max_length=100),
),
migrations.AddField(
model_name='project',
name='full_script',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='project',
name='is_ai_generated',
field=models.BooleanField(default=False),
),
migrations.CreateModel(
name='Scene',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('number', models.PositiveIntegerField(default=1)),
('title', models.CharField(max_length=255)),
('description', models.TextField()),
('visual_prompt', models.TextField(blank=True)),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='scenes', to='core.project')),
],
options={
'ordering': ['number'],
},
),
]

View File

@ -31,11 +31,14 @@ class Project(models.Model):
title = models.CharField(max_length=255) title = models.CharField(max_length=255)
slug = models.SlugField(unique=True, blank=True) slug = models.SlugField(unique=True, blank=True)
project_type = models.CharField(max_length=10, choices=TYPES, default='MOVIE') project_type = models.CharField(max_length=10, choices=TYPES, default='MOVIE')
category = models.CharField(max_length=100, default='Sci-Fi')
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='PRE') status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='PRE')
description = models.TextField(blank=True) description = models.TextField(blank=True)
full_script = models.TextField(blank=True)
thumbnail_url = models.URLField(blank=True, help_text="URL to a representative image") thumbnail_url = models.URLField(blank=True, help_text="URL to a representative image")
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
is_ai_generated = models.BooleanField(default=False)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.slug: if not self.slug:
@ -45,6 +48,19 @@ class Project(models.Model):
def __str__(self): def __str__(self):
return self.title return self.title
class Scene(models.Model):
project = models.ForeignKey(Project, related_name='scenes', on_delete=models.CASCADE)
number = models.PositiveIntegerField(default=1)
title = models.CharField(max_length=255)
description = models.TextField()
visual_prompt = models.TextField(blank=True)
class Meta:
ordering = ['number']
def __str__(self):
return f"Scene {self.number}: {self.title}"
class PipelineStep(models.Model): class PipelineStep(models.Model):
STAGES = ( STAGES = (
# Pre-Production # Pre-Production
@ -85,6 +101,7 @@ class CgiAsset(models.Model):
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
asset_type = models.CharField(max_length=10, choices=ASSET_TYPES) asset_type = models.CharField(max_length=10, choices=ASSET_TYPES)
is_realistic = models.BooleanField(default=True) is_realistic = models.BooleanField(default=True)
physical_description = models.TextField(blank=True, help_text="Detailed physical appearance based on real humans")
current_stage = models.CharField(max_length=100, default='Modeling') current_stage = models.CharField(max_length=100, default='Modeling')
version = models.PositiveIntegerField(default=1) version = models.PositiveIntegerField(default=1)
file_location = models.CharField(max_length=500, blank=True, help_text="Path or URL to the digital file") file_location = models.CharField(max_length=500, blank=True, help_text="Path or URL to the digital file")

View File

@ -28,6 +28,9 @@
<a href="{% url 'asset_library' %}" class="btn btn-outline-purple shadow-neon"> <a href="{% url 'asset_library' %}" class="btn btn-outline-purple shadow-neon">
<i class="bi bi-box-seam me-2"></i> Biblioteca de Assets <i class="bi bi-box-seam me-2"></i> Biblioteca de Assets
</a> </a>
<a href="{% url 'studio_ai' %}" class="btn btn-neon-purple">
<i class="bi bi-magic me-2"></i> STUDIO AI (Criador Automático)
</a>
</div> </div>
<div> <div>
{% if is_admin %} {% if is_admin %}
@ -55,6 +58,9 @@
{% else %} {% else %}
<div class="text-center"> <div class="text-center">
<i class="bi bi-camera-reels text-purple display-1"></i> <i class="bi bi-camera-reels text-purple display-1"></i>
{% if project.is_ai_generated %}
<div class="mt-2"><span class="badge bg-purple small">AI GENERATED</span></div>
{% endif %}
</div> </div>
{% endif %} {% endif %}
</div> </div>
@ -62,20 +68,23 @@
<div class="card-body p-4"> <div class="card-body p-4">
<div class="d-flex justify-content-between align-items-start mb-2"> <div class="d-flex justify-content-between align-items-start mb-2">
<h3 class="card-title text-white font-syncopate h4 mb-0">{{ project.title }}</h3> <h3 class="card-title text-white font-syncopate h4 mb-0">{{ project.title }}</h3>
<span class="badge {% if project.status == 'PROD' %}bg-cyan text-dark{% else %}bg-purple{% endif %}"> <div class="text-end">
{{ project.get_status_display }} <span class="badge {% if project.status == 'PROD' %}bg-cyan text-dark{% else %}bg-purple{% endif %} d-block mb-1">
</span> {{ project.get_status_display }}
</span>
<span class="badge bg-dark border border-secondary small">{{ project.category }}</span>
</div>
</div> </div>
<p class="text-secondary small mb-4">{{ project.description|truncatewords:20 }}</p> <p class="text-secondary small mb-4">{{ project.description|truncatewords:20 }}</p>
<!-- Pipeline Progress Mini --> <!-- Pipeline Progress Mini -->
<div class="mb-4"> <div class="mb-4">
<div class="d-flex justify-content-between small text-secondary mb-1"> <div class="d-flex justify-content-between small text-secondary mb-1">
<span>Progresso Geral</span> <span>Status Pipeline</span>
<span>85%</span> <!-- Dynamic calculation could go here --> <span>{% with last=project.steps.last %}{{ last.progress|default:0 }}{% endwith %}%</span>
</div> </div>
<div class="progress bg-black" style="height: 6px;"> <div class="progress bg-black" style="height: 6px;">
<div class="progress-bar bg-cyan shadow-neon" role="progressbar" style="width: 85%;"></div> <div class="progress-bar bg-cyan shadow-neon" role="progressbar" style="width: {% with last=project.steps.last %}{{ last.progress|default:0 }}{% endwith %}%;"></div>
</div> </div>
</div> </div>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %} {% load static %}
{% block title %}{{ project.title }} | Pipeline Detail{% endblock %} {% block title %}{{ project.title }} | Studio AI Production{% endblock %}
{% block content %} {% block content %}
<div class="hero-section py-5"> <div class="hero-section py-5">
@ -15,13 +15,19 @@
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-lg-8"> <div class="col-lg-8">
<span class="pipeline-badge badge-{{ project.status|lower }} mb-3 d-inline-block">{{ project.get_status_display }}</span> <div class="d-flex align-items-center mb-3">
<h1 class="display-4 outfit mb-3">{{ project.title }}</h1> <span class="pipeline-badge badge-{{ project.status|lower }} me-2">{{ project.get_status_display }}</span>
{% if project.is_ai_generated %}
<span class="badge bg-purple text-white rounded-pill px-3 py-2"><i class="bi bi-robot me-1"></i> AI GENERATED</span>
{% endif %}
<span class="badge bg-dark border border-secondary rounded-pill px-3 py-2 ms-2">{{ project.category }}</span>
</div>
<h1 class="display-4 outfit mb-3 text-gradient-neon">{{ project.title }}</h1>
<p class="lead text-muted">{{ project.description }}</p> <p class="lead text-muted">{{ project.description }}</p>
</div> </div>
<div class="col-lg-4 text-lg-end"> <div class="col-lg-4 text-lg-end">
<div class="stats-card bg-opacity-10 border-opacity-10"> <div class="stats-card bg-opacity-10 border-opacity-10">
<span class="text-muted small text-uppercase d-block mb-1">Global Delivery</span> <span class="text-muted small text-uppercase d-block mb-1">Status da Produção</span>
<span class="display-6 outfit fw-bold text-cyan"> <span class="display-6 outfit fw-bold text-cyan">
{% with last_step=project.steps.last %}{{ last_step.progress|default:0 }}{% endwith %}% {% with last_step=project.steps.last %}{{ last_step.progress|default:0 }}{% endwith %}%
</span> </span>
@ -33,71 +39,105 @@
<section class="py-5"> <section class="py-5">
<div class="container"> <div class="container">
<div class="row g-5"> <ul class="nav nav-pills mb-5 justify-content-center" id="prodTab" role="tablist">
<div class="col-lg-8"> <li class="nav-item" role="presentation">
<h3 class="h4 outfit mb-4 section-title">CGI Production Pipeline</h3> <button class="nav-link active fw-bold px-4" id="pipeline-tab" data-bs-toggle="pill" data-bs-target="#pipeline" type="button" role="tab">PIPELINE CGI</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link fw-bold px-4" id="script-tab" data-bs-toggle="pill" data-bs-target="#script" type="button" role="tab">ROTEIRO & CENAS</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link fw-bold px-4" id="cast-tab" data-bs-toggle="pill" data-bs-target="#cast" type="button" role="tab">PERSONAGENS (REAL)</button>
</li>
</ul>
<div class="pipeline-flow"> <div class="tab-content" id="prodTabContent">
<!-- Pipeline Tab -->
<div class="tab-pane fade show active" id="pipeline" role="tabpanel">
<div class="row g-4">
{% for step in steps %} {% for step in steps %}
<div class="project-card p-4 mb-3 border-opacity-10"> <div class="col-md-4 col-lg-3">
<div class="d-flex justify-content-between align-items-center mb-3"> <div class="project-card p-4 h-100 border-opacity-10">
<div class="d-flex align-items-center"> <div class="text-muted small mb-2">ETAPA {{ forloop.counter }}</div>
<div class="bg-cyan bg-opacity-10 text-cyan rounded-circle d-flex align-items-center justify-content-center me-3" style="width: 32px; height: 32px; font-size: 0.8rem; font-weight: bold;"> <h4 class="h6 mb-3 outfit">{{ step.get_name_display }}</h4>
{{ forloop.counter }} <div class="d-flex justify-content-between align-items-center mb-2">
</div> <span class="small text-muted">Progresso</span>
<h4 class="h6 mb-0 outfit">{{ step.get_name_display }}</h4> <span class="small text-cyan">{{ step.progress }}%</span>
</div>
<div class="progress" style="height: 4px;">
<div class="progress-bar {% if step.is_completed %}bg-cyan{% endif %}" style="width: {{ step.progress }}%"></div>
</div> </div>
{% if step.is_completed %}
<span class="badge bg-cyan text-black rounded-pill">COMPLETO</span>
{% else %}
<span class="text-muted small">{{ step.progress }}%</span>
{% endif %}
</div> </div>
<div class="progress" style="height: 4px;">
<div class="progress-bar {% if step.is_completed %}bg-cyan{% endif %}" style="width: {{ step.progress }}%"></div>
</div>
</div>
{% empty %}
<div class="text-center py-5 border border-dashed border-secondary border-opacity-25 rounded-4">
<p class="text-muted">Pipeline não inicializado. Configure as etapas no Admin.</p>
<a href="/admin/core/pipelinestep/add/?project={{ project.id }}" class="btn btn-sm btn-outline-cyan">Adicionar Etapa +</a>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
<div class="col-lg-4"> <!-- Script & Scenes Tab -->
<div class="sticky-top" style="top: 100px;"> <div class="tab-pane fade" id="script" role="tabpanel">
<h3 class="h4 outfit mb-4 section-title">Assets Digitais</h3> <div class="row justify-content-center">
<div class="assets-container"> <div class="col-lg-9">
{% for asset in assets %} {% for scene in scenes %}
<div class="asset-list-item d-flex justify-content-between align-items-center"> <div class="scene-item mb-5 p-4 glass-card border-opacity-10 rounded-4">
<div> <div class="d-flex align-items-center mb-3">
<span class="d-block outfit fw-bold small">{{ asset.name }}</span> <div class="bg-cyan text-black fw-bold rounded-circle d-flex align-items-center justify-content-center me-3" style="width: 40px; height: 40px;">
<span class="text-muted" style="font-size: 0.7rem;">{{ asset.get_asset_type_display }} • {{ asset.current_stage }}</span> {{ scene.number }}
</div>
<h3 class="h4 outfit mb-0 text-cyan">{{ scene.title }}</h3>
</div>
<div class="scene-body text-light lead-sm border-start border-cyan border-opacity-25 ps-4 ms-3">
{{ scene.description|linebreaks }}
</div> </div>
{% if asset.is_realistic %}
<span class="badge bg-purple bg-opacity-10 text-purple border border-purple border-opacity-25" style="font-size: 0.6rem;">REALISTA</span>
{% endif %}
</div> </div>
{% empty %} {% empty %}
<p class="text-muted small">Nenhum asset (personagens/cenários) vinculado.</p> <p class="text-center text-muted py-5">Nenhuma cena gerada para este roteiro.</p>
{% endfor %} {% endfor %}
</div> </div>
</div>
</div>
<div class="mt-5 p-4 bg-purple bg-opacity-05 rounded-4 border border-purple border-opacity-10"> <!-- Cast & Characters Tab -->
<h5 class="h6 outfit text-purple mb-3">Diretrizes de Qualidade</h5> <div class="tab-pane fade" id="cast" role="tabpanel">
<ul class="list-unstyled small text-muted"> <div class="row g-4">
<li class="mb-2">✓ Topologia limpa para rigging</li> {% for asset in assets %}
<li class="mb-2">✓ Texturas 4K/8K PBR</li> {% if asset.asset_type == 'CHAR' %}
<li class="mb-2">✓ Iluminação física (PBR)</li> <div class="col-md-6">
<li>✓ Renderização em EXR Multi-camada</li> <div class="project-card p-4 h-100 border-opacity-10 d-flex gap-4">
</ul> <div class="char-placeholder rounded-4 bg-dark border border-secondary border-opacity-25 d-flex align-items-center justify-content-center" style="width: 120px; height: 160px; min-width: 120px;">
<i class="bi bi-person-bounding-box text-muted display-4"></i>
</div>
<div>
<h4 class="outfit text-cyan mb-2">{{ asset.name }}</h4>
<span class="badge bg-purple bg-opacity-10 text-purple border border-purple border-opacity-25 mb-3">APARÊNCIA REALISTA</span>
<p class="small text-muted mb-0"><strong>Físico:</strong> {{ asset.physical_description }}</p>
</div>
</div>
</div> </div>
{% endif %}
{% empty %}
<p class="text-center text-muted py-5">Nenhum personagem definido no elenco.</p>
{% endfor %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<style>
.nav-pills .nav-link {
color: rgba(255,255,255,0.5);
border: 1px solid rgba(255,255,255,0.1);
margin: 0 5px;
border-radius: 50px;
}
.nav-pills .nav-link.active {
background-color: #00e5ff;
color: #000;
box-shadow: 0 0 15px rgba(0,229,255,0.4);
}
.lead-sm {
font-size: 1.1rem;
line-height: 1.8;
}
</style>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,79 @@
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card glass-card border-0 shadow-lg text-white">
<div class="card-body p-5">
<div class="text-center mb-5">
<i class="bi bi-cpu-fill text-cyan display-1 mb-3"></i>
<h1 class="display-4 fw-bold text-gradient-neon">STUDIO AI AUTOMÁTICO</h1>
<p class="lead text-muted">Crie Super-Produções (Filmes e Séries) totalmente automatizadas com IA.</p>
</div>
<form action="{% url 'generate_production' %}" method="POST" id="aiForm">
{% csrf_token %}
<div class="mb-4">
<label class="form-label text-cyan fw-bold">TIPO DE PRODUÇÃO</label>
<select name="project_type" class="form-select bg-dark text-white border-secondary">
<option value="MOVIE">Longa-Metragem (Filme)</option>
<option value="SERIES">Série de TV</option>
<option value="SHORT">Curta-Metragem</option>
</select>
</div>
<div class="mb-4">
<label class="form-label text-cyan fw-bold">CATEGORIA / GÊNERO</label>
<select name="category" class="form-select bg-dark text-white border-secondary">
<option value="Sci-Fi">Ficção Científica</option>
<option value="Ação">Ação & Aventura</option>
<option value="Drama">Drama Cinematográfico</option>
<option value="Terror">Terror Psicológico</option>
<option value="Fantasia">Fantasia Épica</option>
<option value="Cyberpunk">Cyberpunk / Neon-Noir</option>
</select>
</div>
<div class="mb-4">
<label class="form-label text-cyan fw-bold">TEMA OU IDEIA CENTRAL</label>
<textarea name="theme" class="form-control bg-dark text-white border-secondary" rows="3" placeholder="Ex: Uma guerra entre IAs e humanos em um Rio de Janeiro futurista..."></textarea>
<div class="form-text text-muted">A IA criará personagens reais, cenas e roteiro completo com base nisso.</div>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-lg btn-neon-purple fw-bold py-3" id="generateBtn">
<span class="spinner-border spinner-border-sm d-none" id="loader"></span>
<i class="bi bi-magic me-2"></i> LANÇAR SUPER-PRODUÇÃO AGORA
</button>
</div>
</form>
<div id="loadingMsg" class="text-center mt-4 d-none">
<p class="text-cyan animate-pulse">A IA está gerando o roteiro, personagens e cenas... Aguarde o processamento cinematográfico.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
document.getElementById('aiForm').onsubmit = function() {
document.getElementById('generateBtn').disabled = true;
document.getElementById('loader').classList.remove('d-none');
document.getElementById('loadingMsg').classList.remove('d-none');
};
</script>
<style>
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: .5; }
}
</style>
{% endblock %}

View File

@ -1,10 +1,12 @@
from django.urls import path from django.urls import path
from .views import home, project_detail, asset_library, admin_login, admin_logout from . import views
urlpatterns = [ urlpatterns = [
path("", home, name="home"), path('', views.home, name='home'),
path("project/<slug:slug>/", project_detail, name="project_detail"), path('admin-login/', views.admin_login, name='admin_login'),
path("assets/", asset_library, name="asset_library"), path('admin-logout/', views.admin_logout, name='admin_logout'),
path("studio-admin/login/", admin_login, name="admin_login"), path('assets/', views.asset_library, name='asset_library'),
path("studio-admin/logout/", admin_logout, name="admin_logout"), path('studio-ai/', views.studio_ai, name='studio_ai'),
path('generate-production/', views.generate_production, name='generate_production'),
path('project/<slug:slug>/', views.project_detail, name='project_detail'),
] ]

View File

@ -1,11 +1,12 @@
import os import os
import platform import json
from functools import wraps from functools import wraps
from django import get_version as django_version
from django.shortcuts import render, get_object_or_404, redirect from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone from django.utils import timezone
from django.contrib import messages from django.contrib import messages
from .models import Project, PipelineStep, CgiAsset, StudioConfig from django.http import JsonResponse
from .models import Project, PipelineStep, CgiAsset, StudioConfig, Scene
from ai.local_ai_api import LocalAIApi
def studio_admin_required(view_func): def studio_admin_required(view_func):
"""Decorator to restrict access to studio admin only.""" """Decorator to restrict access to studio admin only."""
@ -19,12 +20,11 @@ def studio_admin_required(view_func):
def home(request): def home(request):
"""Render the CGI Studio Command Center.""" """Render the CGI Studio Command Center."""
# Ensure StudioConfig exists and generate key if needed
config, created = StudioConfig.objects.get_or_create(id=1) config, created = StudioConfig.objects.get_or_create(id=1)
if created or not config.admin_access_key: if created or not config.admin_access_key:
config.save() # Triggers uuid generation config.save()
projects = Project.objects.prefetch_related('steps').all() projects = Project.objects.prefetch_related('steps').all().order_by('-created_at')
total_projects = projects.count() total_projects = projects.count()
active_productions = projects.filter(status='PROD').count() active_productions = projects.filter(status='PROD').count()
@ -40,6 +40,96 @@ def home(request):
} }
return render(request, "core/index.html", context) return render(request, "core/index.html", context)
@studio_admin_required
def studio_ai(request):
"""Page to configure and launch AI-automated productions."""
return render(request, "core/studio_ai.html")
@studio_admin_required
def generate_production(request):
"""AI logic to create a full production automatically."""
if request.method == "POST":
category = request.POST.get("category", "Sci-Fi")
proj_type = request.POST.get("project_type", "MOVIE")
theme = request.POST.get("theme", "Future of humanity")
prompt = f"""
Create a detailed production plan for a {proj_type} in the {category} category.
Theme: {theme}
Requirements:
1. Unique Title and a compelling description.
2. 3 Main Characters with names and detailed physical descriptions (based on REAL humans/actors style).
3. 5 Key Scenes with titles and narrative descriptions.
Return the result ONLY in JSON format with the following structure:
{{
"title": "...",
"description": "...",
"characters": [
{{"name": "...", "description": "...", "type": "CHAR"}}
],
"scenes": [
{{"title": "...", "description": "..."}}
]
}}
"""
response = LocalAIApi.create_response({
"input": [
{"role": "system", "content": "You are an expert Hollywood Producer and AI Cinema Director."},
{"role": "user", "content": prompt},
],
"response_format": {"type": "json_object"},
})
if response.get("success"):
try:
data = LocalAIApi.decode_json_from_response(response)
# Create the Project
project = Project.objects.create(
title=data['title'],
project_type=proj_type,
category=category,
description=data['description'],
is_ai_generated=True,
status='PRE'
)
# Create default Pipeline Steps
stages = [s[0] for s in PipelineStep.STAGES]
for stage in stages:
PipelineStep.objects.create(project=project, name=stage, progress=0)
# Create Characters (Assets)
for char in data['characters']:
CgiAsset.objects.create(
project=project,
name=char['name'],
asset_type='CHAR',
physical_description=char['description']
)
# Create Scenes
for i, scene in enumerate(data['scenes']):
Scene.objects.create(
project=project,
number=i+1,
title=scene['title'],
description=scene['description']
)
messages.success(request, f"Super Produção '{project.title}' criada com sucesso pelo Studio AI!")
return redirect('project_detail', slug=project.slug)
except Exception as e:
messages.error(request, f"Erro ao processar resposta da AI: {str(e)}")
else:
messages.error(request, f"Erro na API de IA: {response.get('error')}")
return redirect('studio_ai')
def admin_login(request): def admin_login(request):
"""View to enter the unique admin access key.""" """View to enter the unique admin access key."""
if request.method == "POST": if request.method == "POST":
@ -80,11 +170,12 @@ def asset_library(request):
def project_detail(request, slug): def project_detail(request, slug):
"""Render the detailed pipeline for a specific production.""" """Render the detailed pipeline for a specific production."""
project = get_object_or_404(Project.objects.prefetch_related('steps', 'assets'), slug=slug) project = get_object_or_404(Project.objects.prefetch_related('steps', 'assets', 'scenes'), slug=slug)
context = { context = {
"project": project, "project": project,
"steps": project.steps.all(), "steps": project.steps.all(),
"assets": project.assets.all(), "assets": project.assets.all(),
"scenes": project.scenes.all(),
} }
return render(request, "core/project_detail.html", context) return render(request, "core/project_detail.html", context)