feat: add error handling to downloader

Added DownloadError exception class that is raised when yt-dlp fails.
The download_space function now wraps yt-dlp calls in a try-except block
to catch any exceptions and raise a custom DownloadError with a clear message.

Test added to verify that DownloadError is raised when yt-dlp fails.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
synapz-org
2025-10-26 09:45:09 -04:00
parent 73775c122b
commit 5eec3129bc
2 changed files with 33 additions and 3 deletions

View File

@@ -4,6 +4,11 @@ from typing import Optional
from src.validator import validate_space_url from src.validator import validate_space_url
class DownloadError(Exception):
"""Raised when download fails."""
pass
def download_space( def download_space(
space_url: str, space_url: str,
output_dir: str, output_dir: str,
@@ -19,6 +24,9 @@ def download_space(
Returns: Returns:
Path to the downloaded MP3 file Path to the downloaded MP3 file
Raises:
DownloadError: If download fails
""" """
# Extract space ID from URL using validator for robustness # Extract space ID from URL using validator for robustness
is_valid, space_id, error_message = validate_space_url(space_url) is_valid, space_id, error_message = validate_space_url(space_url)
@@ -45,8 +53,11 @@ def download_space(
ydl_opts['cookiefile'] = cookies_file ydl_opts['cookiefile'] = cookies_file
# Download with yt-dlp # Download with yt-dlp
with yt_dlp.YoutubeDL(ydl_opts) as ydl: try:
ydl.extract_info(space_url, download=True) with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.extract_info(space_url, download=True)
except Exception as e:
raise DownloadError(f"Failed to download Space: {str(e)}")
# Return the expected output path # Return the expected output path
return os.path.join(output_dir, f'space_{space_id}.mp3') return os.path.join(output_dir, f'space_{space_id}.mp3')

View File

@@ -1,6 +1,6 @@
import pytest import pytest
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from src.downloader import download_space from src.downloader import download_space, DownloadError
def test_download_space_basic(tmp_path): def test_download_space_basic(tmp_path):
@@ -83,3 +83,22 @@ def test_download_space_ytdlp_configuration(tmp_path):
assert len(call_args['postprocessors']) > 0 assert len(call_args['postprocessors']) > 0
assert call_args['postprocessors'][0]['key'] == 'FFmpegExtractAudio' assert call_args['postprocessors'][0]['key'] == 'FFmpegExtractAudio'
assert call_args['postprocessors'][0]['preferredcodec'] == 'mp3' assert call_args['postprocessors'][0]['preferredcodec'] == 'mp3'
def test_download_space_error_handling(tmp_path):
"""Test that download errors are properly caught and wrapped."""
space_url = "https://x.com/i/spaces/invalid"
with patch('src.downloader.yt_dlp.YoutubeDL') as mock_ytdl_class:
mock_ytdl = Mock()
mock_ytdl_class.return_value.__enter__.return_value = mock_ytdl
mock_ytdl.extract_info.side_effect = Exception("Video unavailable")
with pytest.raises(DownloadError) as exc_info:
download_space(
space_url=space_url,
output_dir=str(tmp_path),
cookies_file=None
)
assert "Failed to download" in str(exc_info.value)