模特库标签分页与全能创作收口:藏长视频、选择器分页
角色库导入打标签并支持筛选;模特库与全能创作角色/商品选择改为每页 20 条分页。临时限制成片 ≤60 秒,过滤对话里的超长时长选项,并收拢本地全能创作与后台用户相关修复。
This commit is contained in:
@@ -43,6 +43,15 @@ _KNOWN_CATS = [
|
||||
]
|
||||
|
||||
|
||||
def _not_omni_create_q() -> Q:
|
||||
"""「不是全能创作产出」。不能只写 ~Q(metadata__feature="omni_create")。
|
||||
|
||||
metadata 里没有 feature 键时取值是 SQL NULL,NOT(NULL = 'x') 还是 NULL,整行会被筛掉 ——
|
||||
历史资产(没写过 feature)会从「视频自由创作」「自由创作」「素材」这些分桶里整批消失。
|
||||
"""
|
||||
return Q(metadata__feature__isnull=True) | ~Q(metadata__feature="omni_create")
|
||||
|
||||
|
||||
def _tab_q(tab: str) -> Q:
|
||||
if tab == "people":
|
||||
return Q(category="person")
|
||||
@@ -57,13 +66,13 @@ def _tab_q(tab: str) -> Q:
|
||||
if tab == "image_creations": # 图片自由创作
|
||||
return Q(category="free_create", asset_type="image")
|
||||
if tab == "video_creations": # 视频自由创作
|
||||
return Q(category="free_create", asset_type="video") & ~Q(metadata__feature="omni_create")
|
||||
return Q(category="free_create", asset_type="video") & _not_omni_create_q()
|
||||
if tab == "creations": # 自由创作(兼容旧入口:图片+视频)
|
||||
return Q(category="free_create") & ~Q(metadata__feature="omni_create")
|
||||
return Q(category="free_create") & _not_omni_create_q()
|
||||
if tab == "uploads":
|
||||
return Q(category="upload")
|
||||
if tab == "materials": # 素材(期3):视频素材 = 所有视频,排除「最终成片」(final_video 隐藏不列)
|
||||
return ~Q(category="final_video") & Q(asset_type="video") & ~Q(metadata__feature="omni_create")
|
||||
return ~Q(category="final_video") & Q(asset_type="video") & _not_omni_create_q()
|
||||
if tab == "others": # 其他(资产库成品化):我的上传 + 未归类非视频(兜底)
|
||||
return Q(category="upload") | (~Q(category__in=_KNOWN_CATS) & ~Q(asset_type="video"))
|
||||
if tab == "unclassified": # 未归类且非视频(也不含最终成片)
|
||||
@@ -482,6 +491,14 @@ class ModelLibraryViewSet(ModelViewSet):
|
||||
if self.request.query_params.get("q"):
|
||||
q = self.request.query_params["q"]
|
||||
qs = qs.filter(Q(name__icontains=q) | Q(description__icontains=q))
|
||||
# 标签筛选: ?tag=男人 或 ?tag=男人&tag=警察(多标签 AND)
|
||||
raw_tags = self.request.query_params.getlist("tag") or []
|
||||
if not raw_tags:
|
||||
joined = (self.request.query_params.get("tags") or "").strip()
|
||||
if joined:
|
||||
raw_tags = [t.strip() for t in joined.split(",") if t.strip()]
|
||||
for tag in raw_tags:
|
||||
qs = qs.filter(metadata__tags__contains=[tag])
|
||||
# 官方模板靠前,再按新→旧
|
||||
return qs.order_by("-is_official", "-created_at")
|
||||
|
||||
@@ -605,6 +622,18 @@ class ModelLibraryViewSet(ModelViewSet):
|
||||
model.save(update_fields=["purged_at", "updated_at"])
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
@action(detail=False, methods=["get"], url_path="tags")
|
||||
def tags(self, request):
|
||||
"""当前可见模特里出现过的标签(供前端筛选条)· 只返回后台标记为显示的。"""
|
||||
from .model_library_tags import public_tag_rows
|
||||
|
||||
tab = (request.query_params.get("tab") or "").strip() or None
|
||||
if tab not in (None, "official", "mine"):
|
||||
tab = None
|
||||
team = self.get_team()
|
||||
items = public_tag_rows(tab=tab, team_id=getattr(team, "id", None))
|
||||
return Response({"results": items})
|
||||
|
||||
@action(detail=False, methods=["post"], url_path="upload", parser_classes=[MultiPartParser, FormParser])
|
||||
def upload(self, request):
|
||||
"""真人上传:一张人像图 → 建 model_portrait 资产 + Model(source=upload)。三视图/声线后续补。"""
|
||||
@@ -640,13 +669,27 @@ class ModelLibraryViewSet(ModelViewSet):
|
||||
size_bytes=stored.size_bytes,
|
||||
is_primary=True,
|
||||
)
|
||||
raw_tags = request.data.get("tags")
|
||||
tags: list[str] = []
|
||||
if isinstance(raw_tags, str) and raw_tags.strip():
|
||||
import json
|
||||
try:
|
||||
parsed = json.loads(raw_tags)
|
||||
if isinstance(parsed, list):
|
||||
tags = [str(t).strip() for t in parsed if str(t).strip()]
|
||||
else:
|
||||
tags = [t.strip() for t in raw_tags.split(",") if t.strip()]
|
||||
except Exception:
|
||||
tags = [t.strip() for t in raw_tags.split(",") if t.strip()]
|
||||
elif isinstance(raw_tags, list):
|
||||
tags = [str(t).strip() for t in raw_tags if str(t).strip()]
|
||||
model = Model.objects.create(
|
||||
team=team,
|
||||
created_by=request.user,
|
||||
name=name,
|
||||
source=Model.Source.UPLOAD,
|
||||
portrait_asset=portrait,
|
||||
metadata={"source": "upload"},
|
||||
metadata={"source": "upload", "tags": tags},
|
||||
)
|
||||
return Response(ModelLibrarySerializer(model).data, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user