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 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')
|
||||||
@ -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)
|
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,10 +101,11 @@ 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")
|
||||||
assigned_artist = models.CharField(max_length=100, blank=True)
|
assigned_artist = models.CharField(max_length=100, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
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">
|
<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>
|
||||||
|
|
||||||
@ -98,4 +107,4 @@
|
|||||||
}
|
}
|
||||||
.bg-cyan { background-color: var(--electric-cyan) !important; }
|
.bg-cyan { background-color: var(--electric-cyan) !important; }
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -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>
|
||||||
<div class="pipeline-flow">
|
<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 %}
|
{% 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 class="mt-5 p-4 bg-purple bg-opacity-05 rounded-4 border border-purple border-opacity-10">
|
</div>
|
||||||
<h5 class="h6 outfit text-purple mb-3">Diretrizes de Qualidade</h5>
|
|
||||||
<ul class="list-unstyled small text-muted">
|
<!-- Cast & Characters Tab -->
|
||||||
<li class="mb-2">✓ Topologia limpa para rigging</li>
|
<div class="tab-pane fade" id="cast" role="tabpanel">
|
||||||
<li class="mb-2">✓ Texturas 4K/8K PBR</li>
|
<div class="row g-4">
|
||||||
<li class="mb-2">✓ Iluminação física (PBR)</li>
|
{% for asset in assets %}
|
||||||
<li>✓ Renderização em EXR Multi-camada</li>
|
{% if asset.asset_type == 'CHAR' %}
|
||||||
</ul>
|
<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>
|
</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>
|
||||||
{% 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 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'),
|
||||||
|
]
|
||||||
107
core/views.py
107
core/views.py
@ -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)
|
||||||
Loading…
x
Reference in New Issue
Block a user