From 5eec3129bce442fc3efea426bc228fa8d4053988 Mon Sep 17 00:00:00 2001 From: synapz-org Date: Sun, 26 Oct 2025 09:45:09 -0400 Subject: [PATCH] feat: add error handling to downloader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/downloader.py | 15 +++++++++++++-- tests/test_downloader.py | 21 ++++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/downloader.py b/src/downloader.py index c19e9c1..7d870d9 100644 --- a/src/downloader.py +++ b/src/downloader.py @@ -4,6 +4,11 @@ from typing import Optional from src.validator import validate_space_url +class DownloadError(Exception): + """Raised when download fails.""" + pass + + def download_space( space_url: str, output_dir: str, @@ -19,6 +24,9 @@ def download_space( Returns: Path to the downloaded MP3 file + + Raises: + DownloadError: If download fails """ # Extract space ID from URL using validator for robustness is_valid, space_id, error_message = validate_space_url(space_url) @@ -45,8 +53,11 @@ def download_space( ydl_opts['cookiefile'] = cookies_file # Download with yt-dlp - with yt_dlp.YoutubeDL(ydl_opts) as ydl: - ydl.extract_info(space_url, download=True) + try: + 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 os.path.join(output_dir, f'space_{space_id}.mp3') diff --git a/tests/test_downloader.py b/tests/test_downloader.py index f8ec44f..5e2517b 100644 --- a/tests/test_downloader.py +++ b/tests/test_downloader.py @@ -1,6 +1,6 @@ import pytest 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): @@ -83,3 +83,22 @@ def test_download_space_ytdlp_configuration(tmp_path): assert len(call_args['postprocessors']) > 0 assert call_args['postprocessors'][0]['key'] == 'FFmpegExtractAudio' 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)