Files
blog-writer/scripts/download_fonts.py
sinmb79 8a7a122bb3 feat: v3.0 엔진 추상화 + 소설 파이프라인 추가
[1순위] 엔진 추상화 리팩토링
- config/engine.json: 단일 설정 파일로 writing/tts/image/video/publishing 엔진 제어
- bots/engine_loader.py: EngineLoader 팩토리 클래스 (Claude/OpenClaw/Gemini Writer, gTTS/GoogleCloud/OpenAI/ElevenLabs TTS, DALL-E/External 이미지)

[2순위] VideoEngine 추상화
- bots/converters/video_engine.py: VideoEngine ABC + FFmpegSlidesEngine/SeedanceEngine/SoraEngine/RunwayEngine/VeoEngine 구현
- Seedance 2.0 API 연동 + 실패 시 ffmpeg_slides 자동 fallback

[3순위] 소설 연재 파이프라인
- bots/novel/novel_writer.py: AI 에피소드 자동 생성 (Claude/엔진 추상화)
- bots/novel/novel_blog_converter.py: 에피소드 → 장르별 테마 Blogger HTML
- bots/novel/novel_shorts_converter.py: key_scenes → TTS + Pillow + VideoEngine → MP4
- bots/novel/novel_manager.py: 전체 파이프라인 조율 + Telegram 명령 처리
- config/novels/shadow-protocol.json: 예시 소설 설정 (2040 서울 SF 스릴러)

[스케줄러] 소설 파이프라인 통합
- 매주 월/목 09:00 자동 실행 (job_novel_pipeline)
- Telegram 명령: /novel_list, /novel_gen, /novel_status

[기타 수정]
- collector_bot.py: 한국어 유니코드 감지 + RSS 신뢰도 override 버그 수정
- quality_rules.json: min_score 70→60
- scripts/get_token.py: YouTube OAuth scope 추가
- .env.example: SEEDANCE/ELEVENLABS/GEMINI/RUNWAY API 키 항목 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 09:33:04 +09:00

140 lines
4.3 KiB
Python

"""
폰트 다운로드 스크립트
Noto Sans KR 폰트를 assets/fonts/에 다운로드.
카드 변환봇(card_converter.py)에서 사용.
실행: python scripts/download_fonts.py
"""
import os
import sys
import urllib.request
from pathlib import Path
BASE_DIR = Path(__file__).parent.parent
FONTS_DIR = BASE_DIR / 'assets' / 'fonts'
FONTS_DIR.mkdir(parents=True, exist_ok=True)
# Google Fonts에서 직접 다운로드 (Noto Sans KR)
FONTS = {
'NotoSansKR-Regular.ttf': (
'https://github.com/notofonts/noto-cjk/raw/main/Sans/OTF/Korean/'
'NotoSansCJKkr-Regular.otf'
),
'NotoSansKR-Bold.ttf': (
'https://github.com/notofonts/noto-cjk/raw/main/Sans/OTF/Korean/'
'NotoSansCJKkr-Bold.otf'
),
'NotoSansKR-Medium.ttf': (
'https://github.com/notofonts/noto-cjk/raw/main/Sans/OTF/Korean/'
'NotoSansCJKkr-Medium.otf'
),
}
# Windows에 이미 설치된 한글 폰트를 복사하는 대안
WINDOWS_FONT_CANDIDATES = [
('malgunbd.ttf', 'NotoSansKR-Bold.ttf'), # 맑은고딕 Bold
('malgun.ttf', 'NotoSansKR-Regular.ttf'), # 맑은고딕 Regular
('malgun.ttf', 'NotoSansKR-Medium.ttf'),
]
def copy_windows_fonts() -> list[str]:
"""Windows 기본 한글 폰트를 assets/fonts/에 복사"""
import shutil
win_fonts = Path('C:/Windows/Fonts')
copied = []
for src_name, dst_name in WINDOWS_FONT_CANDIDATES:
src = win_fonts / src_name
dst = FONTS_DIR / dst_name
if dst.exists():
print(f" 이미 존재: {dst_name}")
copied.append(dst_name)
continue
if src.exists():
shutil.copy2(src, dst)
print(f" 복사: {src_name}{dst_name}")
copied.append(dst_name)
else:
print(f" 없음: {src_name}")
return copied
def download_from_url(url: str, dst_path: Path) -> bool:
"""URL에서 폰트 파일 다운로드"""
try:
print(f" 다운로드 중: {dst_path.name} ...")
headers = {'User-Agent': 'Mozilla/5.0'}
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req, timeout=30) as resp:
dst_path.write_bytes(resp.read())
print(f" 완료: {dst_path.name} ({dst_path.stat().st_size // 1024} KB)")
return True
except Exception as e:
print(f" 실패: {e}")
return False
def verify_font(font_path: Path) -> bool:
"""PIL로 폰트 로드 테스트"""
try:
from PIL import ImageFont
ImageFont.truetype(str(font_path), 30)
return True
except Exception as e:
print(f" 폰트 검증 실패: {e}")
return False
def main():
print("=== Noto Sans KR 폰트 설치 ===\n")
print(f"대상 폴더: {FONTS_DIR}\n")
# 1단계: Windows 기본 폰트 복사 시도 (가장 빠름)
print("[1단계] Windows 기본 한글 폰트 복사...")
copied = copy_windows_fonts()
if len(copied) >= 2:
print(f"\n[OK] Windows 폰트 복사 완료 ({len(copied)}개)")
_verify_all()
return
# 2단계: GitHub에서 직접 다운로드
print("\n[2단계] GitHub Noto Sans CJK KR 다운로드...")
downloaded = 0
for filename, url in FONTS.items():
dst = FONTS_DIR / filename
if dst.exists() and dst.stat().st_size > 1000:
print(f" 이미 존재: {filename}")
downloaded += 1
continue
if download_from_url(url, dst):
downloaded += 1
if downloaded > 0:
print(f"\n✅ 다운로드 완료 ({downloaded}개)")
_verify_all()
else:
print("\n❌ 폰트 설치 실패. 수동 설치 방법:")
print(" 1. https://fonts.google.com/noto/specimen/Noto+Sans+KR 에서 다운로드")
print(f" 2. TTF 파일들을 {FONTS_DIR} 에 복사")
print(" 3. NotoSansKR-Regular.ttf, NotoSansKR-Bold.ttf 로 이름 변경")
sys.exit(1)
def _verify_all():
print("\n[검증] 폰트 로드 테스트...")
ok = True
for f in FONTS_DIR.glob('*.ttf'):
if verify_font(f):
print(f"{f.name}")
else:
print(f"{f.name}")
ok = False
if ok:
print("\n카드 변환봇 준비 완료!")
else:
print("\n일부 폰트 오류. card_converter.py는 대체 폰트로 동작합니다.")
if __name__ == '__main__':
main()