fix(anime): batch_download list crash + title 오분류 fallback 수정

This commit is contained in:
2026-03-18 22:45:32 +09:00
parent 7a3df96a95
commit 7d36be0497
3 changed files with 46 additions and 12 deletions

View File

@@ -123,3 +123,15 @@
- **원인**: 존재하지 않는 함수명으로 import (실제: `handle_anime_message`) - **원인**: 존재하지 않는 함수명으로 import (실제: `handle_anime_message`)
- **해결**: import 수정 + 시그니처 확인 `(message, parsed)` - **해결**: import 수정 + 시그니처 확인 `(message, parsed)`
- **주의**: 핸들러 연결 시 반드시 실제 모듈의 함수명/시그니처 확인 후 코드 작성 - **주의**: 핸들러 연결 시 반드시 실제 모듈의 함수명/시그니처 확인 후 코드 작성
### [2026-03-18] anime_handler — batch_download list 반환값 crash
- **증상**: title 없이 배치 다운로드 시 `AttributeError: 'list' object has no attribute 'message'`
- **원인**: `batch_download()``list[DownloadResult]`를 반환하지만, 렌더링 코드가 단일 `result.message` 접근
- **해결**: `isinstance(result, list)` 체크 추가 → list면 합산 Embed 렌더링
- **주의**: pipeline 메서드 반환 타입을 반드시 확인하고 handler에서 처리할 것
### [2026-03-18] anime_handler — NLU title에 범위 한정자 진입
- **증상**: "이번분기 애니 업데이트" → `title="이번분기"``download("이번분기")` → "검색 결과가 없습니다"
- **원인**: handler line 90이 `title` truthy면 무조건 단건 다운로드. title 유효성 검증 없음
- **해결**: `download()` resolve 실패 + episode 미지정 시 `batch_download()` fallback 추가
- **주의**: AI NLU 출력을 무비판적으로 신뢰하지 말 것. 코드에서 반드시 방어적 검증 필요

View File

@@ -13,3 +13,4 @@
| 011 | 19:40 | anime 분류 정확도 강화 (6/6 테스트 통과) | `77bd211` | ✅ | | 011 | 19:40 | anime 분류 정확도 강화 (6/6 테스트 통과) | `77bd211` | ✅ |
| 012 | 19:45 | Foreman E2E 테스트 (분해→수정→Vikunja 등록) | `489755f` | ✅ | | 012 | 19:45 | Foreman E2E 테스트 (분해→수정→Vikunja 등록) | `489755f` | ✅ |
| 013 | 21:35 | anime handler import 수정 + action 분기 완성 | `28ae0d5` | ✅ | | 013 | 21:35 | anime handler import 수정 + action 분기 완성 | `28ae0d5` | ✅ |
| 014 | 22:40 | anime handler batch crash + title 오분류 수정 | - | ✅ |

View File

@@ -91,6 +91,12 @@ async def handle_anime_message(
mode = parsed.get("download_mode", "auto") mode = parsed.get("download_mode", "auto")
episode = parsed.get("episode") episode = parsed.get("episode")
result = await pipeline.download(title, mode=mode, episode=episode) result = await pipeline.download(title, mode=mode, episode=episode)
# resolve 실패 시 batch fallback (title이 애니 제목이 아닌 경우)
if not result.success and not episode:
logger.info(f"단건 resolve 실패 → 배치 fallback: '{title}'")
dl_filter = parsed.get("filter", "")
batch_mode = "sub_only" if "sub" in dl_filter else "auto"
result = await pipeline.batch_download(mode=batch_mode)
elif action == "download" and not title: elif action == "download" and not title:
# 이번 분기 전체 다운로드 (자막 최신화 등) # 이번 분기 전체 다운로드 (자막 최신화 등)
dl_filter = parsed.get("filter", "") dl_filter = parsed.get("filter", "")
@@ -143,7 +149,22 @@ async def handle_anime_message(
)) ))
return return
# 결과 임베드 # 결과 임베드 — batch_download는 list[DownloadResult] 반환
if isinstance(result, list):
successes = [r for r in result if r.success]
failures = [r for r in result if not r.success]
lines = []
for r in result:
icon = "" if r.success else ""
name = r.anime.subject if r.anime else "?"
lines.append(f"{icon} {name}: {r.message[:60]}")
embed = discord.Embed(
title=f"📥 일괄 다운로드 ({len(successes)}/{len(result)}건 성공)",
description="\n".join(lines)[:2000] if lines else "처리 완료",
color=0x2ECC71 if not failures else 0xF39C12,
)
await safe_send_embed(message.channel, embed)
else:
embed = discord.Embed( embed = discord.Embed(
title=f"🎬 {result.message[:100]}" if result.message else "🎬 결과", title=f"🎬 {result.message[:100]}" if result.message else "🎬 결과",
description=result.message[:2000] if result.message else "완료", description=result.message[:2000] if result.message else "완료",