Media pool: this project and all assets

This project lists every symbol in the document and the video it uses or was
given this session, each with a frame from its proxy as a thumbnail. All
assets lists every upload on the server and every other saved project's
symbols, from a new /api/symbols that reads symbol leaf paths. Every row is a
drag source (symbol:, import:, footage:). Uploading no longer runs straight
into detection; it lands in the project's media.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Olive Vaughn 2026-09-29 12:59:17 -04:00
parent c81f91c442
commit d5f044c6da
11 changed files with 270 additions and 105 deletions

View file

@ -273,6 +273,44 @@ def footage_list(request):
)
_SYMBOL_LEAF = re.compile(r"^clip/([^/]+)/symbol/([^/]+)$")
def _transit_fields(value, *keys):
"""Top-level fields of a transit map leaf, by keyword name. A leaf's own
facts are a small flat map, so no key repeats and transit's cache never
stands in for one; anything else reads as absent."""
if not (isinstance(value, list) and value[:1] == ["^ "]):
return {}
pairs = dict(zip(value[1::2], value[2::2]))
return {k: pairs.get(f"~:{k}") for k in keys}
@require_http_methods(["GET"])
def symbols(request):
"""Every symbol in every saved project, for the pool's all-assets folder.
Read off the leaf PATHS rather than by loading documents: a symbol's own leaf
is `clip/<cid>/symbol/<sid>`, so listing them is one query and no decoding
beyond the name and length its value carries."""
rows = []
for leaf in Leaf.objects.filter(path__contains="/symbol/").select_related("project"):
m = _SYMBOL_LEAF.match(leaf.path)
if not m:
continue
fields = _transit_fields(leaf.value, "name", "frames")
rows.append({
"project": str(leaf.project_id),
"project_name": leaf.project.name,
"cid": m.group(1),
"symbol": m.group(2),
"name": fields.get("name") or m.group(2).replace("~", "/"),
"frames": fields.get("frames"),
})
rows.sort(key=lambda r: (r["project_name"], r["project"], r["name"]))
return JsonResponse({"symbols": rows})
@require_http_methods(["GET"])
def footage_detail(request, footage_id):
try: