From 7d36be0497e00443f244f9c1af535ef700233499 Mon Sep 17 00:00:00 2001 From: Variet Agent Date: Wed, 18 Mar 2026 22:45:32 +0900 Subject: [PATCH] =?UTF-8?q?fix(anime):=20batch=5Fdownload=20list=20crash?= =?UTF-8?q?=20+=20title=20=EC=98=A4=EB=B6=84=EB=A5=98=20fallback=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agent/references/known-issues.md | 12 +++++++++ docs/devlog/2026-03-18.md | 1 + handlers/anime_handler.py | 45 ++++++++++++++++++++++--------- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/.agent/references/known-issues.md b/.agent/references/known-issues.md index be8feea..7e57ef6 100644 --- a/.agent/references/known-issues.md +++ b/.agent/references/known-issues.md @@ -123,3 +123,15 @@ - **원인**: 존재하지 않는 함수명으로 import (실제: `handle_anime_message`) - **해결**: 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 출력을 무비판적으로 신뢰하지 말 것. 코드에서 반드시 방어적 검증 필요 diff --git a/docs/devlog/2026-03-18.md b/docs/devlog/2026-03-18.md index 51f40b9..f15387a 100644 --- a/docs/devlog/2026-03-18.md +++ b/docs/devlog/2026-03-18.md @@ -13,3 +13,4 @@ | 011 | 19:40 | anime 분류 정확도 강화 (6/6 테스트 통과) | `77bd211` | ✅ | | 012 | 19:45 | Foreman E2E 테스트 (분해→수정→Vikunja 등록) | `489755f` | ✅ | | 013 | 21:35 | anime handler import 수정 + action 분기 완성 | `28ae0d5` | ✅ | +| 014 | 22:40 | anime handler batch crash + title 오분류 수정 | - | ✅ | diff --git a/handlers/anime_handler.py b/handlers/anime_handler.py index 16fe4fa..455cfae 100644 --- a/handlers/anime_handler.py +++ b/handlers/anime_handler.py @@ -91,6 +91,12 @@ async def handle_anime_message( mode = parsed.get("download_mode", "auto") episode = parsed.get("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: # 이번 분기 전체 다운로드 (자막 최신화 등) dl_filter = parsed.get("filter", "") @@ -143,19 +149,34 @@ async def handle_anime_message( )) return - # 결과 임베드 - embed = discord.Embed( - title=f"🎬 {result.message[:100]}" if result.message else "🎬 결과", - description=result.message[:2000] if result.message else "완료", - color=0x2ECC71 if result.success else 0xE74C3C, - ) - if result.errors: - embed.add_field( - name="⚠️ 오류", - value="\n".join(f"• {e}" for e in result.errors[:5])[:1000], - inline=False, + # 결과 임베드 — 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) + await safe_send_embed(message.channel, embed) + else: + embed = discord.Embed( + title=f"🎬 {result.message[:100]}" if result.message else "🎬 결과", + description=result.message[:2000] if result.message else "완료", + color=0x2ECC71 if result.success else 0xE74C3C, + ) + if result.errors: + embed.add_field( + name="⚠️ 오류", + value="\n".join(f"• {e}" for e in result.errors[:5])[:1000], + inline=False, + ) + await safe_send_embed(message.channel, embed) except Exception as e: logger.error(f"애니 핸들러 오류: {e}", exc_info=True)