后端生成闸+多项修复;前端全站更新;QA 审计与报告
后端: - 新增 celery_health 生成前置闸——无 worker 在线时图片/视频生成入口 一律 503,防"提交到 ARK 后无人轮询、结果悬空+额度冻结"的数据丢失 - 拼接导出:帧率跟随源众数、字幕逐句重映射、原声/BGM 混音修复 - 资金核算审计脚本 + genesis 账目回填 + 滞留预留清理命令 - 接入 yunqi provider 与豆包 TTS 模型(catalog/migrations/bootstrap 命令) 前端:全站页面更新(pipeline/library/products/projects/team/account 等), 新增共享 pager 分页组件 QA:刷新 function-audit 全量输出,新增 full-qa 报告 文档:BP 产品介绍资料、design/CLAUDE.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
from pathlib import Path
|
||||
import uuid
|
||||
|
||||
import requests
|
||||
from django.db import transaction
|
||||
from django.http import StreamingHttpResponse
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.parsers import FormParser, MultiPartParser
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
@@ -21,6 +24,29 @@ class AssetViewSet(TeamScopedViewSetMixin, ModelViewSet):
|
||||
search_fields = ["name", "description"]
|
||||
ordering_fields = ["created_at", "updated_at", "name"]
|
||||
|
||||
@action(detail=True, methods=["get"], url_path="raw")
|
||||
def raw(self, request, pk=None):
|
||||
"""同源流式代理资产主文件。TOS 桶未配 CORS,浏览器 JS 读不到跨域媒体数据——
|
||||
编辑器抽视频缩略图(canvas 会被 taint)/解码音频波形(fetch 被拦)都需要走这里。"""
|
||||
asset = self.get_object()
|
||||
primary = asset.files.filter(is_primary=True).first() or asset.files.first()
|
||||
if primary is None:
|
||||
return Response({"detail": "asset has no file"}, status=status.HTTP_404_NOT_FOUND)
|
||||
url = TosStorage().presigned_get_url(object_key=primary.object_key, expires_in=600)
|
||||
upstream_headers = {}
|
||||
if request.META.get("HTTP_RANGE"):
|
||||
upstream_headers["Range"] = request.META["HTTP_RANGE"]
|
||||
upstream = requests.get(url, headers=upstream_headers, stream=True, timeout=120)
|
||||
response = StreamingHttpResponse(
|
||||
upstream.iter_content(chunk_size=256 * 1024),
|
||||
status=upstream.status_code,
|
||||
content_type=primary.content_type or "application/octet-stream",
|
||||
)
|
||||
for header in ("Content-Length", "Content-Range", "Accept-Ranges"):
|
||||
if header in upstream.headers:
|
||||
response[header] = upstream.headers[header]
|
||||
return response
|
||||
|
||||
|
||||
class AssetUploadView(APIView):
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
Reference in New Issue
Block a user