5 min read

[aniflux 3/5] 한국어 자막을 자동으로 — Anissia API와 블로그 크롤링

도입

애니메이션 자막 자동화에서 제일 어려운 건 에피소드 수집이 아니었습니다.
한국어 자막이었습니다.

영어 자막은 SubsPlease가 릴리스에 내장해서 배포합니다.
일본어는 당연히 원본에 있고요.
그런데 한국어 자막은 공식 배포 채널이 없습니다.

개인 자막 제작자들이 blogspot, tistory, 네이버 블로그에 올리는 게 전부더라고요.
zip으로 묶어서 올리는 사람, Google Drive 링크만 던지는 사람, 에피소드마다 포스트 하나씩 쓰는 사람.
패턴이 제각각입니다.

Sonarr에는 이런 소스를 처리할 플러그인이 없습니다.
직접 만들 수밖에 없었습니다.

배경

한국 애니 자막 생태계의 중심에 Anissia라는 커뮤니티가 있습니다.
api.anissia.net에서 방영 중인 애니 목록과 자막 제작자 정보를 JSON으로 제공합니다.

자막 제작자의 블로그 URL을 반환해준다는 점입니다.
Anissia 자체가 자막 파일을 호스팅하지는 않습니다.
이 사람이 이 작품 자막을 만들고 있고, 블로그 주소는 여기야 정도의 메타데이터만 줍니다.

그래서 실제 자막 파일을 가져오려면 블로그를 직접 크롤링해야 합니다.
블로그 플랫폼별로 Provider를 분리했습니다.
blogspot, tistory, naver, gdrive — 각각 HTML 구조가 다르니까요.

과정

자막 수집 파이프라인은 이렇게 흘러갑니다.

Resolve(제목) → GetCaptions(anissia_id, episode)
→ Download(blogURL) → Extract(zip/srt) → Place(Season 폴더)

Resolve 단계에서 작품 제목으로 Anissia ID를 찾습니다.
GetCaptions에서 해당 작품의 자막 제작자 목록과 블로그 URL을 받아옵니다.
Download에서 블로그에 접근해 실제 파일 링크를 추출하고요.
Extract에서 zip이면 풀고, srt/ass면 그대로 둡니다.
Place에서 Plex 폴더 구조에 맞춰 배치합니다.

Backfill 시나리오가 좀 복잡했습니다.
자막 제작자마다 배포 방식이 달라서 세 가지 타입으로 분류했거든요.

Type A는 에피소드마다 개별 포스트에 올리는 경우.
Type B-1은 여러 에피소드를 하나의 zip으로 묶어서 올리는 경우.
Type B-3은 한 포스트에 여러 개의 다운로드 링크를 나열하는 경우.

각 타입에 맞는 파싱 로직을 Provider 안에 구현했습니다.

삽질

zip 파일 안에서 에피소드 번호를 추출하는 게 첫 번째 난관이었습니다.
자막 제작자마다 파일명 규칙이 다릅니다.
"작품명 03.srt", "03화.ass", "[03] subtitle.srt" 같은 식으로요.
정규식을 여러 패턴 돌려서 매칭되는 걸 쓰는 방식으로 처리했습니다.

patterns := []string{
    `(\d{2,3})화`,
    `\[(\d{2,3})\]`,
    `[_ ](\d{2,3})\.`,
    `E(\d{2,3})`,
}

Google Drive 링크도 문제였습니다.
블로그에 올라오는 URL은 공유 링크 형태인데, 이걸 직접 다운로드 URL로 변환해야 합니다.
/file/d/{id}/viewuc?export=download&id={id}로 바꾸는 건 알려진 방법이지만, 대용량 파일이면 중간에 확인 페이지가 끼어듭니다.
confirm 토큰을 파싱해서 다시 요청하는 로직을 추가했습니다.

가장 짜증났던 건 빈 파일 문제입니다.
블로그가 삭제되었거나 링크가 만료된 경우, HTTP 200으로 HTML 에러 페이지가 내려옵니다.
Content-Type 체크와 파일 크기 최소값 검증을 넣어서 무효한 파일을 걸러냈습니다.
100바이트 미만이거나 <html로 시작하면 자막이 아니라고 판단합니다.

다운로드 실패 시 retry를 5회까지 하고, 그래도 안 되면 permanent failure로 마킹해서 다시 시도하지 않습니다.
안 그러면 죽은 링크를 매 스케줄마다 계속 두드리게 되거든요.

결과

자막 제작자가 블로그에 올리면 15분 내에 자동으로 매칭됩니다.
Plex에서 재생할 때 한국어 자막이 바로 선택 가능하고요.

솔직히 말하면, 이 자막 파이프라인이 aniflux에서 가장 취약한 부분입니다.
블로그 HTML 구조가 바뀌면 Provider를 수정해야 하고, Anissia API가 중단되면 대안이 마땅치 않습니다.
외부 의존성이 많은 건 어쩔 수 없는 한계입니다.

그래도 수동으로 매주 블로그 돌아다니면서 자막 받던 시절에 비하면 충분히 나아졌습니다.
Provider 패턴을 분리해둔 덕에 새로운 블로그 플랫폼이 생겨도 하나만 추가하면 되고요.

마무리

한국어 자막 자동화는 기술적 난이도보다 생태계의 파편화가 더 큰 문제였습니다.
표준이 없으니 각각에 맞춰야 하고, 그 각각이 언제든 바뀔 수 있다는 점이 불안 요소입니다.

그래도 Anissia가 메타데이터 허브 역할을 해주는 동안은 이 구조가 동작합니다.
완벽하지는 않지만, 매주 30분씩 쓰던 수동 작업을 없앤 건 확실합니다.

참고