import json from django.db.models import Q from django.http import FileResponse, HttpResponseNotAllowed, JsonResponse from django.shortcuts import get_object_or_404 from django.utils import timezone from django.views.decorators.csrf import csrf_exempt from .attribution import annotation_layer, apply_attribution from .models import Project, Revision def _auth(request): """None if logged in, else a 401 JSON response (so fetch() doesn't get a login redirect).""" if not request.user.is_authenticated: return JsonResponse({"detail": "authentication required"}, status=401) return None def _visible(request): """Projects the user owns or collaborates on.""" return Project.objects.filter( Q(owner=request.user) | Q(collaborators=request.user)).distinct() def _owned(request, pk): return get_object_or_404(_visible(request), pk=pk) def projects(request): """GET /api/projects/ — the projects the user owns or collaborates on.""" if (resp := _auth(request)): return resp rows = _visible(request).values("id", "name", "fps", "updated", "owner__username") return JsonResponse(list(rows), safe=False) @csrf_exempt # dev convenience: session-auth API without a CSRF token round-trip def scene(request, pk): """GET/PUT /api/projects//scene/ — read or replace the timeline scene.""" if (resp := _auth(request)): return resp project = _owned(request, pk) if request.method == "GET": return JsonResponse({"fps": project.fps, "scene": project.scene}) if request.method == "PUT": data = json.loads(request.body or "{}") summary = None if "scene" in data: project.scene, summary, _ = apply_attribution( project.scene, data["scene"], request.user.get_username(), timezone.now().isoformat()) Revision.objects.create(project=project, user=request.user, summary=summary, annotations=annotation_layer(project.scene)) if "fps" in data: project.fps = data["fps"] project.save(update_fields=["scene", "fps", "updated"]) return JsonResponse({"ok": True, "updated": project.updated, "summary": summary}) return HttpResponseNotAllowed(["GET", "PUT"]) def revisions(request, pk): """GET /api/projects//revisions/ — the save history (who/when/what).""" if (resp := _auth(request)): return resp project = _owned(request, pk) rows = project.revisions.values("id", "user__username", "created", "summary") return JsonResponse(list(rows), safe=False) def otio(request, pk): """GET /api/projects//otio/ — serve the uploaded OTIO file.""" if (resp := _auth(request)): return resp project = _owned(request, pk) if not project.otio: return JsonResponse({"detail": "no otio uploaded"}, status=404) return FileResponse(project.otio.open("rb"), content_type="application/json")