From b74cc323d1b2c4d637931de6d12bc739a07d1e2a Mon Sep 17 00:00:00 2001 From: synapz-org Date: Sun, 26 Oct 2025 09:47:35 -0400 Subject: [PATCH] feat: add CLI main module with Click integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented the CLI entry point using Click framework. The CLI: - Accepts Space URL as an argument - Supports --cookies and --output options - Validates URLs using the validator module - Downloads Spaces using the downloader module - Provides clear success and error messages - Uses proper exit codes (0=success, 1=validation error, 2=download error) Test uses Click's CliRunner to test CLI without running actual downloads. Mocks validate_space_url and download_space functions for isolated testing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/main.py | 35 +++++++++++++++++++++++++++++++++++ tests/test_main.py | 21 +++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/main.py create mode 100644 tests/test_main.py diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..2c30f76 --- /dev/null +++ b/src/main.py @@ -0,0 +1,35 @@ +import click +import os +from src.validator import validate_space_url +from src.downloader import download_space, DownloadError + + +@click.command() +@click.argument('url') +@click.option('--cookies', '-c', type=click.Path(exists=True), help='Path to cookies file') +@click.option('--output', '-o', type=click.Path(), default='.', help='Output directory') +def cli(url, cookies, output): + """ + Download X (Twitter) Space recordings as MP3 files. + + Example: + x-space-dl https://x.com/i/spaces/1ZkKzZWnVRRKv + """ + # Validate URL + is_valid, space_id, error = validate_space_url(url) + if not is_valid: + click.echo(f"Error: {error}", err=True) + raise click.Exit(1) + + # Download the Space + try: + click.echo(f"Downloading Space: {space_id}") + output_path = download_space(url, output, cookies) + click.echo(f"Successfully downloaded to: {output_path}") + except DownloadError as e: + click.echo(f"Error: {str(e)}", err=True) + raise click.Exit(2) + + +if __name__ == '__main__': + cli() diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 0000000..d94bc80 --- /dev/null +++ b/tests/test_main.py @@ -0,0 +1,21 @@ +import pytest +from click.testing import CliRunner +from unittest.mock import patch, Mock +from src.main import cli + + +def test_cli_valid_url(tmp_path): + """Test CLI with valid URL.""" + runner = CliRunner() + + with patch('src.main.validate_space_url') as mock_validate, \ + patch('src.main.download_space') as mock_download: + + mock_validate.return_value = (True, "1ZkKzZWnVRRKv", None) + mock_download.return_value = str(tmp_path / "space_1ZkKzZWnVRRKv.mp3") + + result = runner.invoke(cli, ['https://x.com/i/spaces/1ZkKzZWnVRRKv']) + + assert result.exit_code == 0 + assert "Successfully downloaded" in result.output + assert "space_1ZkKzZWnVRRKv.mp3" in result.output