Skip to content

Instantly share code, notes, and snippets.

@vimholic
Last active March 3, 2026 01:48
Show Gist options
  • Select an option

  • Save vimholic/f595c4ccafc91ede4c6f3f246ede3a51 to your computer and use it in GitHub Desktop.

Select an option

Save vimholic/f595c4ccafc91ede4c6f3f246ede3a51 to your computer and use it in GitHub Desktop.
Plex + SJVA 에이전트 VP8X WebP 크래시 분석 및 해결 스크립트

Plex Media Server WebP 크래시 분석 및 해결

환경: Synology DS918+ / PMS v1.43.0.10492 / SJVA 에이전트 최종 업데이트: 2026-03-03


TL;DR — 핵심 요약

  • 문제: PMS v1.43의 libfreeimage.so모든 WebP 이미지 (VP8X, VP8, VP8L)에서 EXIF 파싱 시 NULL 역참조 → SIGSEGV 크래시
  • 해결: 메타데이터 내 모든 WebP → JPEG 변환 (VP8→VP8 재인코딩은 v1.43에서 효과 없음)
  • 도구: fix_vp8x.py — ffmpeg 기반 WebP→JPEG 일괄 변환 스크립트
  • SJVA 패치: agent_base.py + module_ktv.py + module_yaml_base.py 3개 파일 수정 필요
  • ⚠️ PMS 버전 의존: v1.43에서는 모든 WebP 크래시. v1.42 이하에서는 VP8X만 문제일 수 있음 (libfreeimage.so 버전 차이)
적용 방법 (Quick Start) — 클릭하여 펼치기
# 1. ffmpeg-static 설치 (Synology 기본 ffmpeg엔 libwebp 없음)
#    https://johnvansickle.com/ffmpeg/ 에서 다운로드
sudo cp ffmpeg /usr/local/bin/ffmpeg-static
# 또는 패키지 센터 ffmpeg7 설치 후 심볼릭 링크:
# sudo ln -s /volume1/@appstore/ffmpeg7/bin/ffmpeg7 /usr/local/bin/ffmpeg-static

# 2. fix_vp8x.py 배포 (소스 Gist에서 다운로드)
sudo cp fix_vp8x.py /usr/local/bin/fix_vp8x.py

# 3. 전체 스캔 실행
python3 /usr/local/bin/fix_vp8x.py --days 0

# 4. DSM 작업 스케줄러에 daily cron 등록 (Butler 시작 전 실행)
#    python3 /usr/local/bin/fix_vp8x.py --days 3 --quiet >> /var/log/fix_vp8x.log 2>&1

# 5. (선택) SJVA 에이전트 패치 — 신규 VP8X 유입 차단
#    소스 Gist의 agent_base.py, module_ktv.py, module_yaml_base.py를
#    SjvaAgent.bundle/Contents/Code/ 에 덮어쓰기 후 PMS 재시작
#    패치 보호: git update-index --assume-unchanged Contents/Code/agent_base.py ...

변경 이력

날짜 상태 내용
2026-02-22 🔴 크래시 15회 최초 발생, VP8X 1,101개 변환, 안정화
2026-02-24~26 🔴 재발 SJVA/Butler 스케줄 충돌 → 스케줄 조정
2026-02-28 🟡 패치 적용 키효님 agent_base.py 패치 배포
2026-03-01 🔴 재발 패치 미작동 발견 (module 오버라이드)
2026-03-01 🟢 해결 GDB 분석 → 모든 WebP 크래시 확정 → 86,917개 JPEG 일괄 변환
2026-03-01 🟢 안정 module 패치 완료, watchdog 배포, Butler 테스트 통과
2026-03-03 🟢 2일째 크래시 0건 완전 해소 확인

1. 증상

  • Plex Butler 실행 시 UltraBlurProcessor가 WebP 이미지를 처리하다 SIGSEGV 크래시
  • 자동 재기동 → 동일 이미지 재처리 → 재크래시 (79분 간격 반복)
  • 최악의 경우 한 세션에 15회 연속 크래시

