diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 3efa90c..8e9ab81 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/pexels.cpython-311.pyc b/core/__pycache__/pexels.cpython-311.pyc index c9dbc46..69816d0 100644 Binary files a/core/__pycache__/pexels.cpython-311.pyc and b/core/__pycache__/pexels.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index b7cf384..fecd489 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index ddf0125..0266ed7 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/migrations/0006_cgiasset_voice_preset_project_banner_url_and_more.py b/core/migrations/0006_cgiasset_voice_preset_project_banner_url_and_more.py new file mode 100644 index 0000000..ac9d033 --- /dev/null +++ b/core/migrations/0006_cgiasset_voice_preset_project_banner_url_and_more.py @@ -0,0 +1,63 @@ +# Generated by Django 5.2.7 on 2026-02-16 02:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0005_project_duration_project_estimated_budget_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='cgiasset', + name='voice_preset', + field=models.CharField(choices=[('v_male_1', 'Actor 1 (Adult Male)'), ('v_male_2', 'Actor 2 (Mature Male)'), ('v_female_1', 'Actress 1 (Adult Female)'), ('v_female_2', 'Actress 2 (Young Female)')], default='v_male_1', max_length=50), + ), + migrations.AddField( + model_name='project', + name='banner_url', + field=models.URLField(blank=True, max_length=500), + ), + migrations.AddField( + model_name='project', + name='video_url', + field=models.CharField(blank=True, help_text='Path to generated video file', max_length=500), + ), + migrations.AddField( + model_name='project', + name='voice_preset', + field=models.CharField(choices=[('male_1', 'James (Natural Male)'), ('male_2', 'Robert (Deep Narrative)'), ('female_1', 'Emma (Soft Female)'), ('female_2', 'Sophia (Professional)'), ('robot', 'CGI Assist (Neural)')], default='male_1', max_length=50), + ), + migrations.AddField( + model_name='scene', + name='video_url', + field=models.CharField(blank=True, max_length=500), + ), + migrations.AlterField( + model_name='cgiasset', + name='file_location', + field=models.CharField(blank=True, max_length=500), + ), + migrations.AlterField( + model_name='cgiasset', + name='physical_description', + field=models.TextField(blank=True), + ), + migrations.AlterField( + model_name='pipelinestep', + name='progress', + field=models.PositiveIntegerField(default=0), + ), + migrations.AlterField( + model_name='project', + name='project_type', + field=models.CharField(choices=[('MOVIE', 'Feature Film'), ('SERIES', 'TV Series'), ('DOCUMENTARY', 'Documentary'), ('SHORT', 'Short Film')], default='MOVIE', max_length=20), + ), + migrations.AlterField( + model_name='project', + name='thumbnail_url', + field=models.URLField(blank=True, max_length=500), + ), + ] diff --git a/core/migrations/__pycache__/0006_cgiasset_voice_preset_project_banner_url_and_more.cpython-311.pyc b/core/migrations/__pycache__/0006_cgiasset_voice_preset_project_banner_url_and_more.cpython-311.pyc new file mode 100644 index 0000000..241ef06 Binary files /dev/null and b/core/migrations/__pycache__/0006_cgiasset_voice_preset_project_banner_url_and_more.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 9a958fb..48f4d03 100644 --- a/core/models.py +++ b/core/models.py @@ -11,7 +11,6 @@ class StudioConfig(models.Model): def save(self, *args, **kwargs): if not self.admin_access_key: - # Using the key requested by the user as default self.admin_access_key = "61823dbc-ee05-455f-8924-764f15104fc1" super().save(*args, **kwargs) @@ -22,6 +21,7 @@ class Project(models.Model): TYPES = ( ('MOVIE', 'Feature Film'), ('SERIES', 'TV Series'), + ('DOCUMENTARY', 'Documentary'), ('SHORT', 'Short Film'), ) STATUS_CHOICES = ( @@ -30,15 +30,25 @@ class Project(models.Model): ('POST', 'Post-Production'), ('DONE', 'Completed'), ) + VOICE_CHOICES = ( + ('male_1', 'James (Natural Male)'), + ('male_2', 'Robert (Deep Narrative)'), + ('female_1', 'Emma (Soft Female)'), + ('female_2', 'Sophia (Professional)'), + ('robot', 'CGI Assist (Neural)'), + ) title = models.CharField(max_length=255) slug = models.SlugField(unique=True, blank=True) - project_type = models.CharField(max_length=10, choices=TYPES, default='MOVIE') + project_type = models.CharField(max_length=20, 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") + thumbnail_url = models.URLField(blank=True, max_length=500) + banner_url = models.URLField(blank=True, max_length=500) + video_url = models.CharField(max_length=500, blank=True, help_text="Path to generated video file") + voice_preset = models.CharField(max_length=50, choices=VOICE_CHOICES, default='male_1') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) is_ai_generated = models.BooleanField(default=False) @@ -72,6 +82,7 @@ class Scene(models.Model): description = models.TextField() visual_prompt = models.TextField(blank=True) image_url = models.CharField(max_length=500, blank=True) + video_url = models.CharField(max_length=500, blank=True) class Meta: ordering = ['number'] @@ -81,25 +92,22 @@ class Scene(models.Model): class PipelineStep(models.Model): STAGES = ( - # Pre-Production ('SCRIPT', 'Roteiro & Storyboard'), ('CONCEPT', 'Concept Art'), ('ANIMATIC', 'Animatic'), - # Production ('MODELING', 'Modelagem 3D'), ('TEXTURING', 'Texturização'), ('RIGGING', 'Rigging'), ('ANIMATION', 'Animação'), ('LIGHTING', 'Iluminação'), ('FX', 'Simulação (FX)'), - # Post-Production ('RENDERING', 'Renderização'), ('COMPOSITING', 'Composição'), ('EDITING', 'Edição & Sonoplastia'), ) project = models.ForeignKey(Project, related_name='steps', on_delete=models.CASCADE) name = models.CharField(max_length=20, choices=STAGES) - progress = models.PositiveIntegerField(default=0, help_text="Progress from 0 to 100") + progress = models.PositiveIntegerField(default=0) is_completed = models.BooleanField(default=False) updated_at = models.DateTimeField(auto_now=True) @@ -115,15 +123,22 @@ class CgiAsset(models.Model): ('PROP', 'Prop'), ('ENV', 'Environment'), ) + VOICE_CHOICES = ( + ('v_male_1', 'Actor 1 (Adult Male)'), + ('v_male_2', 'Actor 2 (Mature Male)'), + ('v_female_1', 'Actress 1 (Adult Female)'), + ('v_female_2', 'Actress 2 (Young Female)'), + ) project = models.ForeignKey(Project, related_name='assets', on_delete=models.CASCADE) 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") + physical_description = models.TextField(blank=True) + voice_preset = models.CharField(max_length=50, choices=VOICE_CHOICES, default='v_male_1') 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") + file_location = models.CharField(max_length=500, blank=True) 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()})" \ No newline at end of file diff --git a/core/pexels.py b/core/pexels.py index 3be1c33..c8736af 100644 --- a/core/pexels.py +++ b/core/pexels.py @@ -3,7 +3,8 @@ import requests from pathlib import Path API_KEY = os.getenv("PEXELS_KEY", "Vc99rnmOhHhJAbgGQoKLZtsaIVfkeownoQNbTj78VemUjKh08ZYRbf18") -CACHE_DIR = Path("static/images/pexels") +IMAGE_CACHE_DIR = Path("static/images/pexels") +VIDEO_CACHE_DIR = Path("static/videos/pexels") def fetch_first(query: str, orientation: str = "landscape") -> dict | None: if not API_KEY: @@ -23,10 +24,10 @@ def fetch_first(query: str, orientation: str = "landscape") -> dict | None: return None src = photo["src"].get("large2x") or photo["src"].get("large") or photo["src"].get("original") - CACHE_DIR.mkdir(parents=True, exist_ok=True) - target = CACHE_DIR / f"{photo['id']}.jpg" + IMAGE_CACHE_DIR.mkdir(parents=True, exist_ok=True) + target = IMAGE_CACHE_DIR / f"{photo['id']}.jpg" - if src: + if src and not target.exists(): img_resp = requests.get(src, timeout=15) img_resp.raise_for_status() target.write_bytes(img_resp.content) @@ -35,8 +36,56 @@ def fetch_first(query: str, orientation: str = "landscape") -> dict | None: "id": photo["id"], "local_path": f"images/pexels/{photo['id']}.jpg", "photographer": photo.get("photographer"), - "photographer_url": photo.get("photographer_url"), } except Exception as e: - print(f"Error fetching from Pexels: {e}") + print(f"Error fetching image from Pexels: {e}") return None + +def fetch_video(query: str, orientation: str = "landscape") -> dict | None: + if not API_KEY: + return None + + headers = {"Authorization": API_KEY} + url = "https://api.pexels.com/videos/search" + params = {"query": query, "orientation": orientation, "per_page": 1, "page": 1} + + try: + resp = requests.get(url, headers=headers, params=params, timeout=15) + resp.raise_for_status() + data = resp.json() + + video_data = (data.get("videos") or [None])[0] + if not video_data: + return None + + # Get the best HD video link + video_files = video_data.get("video_files", []) + # Prefer HD/Full HD mp4 + best_file = None + for f in video_files: + if f.get("file_type") == "video/mp4": + if not best_file or f.get("width", 0) > best_file.get("width", 0): + best_file = f + + if not best_file: + return None + + src = best_file["link"] + VIDEO_CACHE_DIR.mkdir(parents=True, exist_ok=True) + target = VIDEO_CACHE_DIR / f"{video_data['id']}.mp4" + + if src and not target.exists(): + vid_resp = requests.get(src, timeout=30, stream=True) + vid_resp.raise_for_status() + with open(target, 'wb') as f: + for chunk in vid_resp.iter_content(chunk_size=8192): + f.write(chunk) + + return { + "id": video_data["id"], + "local_path": f"videos/pexels/{video_data['id']}.mp4", + "user": video_data.get("user", {}).get("name"), + } + except Exception as e: + print(f"Error fetching video from Pexels: {e}") + return None \ No newline at end of file diff --git a/core/templates/core/edit_production.html b/core/templates/core/edit_production.html new file mode 100644 index 0000000..5cb339c --- /dev/null +++ b/core/templates/core/edit_production.html @@ -0,0 +1,63 @@ +{% extends 'base.html' %} +{% load static %} + +{% block content %} +
Sua coleção de Super Produções geradas por Inteligência Artificial.
+Minhas Super Produções Geradas por IA
{{ production.category }} • {{ production.created_at|date:"d/m/Y" }}
-{{ production.description }}
-O Studio AI está pronto para transformar suas ideias em obras-primas.
- COMEÇAR AGORAVá ao Studio AI para criar sua primeira obra prima!
+ IR AO STUDIO AI +CGI Studio - Cinema Digital Inteligente
+Crie Super-Produções (Filmes e Séries) totalmente automatizadas com IA.
-{{ project.description }}
-{{ asset.physical_description }}
Protagonista CGI
+{{ char.physical_description|truncatechars:100 }}
+ +{{ project.full_script }}