Initial commit: YouTube Shorts maker application
Features: - Video download from TikTok/Douyin using yt-dlp - Audio transcription with OpenAI Whisper - GPT-4 translation (direct/summarize/rewrite modes) - Subtitle generation with ASS format - Video trimming with frame-accurate preview - BGM integration with volume control - Intro text overlay support - Thumbnail generation with text overlay Tech stack: - Backend: FastAPI, Python 3.11+ - Frontend: React, Vite, TailwindCSS - Video processing: FFmpeg - AI: OpenAI Whisper, GPT-4 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
163
backend/app/routers/fonts.py
Normal file
163
backend/app/routers/fonts.py
Normal file
@@ -0,0 +1,163 @@
|
||||
"""
|
||||
Fonts Router - Korean font management for subtitles.
|
||||
|
||||
Provides font listing and recommendations for YouTube Shorts subtitles.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from app.models.schemas import FontInfo, KOREAN_FONTS, FONT_RECOMMENDATIONS
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/")
|
||||
async def list_fonts():
|
||||
"""
|
||||
List all available Korean fonts for subtitles.
|
||||
|
||||
Returns font information including:
|
||||
- id: System font name to use in subtitle_style.font_name
|
||||
- name: Display name in Korean
|
||||
- style: Font style description
|
||||
- recommended_for: Content types this font works well with
|
||||
- download_url: Where to download the font
|
||||
- license: Font license information
|
||||
"""
|
||||
fonts = []
|
||||
for font_id, font_info in KOREAN_FONTS.items():
|
||||
fonts.append({
|
||||
"id": font_info.id,
|
||||
"name": font_info.name,
|
||||
"style": font_info.style,
|
||||
"recommended_for": font_info.recommended_for,
|
||||
"download_url": font_info.download_url,
|
||||
"license": font_info.license,
|
||||
})
|
||||
|
||||
return {
|
||||
"fonts": fonts,
|
||||
"total": len(fonts),
|
||||
"default": "NanumGothic",
|
||||
"usage": "Set subtitle_style.font_name to the font id",
|
||||
}
|
||||
|
||||
|
||||
@router.get("/recommend/{content_type}")
|
||||
async def recommend_fonts(content_type: str):
|
||||
"""
|
||||
Get font recommendations for a specific content type.
|
||||
|
||||
Available content types:
|
||||
- tutorial: 튜토리얼, 강의
|
||||
- gaming: 게임, 리액션
|
||||
- cooking: 요리, 먹방
|
||||
- comedy: 코미디, 유머
|
||||
- travel: 여행, 브이로그
|
||||
- news: 뉴스, 정보
|
||||
- asmr: ASMR, 릴렉스
|
||||
- fitness: 운동, 피트니스
|
||||
- tech: 기술, IT
|
||||
- lifestyle: 라이프스타일, 일상
|
||||
"""
|
||||
content_type_lower = content_type.lower()
|
||||
|
||||
if content_type_lower not in FONT_RECOMMENDATIONS:
|
||||
available_types = list(FONT_RECOMMENDATIONS.keys())
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Unknown content type. Available: {', '.join(available_types)}"
|
||||
)
|
||||
|
||||
recommended_ids = FONT_RECOMMENDATIONS[content_type_lower]
|
||||
recommendations = []
|
||||
|
||||
for font_id in recommended_ids:
|
||||
if font_id in KOREAN_FONTS:
|
||||
font = KOREAN_FONTS[font_id]
|
||||
recommendations.append({
|
||||
"id": font.id,
|
||||
"name": font.name,
|
||||
"style": font.style,
|
||||
"download_url": font.download_url,
|
||||
})
|
||||
|
||||
return {
|
||||
"content_type": content_type_lower,
|
||||
"recommendations": recommendations,
|
||||
"primary": recommended_ids[0] if recommended_ids else "NanumGothic",
|
||||
}
|
||||
|
||||
|
||||
@router.get("/categories")
|
||||
async def list_font_categories():
|
||||
"""
|
||||
List fonts grouped by style category.
|
||||
"""
|
||||
categories = {
|
||||
"clean": {
|
||||
"name": "깔끔/모던",
|
||||
"description": "정보성 콘텐츠, 튜토리얼에 적합",
|
||||
"fonts": ["Pretendard", "SpoqaHanSansNeo", "NanumGothic"],
|
||||
},
|
||||
"friendly": {
|
||||
"name": "친근/둥글",
|
||||
"description": "일상, 라이프스타일 콘텐츠에 적합",
|
||||
"fonts": ["GmarketSans", "NanumSquareRound", "Cafe24SsurroundAir"],
|
||||
},
|
||||
"handwriting": {
|
||||
"name": "손글씨/캐주얼",
|
||||
"description": "먹방, 요리, 유머 콘텐츠에 적합",
|
||||
"fonts": ["BMDoHyeon", "BMJua", "DoHyeon"],
|
||||
},
|
||||
"impact": {
|
||||
"name": "강조/임팩트",
|
||||
"description": "게임, 하이라이트, 리액션에 적합",
|
||||
"fonts": ["Cafe24Ssurround", "BlackHanSans"],
|
||||
},
|
||||
}
|
||||
|
||||
# Add font details to each category
|
||||
for category_id, category_info in categories.items():
|
||||
font_details = []
|
||||
for font_id in category_info["fonts"]:
|
||||
if font_id in KOREAN_FONTS:
|
||||
font = KOREAN_FONTS[font_id]
|
||||
font_details.append({
|
||||
"id": font.id,
|
||||
"name": font.name,
|
||||
})
|
||||
category_info["font_details"] = font_details
|
||||
|
||||
return {
|
||||
"categories": categories,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/{font_id}")
|
||||
async def get_font(font_id: str):
|
||||
"""
|
||||
Get detailed information about a specific font.
|
||||
"""
|
||||
if font_id not in KOREAN_FONTS:
|
||||
available_fonts = list(KOREAN_FONTS.keys())
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Font not found. Available fonts: {', '.join(available_fonts)}"
|
||||
)
|
||||
|
||||
font = KOREAN_FONTS[font_id]
|
||||
return {
|
||||
"id": font.id,
|
||||
"name": font.name,
|
||||
"style": font.style,
|
||||
"recommended_for": font.recommended_for,
|
||||
"download_url": font.download_url,
|
||||
"license": font.license,
|
||||
"usage_example": {
|
||||
"subtitle_style": {
|
||||
"font_name": font.id,
|
||||
"font_size": 36,
|
||||
"position": "center",
|
||||
}
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user