feat(anime): 자막/토렌트 파이프라인 대폭 개선

- Blogspot Atom Feed API로 전체 에피소드 자막 URL 발견
- AniList prequel 체인 기반 시즌 에피소드 오프셋 자동 감지
- Nyaa S-tag 감지 → 절대/시즌 번호 체계 자동 판별
- 기존 자막 에피소드 스킵 (URL 페치 전 pre-skip)
- 오프셋 적용 자막 리네임 (시즌번호→절대번호 매칭)
- ASW HEVC 토렌트 우선 정렬 (truncation 방지)
- 토렌트 완료 대기 → 자동 삭제 라이프사이클
- 중복 자막 자동 삭제
- .smi 자막 확장자 지원
This commit is contained in:
2026-03-15 18:23:57 +09:00
parent 9f74812710
commit 3618387b8e
8 changed files with 1386 additions and 532 deletions

View File

@@ -49,6 +49,7 @@ class AnissiaClient:
def __init__(self, timeout: float = 15.0):
self._timeout = timeout
self._schedule_cache: list[AnimeInfo] | None = None
async def get_schedule(self, week: int) -> list[AnimeInfo]:
"""요일별 편성표 조회 (week: 0=일 ~ 6=토, 7=기타)."""
@@ -110,10 +111,17 @@ class AnissiaClient:
]
async def search_anime(self, keyword: str) -> list[AnimeInfo]:
"""키워드로 전체 편성표에서 검색 (한글/일어/영문 fuzzy 매칭)."""
"""키워드로 전체 편성표에서 검색 (한글/일어/영문 fuzzy 매칭).
스케줄은 세션당 1회만 API 호출, 이후 캐시 사용.
"""
import re as _re
all_anime = await self.get_all_schedule()
# 캐시 사용
if self._schedule_cache is None:
self._schedule_cache = await self.get_all_schedule()
logger.info(f"스케줄 캐시 로드: {len(self._schedule_cache)}")
all_anime = self._schedule_cache
keyword_lower = keyword.lower()
# 특수문자 제거 버전 (따옴표, 괄호 등)
keyword_norm = _re.sub(r'[^\w\s]', '', keyword_lower)
@@ -133,9 +141,13 @@ class AnissiaClient:
# 특수문자 제거 버전
subj_norm = _re.sub(r'[^\w\s]', '', subj_lower)
orig_norm = _re.sub(r'[^\w\s]', '', orig_lower)
# 공백까지 제거 버전 (NAS 폴더명→Anissia 매칭용)
subj_compact = _re.sub(r'\s+', '', subj_norm)
keyword_compact = _re.sub(r'\s+', '', keyword_norm)
# 1차: substring 매칭 (원본 + 정규화)
if (keyword_lower in subj_lower or keyword_norm in subj_norm):
# 1차: substring 매칭 (원본 + 정규화 + 공백제거)
if (keyword_lower in subj_lower or keyword_norm in subj_norm
or keyword_compact in subj_compact):
results.append(a)
elif (keyword_lower in orig_lower or keyword_norm in orig_norm):
results.append(a)