230 lines
9.8 KiB
Python
230 lines
9.8 KiB
Python
"""애니메이션 핸들러 — 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"):
|
|
# 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 == "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("애니메이션 슬래시 커맨드 등록 완료")
|