Nota: Clique em Salvar no editor Flatlogic para pe
This commit is contained in:
parent
cfc1f5d70a
commit
a195d0853c
BIN
ai/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
ai/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
ai/__pycache__/local_ai_api.cpython-311.pyc
Normal file
BIN
ai/__pycache__/local_ai_api.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from .models import Project, PipelineStep, CgiAsset
|
||||
from .models import Project, PipelineStep, CgiAsset, Scene, StudioConfig
|
||||
|
||||
class PipelineStepInline(admin.TabularInline):
|
||||
model = PipelineStep
|
||||
@ -9,13 +9,17 @@ class CgiAssetInline(admin.TabularInline):
|
||||
model = CgiAsset
|
||||
extra = 1
|
||||
|
||||
class SceneInline(admin.TabularInline):
|
||||
model = Scene
|
||||
extra = 1
|
||||
|
||||
@admin.register(Project)
|
||||
class ProjectAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'project_type', 'status', 'created_at')
|
||||
list_filter = ('project_type', 'status')
|
||||
list_display = ('title', 'project_type', 'category', 'status', 'is_ai_generated', 'created_at')
|
||||
list_filter = ('project_type', 'status', 'is_ai_generated')
|
||||
search_fields = ('title', 'description')
|
||||
prepopulated_fields = {'slug': ('title',)}
|
||||
inlines = [PipelineStepInline, CgiAssetInline]
|
||||
inlines = [PipelineStepInline, CgiAssetInline, SceneInline]
|
||||
|
||||
@admin.register(PipelineStep)
|
||||
class PipelineStepAdmin(admin.ModelAdmin):
|
||||
@ -26,3 +30,12 @@ class PipelineStepAdmin(admin.ModelAdmin):
|
||||
class CgiAssetAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'project', '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')
|
||||
@ -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'],
|
||||
},
|
||||
),
|
||||
]
|
||||
Binary file not shown.
@ -31,11 +31,14 @@ class Project(models.Model):
|
||||
title = models.CharField(max_length=255)
|
||||
slug = models.SlugField(unique=True, blank=True)
|
||||
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')
|
||||
description = models.TextField(blank=True)
|
||||
full_script = models.TextField(blank=True)
|
||||
thumbnail_url = models.URLField(blank=True, help_text="URL to a representative image")
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
is_ai_generated = models.BooleanField(default=False)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
@ -45,6 +48,19 @@ class Project(models.Model):
|
||||
def __str__(self):
|
||||
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):
|
||||
STAGES = (
|
||||
# Pre-Production
|
||||
@ -85,10 +101,11 @@ class CgiAsset(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
asset_type = models.CharField(max_length=10, choices=ASSET_TYPES)
|
||||
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')
|
||||
version = models.PositiveIntegerField(default=1)
|
||||
file_location = models.CharField(max_length=500, blank=True, help_text="Path or URL to the digital file")
|
||||
assigned_artist = models.CharField(max_length=100, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.get_asset_type_display()})"
|
||||
return f"{self.name} ({self.get_asset_type_display()})"
|
||||
@ -28,6 +28,9 @@
|
||||
<a href="{% url 'asset_library' %}" class="btn btn-outline-purple shadow-neon">
|
||||
<i class="bi bi-box-seam me-2"></i> Biblioteca de Assets
|
||||
</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>
|
||||
{% if is_admin %}
|
||||
@ -55,6 +58,9 @@
|
||||
{% else %}
|
||||
<div class="text-center">
|
||||
<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>
|
||||
{% endif %}
|
||||
</div>
|
||||
@ -62,20 +68,23 @@
|
||||
<div class="card-body p-4">
|
||||
<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>
|
||||
<span class="badge {% if project.status == 'PROD' %}bg-cyan text-dark{% else %}bg-purple{% endif %}">
|
||||
{{ project.get_status_display }}
|
||||
</span>
|
||||
<div class="text-end">
|
||||
<span class="badge {% if project.status == 'PROD' %}bg-cyan text-dark{% else %}bg-purple{% endif %} d-block mb-1">
|
||||
{{ project.get_status_display }}
|
||||
</span>
|
||||
<span class="badge bg-dark border border-secondary small">{{ project.category }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-secondary small mb-4">{{ project.description|truncatewords:20 }}</p>
|
||||
|
||||
<!-- Pipeline Progress Mini -->
|
||||
<div class="mb-4">
|
||||
<div class="d-flex justify-content-between small text-secondary mb-1">
|
||||
<span>Progresso Geral</span>
|
||||
<span>85%</span> <!-- Dynamic calculation could go here -->
|
||||
<span>Status Pipeline</span>
|
||||
<span>{% with last=project.steps.last %}{{ last.progress|default:0 }}{% endwith %}%</span>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
@ -98,4 +107,4 @@
|
||||
}
|
||||
.bg-cyan { background-color: var(--electric-cyan) !important; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@ -1,7 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ project.title }} | Pipeline Detail{% endblock %}
|
||||
{% block title %}{{ project.title }} | Studio AI Production{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="hero-section py-5">
|
||||
@ -15,13 +15,19 @@
|
||||
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-8">
|
||||
<span class="pipeline-badge badge-{{ project.status|lower }} mb-3 d-inline-block">{{ project.get_status_display }}</span>
|
||||
<h1 class="display-4 outfit mb-3">{{ project.title }}</h1>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<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>
|
||||
</div>
|
||||
<div class="col-lg-4 text-lg-end">
|
||||
<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">
|
||||
{% with last_step=project.steps.last %}{{ last_step.progress|default:0 }}{% endwith %}%
|
||||
</span>
|
||||
@ -33,71 +39,105 @@
|
||||
|
||||
<section class="py-5">
|
||||
<div class="container">
|
||||
<div class="row g-5">
|
||||
<div class="col-lg-8">
|
||||
<h3 class="h4 outfit mb-4 section-title">CGI Production Pipeline</h3>
|
||||
|
||||
<div class="pipeline-flow">
|
||||
<ul class="nav nav-pills mb-5 justify-content-center" id="prodTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<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="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 %}
|
||||
<div class="project-card p-4 mb-3 border-opacity-10">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<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;">
|
||||
{{ forloop.counter }}
|
||||
</div>
|
||||
<h4 class="h6 mb-0 outfit">{{ step.get_name_display }}</h4>
|
||||
<div class="col-md-4 col-lg-3">
|
||||
<div class="project-card p-4 h-100 border-opacity-10">
|
||||
<div class="text-muted small mb-2">ETAPA {{ forloop.counter }}</div>
|
||||
<h4 class="h6 mb-3 outfit">{{ step.get_name_display }}</h4>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="small text-muted">Progresso</span>
|
||||
<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>
|
||||
{% 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 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>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="sticky-top" style="top: 100px;">
|
||||
<h3 class="h4 outfit mb-4 section-title">Assets Digitais</h3>
|
||||
<div class="assets-container">
|
||||
{% for asset in assets %}
|
||||
<div class="asset-list-item d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="d-block outfit fw-bold small">{{ asset.name }}</span>
|
||||
<span class="text-muted" style="font-size: 0.7rem;">{{ asset.get_asset_type_display }} • {{ asset.current_stage }}</span>
|
||||
|
||||
<!-- Script & Scenes Tab -->
|
||||
<div class="tab-pane fade" id="script" role="tabpanel">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-9">
|
||||
{% for scene in scenes %}
|
||||
<div class="scene-item mb-5 p-4 glass-card border-opacity-10 rounded-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<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;">
|
||||
{{ 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>
|
||||
{% 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>
|
||||
{% 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 %}
|
||||
</div>
|
||||
|
||||
<div class="mt-5 p-4 bg-purple bg-opacity-05 rounded-4 border border-purple border-opacity-10">
|
||||
<h5 class="h6 outfit text-purple mb-3">Diretrizes de Qualidade</h5>
|
||||
<ul class="list-unstyled small text-muted">
|
||||
<li class="mb-2">✓ Topologia limpa para rigging</li>
|
||||
<li class="mb-2">✓ Texturas 4K/8K PBR</li>
|
||||
<li class="mb-2">✓ Iluminação física (PBR)</li>
|
||||
<li>✓ Renderização em EXR Multi-camada</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cast & Characters Tab -->
|
||||
<div class="tab-pane fade" id="cast" role="tabpanel">
|
||||
<div class="row g-4">
|
||||
{% for asset in assets %}
|
||||
{% if asset.asset_type == 'CHAR' %}
|
||||
<div class="col-md-6">
|
||||
<div class="project-card p-4 h-100 border-opacity-10 d-flex gap-4">
|
||||
<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>
|
||||
{% endif %}
|
||||
{% empty %}
|
||||
<p class="text-center text-muted py-5">Nenhum personagem definido no elenco.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
<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 %}
|
||||
|
||||
79
core/templates/core/studio_ai.html
Normal file
79
core/templates/core/studio_ai.html
Normal 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 %}
|
||||
16
core/urls.py
16
core/urls.py
@ -1,10 +1,12 @@
|
||||
from django.urls import path
|
||||
from .views import home, project_detail, asset_library, admin_login, admin_logout
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", home, name="home"),
|
||||
path("project/<slug:slug>/", project_detail, name="project_detail"),
|
||||
path("assets/", asset_library, name="asset_library"),
|
||||
path("studio-admin/login/", admin_login, name="admin_login"),
|
||||
path("studio-admin/logout/", admin_logout, name="admin_logout"),
|
||||
]
|
||||
path('', views.home, name='home'),
|
||||
path('admin-login/', views.admin_login, name='admin_login'),
|
||||
path('admin-logout/', views.admin_logout, name='admin_logout'),
|
||||
path('assets/', views.asset_library, name='asset_library'),
|
||||
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'),
|
||||
]
|
||||
107
core/views.py
107
core/views.py
@ -1,11 +1,12 @@
|
||||
import os
|
||||
import platform
|
||||
import json
|
||||
from functools import wraps
|
||||
from django import get_version as django_version
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.utils import timezone
|
||||
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):
|
||||
"""Decorator to restrict access to studio admin only."""
|
||||
@ -19,12 +20,11 @@ def studio_admin_required(view_func):
|
||||
|
||||
def home(request):
|
||||
"""Render the CGI Studio Command Center."""
|
||||
# Ensure StudioConfig exists and generate key if needed
|
||||
config, created = StudioConfig.objects.get_or_create(id=1)
|
||||
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()
|
||||
active_productions = projects.filter(status='PROD').count()
|
||||
@ -40,6 +40,96 @@ def home(request):
|
||||
}
|
||||
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):
|
||||
"""View to enter the unique admin access key."""
|
||||
if request.method == "POST":
|
||||
@ -80,11 +170,12 @@ def asset_library(request):
|
||||
|
||||
def project_detail(request, slug):
|
||||
"""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 = {
|
||||
"project": project,
|
||||
"steps": project.steps.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)
|
||||
Loading…
x
Reference in New Issue
Block a user