2. 원인 분석

근본 원인 (GDB 코어 덤프 분석으로 확정)

libfreeimage.so + 0x43e74 — WebP EXIF 파싱 시 NULL 포인터 역참조 → SIGSEGV
RAX = 0x66697845 ("Exif") — EXIF 매직넘버 처리 중 크래시
  • PMS v1.43의 FreeImage 라이브러리가 모든 WebP 포맷에서 EXIF 파서를 호출
  • EXIF 데이터가 없으면 NULL 포인터 → 크래시
  • VP8X뿐 아니라 VP8, VP8L 모두 해당 (v1.43 기준)
  • v1.42 이하에서는 VP8X만 문제일 수 있음 (libfreeimage.so 버전 차이)

SJVA 에이전트 패치 미작동 문제

키효님의 agent_base.py 패치(VP8X→VP8 변환)가 실제로 호출되지 않았습니다:

AgentBase(agent_base.py) — safe_image_content() 정의 ← 여기에 패치
  └─ ModuleYamlBase(module_yaml_base.py) — set_data_media() 오버라이드 → HTTP.Request() 직접 호출
  └─ ModuleKtv(module_ktv.py) — HTTP.Request() 직접 호출 (14곳)

해결: 3개 파일 모두 HTTP.Request(URL).contentself.safe_image_content(URL) 교체 필요

WebP 포맷 종류 및 Plex 처리 결과
헤더 설명 PMS v1.42 이하 PMS v1.43
VP8 단순 손실 압축 WebP 정상 크래시
VP8L 단순 무손실 WebP 정상 크래시
VP8X 확장 WebP (투명도, ICC 등) 크래시 크래시
JPEG 정상 정상

3. 해결 방법

fix_vp8x.py — WebP→JPEG 일괄 변환

fix_vp8x.py를 사용합니다.

# 전체 스캔
python3 fix_vp8x.py --days 0

# 최근 3일 증분 스캔 (daily cron용)
python3 fix_vp8x.py --days 3 --quiet

# 확인만 (변환 안 함)
python3 fix_vp8x.py --dry-run

# 경로 지정
python3 fix_vp8x.py --base "/volume1/.../Metadata"
DSM 작업 스케줄러 등록 방법

⚠️ Synology /etc/crontab은 DSM 업데이트 시 초기화됩니다. 반드시 DSM 작업 스케줄러 사용.

DSM 제어판 → 작업 스케줄러 → 생성 → 예약된 작업 → 사용자 정의 스크립트

[일반] 작업명: Plex VP8X Fix / 사용자: root / 활성화: ✓
[스케줄] 매일, Butler 시작 전 실행 (예: 06:15)
[작업 설정]
  python3 /usr/local/bin/fix_vp8x.py --days 3 --quiet >> /var/log/fix_vp8x.log 2>&1
ffmpeg-static 설치 (Synology)

Synology 기본 ffmpeg에는 libwebp 인코더가 없습니다. 정적 빌드를 설치해야 합니다.

# johnvansickle.com 정적 빌드 다운로드
wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
tar xf ffmpeg-release-amd64-static.tar.xz
sudo cp ffmpeg-*-static/ffmpeg /usr/local/bin/ffmpeg-static

# 또는 패키지 센터 ffmpeg7 설치 후 심볼릭 링크
sudo ln -s /volume1/@appstore/ffmpeg7/bin/ffmpeg7 /usr/local/bin/ffmpeg-static

# 확인
/usr/local/bin/ffmpeg-static -encoders 2>/dev/null | grep webp

SJVA 에이전트 패치 — 신규 VP8X 유입 차단

소스 Gist에서 agent_base.py, module_ktv.py, module_yaml_base.py를 다운로드하여 적용합니다.

