Compare commits
No commits in common. "969283690fb1cc8d530402236bd2f0262dbd939c" and "6d4142fff081f8b55872ad23b11913693a337c50" have entirely different histories.
969283690f
...
6d4142fff0
@ -2726,8 +2726,10 @@ def _assets_api_call(func, *args, **kwargs):
|
||||
return result, None
|
||||
except AssetsAPIError as e:
|
||||
logger.warning('Assets API error: %s', e)
|
||||
# Surface readable message for known parameter errors
|
||||
msg = e.api_message if hasattr(e, 'api_message') else str(e)
|
||||
return None, Response(
|
||||
{'error': 'assets_api_error', 'message': e.user_message},
|
||||
{'error': 'assets_api_error', 'message': msg},
|
||||
status=status.HTTP_502_BAD_GATEWAY,
|
||||
)
|
||||
except Exception as e:
|
||||
@ -2748,35 +2750,18 @@ def asset_groups_view(request):
|
||||
team = request.user.team
|
||||
|
||||
if request.method == 'GET':
|
||||
groups = list(
|
||||
groups = (
|
||||
AssetGroup.objects
|
||||
.filter(team=team)
|
||||
.annotate(asset_count=Count('assets'))
|
||||
.order_by('-created_at')
|
||||
)
|
||||
|
||||
# 从火山同步素材组名字(一次 API 调用)
|
||||
remote_ids = [g.remote_group_id for g in groups if g.remote_group_id]
|
||||
if remote_ids:
|
||||
try:
|
||||
from utils.assets_client import list_asset_groups
|
||||
remote_items, _ = list_asset_groups(page=1, page_size=100)
|
||||
remote_name_map = {item['Id']: item.get('Name', '') for item in remote_items if 'Id' in item}
|
||||
for g in groups:
|
||||
if g.remote_group_id and g.remote_group_id in remote_name_map:
|
||||
remote_name = remote_name_map[g.remote_group_id]
|
||||
if remote_name and remote_name != g.name:
|
||||
g.name = remote_name
|
||||
g.save(update_fields=['name'])
|
||||
except Exception:
|
||||
pass # 同步失败不影响列表展示
|
||||
|
||||
results = []
|
||||
for g in groups:
|
||||
results.append({
|
||||
'id': g.id,
|
||||
'name': g.name,
|
||||
'thumbnail_url': g.thumbnail_url if g.asset_count > 0 else '',
|
||||
'thumbnail_url': g.thumbnail_url,
|
||||
'asset_count': g.asset_count,
|
||||
'remote_group_id': g.remote_group_id,
|
||||
'created_at': g.created_at.isoformat(),
|
||||
@ -2799,12 +2784,12 @@ def asset_groups_view(request):
|
||||
w, h = img.size
|
||||
if w < 300 or h < 300:
|
||||
return Response(
|
||||
{'error': f'图片太小了,请上传更大的图片(当前 {w}x{h},最小要求 300x300)'},
|
||||
{'error': f'图片尺寸太小({w}x{h}),宽高均需 ≥ 300px'},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
if w > 6000 or h > 6000:
|
||||
return Response(
|
||||
{'error': f'图片太大了,请压缩后重试(当前 {w}x{h},最大支持 6000x6000)'},
|
||||
{'error': f'图片尺寸太大({w}x{h}),宽高均需 ≤ 6000px'},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
file.seek(0) # Reset after PIL read
|
||||
@ -2883,18 +2868,6 @@ def asset_group_detail_view(request, group_id):
|
||||
return Response({'error': '素材组不存在'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
if request.method == 'GET':
|
||||
# 同步火山端的素材组名字
|
||||
if group.remote_group_id:
|
||||
try:
|
||||
from utils.assets_client import get_asset_group
|
||||
remote = get_asset_group(group.remote_group_id)
|
||||
remote_name = remote.get('Name', '')
|
||||
if remote_name and remote_name != group.name:
|
||||
group.name = remote_name
|
||||
group.save(update_fields=['name'])
|
||||
except Exception:
|
||||
pass # 查不到就用本地名字
|
||||
|
||||
assets_qs = Asset.objects.filter(group=group).order_by('-created_at')
|
||||
asset_list = []
|
||||
for a in assets_qs:
|
||||
@ -2974,12 +2947,12 @@ def asset_group_add_asset_view(request, group_id):
|
||||
w, h = img.size
|
||||
if w < 300 or h < 300:
|
||||
return Response(
|
||||
{'error': f'图片太小了,请上传更大的图片(当前 {w}x{h},最小要求 300x300)'},
|
||||
{'error': f'图片尺寸太小({w}x{h}),宽高均需 ≥ 300px'},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
if w > 6000 or h > 6000:
|
||||
return Response(
|
||||
{'error': f'图片太大了,请压缩后重试(当前 {w}x{h},最大支持 6000x6000)'},
|
||||
{'error': f'图片尺寸太大({w}x{h}),宽高均需 ≤ 6000px'},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
file.seek(0)
|
||||
@ -3080,7 +3053,6 @@ def asset_search_view(request):
|
||||
groups = (
|
||||
AssetGroup.objects
|
||||
.filter(team=team, name__icontains=q)
|
||||
.annotate(asset_count=Count('assets'))
|
||||
.order_by('-created_at')[:20]
|
||||
)
|
||||
results = []
|
||||
@ -3088,8 +3060,7 @@ def asset_search_view(request):
|
||||
results.append({
|
||||
'id': g.id,
|
||||
'name': g.name,
|
||||
'thumbnail_url': g.thumbnail_url if g.asset_count > 0 else '',
|
||||
'asset_count': g.asset_count,
|
||||
'thumbnail_url': g.thumbnail_url,
|
||||
'remote_group_id': g.remote_group_id,
|
||||
})
|
||||
return Response({'results': results})
|
||||
|
||||
@ -21,41 +21,12 @@ HOST = 'open.volcengineapi.com'
|
||||
PROJECT_NAME = 'int_dev_Airlabs'
|
||||
|
||||
|
||||
_ASSETS_ERROR_MESSAGES = {
|
||||
'ConfigError': '素材服务未配置,请联系管理员',
|
||||
'RequestError': '素材服务暂时不可用,请稍后重试',
|
||||
'InvalidParameter': '素材参数无效,请检查输入',
|
||||
'NotFound': '素材不存在或已被删除',
|
||||
'NotExist': '素材不存在或已被删除',
|
||||
'InternalError': '素材服务异常,请稍后重试',
|
||||
'Forbidden': '没有权限操作该素材',
|
||||
'RateLimitExceeded': '操作过于频繁,请稍后重试',
|
||||
}
|
||||
|
||||
_ASSETS_MESSAGE_KEYWORDS = {
|
||||
'dimension': '图片尺寸不符合要求(宽高需在 300~6000 像素之间)',
|
||||
'size': '文件大小超出限制',
|
||||
'format': '不支持的文件格式',
|
||||
'not found': '素材不存在或已被删除',
|
||||
'permission': '没有权限操作该素材',
|
||||
}
|
||||
|
||||
|
||||
class AssetsAPIError(Exception):
|
||||
"""Raised when the Assets API returns an error."""
|
||||
def __init__(self, code, message, status_code=400):
|
||||
self.code = code
|
||||
self.api_message = message
|
||||
self.status_code = status_code
|
||||
# 中文友好提示
|
||||
friendly = _ASSETS_ERROR_MESSAGES.get(code)
|
||||
if not friendly:
|
||||
msg_lower = (message or '').lower()
|
||||
for keyword, hint in _ASSETS_MESSAGE_KEYWORDS.items():
|
||||
if keyword in msg_lower:
|
||||
friendly = hint
|
||||
break
|
||||
self.user_message = friendly or '素材操作失败,请稍后重试'
|
||||
super().__init__(f'[{code}] {message}')
|
||||
|
||||
|
||||
@ -196,12 +167,6 @@ def get_asset(asset_id: str) -> dict:
|
||||
return _do_request('GetAsset', body)
|
||||
|
||||
|
||||
def get_asset_group(group_id: str) -> dict:
|
||||
"""Get single asset group details."""
|
||||
body = {'Id': group_id, 'ProjectName': PROJECT_NAME}
|
||||
return _do_request('GetAssetGroup', body)
|
||||
|
||||
|
||||
def update_asset_group(group_id: str, name: str = None, description: str = None):
|
||||
"""Update an asset group's name and/or description."""
|
||||
body = {'Id': group_id, 'ProjectName': PROJECT_NAME}
|
||||
|
||||
@ -211,11 +211,7 @@ export function AssetLibraryModal({ open, onClose }: Props) {
|
||||
<div className={styles.grid}>
|
||||
{groups.map((group) => (
|
||||
<div key={group.id} className={styles.card} onClick={() => handleGroupClick(group)}>
|
||||
{group.asset_count === 0 ? (
|
||||
<div className={styles.cardThumb} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--color-text-disabled)', fontSize: 12 }}>暂无图片</div>
|
||||
) : (
|
||||
<img src={tosThumb(group.thumbnail_url, 300)} alt={group.name} className={styles.cardThumb} />
|
||||
)}
|
||||
<div className={styles.cardInfo}>
|
||||
{editingName && editingName.id === group.id ? (
|
||||
<div className={styles.inlineEditWrap} onClick={(e) => e.stopPropagation()}>
|
||||
@ -410,7 +406,6 @@ export function AssetLibraryModal({ open, onClose }: Props) {
|
||||
</>
|
||||
)}
|
||||
<div className={styles.dropZoneWarning}>⚠️ 素材上传后无法删除,请确认后再上传</div>
|
||||
<div className={styles.dropZoneWarning}>⚠️ 图片尺寸要求:宽高均需在 300~6000 像素之间</div>
|
||||
</div>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
|
||||
@ -125,9 +125,6 @@
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
background: #2a2a3a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.thumbMedia {
|
||||
|
||||
@ -271,8 +271,8 @@ export function PromptInput() {
|
||||
typedAtRef.current = true;
|
||||
setMentionMode('references');
|
||||
openMentionPopup();
|
||||
} else if (textAfterAt.length > 0 && !textAfterAt.includes(' ')) {
|
||||
// Text after @, search assets (Chinese + English)
|
||||
} else if (/[\u4e00-\u9fff]+/.test(textAfterAt) && !textAfterAt.includes(' ')) {
|
||||
// Chinese text after @, search assets
|
||||
if (searchTimerRef.current) clearTimeout(searchTimerRef.current);
|
||||
searchTimerRef.current = setTimeout(() => {
|
||||
assetsApi.search(textAfterAt).then((res) => {
|
||||
@ -641,11 +641,7 @@ export function PromptInput() {
|
||||
}}
|
||||
>
|
||||
<div className={styles.mentionThumb}>
|
||||
{group.thumbnail_url ? (
|
||||
<img src={tosThumb(group.thumbnail_url, 72)} alt="" className={styles.thumbMedia} />
|
||||
) : (
|
||||
<span style={{ fontSize: 9, color: 'var(--color-text-disabled)' }}>无图</span>
|
||||
)}
|
||||
</div>
|
||||
<span className={styles.mentionLabel}>{group.name}</span>
|
||||
<span className={styles.mentionType}>人像</span>
|
||||
|
||||
@ -3,8 +3,6 @@
|
||||
margin: 0 auto;
|
||||
padding: 24px 20px 60px;
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user