Remove Shikimori API, use AnimeThemes only, switch to WebM format

- Remove ShikimoriService, use AnimeThemes API for search
- Replace shikimori_id with animethemes_slug as primary identifier
- Remove FFmpeg MP3 conversion, download WebM directly
- Add .webm support in storage and upload endpoints
- Update frontend to use animethemes_slug

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-01-12 11:22:46 +03:00
parent 333de65fbd
commit cc11f0b773
12 changed files with 138 additions and 263 deletions

View File

@@ -173,11 +173,10 @@ class DownloadService:
if not theme.animethemes_video_url:
raise ValueError("No video URL available")
# Download and convert in temp directory
# Download WebM file directly (no conversion needed)
with tempfile.TemporaryDirectory() as tmp_dir:
tmp_path = Path(tmp_dir)
webm_file = tmp_path / "video.webm"
mp3_file = tmp_path / "audio.mp3"
webm_file = tmp_path / "audio.webm"
# Stream download WebM file
async with httpx.AsyncClient() as client:
@@ -192,44 +191,23 @@ class DownloadService:
async for chunk in response.aiter_bytes(chunk_size=8192):
f.write(chunk)
task.progress_percent = 40
task.status = DownloadStatus.CONVERTING
await self.db.commit()
# Convert to MP3 with FFmpeg
process = await asyncio.create_subprocess_exec(
"ffmpeg", "-i", str(webm_file),
"-vn", "-acodec", "libmp3lame", "-q:a", "2",
str(mp3_file),
"-y",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await process.communicate()
if process.returncode != 0:
raise RuntimeError(f"FFmpeg error: {stderr.decode()[:500]}")
if not mp3_file.exists():
raise RuntimeError("FFmpeg did not create output file")
task.progress_percent = 70
task.status = DownloadStatus.UPLOADING
await self.db.commit()
# Generate safe S3 key
anime_name = self._sanitize_filename(
anime.title_english or anime.title_russian or f"anime_{anime.shikimori_id}"
anime.title_english or f"anime_{anime.animethemes_slug}"
)
theme_name = theme.full_name
song_part = f"_{self._sanitize_filename(theme.song_title)}" if theme.song_title else ""
s3_key = f"audio/{anime_name}_{theme_name}{song_part}.mp3"
s3_key = f"audio/{anime_name}_{theme_name}{song_part}.webm"
# Read file and upload to S3
file_data = mp3_file.read_bytes()
file_data = webm_file.read_bytes()
file_size = len(file_data)
success = storage.upload_file(s3_key, file_data, "audio/mpeg")
success = storage.upload_file(s3_key, file_data, "video/webm")
if not success:
raise RuntimeError("Failed to upload to S3")
@@ -239,7 +217,7 @@ class DownloadService:
# Create Opening entity in main table
opening = Opening(
anime_name=anime.title_russian or anime.title_english or f"Anime {anime.shikimori_id}",
anime_name=anime.title_english or f"Anime {anime.animethemes_slug}",
op_number=theme_name,
song_name=theme.song_title,
audio_file=s3_key.replace("audio/", ""),