현재 에이전트 패치는 VP8X → VP8 WebP 변환입니다 (키효님 원본 방식).

  • v1.42 이하: 이것만으로 크래시 방지 충분
  • v1.43: VP8 WebP도 크래시하므로, fix_vp8x.py daily cron이 후속으로 JPEG 변환 필요
# 1. 파일 덮어쓰기
cd "SjvaAgent.bundle/Contents/Code/"
# agent_base.py, module_ktv.py, module_yaml_base.py 복사

# 2. SJVA 업데이트 시 덮어쓰기 방지
cd "SjvaAgent.bundle"
git update-index --assume-unchanged Contents/Code/agent_base.py
git update-index --assume-unchanged Contents/Code/module_ktv.py
git update-index --assume-unchanged Contents/Code/module_yaml_base.py

# 3. PMS 재시작
패치 상세 — 변경 내용

agent_base.py (키효님 원본):

  • webp_chunk(), is_webp_vp8x(), ffmpeg_convert_vp8_webp(), safe_image_content() 4개 메서드 추가
  • set_data_media()에서 HTTP.Request()safe_image_content() 교체
  • 변환: VP8X → VP8 WebP (ffmpeg -c:v libwebp)

module_ktv.py (14곳 변경):

# 변경 전
ProxyClass(HTTP.Request(item['value']).content, sort_order=...)
# 변경 후
ProxyClass(self.safe_image_content(item['value']), sort_order=...)

module_yaml_base.py (set_data_media() 내 2곳 변경):

# 변경 전
meta[media['url']] = Proxy.Preview(HTTP.Request(media['thumb']).content, ...)
# 변경 후
meta[media['url']] = Proxy.Preview(self.safe_image_content(media['thumb']), ...)

4. 방어 체계

v1.42 이하

[1단계] SJVA 에이전트 패치 (VP8X→VP8, 신규 유입 차단) — 이것만으로 충분
[2단계] fix_vp8x.py daily cron (보험 — 기존 VP8X 파일 JPEG 정리)

v1.43

[1단계] SJVA 에이전트 패치 (VP8X→VP8, 신규 VP8X 유입은 차단)
[2단계] fix_vp8x.py daily cron (핵심 — 모든 WebP→JPEG 변환)
[3단계] plex_crash_watchdog.py 5분 크론 (크래시 시 코어 덤프 분석→변환→정리)
[4단계] watch_pms.sh 1분 크론 (PMS 자동 재시작)

v1.43에서 크래시가 계속 발생할 경우, agent_base.py의 safe_image_content()를 JPEG 변환으로 변경하면 신규 유입 시점에서 바로 JPEG로 저장됩니다.


5. 처리 실적

일괄 변환 (2026-03-01)

라이브러리 WebP 파일 수 변환 결과 소요 시간
Movies 10,563 전체 성공 5분 31초 (31.9/s)
TV Shows 76,354 전체 성공 122분 (10.5/s)
합계 86,917 fail 0 ~128분

크래시 이력

기간 크래시 횟수 비고
02-22 15회 최초 발생
02-24~26 10회+ 스케줄 충돌 재발
02-28 0회 agent_base.py 패치 (실제 미작동)
03-01 07시 9회 패치 미작동으로 재발
03-01 11시 3회 코어 덤프 분석용
03-01 17시 ~ 현재 0회 🟢 일괄 변환 후 완전 해소

6. 상세 분석 — 최초 로그 분석 (2026-02-22)

크래시 직전 로그

2026-02-22 04:49:06 - 기동 (WAL에서 659 프레임 복구 ← 비정상 종료 흔적)
...
2026-02-22 04:57:09 - [UltraBlurProcessor] Failed to decode image data  ← 15회 연속
2026-02-22 04:57:10 - Crash: Crash reporting disabled

크래시 패턴

