import os import json from functools import wraps from django.shortcuts import render, get_object_or_404, redirect from django.utils import timezone from django.contrib import messages from django.http import JsonResponse from .models import Project, PipelineStep, CgiAsset, StudioConfig, Scene from ai.local_ai_api import LocalAIApi from .pexels import fetch_first, fetch_video def studio_admin_required(view_func): """Decorator to restrict access to studio admin only.""" @wraps(view_func) def _wrapped_view(request, *args, **kwargs): if not request.session.get('is_studio_admin', False): messages.warning(request, "Acesso restrito. Por favor, insira sua chave de administrador.") return redirect('admin_login') return view_func(request, *args, **kwargs) return _wrapped_view def home(request): """Render the CGI Studio Command Center.""" config, created = StudioConfig.objects.get_or_create(id=1) if created or not config.admin_access_key: config.save() projects = Project.objects.prefetch_related('steps').all().order_by('-created_at') total_projects = projects.count() active_productions = projects.filter(status='PROD').count() completed_projects = projects.filter(status='DONE').count() context = { "projects": projects, "total_projects": total_projects, "active_productions": active_productions, "completed_projects": completed_projects, "current_time": timezone.now(), "is_admin": request.session.get('is_studio_admin', False), } return render(request, "core/index.html", context) @studio_admin_required def studio_ai(request): """Page to configure and launch AI-automated productions.""" context = { "project_types": Project.TYPES, "voice_presets": Project.VOICE_CHOICES, } return render(request, "core/studio_ai.html", context) @studio_admin_required def generate_production(request): """AI logic to create a full SUPER PRODUCTION with Video, Audio, and CRUD.""" 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") voice_preset = request.POST.get("voice_preset", "male_1") prompt = f""" Create a detailed SUPER PRODUCTION plan for a {proj_type} in the {category} category. Theme: {theme} Requirements: 1. Unique Title and a compelling description. 2. A full cinematic screenplay (script). 3. A visual query for a Banner/Capa (2-4 words). 4. 3 Characters with names, physical descriptions, and a 'voice_preset' (choice of: v_male_1, v_male_2, v_female_1, v_female_2). 5. 6 Key Scenes with titles, descriptions, and a unique 'video_query' (2-4 words) for each scene. 6. Metadata: Budget, Rating, Duration. Return JSON: {{ "title": "...", "description": "...", "full_script": "...", "banner_query": "...", "budget": "...", "rating": "...", "duration": "...", "characters": [ {{"name": "...", "description": "...", "voice_preset": "..."}} ], "scenes": [ {{"title": "...", "description": "...", "video_query": "..."}} ] }} """ response = LocalAIApi.create_response({ "input": [ {"role": "system", "content": "You are a Hollywood AI Director creating cinematic video-based productions."}, {"role": "user", "content": prompt}, ], "text": {"format": {"type": "json_object"}}, }) if response.get("success"): try: data = LocalAIApi.decode_json_from_response(response) # Fetch Banner banner_data = fetch_first(data.get('banner_query', data['title'])) banner_url = banner_data['local_path'] if banner_data else "" # Create the Project project = Project.objects.create( title=data['title'], project_type=proj_type, category=category, description=data['description'], full_script=data.get('full_script', ''), thumbnail_url=banner_url, banner_url=banner_url, is_ai_generated=True, status='DONE', voice_preset=voice_preset, estimated_budget=data.get('budget', '$200M'), rating=data.get('rating', 'PG-13'), duration=data.get('duration', '130 min') ) # Create default Pipeline Steps for stage in [s[0] for s in PipelineStep.STAGES]: PipelineStep.objects.create(project=project, name=stage, progress=100, is_completed=True) # Characters for char in data['characters']: CgiAsset.objects.create( project=project, name=char['name'], asset_type='CHAR', physical_description=char['description'], voice_preset=char.get('voice_preset', 'v_male_1') ) # Scenes + Videos for i, scene_data in enumerate(data['scenes']): v_query = scene_data.get('video_query', scene_data['title']) # Fetch Video for scene video_res = fetch_video(f"{data['title']} {v_query}") video_path = video_res['local_path'] if video_res else "" # Fetch Image as fallback/thumbnail for scene image_res = fetch_first(f"{data['title']} {v_query}", orientation="landscape") image_path = image_res['local_path'] if image_res else "" Scene.objects.create( project=project, number=i+1, title=scene_data['title'], description=scene_data['description'], visual_prompt=v_query, image_url=image_path, video_url=video_path ) # Overall production video (using the first scene's video as main) if project.scenes.exists(): project.video_url = project.scenes.first().video_url project.save() messages.success(request, f"Produção '{project.title}' gerada com Sucesso! Banner e Vídeos salvos.") return redirect('production_library') except Exception as e: messages.error(request, f"Erro ao processar: {str(e)}") else: messages.error(request, f"Erro AI: {response.get('error')}") return redirect('studio_ai') @studio_admin_required def edit_production(request, slug): """View to edit an existing production's metadata.""" project = get_object_or_404(Project, slug=slug) if request.method == "POST": project.title = request.POST.get("title", project.title) project.description = request.POST.get("description", project.description) project.category = request.POST.get("category", project.category) project.estimated_budget = request.POST.get("budget", project.estimated_budget) project.duration = request.POST.get("duration", project.duration) project.rating = request.POST.get("rating", project.rating) project.save() messages.success(request, f"Produção '{project.title}' atualizada com sucesso.") return redirect('production_library') return render(request, "core/edit_production.html", {"project": project}) @studio_admin_required def delete_production(request, slug): """View to delete a production.""" project = get_object_or_404(Project, slug=slug) title = project.title project.delete() messages.success(request, f"Produção '{title}' excluída.") return redirect('production_library') @studio_admin_required def production_library(request): """View to see all completed AI productions.""" productions = Project.objects.filter(is_ai_generated=True).order_by('-created_at') return render(request, "core/production_library.html", {"productions": productions}) @studio_admin_required def watch_production(request, slug): """View to 'watch' (read and experience) a complete production.""" project = get_object_or_404(Project.objects.prefetch_related('assets', 'scenes'), slug=slug) return render(request, "core/watch_production.html", {"project": project}) def admin_login(request): """View to enter the unique admin access key.""" if request.method == "POST": key = request.POST.get("access_key") try: config = StudioConfig.objects.get(id=1) if key == config.admin_access_key: request.session['is_studio_admin'] = True messages.success(request, "Bem-vindo, Comandante do Estúdio!") return redirect('home') else: messages.error(request, "Chave de acesso inválida.") except StudioConfig.DoesNotExist: messages.error(request, "Configuração do estúdio não encontrada.") return render(request, "core/admin_login.html") def admin_logout(request): """Logout the studio admin.""" request.session['is_studio_admin'] = False return redirect('home') @studio_admin_required def asset_library(request): """View all digital assets (Characters, Props, Environments).""" assets = CgiAsset.objects.select_related('project').all() asset_types = { 'CHAR': assets.filter(asset_type='CHAR'), 'PROP': assets.filter(asset_type='PROP'), 'ENV': assets.filter(asset_type='ENV'), } return render(request, "core/asset_library.html", {"assets": assets, "asset_types": asset_types}) def project_detail(request, slug): """Render the detailed pipeline for a specific production.""" project = get_object_or_404(Project.objects.prefetch_related('steps', 'assets', 'scenes'), slug=slug) return render(request, "core/project_detail.html", {"project": project})