Files
2026-hypn0-site/hypn0/hypn0_site/views.py
T

173 lines
6.6 KiB
Python

from django.core.files.base import ContentFile
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_POST
from .forms import HalftoneGenerateForm
from .models import TbHypn0Item
from .services.halftone import generate_halftone_svg, prepare_gallery_svg
from .services.naming import generate_hypno_title
@ensure_csrf_cookie
def index(request: HttpRequest | None) -> HttpResponse:
return render(request, "index.html", {})
def tmp(request: HttpRequest | None) -> HttpResponse:
return render(request, "tmp.html", {})
@require_POST
def generate(request: HttpRequest) -> HttpResponse:
"""
HTMX-эндпоинт для генерации гипнотического полутонового SVG на лету.
Не сохраняет данные в БД (Zero-Disk / Zero-PII).
"""
form = HalftoneGenerateForm(request.POST, request.FILES)
if not form.is_valid():
error_msg = next(iter(form.errors.values()))[0] if form.errors else "Ошибка параметров генерации"
return render(request, "block/preview.html", {"error": error_msg})
image_file = form.cleaned_data["image"]
shape = form.cleaned_data["shape"]
cols = form.cleaned_data["cols"]
max_radius = form.cleaned_data["max_radius"]
blink = form.cleaned_data["blink"]
rotation = form.cleaned_data["rotation"]
scale = form.cleaned_data["scale"]
angle = form.cleaned_data["angle"]
# Извлечение основного цвета (поддержка монотонной схемы)
colors = request.POST.getlist("colors")
primary_color = colors[0] if colors and colors[0] else "#a855ff"
try:
svg_content = generate_halftone_svg(
image=image_file,
cols=cols,
max_radius=max_radius,
shape=shape,
color=primary_color,
blink=blink,
rotation=rotation,
scale=scale,
angle=angle,
)
except Exception as e:
return render(
request,
"block/preview.html",
{"error": f"Сбой в матрице гипноза: {str(e)}"},
)
return render(
request,
"block/preview.html",
{
"svg_content": svg_content,
"form_data": form.cleaned_data,
"primary_color": primary_color,
},
)
@require_POST
def publish(request: HttpRequest) -> HttpResponse:
"""
HTMX-эндпоинт для сохранения текущей генерации как кандидата в галерею транса.
Требует согласия на отслеживание (Zero-PII cookie hypn0_vid).
"""
svg_content = request.POST.get("svg_content", "").strip()
if not svg_content:
return render(
request,
"block/publish_status.html",
{"error": "Нет данных SVG для публикации. Попробуйте сгенерировать заново."},
)
visitor_uuid = request.COOKIES.get("hypn0_vid")
if not visitor_uuid:
# Мозговые слизняки протестуют: пользователь не дал согласия и не подчинился Гипножабе
return render(
request,
"block/publish_status.html",
{
"not_agreed": True,
"svg_content": svg_content,
"shape": request.POST.get("shape", "circle"),
"cols": request.POST.get("cols", "35"),
"max_radius": request.POST.get("max_radius", "8"),
"blink": request.POST.get("blink", "6"),
"rotation": request.POST.get("rotation", "0"),
"scale": request.POST.get("scale", "980"),
"angle": request.POST.get("angle", "0"),
"color": request.POST.get("color", "#a855ff"),
},
)
# 1. Генерируем гипнотическое название
title = generate_hypno_title()
# 2. Подготавливаем SVG для галереи (пауза по умолчанию + hover)
gallery_svg = prepare_gallery_svg(svg_content)
svg_bytes = gallery_svg.encode("utf-8")
# 3. Собираем параметры генерации в метаданные
metadata = {
"shape": request.POST.get("shape", "circle"),
"cols": int(request.POST.get("cols", 35)) if request.POST.get("cols", "").isdigit() else 35,
"max_radius": int(request.POST.get("max_radius", 8)) if request.POST.get("max_radius", "").isdigit() else 8,
"blink": int(request.POST.get("blink", 6)) if request.POST.get("blink", "").isdigit() else 6,
"rotation": int(request.POST.get("rotation", 0)) if request.POST.get("rotation", "").lstrip("-").isdigit() else 0,
"scale": int(request.POST.get("scale", 980)) if request.POST.get("scale", "").isdigit() else 980,
"angle": int(request.POST.get("angle", 0)) if request.POST.get("angle", "").lstrip("-").isdigit() else 0,
"color": request.POST.get("color", "#a855ff"),
}
try:
svg_file = ContentFile(svg_bytes, name="hypn0.svg")
item = TbHypn0Item(
s_title=title,
file_svg=svg_file,
i_file_size=len(svg_bytes),
j_metadata=metadata,
i_level=TbHypn0Item.Level.CANDIDATE,
is_public=True,
)
item.save(visitor_uuid_or_fp=visitor_uuid)
except PermissionError:
return render(
request,
"block/publish_status.html",
{
"not_agreed": True,
"svg_content": svg_content,
"shape": request.POST.get("shape", "circle"),
"cols": request.POST.get("cols", "35"),
"max_radius": request.POST.get("max_radius", "8"),
"blink": request.POST.get("blink", "6"),
"rotation": request.POST.get("rotation", "0"),
"scale": request.POST.get("scale", "980"),
"angle": request.POST.get("angle", "0"),
"color": request.POST.get("color", "#a855ff"),
},
)
except Exception as e:
return render(
request,
"block/publish_status.html",
{"error": f"Сбой фиксации в трансе: {str(e)}"},
)
return render(
request,
"block/publish_status.html",
{
"success": True,
"item": item,
},
)