Plex Butler 시작 (예약 시간)
  → UltraBlurProcessor: WebP 이미지 처리 시도
  → Failed to decode image data (연속)
  → SIGSEGV 크래시
  → 자동 재기동 → 동일 이미지 재처리 → 재크래시 (7~9분 간격 루프)

UltraBlurProcessor란?

Plex 클라이언트(Plex Web, Plexamp 등)에서 포스터 배경에 부드러운 색상 그라데이션을 표시하기 위해 이미지 4개 코너에서 색상을 추출하는 내부 컴포넌트.

7. 상세 분석 — VP8X 확인 스크립트
# check_webp.py — WebP 파일 개수 확인
import os

META_BASE = '/volume5/PlexMediaServer/AppData/Plex Media Server/Metadata'
LIBS = ['Movies', 'TV Shows']

total = vp8x = webp = 0
for lib in LIBS:
    lib_path = os.path.join(META_BASE, lib)
    for root, dirs, files in os.walk(lib_path):
        for fname in files:
            fpath = os.path.join(root, fname)
            if os.path.islink(fpath):
                continue
            try:
                total += 1
                with open(fpath, 'rb') as f:
                    h = f.read(16)
                if len(h) >= 12 and h[:4] == b'RIFF' and h[8:12] == b'WEBP':
                    webp += 1
                    if len(h) >= 16 and h[12:16] == b'VP8X':
                        vp8x += 1
            except:
                pass
print(f'전체: {total} / WebP: {webp} / VP8X: {vp8x}')
8. 상세 분석 — inotify 및 VP8 vs JPEG 비교

inotify 조사 결과 (DS918+)

# 커널 inotify 지원: O
cat /proc/sys/fs/inotify/max_user_watches  # → 524288
# inotify-tools: 없음 / pip: 없음
# ctypes 직접 호출: 가능하나 복잡도 높아 daily cron 유지 결정

VP8 vs JPEG 변환 포맷 비교

항목 VP8 WebP (ffmpeg) JPEG (ffmpeg/ImageMagick)
PMS v1.43 크래시 정상
PMS v1.42 이하 정상 정상
파일 크기 더 작음 더 큼
변환 성능 보통 빠름

결론: v1.43에서는 JPEG만 안전. v1.42 이하에서는 VP8도 가능.

9. 스케줄 최적화 이력

1차 조정 (2026-02-25)

  • Butler 05:00~06:00, VP8X Fix 06:00 (동시!) → 04:45로 앞당김

2차 조정 (2026-02-28)

  • SJVA 대량 갱신일 06:53까지 실행 → VP8X Fix 07:15 / Butler 07:30~08:30

최종 스케줄 (2026-03-01)

시간 작업
06:15 fix_vp8x.py v4 (WebP→JPEG)
07:00~08:00 Plex Butler
08:30 Plex DB Repair

Butler 설정 주의: Preferences.xml 직접 편집은 PMS 재시작 시 메모리 값으로 덮어씀. 반드시 API로 변경: curl -X PUT "http://localhost:32400/:/prefs?ButlerStartHour=7&ButlerEndHour=8&X-Plex-Token=TOKEN"

10. fix_vp8x.py 버전 이력
버전 날짜 주요 변경
v1 02-22 최초 작성, VP8X→JPEG (ImageMagick)
v2 02-22 extract_bundle_hash 버그 수정
v3 02-25 --quiet WARNING 로그, thread-safe tmp, Discord 웹훅 UA
v4 03-01 모든 WebP 탐지, ffmpeg-static WebP→JPEG (-q:v 2)

참고

  • Plex 공식 포럼 PM-2597: UltraBlurProcessor 로그 스팸 (v1.41.5에서 수정, 크래시와 별도)
  • 현재 v1.43.0.10492 기준 WebP 크래시 관련 공식 픽스 없음
  • 패치 원작자: 키효님 (SJVA Discord, 2026-02-23) — agent_base.py VP8X→VP8 변환
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment