"""애니메이션 핸들러 — Discord Bot에서 분리된 애니 관련 처리. AnimePipeline을 직접 호출하여 결과를 Discord Embed로 렌더링합니다. NLU에서 mode="anime"로 분류된 요청도 처리합니다. """ import logging import discord from discord import app_commands from handlers.renderer import safe_send_embed logger = logging.getLogger("variet.handlers.anime") async def handle_anime_message( message: discord.Message, parsed: dict, ): """NLU에서 anime으로 분류된 메시지 처리. Args: message: Discord 메시지 parsed: NLU 분류 결과 dict {mode, action, title, episode?, ...} """ from tools.anime_pipeline import AnimePipeline pipeline = AnimePipeline() action = parsed.get("action", "search") title = parsed.get("title", parsed.get("query", "")) async with message.channel.typing(): try: if action in ("list", "scan", "schedule"): # NAS 현황 조회 from tools.nas_scanner import NasScanner scanner = NasScanner() if not scanner.is_accessible(): await message.channel.send(embed=discord.Embed( title="❌ NAS 접근 불가", description=f"경로: `{scanner.base_path}`", color=0xE74C3C, )) return # title이 있으면 키워드 검색, 없으면 이번 분기 if title: folders = scanner.search(title) label = f"'{title}' 검색 결과" # 키워드 검색 0건이면 이번 분기로 fallback if not folders: folders = scanner.get_current_quarter_anime() label = "이번 분기 애니" else: folders = scanner.get_current_quarter_anime() label = "이번 분기 애니" if not folders: await message.channel.send(embed=discord.Embed( title=f"📁 {label}", description="해당하는 폴더가 없습니다.", color=0xF39C12, )) return desc_lines = [] for f in folders[:15]: sub_info = f"자막 {f.subtitle_count}" if f.subtitle_count else "자막 없음" desc_lines.append( f"• `{f.folder_name}`\n 영상 {f.video_count}개 | {sub_info} | {f.total_size_gb:.1f}GB" ) embed = discord.Embed( title=f"📁 {label} ({len(folders)}개)", description="\n".join(desc_lines)[:2000], color=0x3498DB, ) await safe_send_embed(message.channel, embed) return elif action == "search" and title: result = await pipeline.search(title) elif action == "search" and not title: await message.channel.send(embed=discord.Embed( title="🔍 애니 검색", description="검색할 제목을 입력해주세요.\n예: `프리렌 검색해줘`", color=0xF39C12, )) return elif action == "download" and title: mode = parsed.get("download_mode", "auto") episode = parsed.get("episode") result = await pipeline.download(title, mode=mode, episode=episode) elif action == "download" and not title: # 이번 분기 전체 다운로드 (자막 최신화 등) dl_filter = parsed.get("filter", "") if "sub" in dl_filter: result = await pipeline.batch_download(mode="sub_only") else: result = await pipeline.batch_download(mode="auto") elif action == "sub_only": # 자막만 다운로드 episode = parsed.get("episode") if title: result = await pipeline.download(title, mode="sub_only", episode=episode) else: result = await pipeline.batch_download(mode="sub_only") elif action == "video_only": # 영상만 다운로드 episode = parsed.get("episode") if title: result = await pipeline.download(title, mode="video_only", episode=episode) else: result = await pipeline.batch_download(mode="video_only") elif action == "status": status = await pipeline.get_status() if not status: await message.channel.send(embed=discord.Embed( title="🎬 다운로드 현황", description="다운로드 중인 항목 없음", color=0x3498DB, )) return desc = "\n".join( f"• {s['progress']} `{s['name'][:40]}` {s['speed']}" for s in status ) await message.channel.send(embed=discord.Embed( title=f"🎬 다운로드 현황 ({len(status)}건)", description=desc[:2000], color=0x3498DB, )) return else: # 알 수 없는 action → search로 fallback if title: result = await pipeline.search(title) else: await message.channel.send(embed=discord.Embed( title="❓ 애니 명령", description="무엇을 도와드릴까요?\n• 목록 조회: `NAS에 뭐있어?`\n• 검색: `프리렌 검색`\n• 다운로드: `프리렌 10화 받아줘`\n• 상태: `다운로드 현황`", color=0xF39C12, )) 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, ) await safe_send_embed(message.channel, embed) except Exception as e: logger.error(f"애니 핸들러 오류: {e}", exc_info=True) await message.channel.send(embed=discord.Embed( title="❌ 애니 처리 오류", description=f"```{str(e)[:300]}```", color=0xE74C3C, )) def register_anime_commands(bot, ws_manager): """애니메이션 슬래시 커맨드 등록.""" anime_group = app_commands.Group(name="anime", description="애니메이션 자막/영상 자동화") @anime_group.command(name="search", description="애니 검색 (편성표 + 자막 + 토렌트)") @app_commands.describe(title="검색할 애니 제목 (한글)") async def anime_search(interaction: discord.Interaction, title: str): await interaction.response.defer() from tools.anime_pipeline import AnimePipeline pipeline = AnimePipeline() result = await pipeline.search(title) embed = discord.Embed( title=f"🔍 {result.anime.subject}" if result.anime else f"🔍 {title}", description=result.message[:2000], color=0x2ECC71 if result.success else 0xE74C3C, ) await interaction.followup.send(embed=embed) @anime_group.command(name="download", description="자막+영상 자동 다운로드") @app_commands.describe(title="애니 제목 (한글)", episode="특정 화수 (없으면 최신)") async def anime_download(interaction: discord.Interaction, title: str, episode: int = None): await interaction.response.defer() from tools.anime_pipeline import AnimePipeline pipeline = AnimePipeline() result = await pipeline.download(title, mode="auto", episode=episode) embed = discord.Embed( title=f"📥 {title}", description=result.message[:2000], color=0x2ECC71 if result.success else 0xE74C3C, ) await interaction.followup.send(embed=embed) @anime_group.command(name="sub", description="자막만 다운로드") @app_commands.describe(title="애니 제목 (한글)", episode="특정 화수") async def anime_sub(interaction: discord.Interaction, title: str, episode: int = None): await interaction.response.defer() from tools.anime_pipeline import AnimePipeline pipeline = AnimePipeline() result = await pipeline.download(title, mode="sub_only", episode=episode) embed = discord.Embed( title=f"📝 자막: {title}", description=result.message[:2000], color=0x2ECC71 if result.success else 0xE74C3C, ) await interaction.followup.send(embed=embed) @anime_group.command(name="video", description="영상만 다운로드 (자막 없어도 강제)") @app_commands.describe(title="애니 제목 (한글)", episode="특정 화수") async def anime_video(interaction: discord.Interaction, title: str, episode: int = None): await interaction.response.defer() from tools.anime_pipeline import AnimePipeline pipeline = AnimePipeline() result = await pipeline.download(title, mode="video_only", episode=episode) embed = discord.Embed( title=f"🎬 영상: {title}", description=result.message[:2000], color=0x2ECC71 if result.success else 0xE74C3C, ) await interaction.followup.send(embed=embed) @anime_group.command(name="status", description="현재 다운로드 큐 상태") async def anime_status(interaction: discord.Interaction): await interaction.response.defer() from tools.anime_pipeline import AnimePipeline pipeline = AnimePipeline() status = await pipeline.get_status() if not status: desc = "다운로드 중인 항목 없음" else: desc = "\n".join( f"• {s['progress']} `{s['name'][:40]}` {s['speed']}" for s in status ) embed = discord.Embed( title=f"🎬 다운로드 현황 ({len(status)}건)", description=desc[:2000], color=0x3498DB, ) await interaction.followup.send(embed=embed) bot.tree.add_command(anime_group) logger.info("애니메이션 슬래시 커맨드 등록 완료")