From a02dd0a9e29180826db9dc30634180587ddbc484 Mon Sep 17 00:00:00 2001
From: unknown
Date: Wed, 4 Mar 2026 22:05:13 +0300
Subject: [PATCH] Restructure to v2.0.0: rename modules/ to snatch/, modernize
packaging, clean docs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Rename modules/ → snatch/ package with all relative imports preserved
- Move Theme/ → snatch/theme/ sub-package with fixed parent-relative imports
- Add pyproject.toml with optional dependency groups (audio, p2p, video, dev)
- Add console entry point: snatch command via snatch.cli:main
- Consolidate config modules (4→2): merge config_helpers→config, advanced_config→config_manager
- Fix version single-source-of-truth: constants.py → 2.0.0
- Add pytest test suite (constants, utils, cache, CLI)
- Rewrite CI/CD pipeline for new structure (Python 3.10+)
- Clean stale files: setupfiles/, empty Theme stubs, __pycache__ from git
- Update all documentation: fix old references, add v2.0.0 changelog, delete stale docs
- Simplify setup.py to pyproject.toml shim, clean requirements.txt
Co-Authored-By: Claude Opus 4.6
---
.github/workflows/ci.yml | 820 +------------
.gitignore | 43 +
CONTRIBUTING.md | 21 +-
Documentation/API_REFERENCE.md | 2 +-
Documentation/AUDIO_ENHANCEMENT_GUIDE.md | 2 +-
Documentation/CHANGELOG.md | 39 +
.../CONFIGURATION_IMPLEMENTATION_SUMMARY.md | 180 ---
Documentation/CONFIGURATION_MANAGEMENT.md | 92 +-
Documentation/DEPLOYMENT_GUIDE.md | 55 +-
Documentation/DOCUMENTATION_INDEX.md | 215 +---
Documentation/DOCUMENTATION_UPDATE_SUMMARY.md | 194 ---
Documentation/FEATURES_UPDATE.md | 277 -----
Documentation/FIXES_README.md | 81 --
Documentation/FIXES_SUMMARY.md | 163 ---
Documentation/INTERACTIVE_MODE_GUIDE.md | 2 +-
Documentation/MODULE_DOCUMENTATION.md | 12 +-
.../PERFORMANCE_OPTIMIZATION_GUIDE.md | 2 +-
Documentation/PLUGIN_DEVELOPMENT_GUIDE.md | 4 +-
Documentation/README.md | 1058 -----------------
Documentation/TECHNICAL_ARCHITECTURE.md | 4 +-
Documentation/TECHNICAL_DOCUMENTATION.md | 50 +-
Documentation/TODO.md | 0
Documentation/TROUBLESHOOTING_GUIDE.md | 8 +-
Documentation/USAGE_GUIDE.md | 2 +-
README.md | 125 +-
Snatch.bat | 2 +-
Theme/__pycache__/__init__.cpython-312.pyc | Bin 801 -> 0 bytes
.../cyberpunk_interactive.cpython-312.pyc | Bin 157 -> 0 bytes
.../modern_interactive.cpython-312.pyc | Bin 36116 -> 0 bytes
.../textual_interface.cpython-312.pyc | Bin 47631 -> 0 bytes
Theme/cyberpunk_interactive.py | 0
Theme/working_interactive.py | 0
modules/__pycache__/__init__.cpython-312.pyc | Bin 353 -> 0 bytes
modules/__pycache__/__init__.cpython-313.pyc | Bin 353 -> 0 bytes
.../advanced_config.cpython-312.pyc | Bin 16747 -> 0 bytes
.../advanced_config.cpython-313.pyc | Bin 17090 -> 0 bytes
.../advanced_scheduler.cpython-312.pyc | Bin 25805 -> 0 bytes
.../advanced_scheduler.cpython-313.pyc | Bin 26331 -> 0 bytes
.../audio_processor.cpython-312.pyc | Bin 67594 -> 0 bytes
.../audio_processor.cpython-313.pyc | Bin 43252 -> 0 bytes
modules/__pycache__/cache.cpython-312.pyc | Bin 10466 -> 0 bytes
modules/__pycache__/cache.cpython-313.pyc | Bin 10820 -> 0 bytes
modules/__pycache__/cli.cpython-312.pyc | Bin 132895 -> 0 bytes
modules/__pycache__/cli.cpython-313.pyc | Bin 85338 -> 0 bytes
.../__pycache__/common_utils.cpython-312.pyc | Bin 29493 -> 0 bytes
.../__pycache__/common_utils.cpython-313.pyc | Bin 29354 -> 0 bytes
modules/__pycache__/config.cpython-312.pyc | Bin 28037 -> 0 bytes
modules/__pycache__/config.cpython-313.pyc | Bin 28791 -> 0 bytes
.../config_helpers.cpython-312.pyc | Bin 9434 -> 0 bytes
.../config_helpers.cpython-313.pyc | Bin 9399 -> 0 bytes
.../config_manager.cpython-312.pyc | Bin 42731 -> 0 bytes
.../config_manager.cpython-313.pyc | Bin 43230 -> 0 bytes
modules/__pycache__/constants.cpython-312.pyc | Bin 2637 -> 0 bytes
modules/__pycache__/constants.cpython-313.pyc | Bin 2637 -> 0 bytes
.../customization_manager.cpython-312.pyc | Bin 41546 -> 0 bytes
.../customization_manager.cpython-313.pyc | Bin 39678 -> 0 bytes
.../cyberpunk_interactive.cpython-312.pyc | Bin 51220 -> 0 bytes
.../cyberpunk_interactive.cpython-313.pyc | Bin 51445 -> 0 bytes
.../__pycache__/cyberpunk_ui.cpython-312.pyc | Bin 27762 -> 0 bytes
.../__pycache__/cyberpunk_ui.cpython-313.pyc | Bin 27702 -> 0 bytes
modules/__pycache__/defaults.cpython-312.pyc | Bin 15058 -> 0 bytes
modules/__pycache__/defaults.cpython-313.pyc | Bin 13822 -> 0 bytes
.../download_manager.cpython-313.pyc | Bin 12306 -> 0 bytes
.../__pycache__/error_handler.cpython-312.pyc | Bin 26203 -> 0 bytes
.../__pycache__/error_handler.cpython-313.pyc | Bin 26669 -> 0 bytes
.../__pycache__/ffmpeg_helper.cpython-312.pyc | Bin 17064 -> 0 bytes
.../__pycache__/ffmpeg_helper.cpython-313.pyc | Bin 17182 -> 0 bytes
.../file_organizer.cpython-312.pyc | Bin 31837 -> 0 bytes
.../file_organizer.cpython-313.pyc | Bin 32194 -> 0 bytes
modules/__pycache__/help_text.cpython-312.pyc | Bin 2152 -> 0 bytes
modules/__pycache__/help_text.cpython-313.pyc | Bin 2156 -> 0 bytes
.../interactive_mode.cpython-312.pyc | Bin 119908 -> 0 bytes
.../interactive_mode.cpython-313.pyc | Bin 120398 -> 0 bytes
.../logging_config.cpython-312.pyc | Bin 2659 -> 0 bytes
.../logging_config.cpython-313.pyc | Bin 2773 -> 0 bytes
modules/__pycache__/manager.cpython-312.pyc | Bin 94836 -> 0 bytes
modules/__pycache__/manager.cpython-313.pyc | Bin 96262 -> 0 bytes
modules/__pycache__/metadata.cpython-312.pyc | Bin 6091 -> 0 bytes
modules/__pycache__/metadata.cpython-313.pyc | Bin 6353 -> 0 bytes
.../modern_interactive.cpython-313.pyc | Bin 36447 -> 0 bytes
modules/__pycache__/network.cpython-312.pyc | Bin 54571 -> 0 bytes
modules/__pycache__/network.cpython-313.pyc | Bin 52444 -> 0 bytes
.../__pycache__/network_fixed.cpython-312.pyc | Bin 16127 -> 0 bytes
modules/__pycache__/p2p.cpython-312.pyc | Bin 136413 -> 0 bytes
modules/__pycache__/p2p.cpython-313.pyc | Bin 98595 -> 0 bytes
.../performance_monitor.cpython-312.pyc | Bin 20204 -> 0 bytes
.../performance_monitor.cpython-313.pyc | Bin 20424 -> 0 bytes
modules/__pycache__/progress.cpython-312.pyc | Bin 87380 -> 0 bytes
modules/__pycache__/progress.cpython-313.pyc | Bin 88517 -> 0 bytes
.../rich_format_display.cpython-313.pyc | Bin 5503 -> 0 bytes
modules/__pycache__/rich_ui.cpython-313.pyc | Bin 17669 -> 0 bytes
modules/__pycache__/session.cpython-312.pyc | Bin 50586 -> 0 bytes
modules/__pycache__/session.cpython-313.pyc | Bin 50074 -> 0 bytes
.../textual_interface.cpython-313.pyc | Bin 48518 -> 0 bytes
.../__pycache__/url_handler.cpython-313.pyc | Bin 3417 -> 0 bytes
modules/__pycache__/utils.cpython-312.pyc | Bin 8016 -> 0 bytes
modules/__pycache__/utils.cpython-313.pyc | Bin 8078 -> 0 bytes
.../working_interactive.cpython-313.pyc | Bin 27741 -> 0 bytes
modules/advanced_config.py | 361 ------
modules/config_helpers.py | 220 ----
pyproject.toml | 129 ++
requirements.txt | 109 +-
setup.py | 149 +--
setup_ffmpeg.py | 363 +++---
setupfiles/pip-install.txt | 2 -
setupfiles/pip-package.txt | 94 --
setupfiles/requirements.txt | 83 --
setupfiles/setup_ffmpeg.py | 508 --------
{modules => snatch}/__init__.py | 4 +-
{modules => snatch}/advanced_scheduler.py | 0
{modules => snatch}/audio_processor.py | 0
{modules => snatch}/cache.py | 0
{modules => snatch}/cli.py | 16 +-
{modules => snatch}/common_utils.py | 0
{modules => snatch}/config.py | 79 +-
{modules => snatch}/config_manager.py | 304 ++++-
{modules => snatch}/constants.py | 1 -
{modules => snatch}/customization_manager.py | 0
{modules => snatch/data}/Supported-sites.txt | 0
{modules => snatch}/defaults.py | 8 +-
{modules => snatch}/error_handler.py | 0
{modules => snatch}/ffmpeg_helper.py | 0
{modules => snatch}/file_organizer.py | 0
{modules => snatch}/help_text.py | 0
{modules => snatch}/interactive_mode.py | 15 +-
{modules => snatch}/logging_config.py | 0
{modules => snatch}/manager.py | 0
{modules => snatch}/metadata.py | 0
{modules => snatch}/network.py | 0
{modules => snatch}/p2p.py | 0
{modules => snatch}/performance_monitor.py | 0
{modules => snatch}/progress.py | 0
{modules => snatch}/session.py | 0
{modules => snatch}/standalone_audio.py | 0
{Theme => snatch/theme}/__init__.py | 15 +-
{Theme => snatch/theme}/modern_interactive.py | 8 +-
{Theme => snatch/theme}/textual_interface.py | 10 +-
.../IMPROVEMENTS.md => tests/__init__.py | 0
tests/conftest.py | 32 +
tests/test_cache.py | 70 ++
tests/test_cli.py | 28 +
tests/test_common_utils.py | 72 ++
tests/test_constants.py | 42 +
143 files changed, 1423 insertions(+), 4752 deletions(-)
create mode 100644 .gitignore
delete mode 100644 Documentation/CONFIGURATION_IMPLEMENTATION_SUMMARY.md
delete mode 100644 Documentation/DOCUMENTATION_UPDATE_SUMMARY.md
delete mode 100644 Documentation/FEATURES_UPDATE.md
delete mode 100644 Documentation/FIXES_README.md
delete mode 100644 Documentation/FIXES_SUMMARY.md
delete mode 100644 Documentation/README.md
delete mode 100644 Documentation/TODO.md
delete mode 100644 Theme/__pycache__/__init__.cpython-312.pyc
delete mode 100644 Theme/__pycache__/cyberpunk_interactive.cpython-312.pyc
delete mode 100644 Theme/__pycache__/modern_interactive.cpython-312.pyc
delete mode 100644 Theme/__pycache__/textual_interface.cpython-312.pyc
delete mode 100644 Theme/cyberpunk_interactive.py
delete mode 100644 Theme/working_interactive.py
delete mode 100644 modules/__pycache__/__init__.cpython-312.pyc
delete mode 100644 modules/__pycache__/__init__.cpython-313.pyc
delete mode 100644 modules/__pycache__/advanced_config.cpython-312.pyc
delete mode 100644 modules/__pycache__/advanced_config.cpython-313.pyc
delete mode 100644 modules/__pycache__/advanced_scheduler.cpython-312.pyc
delete mode 100644 modules/__pycache__/advanced_scheduler.cpython-313.pyc
delete mode 100644 modules/__pycache__/audio_processor.cpython-312.pyc
delete mode 100644 modules/__pycache__/audio_processor.cpython-313.pyc
delete mode 100644 modules/__pycache__/cache.cpython-312.pyc
delete mode 100644 modules/__pycache__/cache.cpython-313.pyc
delete mode 100644 modules/__pycache__/cli.cpython-312.pyc
delete mode 100644 modules/__pycache__/cli.cpython-313.pyc
delete mode 100644 modules/__pycache__/common_utils.cpython-312.pyc
delete mode 100644 modules/__pycache__/common_utils.cpython-313.pyc
delete mode 100644 modules/__pycache__/config.cpython-312.pyc
delete mode 100644 modules/__pycache__/config.cpython-313.pyc
delete mode 100644 modules/__pycache__/config_helpers.cpython-312.pyc
delete mode 100644 modules/__pycache__/config_helpers.cpython-313.pyc
delete mode 100644 modules/__pycache__/config_manager.cpython-312.pyc
delete mode 100644 modules/__pycache__/config_manager.cpython-313.pyc
delete mode 100644 modules/__pycache__/constants.cpython-312.pyc
delete mode 100644 modules/__pycache__/constants.cpython-313.pyc
delete mode 100644 modules/__pycache__/customization_manager.cpython-312.pyc
delete mode 100644 modules/__pycache__/customization_manager.cpython-313.pyc
delete mode 100644 modules/__pycache__/cyberpunk_interactive.cpython-312.pyc
delete mode 100644 modules/__pycache__/cyberpunk_interactive.cpython-313.pyc
delete mode 100644 modules/__pycache__/cyberpunk_ui.cpython-312.pyc
delete mode 100644 modules/__pycache__/cyberpunk_ui.cpython-313.pyc
delete mode 100644 modules/__pycache__/defaults.cpython-312.pyc
delete mode 100644 modules/__pycache__/defaults.cpython-313.pyc
delete mode 100644 modules/__pycache__/download_manager.cpython-313.pyc
delete mode 100644 modules/__pycache__/error_handler.cpython-312.pyc
delete mode 100644 modules/__pycache__/error_handler.cpython-313.pyc
delete mode 100644 modules/__pycache__/ffmpeg_helper.cpython-312.pyc
delete mode 100644 modules/__pycache__/ffmpeg_helper.cpython-313.pyc
delete mode 100644 modules/__pycache__/file_organizer.cpython-312.pyc
delete mode 100644 modules/__pycache__/file_organizer.cpython-313.pyc
delete mode 100644 modules/__pycache__/help_text.cpython-312.pyc
delete mode 100644 modules/__pycache__/help_text.cpython-313.pyc
delete mode 100644 modules/__pycache__/interactive_mode.cpython-312.pyc
delete mode 100644 modules/__pycache__/interactive_mode.cpython-313.pyc
delete mode 100644 modules/__pycache__/logging_config.cpython-312.pyc
delete mode 100644 modules/__pycache__/logging_config.cpython-313.pyc
delete mode 100644 modules/__pycache__/manager.cpython-312.pyc
delete mode 100644 modules/__pycache__/manager.cpython-313.pyc
delete mode 100644 modules/__pycache__/metadata.cpython-312.pyc
delete mode 100644 modules/__pycache__/metadata.cpython-313.pyc
delete mode 100644 modules/__pycache__/modern_interactive.cpython-313.pyc
delete mode 100644 modules/__pycache__/network.cpython-312.pyc
delete mode 100644 modules/__pycache__/network.cpython-313.pyc
delete mode 100644 modules/__pycache__/network_fixed.cpython-312.pyc
delete mode 100644 modules/__pycache__/p2p.cpython-312.pyc
delete mode 100644 modules/__pycache__/p2p.cpython-313.pyc
delete mode 100644 modules/__pycache__/performance_monitor.cpython-312.pyc
delete mode 100644 modules/__pycache__/performance_monitor.cpython-313.pyc
delete mode 100644 modules/__pycache__/progress.cpython-312.pyc
delete mode 100644 modules/__pycache__/progress.cpython-313.pyc
delete mode 100644 modules/__pycache__/rich_format_display.cpython-313.pyc
delete mode 100644 modules/__pycache__/rich_ui.cpython-313.pyc
delete mode 100644 modules/__pycache__/session.cpython-312.pyc
delete mode 100644 modules/__pycache__/session.cpython-313.pyc
delete mode 100644 modules/__pycache__/textual_interface.cpython-313.pyc
delete mode 100644 modules/__pycache__/url_handler.cpython-313.pyc
delete mode 100644 modules/__pycache__/utils.cpython-312.pyc
delete mode 100644 modules/__pycache__/utils.cpython-313.pyc
delete mode 100644 modules/__pycache__/working_interactive.cpython-313.pyc
delete mode 100644 modules/advanced_config.py
delete mode 100644 modules/config_helpers.py
create mode 100644 pyproject.toml
delete mode 100644 setupfiles/pip-install.txt
delete mode 100644 setupfiles/pip-package.txt
delete mode 100644 setupfiles/requirements.txt
delete mode 100644 setupfiles/setup_ffmpeg.py
rename {modules => snatch}/__init__.py (76%)
rename {modules => snatch}/advanced_scheduler.py (100%)
rename {modules => snatch}/audio_processor.py (100%)
rename {modules => snatch}/cache.py (100%)
rename {modules => snatch}/cli.py (99%)
rename {modules => snatch}/common_utils.py (100%)
rename {modules => snatch}/config.py (87%)
rename {modules => snatch}/config_manager.py (74%)
rename {modules => snatch}/constants.py (98%)
rename {modules => snatch}/customization_manager.py (100%)
rename {modules => snatch/data}/Supported-sites.txt (100%)
rename {modules => snatch}/defaults.py (98%)
rename {modules => snatch}/error_handler.py (100%)
rename {modules => snatch}/ffmpeg_helper.py (100%)
rename {modules => snatch}/file_organizer.py (100%)
rename {modules => snatch}/help_text.py (100%)
rename {modules => snatch}/interactive_mode.py (99%)
rename {modules => snatch}/logging_config.py (100%)
rename {modules => snatch}/manager.py (100%)
rename {modules => snatch}/metadata.py (100%)
rename {modules => snatch}/network.py (100%)
rename {modules => snatch}/p2p.py (100%)
rename {modules => snatch}/performance_monitor.py (100%)
rename {modules => snatch}/progress.py (100%)
rename {modules => snatch}/session.py (100%)
rename {modules => snatch}/standalone_audio.py (100%)
rename {Theme => snatch/theme}/__init__.py (51%)
rename {Theme => snatch/theme}/modern_interactive.py (99%)
rename {Theme => snatch/theme}/textual_interface.py (99%)
rename Documentation/IMPROVEMENTS.md => tests/__init__.py (100%)
create mode 100644 tests/conftest.py
create mode 100644 tests/test_cache.py
create mode 100644 tests/test_cli.py
create mode 100644 tests/test_common_utils.py
create mode 100644 tests/test_constants.py
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2b10fdb..03dfb41 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -5,22 +5,20 @@ on:
branches:
- main
paths:
- - "Snatch.py"
+ - "snatch/**"
+ - "tests/**"
- "setup.py"
- - "setup_ffmpeg.py"
- - "interactive_mode.py"
- - "test_run.py"
+ - "pyproject.toml"
- "requirements.txt"
- ".github/workflows/**"
pull_request:
branches:
- main
paths:
- - "Snatch.py"
+ - "snatch/**"
+ - "tests/**"
- "setup.py"
- - "setup_ffmpeg.py"
- - "interactive_mode.py"
- - "test_run.py"
+ - "pyproject.toml"
- "requirements.txt"
- ".github/workflows/**"
schedule:
@@ -28,8 +26,8 @@ on:
workflow_dispatch:
env:
- PYTHON_DEFAULT: "3.10"
- PACKAGE_NAME: "Snatch"
+ PYTHON_DEFAULT: "3.12"
+ PACKAGE_NAME: "snatch-dl"
jobs:
format:
@@ -48,40 +46,22 @@ jobs:
python-version: ${{ env.PYTHON_DEFAULT }}
cache: "pip"
- - name: Cache Python packages
- uses: actions/cache@v3
- with:
- path: ~/.cache/pip
- key: ${{ runner.os }}-pip-format-${{ hashFiles('requirements.txt') }}
- restore-keys: |
- ${{ runner.os }}-pip-format-
-
- name: Install formatting tools
run: |
python -m pip install --upgrade pip
pip install black isort
- - name: Identify Python files
- id: find_files
- run: |
- echo "PYTHON_FILES=$(find . -name '*.py' ! -path '*/\.*' ! -path '*/venv/*' ! -path '*/tests/*' | tr '\n' ' ')" >> $GITHUB_OUTPUT
-
- name: Fix formatting with Black
- id: black
- run: |
- black --verbose ${{ steps.find_files.outputs.PYTHON_FILES }}
+ run: black --verbose snatch/ tests/
continue-on-error: true
- name: Fix imports with isort
- id: isort
- run: |
- isort --profile black ${{ steps.find_files.outputs.PYTHON_FILES }}
+ run: isort --profile black snatch/ tests/
continue-on-error: true
- name: Check if changes were made
id: changes
- run: |
- git diff --exit-code || echo "FORMAT_CHANGED=true" >> $GITHUB_OUTPUT
+ run: git diff --exit-code || echo "FORMAT_CHANGED=true" >> $GITHUB_OUTPUT
- name: Commit formatting changes
if: steps.changes.outputs.FORMAT_CHANGED == 'true' && github.event_name == 'pull_request'
@@ -89,7 +69,7 @@ jobs:
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"
git add .
- git commit -m "📝 Format code with Black and isort" || echo "No changes to commit"
+ git commit -m "Format code with Black and isort" || echo "No changes to commit"
git push || echo "Could not push changes"
continue-on-error: true
@@ -107,116 +87,37 @@ jobs:
python-version: ${{ env.PYTHON_DEFAULT }}
cache: "pip"
- - name: Cache Python packages
- uses: actions/cache@v3
- with:
- path: ~/.cache/pip
- key: ${{ runner.os }}-pip-lint-${{ hashFiles('requirements.txt') }}
- restore-keys: |
- ${{ runner.os }}-pip-lint-
-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pylint bandit mypy types-requests
- pip install -r requirements.txt
-
- - name: Identify Python files
- id: find_files
- run: |
- echo "PYTHON_FILES=$(find . -name '*.py' ! -path '*/\.*' ! -path '*/venv/*' ! -path '*/tests/*' | tr '\n' ' ')" >> $GITHUB_OUTPUT
+ pip install -e ".[dev,all]" || pip install -e ".[dev]"
- name: Configure linters
run: |
mkdir -p reports
- # Configure pylint
- cat > .pylintrc << EOL
- [MASTER]
- init-hook='import sys; sys.path.append(".")'
-
- [MESSAGES CONTROL]
- disable=C0111,C0103,C0303,C0330,C0326,W0511,R0903,R0913,R0914,R0912,R0915,R0902,R0801,W0212,W0703,E1101,E0611
-
- [FORMAT]
- max-line-length=127
- EOL
-
- # Configure flake8
cat > .flake8 << EOL
[flake8]
- max-line-length = 127
+ max-line-length = 120
exclude = .git,__pycache__,build,dist
ignore = E203, W503, E501
EOL
- # Configure mypy
- cat > mypy.ini << EOL
- [mypy]
- python_version = 3.10
- warn_return_any = False
- warn_unused_configs = True
- disallow_untyped_defs = False
- disallow_incomplete_defs = False
-
- [mypy.plugins.numpy.*]
- follow_imports = skip
-
- [mypy-requests.*]
- ignore_missing_imports = True
- EOL
-
- name: Run flake8
run: |
- flake8 ${{ steps.find_files.outputs.PYTHON_FILES }} --count --exit-zero --max-complexity=12 --max-line-length=127 --statistics --output-file=reports/flake8.txt
-
- - name: Run pylint
- run: |
- pylint ${{ steps.find_files.outputs.PYTHON_FILES }} --output-format=text > reports/pylint.txt || echo "Pylint found some issues"
- pylint ${{ steps.find_files.outputs.PYTHON_FILES }} --output-format=json > reports/pylint.json || true
- continue-on-error: true
+ flake8 snatch/ --count --exit-zero --max-complexity=12 --max-line-length=120 --statistics --output-file=reports/flake8.txt
- name: Run bandit security scan
run: |
- bandit -r ${{ steps.find_files.outputs.PYTHON_FILES }} -f json -o reports/bandit.json || echo "Bandit found some issues"
+ bandit -r snatch/ -f json -o reports/bandit.json || echo "Bandit found some issues"
continue-on-error: true
- name: Run mypy type checking
run: |
- mypy --ignore-missing-imports ${{ steps.find_files.outputs.PYTHON_FILES }} > reports/mypy.txt || echo "Mypy found some issues"
+ mypy --ignore-missing-imports snatch/ > reports/mypy.txt || echo "Mypy found some issues"
continue-on-error: true
- - name: Generate summary report
- run: |
- echo "# Code Quality Report" > reports/summary.md
- echo "" >> reports/summary.md
-
- echo "## Flake8 Summary" >> reports/summary.md
- count=$(grep -c "^.*:.* " reports/flake8.txt || echo "0")
- echo "* Found $count issues" >> reports/summary.md
- echo "" >> reports/summary.md
-
- echo "## Pylint Summary" >> reports/summary.md
- if grep -q "rated at" reports/pylint.txt; then
- rating=$(grep "rated at" reports/pylint.txt | sed 's/.*rated at \([0-9.]*\).*/\1/')
- echo "* Rating: $rating/10.0" >> reports/summary.md
- else
- echo "* Rating: not available" >> reports/summary.md
- fi
- echo "" >> reports/summary.md
-
- echo "## Security Issues" >> reports/summary.md
- if [ -f reports/bandit.json ]; then
- high=$(grep -o '"SEVERITY_HIGH_COUNT": [0-9]*' reports/bandit.json | grep -o '[0-9]*' || echo "0")
- medium=$(grep -o '"SEVERITY_MEDIUM_COUNT": [0-9]*' reports/bandit.json | grep -o '[0-9]*' || echo "0")
- low=$(grep -o '"SEVERITY_LOW_COUNT": [0-9]*' reports/bandit.json | grep -o '[0-9]*' || echo "0")
- echo "* High: $high" >> reports/summary.md
- echo "* Medium: $medium" >> reports/summary.md
- echo "* Low: $low" >> reports/summary.md
- else
- echo "* No security scan data available" >> reports/summary.md
- fi
-
- name: Upload code quality reports
uses: actions/upload-artifact@v4
with:
@@ -231,7 +132,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
- python-version: ["3.8", "3.10"]
+ python-version: ["3.10", "3.12"]
fail-fast: false
steps:
@@ -244,35 +145,10 @@ jobs:
python-version: ${{ matrix.python-version }}
cache: "pip"
- - name: Generate unique job identifier
- id: unique_id
- run: |
- if [ "$RUNNER_OS" == "Windows" ]; then
- echo "job_id=$(powershell -command "[guid]::NewGuid().ToString().Substring(0,8)")" >> $GITHUB_OUTPUT
- else
- echo "job_id=$(date +%s%N | md5sum | head -c 8)" >> $GITHUB_OUTPUT
- fi
- shell: bash
-
- - name: Enhanced Python package caching
- uses: actions/cache@v3
- with:
- path: |
- ~/.cache/pip
- ${{ env.pythonLocation }}
- .pytest_cache
- test_output
- key: ${{ runner.os }}-py${{ matrix.python-version }}-deps-${{ hashFiles('requirements.txt') }}-${{ hashFiles('setup.py') }}-${{ github.run_id }}
- restore-keys: |
- ${{ runner.os }}-py${{ matrix.python-version }}-deps-${{ hashFiles('requirements.txt') }}-${{ hashFiles('setup.py') }}-
- ${{ runner.os }}-py${{ matrix.python-version }}-deps-
- ${{ runner.os }}-py${{ matrix.python-version }}-
-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install pytest pytest-cov pytest-xdist pytest-mock pytest-html
- pip install -r requirements.txt
+ pip install -e ".[dev,all]" || pip install -e ".[dev]"
shell: bash
- name: Install FFmpeg (Ubuntu)
@@ -280,7 +156,6 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y ffmpeg
- ffmpeg -version
- name: Install FFmpeg (Windows)
if: matrix.os == 'windows-latest'
@@ -288,298 +163,31 @@ jobs:
choco install ffmpeg -y
refreshenv
echo "$env:ProgramData\chocolatey\bin" | Out-File -FilePath $env:GITHUB_PATH -Append
- ffmpeg -version
shell: pwsh
- - name: Create necessary directories for Windows
- if: matrix.os == 'windows-latest'
+ - name: Run tests
run: |
- # Use PowerShell to safely create directories only if they don't exist
- powershell -Command "if (-not (Test-Path metrics)) { New-Item -ItemType Directory -Path metrics }"
- powershell -Command "if (-not (Test-Path code_analysis)) { New-Item -ItemType Directory -Path code_analysis }"
- powershell -Command "if (-not (Test-Path test_output)) { New-Item -ItemType Directory -Path test_output }"
- shell: pwsh
- continue-on-error: true
-
- - name: Create necessary directories for Linux
- if: matrix.os == 'ubuntu-latest'
- run: |
- mkdir -p metrics code_analysis test_output
+ python -m pytest tests/ -v --cov=snatch --cov-report=xml:coverage.xml --cov-report=term --junitxml=test-results.xml
shell: bash
continue-on-error: true
- - name: Create comprehensive test file
- run: |
- cat > tests/test_comprehensive.py << 'EOL'
- import sys
- import os
- import pytest
- from unittest.mock import patch, MagicMock
-
- # Add project root to path
- sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-
- def test_import():
- """Test that the main module can be imported."""
- try:
- import Snatch
- assert Snatch.__name__ == "Snatch"
- except ImportError as e:
- pytest.skip(f"Snatch module not found: {str(e)}")
-
- def test_basic_functionality():
- """Test that the module has basic expected attributes."""
- try:
- import Snatch
- assert hasattr(Snatch, '__file__')
- except ImportError as e:
- pytest.skip(f"Snatch module not found: {str(e)}")
-
- @pytest.mark.parametrize("test_url", [
- "http://example.com/video.mp4",
- "https://test.org/file.mp4",
- ])
- def test_download_function_mock(test_url):
- """Test download functionality with mocks."""
- try:
- import Snatch
-
- # Create mock objects
- mock_response = MagicMock()
- mock_response.status_code = 200
- mock_response.content = b"test content"
-
- # Patch necessary functions
- with patch('requests.get', return_value=mock_response), \
- patch('builtins.open', MagicMock()), \
- patch('os.path.exists', return_value=True):
-
- # Attempt to call the function if it exists
- if hasattr(Snatch, 'download_file'):
- result = Snatch.download_file(test_url, "test_output.mp4")
- assert result is not None
- else:
- pytest.skip("download_file function not found")
- except ImportError as e:
- pytest.skip(f"Snatch module not found: {str(e)}")
- except Exception as e:
- pytest.skip(f"Test error: {str(e)}")
- EOL
- shell: bash
-
- - name: Run basic tests
- run: |
- python -m pytest tests/test_comprehensive.py -v
- shell: bash
- continue-on-error: true
-
- - name: Run test_run.py
- run: |
- # Create directory first
- mkdir -p test_output
-
- # Run with error handling
- if [ "$RUNNER_OS" == "Windows" ]; then
- # Windows-compatible command
- python test_run.py > test_output/test_run_output.txt 2>&1 || echo "test_run.py failed but continuing"
- else
- # Linux command
- python test_run.py > test_output/test_run_output.txt 2>&1 || echo "test_run.py failed but continuing"
- fi
-
- # Ensure the output file exists even if the command fails
- if [ ! -f test_output/test_run_output.txt ]; then
- echo "test_run.py did not generate output" > test_output/test_run_output.txt
- fi
- shell: bash
- continue-on-error: true
-
- - name: Run comprehensive test suite
- run: |
- mkdir -p test_output/junit
- python -m pytest tests/ --cov=Snatch --cov-report=xml:coverage.xml --cov-report=term --junitxml=test_output/junit/test-results.xml
- shell: bash
- continue-on-error: true
-
- - name: Publish Test Results
- uses: EnricoMi/publish-unit-test-result-action@v2
- if: always() && runner.os == 'Linux' # Only run on Linux as this action is Linux-compatible
- with:
- files: test_output/junit/test-results.xml
- check_name: "Test Results - ${{ matrix.os }} - Python ${{ matrix.python-version }}"
- comment_mode: always
- report_individual_runs: true
- check_run_annotations: all tests
- fail_on: nothing # Don't fail the workflow, just report
- continue-on-error: true
-
- - name: Generate coverage report
- if: always()
- run: |
- mkdir -p coverage_report
- python -m pip install -q coverage
- python -m coverage html -d coverage_report
- echo "# Coverage Summary" > coverage_summary.md
- echo "Current code coverage: $(grep -o 'pc_cov">.*%' coverage_report/index.html | sed 's/pc_cov">//; s/<.*$//')" >> coverage_summary.md
- shell: bash
- continue-on-error: true
-
- - name: Upload coverage reports
+ - name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
- name: coverage-report-${{ matrix.os }}-py${{ matrix.python-version }}-${{ github.run_number }}-${{ steps.unique_id.outputs.job_id }}
+ name: test-results-${{ matrix.os }}-py${{ matrix.python-version }}-${{ github.run_number }}
path: |
- coverage_report/
+ test-results.xml
coverage.xml
- coverage_summary.md
- retention-days: 14
-
- - name: Ensure memory profile results exist
- run: |
- if [ ! -f memory_profile_results.txt ]; then
- echo "# Memory Profile Results" > memory_profile_results.txt
- echo "No results generated during testing." >> memory_profile_results.txt
- fi
- shell: bash
- continue-on-error: true
-
- - name: Profile memory usage for critical functions
- run: |
- python -m pip install -q memory_profiler
- echo "# Memory Profiling Results" > memory_profile_results.txt
- echo "Running memory profiling..." >> memory_profile_results.txt
-
- # Create the profiling script with error handling
- cat > memory_profile.py << 'EOL'
- import os
- import sys
- import traceback
- from memory_profiler import profile
-
- try:
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
- import Snatch
-
- @profile
- def test_memory_usage():
- print("Starting memory profiling...")
- # Test creating DownloadManager (which sets up the environment)
- if hasattr(Snatch, 'DownloadManager'):
- try:
- config = {"ffmpeg_location": "", "video_output": "videos", "audio_output": "audio"}
- dm = Snatch.DownloadManager(config)
- print("Tested DownloadManager creation")
- except Exception as e:
- print(f"Error creating DownloadManager: {e}")
-
- # Test sanitize_filename with many files
- if hasattr(Snatch, 'sanitize_filename'):
- try:
- print("Testing sanitize_filename...")
- for i in range(100): # Reduced from 1000 to avoid timeouts
- Snatch.sanitize_filename(f"Test File with special chars {i}!@#$%")
- print("Completed sanitize_filename test")
- except Exception as e:
- print(f"Error in sanitize_filename: {e}")
-
- # Test other memory-intensive operations as needed
- if hasattr(Snatch, 'is_windows'):
- try:
- print("Testing is_windows...")
- for i in range(10): # Reduced from 100 to avoid timeouts
- Snatch.is_windows()
- print("Completed is_windows test")
- except Exception as e:
- print(f"Error in is_windows: {e}")
-
- print("Memory profiling complete")
-
- if __name__ == '__main__':
- test_memory_usage()
- except Exception as e:
- print(f"Fatal error during memory profiling: {e}")
- traceback.print_exc()
- EOL
-
- # Run the profiler and capture output even if it fails
- python -m memory_profiler memory_profile.py >> memory_profile_results.txt 2>&1 || echo "Memory profiling failed, but continuing" >> memory_profile_results.txt
-
- # Always ensure the file exists with some content
- echo "Memory profiling completed at $(date)" >> memory_profile_results.txt
- echo "System information: $(python --version)" >> memory_profile_results.txt
- shell: bash
- continue-on-error: true
-
- - name: Upload memory profile results
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: memory-profile-${{ matrix.os }}-py${{ matrix.python-version }}-${{ github.run_number }}-${{ steps.unique_id.outputs.job_id }}
- path: memory_profile_results.txt
retention-days: 14
- name: Collect code metrics
run: |
- python -m pip install -q radon
-
- # Don't try to create the directory again
- if [ "$RUNNER_OS" == "Windows" ]; then
- # Windows-specific redirection that works
- powershell -Command "radon cc Snatch.py -s -a | Out-File -FilePath metrics/complexity.txt -Encoding utf8"
- powershell -Command "radon raw Snatch.py | Out-File -FilePath metrics/raw_metrics.txt -Encoding utf8"
- powershell -Command "radon mi Snatch.py | Out-File -FilePath metrics/maintainability.txt -Encoding utf8"
-
- # Create summary with PowerShell
- powershell -Command @'
- "# Code Metrics Summary" | Out-File -FilePath metrics/summary.md -Encoding utf8
- "" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- "## Complexity" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- "```" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- Get-Content metrics/complexity.txt -TotalCount 10 | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- "... (see full report)" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- "```" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
-
- "" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- "## Maintainability Index" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- "```" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- Get-Content metrics/maintainability.txt | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- "```" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
-
- "" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- "## Size Metrics" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- "```" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- Get-Content metrics/raw_metrics.txt -TotalCount 15 | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- "```" | Out-File -FilePath metrics/summary.md -Encoding utf8 -Append
- '@
- else
- # Linux commands
- radon cc Snatch.py -s -a > metrics/complexity.txt || echo "Could not run complexity analysis"
- radon raw Snatch.py > metrics/raw_metrics.txt || echo "Could not run raw metrics analysis"
- radon mi Snatch.py > metrics/maintainability.txt || echo "Could not run maintainability analysis"
-
- # Create a metrics summary
- echo "# Code Metrics Summary" > metrics/summary.md
- echo "" >> metrics/summary.md
- echo "## Complexity" >> metrics/summary.md
- echo '```' >> metrics/summary.md
- cat metrics/complexity.txt | head -n 10 >> metrics/summary.md
- echo '... (see full report)' >> metrics/summary.md
- echo '```' >> metrics/summary.md
-
- echo "" >> metrics/summary.md
- echo "## Maintainability Index" >> metrics/summary.md
- echo '```' >> metrics/summary.md
- cat metrics/maintainability.txt >> metrics/summary.md
- echo '```' >> metrics/summary.md
-
- # Analyze LOC, comments, etc
- echo "" >> metrics/summary.md
- echo "## Size Metrics" >> metrics/summary.md
- echo '```' >> metrics/summary.md
- cat metrics/raw_metrics.txt | head -n 15 >> metrics/summary.md
- echo '```' >> metrics/summary.md
- fi
+ pip install -q radon
+ mkdir -p metrics
+ radon cc snatch/ -s -a > metrics/complexity.txt || echo "Could not run complexity analysis"
+ radon raw snatch/ > metrics/raw_metrics.txt || echo "Could not run raw metrics"
+ radon mi snatch/ > metrics/maintainability.txt || echo "Could not run maintainability analysis"
shell: bash
continue-on-error: true
@@ -587,279 +195,14 @@ jobs:
if: always()
uses: actions/upload-artifact@v4
with:
- name: code-metrics-${{ matrix.os }}-py${{ matrix.python-version }}-${{ github.run_number }}-${{ steps.unique_id.outputs.job_id }}
+ name: code-metrics-${{ matrix.os }}-py${{ matrix.python-version }}-${{ github.run_number }}
path: metrics/
retention-days: 14
- - name: Ensure code analysis directory exists
- run: |
- if [ ! -d code_analysis ]; then
- mkdir -p code_analysis
- echo "# Code Comment Analysis" > code_analysis/comment_report.md
- echo "No code comment analysis was generated." >> code_analysis/comment_report.md
- fi
- shell: bash
- continue-on-error: true
-
- - name: Analyze code comments
- run: |
- # Don't create the directory here again
- python -c "
- import re
- import os
-
- # Create the directory to ensure it exists (OS-agnostic way)
- os.makedirs('code_analysis', exist_ok=True)
-
- try:
- # Analyze Snatch.py
- with open('Snatch.py', 'r', encoding='utf-8') as f:
- content = f.read()
-
- # Find TODOs and FIXMEs
- todos = re.findall(r'#\s*(TODO|FIXME):\s*(.*?)($|\n)', content)
-
- # Find functions with missing docstrings
- function_pattern = re.compile(r'def\s+([a-zA-Z0-9_]+)\s*\([^)]*\):\s*(?:\n\s*\"\"\".*?\"\"\"|\\n\s*[^#\n])', re.DOTALL)
- functions_without_docstrings = []
- matches = function_pattern.finditer(content)
- for match in matches:
- full_match = match.group(0)
- func_name = match.group(1)
- if '\"\"\"' not in full_match.split('\n')[1]:
- functions_without_docstrings.append(func_name)
-
- # Calculate comment statistics
- lines = content.split('\n')
- total_lines = len(lines)
- comment_lines = sum(1 for line in lines if line.strip().startswith('#'))
- code_lines = sum(1 for line in lines if line.strip() and not line.strip().startswith('#'))
- docstring_lines = content.count('\"\"\"') // 2 # Rough estimate
-
- # Write report
- with open('code_analysis/comment_report.md', 'w', encoding='utf-8') as f:
- f.write('# Code Comment Analysis\n\n')
-
- f.write('## Comment Statistics\n')
- f.write(f'- **Total lines**: {total_lines}\n')
- f.write(f'- **Code lines**: {code_lines}\n')
- f.write(f'- **Comment lines**: {comment_lines}\n')
- f.write(f'- **Comment density**: {comment_lines/max(code_lines, 1):.2%}\n\n')
-
- f.write('## TODOs and FIXMEs\n')
- if todos:
- for todo_type, desc, _ in todos:
- f.write(f'- **{todo_type}**: {desc.strip()}\n')
- else:
- f.write('- No TODOs or FIXMEs found\n')
- f.write('\n')
-
- f.write('## Functions Missing Docstrings\n')
- if functions_without_docstrings:
- for func in functions_without_docstrings[:20]:
- f.write(f'- `{func}`\n')
- if len(functions_without_docstrings) > 20:
- f.write(f'- ... and {len(functions_without_docstrings) - 20} more\n')
- else:
- f.write('- No functions missing docstrings\n')
- except Exception as e:
- # Create a fallback file if anything fails
- os.makedirs('code_analysis', exist_ok=True)
- with open('code_analysis/comment_report.md', 'w', encoding='utf-8') as f:
- f.write('# Code Comment Analysis\n\n')
- f.write(f'Error during analysis: {e}\n')
- "
- shell: bash
- continue-on-error: true
-
- - name: Upload code comment analysis
- uses: actions/upload-artifact@v4
- if: always()
- with:
- name: code-comment-analysis-${{ matrix.os }}-py${{ matrix.python-version }}-${{ github.run_number }}-${{ steps.unique_id.outputs.job_id }}
- path: code_analysis/
- retention-days: 14
-
- - name: Create build summary
- if: always()
- run: |
- echo "# Build Summary" > build_summary.md
- echo "" >> build_summary.md
-
- echo "## Environment" >> build_summary.md
- echo "" >> build_summary.md
- echo "- OS: ${{ matrix.os }}" >> build_summary.md
- echo "- Python: ${{ matrix.python-version }}" >> build_summary.md
- echo "" >> build_summary.md
-
- echo "## Test Results" >> build_summary.md
- echo "" >> build_summary.md
- echo "- Basic tests: Run" >> build_summary.md
- echo "- Comprehensive tests: Run" >> build_summary.md
- echo "- Performance tests: Run" >> build_summary.md
- echo "" >> build_summary.md
-
- # Include coverage if available
- if [ -f coverage_summary.md ]; then
- cat coverage_summary.md >> build_summary.md
- echo "" >> build_summary.md
- fi
-
- # Include dependency check results if available
- echo "## Security" >> build_summary.md
- echo "" >> build_summary.md
- if [ -f safety-report.txt ]; then
- echo "```" >> build_summary.md
- cat safety-report.txt | head -n 10 >> build_summary.md
- echo "```" >> build_summary.md
- else
- echo "No vulnerability scan results available" >> build_summary.md
- fi
- echo "" >> build_summary.md
-
- # Add metrics summary if available
- if [ -f metrics/summary.md ]; then
- cat metrics/summary.md >> build_summary.md
- fi
-
- # Add comment analysis to build summary if available
- if [ -f code_analysis/comment_report.md ]; then
- echo "" >> build_summary.md
- echo "## Code Comment Analysis" >> build_summary.md
- echo "" >> build_summary.md
-
- # Extract key metrics
- comment_density=$(grep "Comment density" code_analysis/comment_report.md 2>/dev/null | sed 's/.*: //' || echo "N/A")
- todo_count=$(grep -c "TODO\|FIXME" code_analysis/comment_report.md || echo "0")
- missing_docs=$(grep -c "^- \`" code_analysis/comment_report.md || echo "0")
-
- echo "- Comment density: ${comment_density}" >> build_summary.md
- echo "- TODOs/FIXMEs: ${todo_count}" >> build_summary.md
- echo "- Functions missing docstrings: ${missing_docs}" >> build_summary.md
- echo "" >> build_summary.md
- echo "See full report in the code-comment-analysis artifact." >> build_summary.md
- fi
-
- # Add test results summary if available
- if [ -f test_output/junit/test-results.xml ]; then
- echo "" >> build_summary.md
- echo "## Test Results Summary" >> build_summary.md
- echo "" >> build_summary.md
-
- passed=$(grep -c "testcase" test_output/junit/test-results.xml || echo "0")
- failures=$(grep -c "failure" test_output/junit/test-results.xml || echo "0")
- errors=$(grep -c "error" test_output/junit/test-results.xml || echo "0")
- skipped=$(grep -c "skipped" test_output/junit/test-results.xml || echo "0")
-
- total=$((passed + failures + errors))
-
- echo "- Total tests: ${total}" >> build_summary.md
- echo "- Passed: $((total - failures - errors - skipped))" >> build_summary.md
- echo "- Failures: ${failures}" >> build_summary.md
- echo "- Errors: ${errors}" >> build_summary.md
- echo "- Skipped: ${skipped}" >> build_summary.md
-
- # Calculate pass rate
- if [ "${total}" != "0" ]; then
- pass_rate=$(( 100 * (total - failures - errors - skipped) / total ))
- echo "- Pass rate: ${pass_rate}%" >> build_summary.md
- fi
- fi
- shell: bash
- continue-on-error: true
-
- - name: Upload build summary
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: build-summary-${{ matrix.os }}-py${{ matrix.python-version }}-${{ github.run_number }}-${{ steps.unique_id.outputs.job_id }}
- path: build_summary.md
- retention-days: 14
-
- - name: scan for vulnerable dependencies
- run: |
- pip install safety
- safety scan -r requirements.txt --output text > safety-report.txt || echo "Vulnerabilities found, see report"
- continue-on-error: true
-
- - name: Upload security scan results
- uses: actions/upload-artifact@v4
- with:
- name: dependency-scan-${{ matrix.os }}-py${{ matrix.python-version }}-${{ github.run_number }}-${{ steps.unique_id.outputs.job_id }}
- path: safety-report.txt
- retention-days: 14
-
- - name: Upload test results
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: test-results-${{ matrix.os }}-py${{ matrix.python-version }}-${{ github.run_number }}-${{ steps.unique_id.outputs.job_id }}
- path: |
- test_output/
- .pytest_cache/
- retention-days: 14
-
- fix-code-issues:
- name: Fix Code Issues
- runs-on: ubuntu-latest
- needs: test
- if: ${{ always() }}
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: Set up Python ${{ env.PYTHON_DEFAULT }}
- uses: actions/setup-python@v5
- with:
- python-version: ${{ env.PYTHON_DEFAULT }}
-
- - name: Create automatic fixes for Snatch.py issues
- run: |
- # Create backup
- cp Snatch.py Snatch.py.bak
-
- # Apply fixes
- echo "Applying automatic fixes to common issues..."
-
- # Fix possibly-used-before-assignment issue - line 146
- sed -i '146s/if any_updates_found:/any_updates_found = False\n if any_updates_found:/' Snatch.py
-
- # Fix no-member issue - line 4434
- sed -i '4434s/self\\._cleanup_temporary_files()/# self._cleanup_temporary_files()/' Snatch.py
-
- # Fix no-member issue - line 4951
- sed -i '4951s/self\.non_interactive/False/' Snatch.py
-
- # Fix access-member-before-definition issue - line 2853
- sed -i '2853s/self\.last_speed_update/self._last_speed_update/' Snatch.py
-
- # Create detailed patch file
- echo "Creating patch file..."
- diff -u Snatch.py.bak Snatch.py > snatch_fixes.patch || true
-
- # Create human-readable explanation
- echo "# Automatic Code Fixes" > code_fixes_explanation.md
- echo "" >> code_fixes_explanation.md
- echo "## Fixes Applied" >> code_fixes_explanation.md
- echo "" >> code_fixes_explanation.md
- echo "1. **Line 146**: Fixed 'possibly-used-before-assignment' by initializing 'any_updates_found' variable" >> code_fixes_explanation.md
- echo "2. **Line 4434**: Fixed 'no-member' issue with '_cleanup_temporary_files' by commenting out the problematic call" >> code_fixes_explanation.md
- echo "3. **Line 4951**: Fixed 'no-member' issue with 'non_interactive' by replacing with 'False'" >> code_fixes_explanation.md
- echo "4. **Line 2853**: Fixed 'access-member-before-definition' by renaming 'last_speed_update' to '_last_speed_update'" >> code_fixes_explanation.md
-
- - name: Upload patch files
- uses: actions/upload-artifact@v4
- with:
- name: code-fixes-${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }}
- path: |
- snatch_fixes.patch
- code_fixes_explanation.md
- retention-days: 30
-
build:
name: Build Package
runs-on: ubuntu-latest
- needs: fix-code-issues
+ needs: test
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -875,64 +218,16 @@ jobs:
python -m pip install --upgrade pip
pip install wheel setuptools twine build
- - name: Create CI-friendly setup.py
- run: |
- # Create a backup
- cp setup.py setup.py.bak
-
- # Modify setup.py to work in CI environment
- cat > setup.py << EOL
- from setuptools import setup, find_packages
-
- with open("requirements.txt") as f:
- requirements = f.read().splitlines()
-
- setup(
- name="${{ env.PACKAGE_NAME }}",
- version="0.1.0",
- packages=find_packages(),
- install_requires=requirements,
- entry_points={
- "console_scripts": [
- "snatch=Snatch:main",
- ],
- },
- python_requires=">=3.8",
- author="Snatch Contributors",
- author_email="example@github.com",
- description="Snatch media downloader",
- keywords="video, download, media",
- classifiers=[
- "Development Status :: 3 - Alpha",
- "Intended Audience :: End Users/Desktop",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
- "Programming Language :: Python :: 3.10",
- ],
- )
- EOL
-
- - name: Try build with build module
- id: build_module
- run: |
- python -m build
- continue-on-error: true
-
- - name: Fallback to setuptools if build fails
- if: steps.build_module.outcome != 'success'
- run: |
- echo "Build module failed, falling back to setuptools directly"
- python setup.py sdist bdist_wheel
+ - name: Build package
+ run: python -m build
- name: Verify package
- run: |
- twine check dist/*
+ run: twine check dist/*
- name: Store built package
uses: actions/upload-artifact@v4
with:
- name: dist-packages-${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }}
+ name: dist-packages-${{ github.run_number }}
path: dist/
retention-days: 30
@@ -954,32 +249,18 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install pdoc3 markdown
+ pip install -e ".[all]" || pip install -e .
- name: Generate documentation
run: |
mkdir -p docs
- # Generate module info
- echo "# Snatch Documentation" > docs/index.md
- echo "" >> docs/index.md
- echo "## Overview" >> docs/index.md
- echo "" >> docs/index.md
- echo "Snatch is a media downloading utility." >> docs/index.md
- echo "" >> docs/index.md
-
- # Extract module docstring if available
- if grep -q '"""' Snatch.py; then
- sed -n '/"""/,/"""/p' Snatch.py | sed 's/"""//g' > docs/description.md
- cat docs/description.md >> docs/index.md
- fi
-
- # Generate HTML documentation if possible
- pdoc --html --output-dir docs Snatch.py || echo "Could not generate HTML docs"
+ pdoc --html --output-dir docs snatch || echo "Could not generate HTML docs"
continue-on-error: true
- name: Upload documentation
uses: actions/upload-artifact@v4
with:
- name: documentation-${{ github.sha }}-${{ github.run_number }}-${{ github.run_attempt }}
+ name: documentation-${{ github.run_number }}
path: docs/
retention-days: 30
@@ -989,29 +270,10 @@ jobs:
if: always()
runs-on: ubuntu-latest
steps:
- - name: Set job status
- id: status
+ - name: Print completion message
run: |
if [[ "${{ needs.build.result }}" == "success" ]]; then
- echo "STATUS=✅ CI Pipeline completed successfully" >> $GITHUB_OUTPUT
- echo "COLOR=green" >> $GITHUB_OUTPUT
+ echo "CI Pipeline completed successfully"
else
- echo "STATUS=⚠️ CI Pipeline completed with issues" >> $GITHUB_OUTPUT
- echo "COLOR=yellow" >> $GITHUB_OUTPUT
+ echo "CI Pipeline completed with issues"
fi
-
- - name: Create status badge
- uses: schneegans/dynamic-badges-action@v1.6.0
- with:
- auth: ${{ secrets.GIST_SECRET || github.token }}
- gistID: ${{ secrets.GIST_ID || github.run_id }}
- filename: snatch-ci-status.json
- label: Build
- message: ${{ steps.status.outputs.STATUS }}
- color: ${{ steps.status.outputs.COLOR }}
- continue-on-error: true
-
- - name: Print completion message
- run: |
- echo "${{ steps.status.outputs.STATUS }}"
- echo "All artifacts have been uploaded and are available in the Actions tab"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..99a0b9d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,43 @@
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.egg-info/
+*.egg
+dist/
+build/
+.eggs/
+*.whl
+
+# Runtime directories
+cache/
+sessions/
+downloads/
+
+# Config and logs
+config.json
+*.log
+download_log.txt
+speedtest_result.json
+
+# Environment
+.env
+.venv/
+env/
+venv/
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Testing
+.coverage
+htmlcov/
+.pytest_cache/
+.mypy_cache/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 23ad004..25f9cee 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -29,9 +29,24 @@ Please review our [Code of Conduct](CODE_OF_CONDUCT.md) before contributing.
## Development Setup
1. Fork the repository.
-2. Create a branch for your feature or bug fix.
-3. Commit your changes with clear messages.
-4. Push to your fork and create a pull request.
+2. Clone and install in development mode:
+ ```bash
+ git clone https://github.com/YOUR_USERNAME/Snatch.git
+ cd Snatch
+ pip install -e ".[dev,all]"
+ ```
+3. Run the test suite:
+ ```bash
+ python -m pytest tests/ -v
+ ```
+4. Format your code:
+ ```bash
+ black snatch/ tests/
+ isort --profile black snatch/ tests/
+ ```
+5. Create a branch for your feature or bug fix.
+6. Commit your changes with clear messages.
+7. Push to your fork and create a pull request.
## Communication
diff --git a/Documentation/API_REFERENCE.md b/Documentation/API_REFERENCE.md
index f83901f..807c5cf 100644
--- a/Documentation/API_REFERENCE.md
+++ b/Documentation/API_REFERENCE.md
@@ -440,7 +440,7 @@ AudioProcessor(config: Dict[str, Any])
### EnhancedAudioProcessor
-Enhanced processor with AI-based improvements, professional presets, and advanced algorithms introduced in v1.8.0.
+Enhanced processor with AI-based improvements, professional presets, and advanced algorithms.
#### Constructor
diff --git a/Documentation/AUDIO_ENHANCEMENT_GUIDE.md b/Documentation/AUDIO_ENHANCEMENT_GUIDE.md
index 5f2a139..fa73a71 100644
--- a/Documentation/AUDIO_ENHANCEMENT_GUIDE.md
+++ b/Documentation/AUDIO_ENHANCEMENT_GUIDE.md
@@ -2,7 +2,7 @@
## Overview
-Snatch v1.8.0 introduces a comprehensive audio enhancement system that leverages AI-powered algorithms to improve audio quality, reduce noise, and optimize audio content for different use cases. This guide covers all aspects of the audio enhancement features.
+Snatch includes a comprehensive audio enhancement system that leverages AI-powered algorithms to improve audio quality, reduce noise, and optimize audio content for different use cases. This guide covers all aspects of the audio enhancement features.
## Table of Contents
diff --git a/Documentation/CHANGELOG.md b/Documentation/CHANGELOG.md
index 3983555..d64b748 100644
--- a/Documentation/CHANGELOG.md
+++ b/Documentation/CHANGELOG.md
@@ -5,6 +5,45 @@ All notable changes to the Snatch project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [2.0.0] - 2026-03-04
+
+### 🏗️ Package Restructuring
+
+- **BREAKING**: Renamed `modules/` package to `snatch/` for standard Python naming
+- **BREAKING**: Moved `Theme/` directory into `snatch/theme/` sub-package with fixed relative imports
+- **BREAKING**: Removed `Snatch.py` entry point; use `snatch` command or `python -m snatch.cli`
+- **BREAKING**: Raised minimum Python version to 3.10+
+- **NEW**: Modern `pyproject.toml` packaging with optional dependency groups (`[audio]`, `[p2p]`, `[video]`, `[dev]`)
+- **NEW**: Console entry point `snatch` available globally after `pip install`
+- **NEW**: pytest-based test suite with fixtures for config, cache, CLI, and utilities
+
+### 🔧 Code Consolidation
+
+- **MERGED**: `config_helpers.py` into `config.py` (4 config modules → 2)
+- **MERGED**: `advanced_config.py` into `config_manager.py`
+- **FIXED**: Single source of truth for version (`constants.py` → `2.0.0`)
+- **FIXED**: Duplicate `"organize"` key in `PROCESS_PREFIXES`
+- **FIXED**: `DEFAULT_TIMEOUT` conflict between `defaults.py` and `constants.py`
+- **FIXED**: Broken Theme relative imports (`from .manager` → `from ..manager`)
+
+### 🧹 Cleanup
+
+- **REMOVED**: `setupfiles/` directory (stale v1.7.0 requirements, pip freeze dump)
+- **REMOVED**: Empty placeholder files (`cyberpunk_interactive.py`, `working_interactive.py`)
+- **REMOVED**: Stale documentation summaries (FIXES_README, FIXES_SUMMARY, etc.)
+- **REMOVED**: All `__pycache__/` from git tracking
+- **SIMPLIFIED**: `setup.py` reduced to 2-line shim (config in `pyproject.toml`)
+- **CLEANED**: `requirements.txt` removed transitive dependencies
+
+### 📦 CI/CD
+
+- **REWRITTEN**: GitHub Actions pipeline for `snatch/` package structure
+- **UPDATED**: Python matrix to 3.10, 3.12
+- **FIXED**: Test job uses `pip install -e ".[dev,all]"` and `pytest`
+- **REMOVED**: `fix-code-issues` job that patched nonexistent `Snatch.py`
+
+---
+
## [1.8.0] - 2025-05-31
### 🎵 Major New Features
diff --git a/Documentation/CONFIGURATION_IMPLEMENTATION_SUMMARY.md b/Documentation/CONFIGURATION_IMPLEMENTATION_SUMMARY.md
deleted file mode 100644
index b337b9e..0000000
--- a/Documentation/CONFIGURATION_IMPLEMENTATION_SUMMARY.md
+++ /dev/null
@@ -1,180 +0,0 @@
-# Configuration Management Implementation Summary
-
-## ✅ IMPLEMENTATION COMPLETED
-
-The new configuration management features for Snatch have been successfully implemented and tested. Here's a comprehensive summary of what was accomplished:
-
-## 🚀 New Features Implemented
-
-### 1. Cache Management (`--clear-cache`)
-
-**Status: ✅ COMPLETE AND WORKING**
-
-- **Command**: `python -m modules.cli --clear-cache`
-- **Features**:
- - ✅ Clear all cache types or specific types (metadata, downloads, sessions, thumbnails, temp)
- - ✅ Dry-run mode with `--dry-run` flag
- - ✅ Safety confirmation with `--yes` flag to skip
- - ✅ Detailed statistics and user feedback
- - ✅ Error handling and graceful failure
-
-**Test Results**: ✅ Working correctly in all tested scenarios
-
-### 2. Configuration Editor (`config edit`)
-
-**Status: ✅ COMPLETE AND WORKING**
-
-- **Command**: `python -m modules.cli config edit`
-- **Features**:
- - ✅ Automatic backup creation before editing
- - ✅ Editor auto-detection (VS Code, Notepad++, Notepad)
- - ✅ Custom editor specification with `--editor`
- - ✅ JSON validation after editing
- - ✅ Backup restoration on validation failure
- - ✅ Skip backup with `--no-backup` flag
-
-**Test Results**: ✅ Working correctly with all major editors
-
-### 3. Configuration Display (`config show`)
-
-**Status: ✅ COMPLETE AND WORKING**
-
-- **Command**: `python -m modules.cli config show`
-- **Features**:
- - ✅ Multiple output formats: table (default), JSON, YAML
- - ✅ Category filtering (download, video, audio, network, interface, advanced)
- - ✅ Non-default values only with `--non-default`
- - ✅ Export to file with `--output`
- - ✅ Rich table formatting with color coding
-
-**Test Results**: ✅ All formats and filters working correctly
-
-### 4. Backup Management (`config backup`)
-
-**Status: ✅ COMPLETE AND WORKING**
-
-- **Command**: `python -m modules.cli config backup [action]`
-- **Features**:
- - ✅ List available backups
- - ✅ Create timestamped backups
- - ✅ Restore from specific backup
- - ✅ Automatic backup cleanup
- - ✅ Backup validation and integrity checks
-
-**Test Results**: ✅ All backup operations working correctly
-
-### 5. Configuration Reset (`config reset`)
-
-**Status: ✅ COMPLETE AND WORKING**
-
-- **Command**: `python -m modules.cli config reset`
-- **Features**:
- - ✅ Reset all configuration to defaults
- - ✅ Reset specific categories only
- - ✅ Confirmation prompts for safety
- - ✅ Skip confirmation with `--yes`
-
-**Test Results**: ✅ Reset functionality working correctly
-
-## 🏗️ Technical Implementation
-
-### Core Modules Created/Modified
-
-1. **`modules/config_manager.py`** - ✅ NEW MODULE
- - ConfigurationManager class with comprehensive features
- - CacheType enum for cache categorization
- - Thread-safe operations with proper locking
- - Rich console integration for beautiful output
-
-2. **`modules/cli.py`** - ✅ ENHANCED
- - Added new command definitions
- - Integrated ConfigurationManager
- - Added command implementation methods
- - Maintained existing functionality
-
-### Architecture Highlights
-
-- **Thread Safety**: All operations use proper locking mechanisms
-- **Error Handling**: Comprehensive error handling with graceful degradation
-- **User Experience**: Rich console output with colors and formatting
-- **Safety First**: Confirmation prompts and dry-run modes
-- **Extensibility**: Easy to add new cache types and configuration categories
-
-## 📊 Verification Results
-
-### Functional Testing
-
-- ✅ **Cache Clearing**: All cache types, dry-run mode, confirmations
-- ✅ **Configuration Display**: All formats (table, JSON, YAML), category filtering
-- ✅ **Backup Management**: Create, list, restore operations
-- ✅ **Help System**: All commands have comprehensive help text
-- ✅ **Integration**: Works seamlessly with existing CLI structure
-
-### Example Outputs Verified
-
-```bash
-# Configuration display in JSON format
-python -m modules.cli config show --format=json --category=download
-# Returns properly formatted JSON with download settings
-
-# Cache clearing with dry-run
-python -m modules.cli clear-cache --dry-run --type=metadata
-# Shows what would be deleted without actually deleting
-
-# Backup creation
-python -m modules.cli config backup create
-# Creates timestamped backup successfully
-```
-
-## 📚 Documentation Created
-
-1. **`Documentation/CONFIGURATION_MANAGEMENT.md`** - ✅ COMPLETE
- - Comprehensive user guide
- - All commands with examples
- - Best practices and troubleshooting
- - Integration information
-
-2. **Updated `Documentation/DOCUMENTATION_INDEX.md`** - ✅ UPDATED
- - Added configuration management guide to index
- - Proper categorization
-
-## 🎯 Command Syntax Summary
-
-| Command | Purpose | Example |
-|---------|---------|---------|
-| `--clear-cache` | Clear cached data | `--clear-cache --type=metadata --dry-run` |
-| `config show` | Display configuration | `config show --format=json --category=download` |
-| `config edit` | Edit configuration | `config edit --editor=code` |
-| `config backup` | Manage backups | `config backup create` |
-| `config reset` | Reset to defaults | `config reset --category=download` |
-
-## 🔧 Integration with Existing Features
-
-The new configuration management system integrates seamlessly with:
-
-- ✅ **Download Manager**: Automatic cache management during downloads
-- ✅ **Session Manager**: Configuration-aware session handling
-- ✅ **Performance Monitor**: Configuration impact monitoring
-- ✅ **Error Handler**: Enhanced error reporting for configuration issues
-- ✅ **Interactive Modes**: Configuration commands available in all interfaces
-- ✅ **Rich Console**: Beautiful formatted output throughout
-
-## 🎉 IMPLEMENTATION STATUS: COMPLETE
-
-All requested features have been successfully implemented, tested, and documented. The new configuration management system provides:
-
-1. **Enhanced User Control**: Granular control over cache and configuration
-2. **Safety Features**: Confirmations, backups, and dry-run modes
-3. **Professional UX**: Rich formatting and clear feedback
-4. **Comprehensive Help**: Built-in help for all commands
-5. **Extensible Design**: Easy to add new features in the future
-
-The implementation follows best practices for:
-
-- Thread safety
-- Error handling
-- User experience
-- Code organization
-- Documentation
-
-**Ready for production use! 🚀**
diff --git a/Documentation/CONFIGURATION_MANAGEMENT.md b/Documentation/CONFIGURATION_MANAGEMENT.md
index 6ccbd06..7a0c6b1 100644
--- a/Documentation/CONFIGURATION_MANAGEMENT.md
+++ b/Documentation/CONFIGURATION_MANAGEMENT.md
@@ -13,20 +13,20 @@ Clear cached data with safety checks and user feedback.
#### Usage
```bash
# Clear all cache with confirmation
-python -m modules.cli --clear-cache
+snatch --clear-cache
# Clear specific cache types
-python -m modules.cli --clear-cache --type=metadata
-python -m modules.cli --clear-cache --type=downloads
-python -m modules.cli --clear-cache --type=sessions
-python -m modules.cli --clear-cache --type=thumbnails
-python -m modules.cli --clear-cache --type=temp
+snatch --clear-cache --type=metadata
+snatch --clear-cache --type=downloads
+snatch --clear-cache --type=sessions
+snatch --clear-cache --type=thumbnails
+snatch --clear-cache --type=temp
# Dry run to see what would be deleted
-python -m modules.cli --clear-cache --dry-run
+snatch --clear-cache --dry-run
# Skip confirmation prompt
-python -m modules.cli --clear-cache --yes
+snatch --clear-cache --yes
```
#### Cache Types
@@ -51,14 +51,14 @@ Interactive configuration file editing with validation and backup.
#### Usage
```bash
# Open config in default editor
-python -m modules.cli config edit
+snatch config edit
# Specify custom editor
-python -m modules.cli config edit --editor=notepad
-python -m modules.cli config edit --editor=code
+snatch config edit --editor=notepad
+snatch config edit --editor=code
# Skip backup creation
-python -m modules.cli config edit --no-backup
+snatch config edit --no-backup
```
#### Features
@@ -75,25 +75,25 @@ Display current configuration with multiple output formats and filtering.
#### Usage
```bash
# Show all configuration in table format
-python -m modules.cli config show
+snatch config show
# Different output formats
-python -m modules.cli config show --format=json
-python -m modules.cli config show --format=yaml
-python -m modules.cli config show --format=table
+snatch config show --format=json
+snatch config show --format=yaml
+snatch config show --format=table
# Filter by category
-python -m modules.cli config show --category=download
-python -m modules.cli config show --category=video
-python -m modules.cli config show --category=audio
-python -m modules.cli config show --category=network
+snatch config show --category=download
+snatch config show --category=video
+snatch config show --category=audio
+snatch config show --category=network
# Show only non-default values
-python -m modules.cli config show --non-default
+snatch config show --non-default
# Save to file
-python -m modules.cli config show --output=config_export.json
-python -m modules.cli config show --format=yaml --output=config.yaml
+snatch config show --output=config_export.json
+snatch config show --format=yaml --output=config.yaml
```
#### Output Formats
@@ -116,13 +116,13 @@ Manage configuration backups with create, list, and restore functionality.
#### Usage
```bash
# List available backups
-python -m modules.cli config backup list
+snatch config backup list
# Create new backup
-python -m modules.cli config backup create
+snatch config backup create
# Restore from backup
-python -m modules.cli config backup restore --name=config_backup_20250527_220658.json
+snatch config backup restore --name=config_backup_20250527_220658.json
```
#### Features
@@ -138,13 +138,13 @@ Reset configuration to default values with category support.
#### Usage
```bash
# Reset all configuration (with confirmation)
-python -m modules.cli config reset
+snatch config reset
# Reset specific category
-python -m modules.cli config reset --category=download
+snatch config reset --category=download
# Skip confirmation
-python -m modules.cli config reset --yes
+snatch config reset --yes
```
## Configuration Categories
@@ -200,34 +200,34 @@ python -m modules.cli config reset --yes
### Complete Workflow Example
```bash
# 1. Show current configuration
-python -m modules.cli config show --category=download
+snatch config show --category=download
# 2. Create backup before changes
-python -m modules.cli config backup create
+snatch config backup create
# 3. Edit configuration
-python -m modules.cli config edit
+snatch config edit
# 4. Verify changes
-python -m modules.cli config show --non-default
+snatch config show --non-default
# 5. Clear cache after changes
-python -m modules.cli --clear-cache --type=metadata --dry-run
-python -m modules.cli --clear-cache --type=metadata
+snatch --clear-cache --type=metadata --dry-run
+snatch --clear-cache --type=metadata
```
### Maintenance Workflow
```bash
# Weekly cache cleanup
-python -m modules.cli --clear-cache --type=temp
-python -m modules.cli --clear-cache --type=thumbnails
+snatch --clear-cache --type=temp
+snatch --clear-cache --type=thumbnails
# Monthly full cleanup
-python -m modules.cli --clear-cache --dry-run
-python -m modules.cli --clear-cache
+snatch --clear-cache --dry-run
+snatch --clear-cache
# Export configuration for backup
-python -m modules.cli config show --format=yaml --output=weekly_backup.yaml
+snatch config show --format=yaml --output=weekly_backup.yaml
```
## Error Handling
@@ -271,12 +271,12 @@ All configuration commands include comprehensive error handling:
Use the built-in help system for detailed command information:
```bash
-python -m modules.cli --help
-python -m modules.cli config --help
-python -m modules.cli config show --help
-python -m modules.cli config edit --help
-python -m modules.cli config backup --help
-python -m modules.cli --clear-cache --help
+snatch --help
+snatch config --help
+snatch config show --help
+snatch config edit --help
+snatch config backup --help
+snatch --clear-cache --help
```
## Integration with Existing Features
diff --git a/Documentation/DEPLOYMENT_GUIDE.md b/Documentation/DEPLOYMENT_GUIDE.md
index 4ccdf42..84a3768 100644
--- a/Documentation/DEPLOYMENT_GUIDE.md
+++ b/Documentation/DEPLOYMENT_GUIDE.md
@@ -68,19 +68,15 @@ python -m pip install --upgrade pip
#### 2. Install Dependencies
```bash
-# Install development dependencies
-pip install -r setupfiles/requirements.txt
-pip install -r setupfiles/requirements-dev.txt
-
-# Install in editable mode
-pip install -e .
+# Install in editable mode with development dependencies
+pip install -e ".[dev]"
```
#### 3. Setup FFmpeg
```bash
# Windows (automated)
-python setupfiles/setup_ffmpeg.py
+python setup_ffmpeg.py
# Linux
sudo apt-get install ffmpeg
@@ -179,7 +175,6 @@ source venv/bin/activate
# Install dependencies
pip install --upgrade pip
-pip install -r setupfiles/requirements.txt
pip install .
```
@@ -227,7 +222,7 @@ User=snatch
Group=snatch
WorkingDirectory=/opt/snatch
Environment=PATH=/opt/snatch/venv/bin
-ExecStart=/opt/snatch/venv/bin/python -m modules.cli daemon
+ExecStart=/opt/snatch/venv/bin/snatch daemon
Restart=always
RestartSec=10
@@ -276,10 +271,6 @@ RUN useradd -m -u 1000 snatch
# Set working directory
WORKDIR /app
-# Copy requirements
-COPY setupfiles/requirements.txt .
-RUN pip install --no-cache-dir -r requirements.txt
-
# Copy application
COPY . .
RUN pip install --no-cache-dir .
@@ -297,7 +288,7 @@ EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD snatch --version || exit 1
-CMD ["python", "-m", "modules.cli", "daemon"]
+CMD ["snatch", "daemon"]
```
### Docker Compose
@@ -407,17 +398,16 @@ python -m venv venv
# Install dependencies
python -m pip install --upgrade pip
-pip install -r setupfiles\requirements.txt
pip install .
# Setup FFmpeg
-python setupfiles\setup_ffmpeg.py
+python setup_ffmpeg.py
# Create desktop shortcut
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$env:USERPROFILE\Desktop\Snatch.lnk")
-$Shortcut.TargetPath = "$InstallPath\venv\Scripts\python.exe"
-$Shortcut.Arguments = "-m modules.cli"
+$Shortcut.TargetPath = "$InstallPath\venv\Scripts\snatch.exe"
+$Shortcut.Arguments = ""
$Shortcut.WorkingDirectory = $InstallPath
$Shortcut.Save()
@@ -436,10 +426,10 @@ Write-Host "You can run Snatch from: $InstallPath\venv\Scripts\snatch.exe" -Fore
class Snatch < Formula
desc "Advanced media downloader with modern interface"
homepage "https://github.com/your-username/snatch"
- url "https://github.com/your-username/snatch/archive/v1.8.0.tar.gz"
+ url "https://github.com/your-username/snatch/archive/v2.0.0.tar.gz"
sha256 "your-sha256-hash"
- depends_on "python@3.9"
+ depends_on "python@3.10"
depends_on "ffmpeg"
depends_on "aria2"
@@ -465,14 +455,14 @@ brew install snatch
```bash
# Create .deb package structure
-mkdir -p snatch-1.8.0/DEBIAN
-mkdir -p snatch-1.8.0/opt/snatch
-mkdir -p snatch-1.8.0/usr/bin
+mkdir -p snatch-2.0.0/DEBIAN
+mkdir -p snatch-2.0.0/opt/snatch
+mkdir -p snatch-2.0.0/usr/bin
# Control file
-cat > snatch-1.8.0/DEBIAN/control << EOF
+cat > snatch-2.0.0/DEBIAN/control << EOF
Package: snatch
-Version: 1.8.0
+Version: 2.0.0
Section: utils
Priority: optional
Architecture: all
@@ -484,7 +474,7 @@ Description: Advanced media downloader
EOF
# Post-installation script
-cat > snatch-1.8.0/DEBIAN/postinst << 'EOF'
+cat > snatch-2.0.0/DEBIAN/postinst << 'EOF'
#!/bin/bash
set -e
@@ -499,10 +489,10 @@ echo "Snatch installed successfully!"
echo "Run 'snatch --help' to get started."
EOF
-chmod 755 snatch-1.8.0/DEBIAN/postinst
+chmod 755 snatch-2.0.0/DEBIAN/postinst
# Build package
-dpkg-deb --build snatch-1.8.0
+dpkg-deb --build snatch-2.0.0
```
#### RPM Package (RHEL/CentOS/Fedora)
@@ -510,7 +500,7 @@ dpkg-deb --build snatch-1.8.0
```spec
# snatch.spec
Name: snatch
-Version: 1.8.0
+Version: 2.0.0
Release: 1%{?dist}
Summary: Advanced media downloader
@@ -531,7 +521,6 @@ hundreds of websites with modern interface and features.
%build
python3 -m venv venv
source venv/bin/activate
-pip install -r setupfiles/requirements.txt
pip install .
%install
@@ -545,7 +534,7 @@ ln -s /opt/snatch/venv/bin/snatch %{buildroot}/usr/bin/snatch
/usr/bin/snatch
%changelog
-* Sat May 24 2025 Your Name - 1.8.0-1
+* Sat May 24 2025 Your Name - 2.0.0-1
- Initial RPM package
```
@@ -877,7 +866,7 @@ spec:
spec:
containers:
- name: snatch
- image: snatch:1.8.0
+ image: snatch:2.0.0
resources:
requests:
memory: "512Mi"
@@ -1047,7 +1036,7 @@ sudo systemctl status snatch
sudo journalctl -u snatch -f
# Debug mode
-sudo -u snatch /opt/snatch/venv/bin/python -m modules.cli --debug
+sudo -u snatch /opt/snatch/venv/bin/snatch --debug
```
### Deployment Checklist
diff --git a/Documentation/DOCUMENTATION_INDEX.md b/Documentation/DOCUMENTATION_INDEX.md
index ba86767..4e2370e 100644
--- a/Documentation/DOCUMENTATION_INDEX.md
+++ b/Documentation/DOCUMENTATION_INDEX.md
@@ -1,58 +1,49 @@
-# 📚 Snatch Documentation Index
+# Snatch Documentation Index
-Welcome to the Snatch v1.8.1 documentation! This page provides an overview of all available documentation and guides.
+Welcome to the Snatch v2.0.0 documentation.
-## 🎯 Quick Start
+## Quick Start
**New to Snatch?** Start here:
-1. [Installation Guide](#installation) (in main README)
-2. [Usage Guide](Documentation/USAGE_GUIDE.md) - Complete command examples
-3. [Features Update](Documentation/FEATURES_UPDATE.md) - What's new in v1.8.1
+1. [Installation Guide](../README.md#Installation) (in main README)
+2. [Usage Guide](USAGE_GUIDE.md) - Complete command examples
+3. [Changelog](CHANGELOG.md) - What's new in v2.0.0
-## 📋 Documentation Structure
+## Documentation Structure
-### 🚀 User Documentation
+### User Documentation
| Document | Description | Best For |
|----------|-------------|----------|
-| [📖 Main README](./README.md) | Overview, installation, basic usage | Everyone |
-| [📝 Usage Guide](./USAGE_GUIDE.md) | Complete command examples and workflows | Users wanting comprehensive examples |
-| [⚙️ Configuration Management](./CONFIGURATION_MANAGEMENT.md) | Cache clearing, config editing, backups | Users managing application settings |
-| [✨ Features Update](./FEATURES_UPDATE.md) | New features in v1.8.1 | Users upgrading from previous versions |
-| [📋 Changelog](./CHANGELOG.md) | Version history and changes | Users tracking updates |
+| [Main README](../README.md) | Overview, installation, basic usage | Everyone |
+| [Usage Guide](USAGE_GUIDE.md) | Complete command examples and workflows | Users wanting comprehensive examples |
+| [Configuration Management](CONFIGURATION_MANAGEMENT.md) | Cache clearing, config editing, backups | Users managing application settings |
+| [Customization Guide](CUSTOMIZATION_GUIDE.md) | Themes, profiles, aliases | Users personalizing Snatch |
+| [Audio Enhancement Guide](AUDIO_ENHANCEMENT_GUIDE.md) | Audio processing presets and workflows | Audio processing users |
+| [Interactive Mode Guide](INTERACTIVE_MODE_GUIDE.md) | TUI and interactive features | Interactive mode users |
+| [Changelog](CHANGELOG.md) | Version history and changes | Users tracking updates |
+| [Disclaimer](Disclaimer.md) | Legal notice | Everyone |
-### 🔧 Technical Documentation
+### Technical Documentation
| Document | Description | Best For |
|----------|-------------|----------|
-| [🏗️ Technical Documentation](../Documentation/TECHNICAL_DOCUMENTATION.md) | System architecture | Developers |
-| [📦 Module Documentation](../Documentation/MODULE_DOCUMENTATION.md) | Detailed module analysis | Contributors |
-| [🔌 Plugin Development](../Documentation/PLUGIN_DEVELOPMENT_GUIDE.md) | Plugin system guide | Plugin developers |
-| [⚡ Performance Guide](../Documentation/PERFORMANCE_OPTIMIZATION_GUIDE.md) | Optimization tips | Advanced users |
+| [Technical Architecture](TECHNICAL_ARCHITECTURE.md) | System architecture overview | Developers |
+| [Technical Documentation](TECHNICAL_DOCUMENTATION.md) | Detailed technical reference | Developers |
+| [Module Documentation](MODULE_DOCUMENTATION.md) | Module-level API docs | Contributors |
+| [API Reference](API_REFERENCE.md) | Function signatures and usage | Developers |
+| [Plugin Development](PLUGIN_DEVELOPMENT_GUIDE.md) | Plugin system guide | Plugin developers |
+| [Performance Guide](PERFORMANCE_OPTIMIZATION_GUIDE.md) | Optimization strategies | Advanced users |
-### 🛠️ Setup & Troubleshooting
+### Setup & Troubleshooting
| Document | Description | Best For |
|----------|-------------|----------|
-| [🚀 Deployment Guide](../Documentation/DEPLOYMENT_GUIDE.md) | Installation & deployment | System administrators |
-| [🔍 Troubleshooting Guide](../Documentation/TROUBLESHOOTING_GUIDE.md) | Common issues & solutions | Users with problems |
-| [🧪 Integration Testing](../Documentation/INTEGRATION_TESTING.md) | Testing procedures | Quality assurance |
+| [Deployment Guide](DEPLOYMENT_GUIDE.md) | Installation & deployment | System administrators |
+| [Troubleshooting Guide](TROUBLESHOOTING_GUIDE.md) | Common issues & solutions | Users with problems |
-## 🔥 What's New in v1.8.1
-
-### Critical Fixes
-
-- ✅ **Fixed Resolution Selection**: `--resolution` flags now work correctly
-- ✅ **Proper Quality Selection**: Requesting 2160p actually gets 4K video
-
-### New Features
-
-- 🚀 **AI Video Upscaling**: Enhance video quality with Real-ESRGAN
-- 🎨 **Multiple Upscaling Methods**: AI and traditional upscaling options
-- ⚙️ **Configurable Settings**: Quality presets and scale factors
-
-## 🎯 Common Use Cases
+## Common Use Cases
### Basic Downloads
@@ -60,160 +51,40 @@ Welcome to the Snatch v1.8.1 documentation! This page provides an overview of al
# Download best quality video
snatch download "URL"
-# Download specific resolution (now works correctly!)
+# Download specific resolution
snatch download "URL" --resolution 1080
+
+# Download audio only
+snatch download "URL" --audio-only --format flac
```
### Video Enhancement
```bash
-# AI upscaling for better quality
+# AI upscaling
snatch download "URL" --upscale --upscale-method realesrgan
# Combine resolution + upscaling
snatch download "URL" --resolution 720 --upscale --upscale-factor 2
```
-### Audio Downloads
-
-```bash
-# High-quality audio
-snatch download "URL" --audio-only --format flac
-
-# Standard MP3
-snatch download "URL" --audio-only --format mp3
-```
-
-## 🚀 Getting Help
-
-### Step-by-Step Process
-
-1. **Check the FAQ** (in main README troubleshooting section)
-2. **Run diagnostics**: `python test_features_verification.py`
-3. **Check logs**: Look in `logs/snatch_errors.log`
-4. **Review documentation**: Use this index to find relevant guides
-5. **Test with verbose output**: Add `--verbose` to your commands
-
-### Quick Diagnostics
-
-```bash
-# Verify installation
-snatch --version
-
-# Test system capabilities
-snatch info
-
-# Check network speed
-snatch speedtest
-
-# Verify new features
-python test_features_verification.py
-```
-
-## 📊 Performance Tips
-
-### Optimize Downloads
-
-- Use `--aria2c` for faster downloads
-- Set appropriate `--resolution` to balance quality and speed
-- Use `--throttle` to limit bandwidth usage
-
-### Optimize Upscaling
-
-- Use `realesrgan` for animated content
-- Use `lanczos` for live-action videos
-- Use `--replace-original` to save disk space
-- Consider `--upscale-quality low` for faster processing
-
-## 🔧 Configuration
-
-### Environment Variables
-
-```bash
-set SNATCH_OUTPUT=D:\Downloads # Default download directory
-set SNATCH_FFMPEG=C:\ffmpeg\bin # FFmpeg location
-set SNATCH_LOG_LEVEL=INFO # Logging verbosity
-```
-
-### Config File
+### Audio Enhancement
```bash
-# Edit configuration
-snatch config edit
+# Enhance with preset
+snatch audio enhance "myfile.mp3" --preset music
-# View current settings
-snatch config show
+# Analyze audio quality
+snatch audio analyze "myfile.wav"
```
-## 🌟 Advanced Features
-
-### Batch Processing
-
-```bash
-# Download multiple URLs
-snatch batch urls.txt --upscale
-
-# Process playlists
-snatch download "PLAYLIST_URL" --playlist --upscale
-```
-
-### Automation
-
-```bash
-# Download and enhance in one command
-snatch download "URL" --resolution 720 --upscale --upscale-factor 4 --replace-original --organize
-```
-
-## 📈 Migration Guide
-
-### From v1.8.0 to v1.8.1
+## Getting Help
-1. **Update installation:**
-
- ```bash
- git pull
- pip install -e .
- ```
-
-2. **Test resolution selection:**
-
- ```bash
- # This now works reliably
- snatch download "test-url" --resolution 1080
- ```
-
-3. **Try new upscaling:**
-
- ```bash
- snatch download "test-url" --upscale
- ```
-
-### Update Existing Scripts
-
-- Replace hardcoded resolution workarounds
-- Add upscaling options where beneficial
-- Update error handling for new features
-
-## 📞 Support
-
-### Documentation Hierarchy
-
-1. **Quick issues**: Main README troubleshooting section
-2. **Detailed guides**: This documentation index
-3. **Technical details**: Technical documentation folder
-4. **Code examples**: Usage guide and features update
-
-### Self-Service Tools
-
-- `test_features_verification.py` - Verify installation
-- `snatch info` - System information
-- `snatch --help` - Command reference
-- Log files in `logs/` directory
+1. Check the [Troubleshooting Guide](TROUBLESHOOTING_GUIDE.md)
+2. Run `snatch info` for system diagnostics
+3. Use `snatch --help` for command reference
+4. Add `--verbose` for detailed output
---
-**📝 Note**: This documentation is for Snatch v1.8.1. For older versions, check the changelog for compatibility information.
-
-**🔗 Quick Links:**
-
-- [Main README](./README.md) | [Usage Guide](Documentation/USAGE_GUIDE.md) | [Features Update](Documentation/FEATURES_UPDATE.md) | [Changelog](Documentation/CHANGELOG.md)
+**Snatch v2.0.0** | [Main README](../README.md) | [Usage Guide](USAGE_GUIDE.md) | [Changelog](CHANGELOG.md)
diff --git a/Documentation/DOCUMENTATION_UPDATE_SUMMARY.md b/Documentation/DOCUMENTATION_UPDATE_SUMMARY.md
deleted file mode 100644
index 9ed74b1..0000000
--- a/Documentation/DOCUMENTATION_UPDATE_SUMMARY.md
+++ /dev/null
@@ -1,194 +0,0 @@
-# 📋 Documentation Update Summary
-
-## ✅ Completed Documentation Updates for Snatch v1.8.1
-
-### 🎯 Major Documentation Additions
-
-#### 1. **Enhanced Main README** (`markdownfiles/README.md`)
-
-- ✅ Updated version number to 1.8.1
-- ✅ Added comprehensive resolution selection fix documentation
-- ✅ Added AI video upscaling feature overview
-- ✅ Updated feature list with new capabilities
-- ✅ Enhanced usage examples with correct `snatch download` syntax
-- ✅ Added video enhancement CLI options table
-- ✅ Updated roadmap to reflect completed features
-
-#### 2. **New Feature Documentation**
-
-**📝 FEATURES_UPDATE.md** - Complete guide to new features
-
-- Detailed resolution selection bug fix explanation
-- Comprehensive video upscaling documentation
-- CLI options reference with examples
-- Performance considerations and tips
-- Troubleshooting guide for new features
-
-**📖 USAGE_GUIDE.md** - Complete command examples and workflows
-
-- Resolution selection examples (fixed functionality)
-- Video upscaling workflows with all methods
-- Smart combination strategies
-- Real-world usage scenarios
-- Performance optimization tips
-
-**📋 CHANGELOG.md** - Version history and detailed changes
-
-- Complete changelog for v1.8.1
-- Migration guide from previous versions
-- Usage examples for new features
-- Known issues and limitations
-
-**📚 DOCUMENTATION_INDEX.md** - Central documentation hub
-
-- Organized index of all documentation
-- Quick start guides and navigation
-- Use case specific documentation routing
-- Self-service troubleshooting guide
-
-#### 3. **Updated Technical Documentation**
-
-- ✅ Updated Documentation/README.md with new features
-- ✅ Version number updates across all files
-- ✅ Architecture documentation references to new modules
-
-#### 4. **Configuration Updates**
-
-- ✅ Added UPSCALE_PRESETS to `modules/defaults.py`
-- ✅ Added UPSCALE_METHODS configuration
-- ✅ Updated version number to 1.8.1
-
-#### 5. **Verification Scripts**
-
-- ✅ Created `test_features_verification.py` - Comprehensive system testing
-- ✅ Created `simple_verification.py` - Basic functionality testing
-
-## 🎯 Key Documentation Improvements
-
-### Resolution Selection Fixes
-
-- **Problem**: Documented the critical bug where `--resolution` flags didn't work
-- **Solution**: Explained the format string fixes and fallback chains
-- **Examples**: Provided working command examples for all resolutions
-
-### Video Upscaling Features
-
-- **Methods**: Documented Real-ESRGAN, Lanczos, Bicubic, and Bilinear options
-- **Configuration**: Explained quality presets and scale factors
-- **Workflows**: Provided real-world usage scenarios and optimization tips
-
-### Command Syntax Updates
-
-- **Consistency**: All examples now use `snatch download` format
-- **New Options**: Documented all new CLI arguments with examples
-- **Integration**: Showed how to combine resolution + upscaling effectively
-
-## 📊 Documentation Structure
-
-```
-markdownfiles/
-├── README.md # Main documentation (updated)
-├── FEATURES_UPDATE.md # New features guide (new)
-├── USAGE_GUIDE.md # Complete usage examples (new)
-├── CHANGELOG.md # Version history (new)
-├── DOCUMENTATION_INDEX.md # Central hub (new)
-├── FIXES_README.md # Previous fixes (existing)
-├── IMPROVEMENTS_README.md # Previous improvements (existing)
-└── TODO.md # Task tracking (existing)
-
-Documentation/
-├── README.md # Technical docs (updated)
-├── TECHNICAL_DOCUMENTATION.md # Architecture (existing)
-├── MODULE_DOCUMENTATION.md # Module details (existing)
-└── [other technical docs] # Various guides (existing)
-```
-
-## 🎯 User Experience Improvements
-
-### Quick Start Path
-
-1. **New Users**: README → USAGE_GUIDE → hands-on examples
-2. **Upgrading Users**: FEATURES_UPDATE → CHANGELOG → migration guide
-3. **Developers**: DOCUMENTATION_INDEX → Technical Documentation
-4. **Troubleshooting**: DOCUMENTATION_INDEX → specific guides
-
-### Self-Service Support
-
-- Comprehensive troubleshooting sections in each guide
-- Verification scripts for testing installation
-- Clear error resolution steps
-- Performance optimization guidance
-
-## 🚀 New Features Documented
-
-### ✅ Fixed Resolution Selection
-
-```bash
-# Now works correctly - gets actual requested quality
-snatch download "URL" --resolution 2160 # Actually gets 4K!
-snatch download "URL" --resolution 1080 # Actually gets 1080p!
-```
-
-### ✅ AI Video Upscaling
-
-```bash
-# AI enhancement for better quality
-snatch download "URL" --upscale --upscale-method realesrgan --upscale-factor 2
-
-# Combine resolution + upscaling for optimal bandwidth usage
-snatch download "URL" --resolution 720 --upscale --upscale-factor 4 --replace-original
-```
-
-### ✅ New CLI Options
-
-| Option | Description | Example |
-|--------|-------------|---------|
-| `--upscale` | Enable video upscaling | `--upscale` |
-| `--upscale-method` | Choose algorithm | `--upscale-method realesrgan` |
-| `--upscale-factor` | Scale factor | `--upscale-factor 2` |
-| `--upscale-quality` | Quality preset | `--upscale-quality high` |
-| `--replace-original` | Replace source file | `--replace-original` |
-
-## 📈 Quality Assurance
-
-### Testing Coverage
-
-- ✅ All resolution selection scenarios documented and tested
-- ✅ Video upscaling workflows verified
-- ✅ CLI integration confirmed
-- ✅ Documentation consistency checked
-
-### User Validation
-
-- ✅ Clear examples for each feature
-- ✅ Troubleshooting guides for common issues
-- ✅ Performance optimization tips included
-- ✅ Migration paths from previous versions
-
-## 🎉 Conclusion
-
-The documentation has been comprehensively updated to reflect:
-
-1. **Critical Bug Fixes**: Resolution selection now works correctly
-2. **Major New Features**: AI-powered video upscaling capabilities
-3. **Enhanced User Experience**: Clear guides and examples
-4. **Self-Service Support**: Verification scripts and troubleshooting
-5. **Future-Ready**: Organized structure for ongoing updates
-
-### Next Steps for Users
-
-1. Read `FEATURES_UPDATE.md` for overview of changes
-2. Use `USAGE_GUIDE.md` for practical examples
-3. Run verification scripts to test installation
-4. Refer to `DOCUMENTATION_INDEX.md` for specific needs
-
-### Next Steps for Developers
-
-1. Review technical documentation updates
-2. Test new features with provided scripts
-3. Consider contributing additional examples or improvements
-4. Use the new documentation structure as a template for future updates
-
----
-
-**All documentation is now current for Snatch v1.8.1 with complete coverage of resolution selection fixes and video upscaling features.**
diff --git a/Documentation/FEATURES_UPDATE.md b/Documentation/FEATURES_UPDATE.md
deleted file mode 100644
index 436f84a..0000000
--- a/Documentation/FEATURES_UPDATE.md
+++ /dev/null
@@ -1,277 +0,0 @@
-# 🚀 Snatch v1.8.0 - Resolution & Video Upscaling Update
-
-## 📋 Overview
-
-This update addresses critical resolution selection issues and introduces AI-powered video upscaling capabilities to enhance your media downloading experience.
-
-## 🔧 Critical Fixes
-
-### Resolution Selection Bug Fix
-
-**Problem Solved:** The `--resolution` (`-r`) flag was not working correctly. When requesting 2160p/4K videos, the system would ignore the flag and download in random quality.
-
-**Solution Implemented:**
-
-- Fixed format string generation from `best[height>=1080]` to `bestvideo[height<=1080]+bestaudio/best[height<=1080]`
-- Implemented proper fallback chains: 4K → 1440p → 1080p → 720p → 480p → best available
-- Added enhanced logging for debugging resolution selection
-
-**Before:**
-
-```bash
-# This would often fail or ignore the resolution
-snatch download "URL" --resolution 2160
-```
-
-**After:**
-
-```bash
-# Now works reliably with proper format selection
-snatch download "URL" --resolution 2160 # Gets actual 4K/2160p video
-snatch download "URL" --resolution 1080 # Gets actual 1080p video
-snatch download "URL" --resolution 720 # Gets actual 720p video
-```
-
-## 🎨 New Feature: AI Video Upscaling
-
-### What is Video Upscaling?
-
-Video upscaling enhances video quality by increasing resolution and improving visual details using advanced algorithms, including AI-powered methods.
-
-### Supported Upscaling Methods
-
-| Method | Type | Best For | Quality | Speed |
-|--------|------|----------|---------|-------|
-| `realesrgan` | AI-powered | Anime, cartoons, graphics | Highest | Slower |
-| `lanczos` | Traditional | Live action, photographs | High | Medium |
-| `bicubic` | Traditional | General purpose | Good | Fast |
-| `bilinear` | Traditional | Quick processing | Basic | Fastest |
-
-### Upscaling Configuration
-
-**Factors Available:**
-
-- `2x`: Double the resolution (e.g., 1080p → 2160p)
-- `4x`: Quadruple the resolution (e.g., 720p → 2880p)
-
-**Quality Presets:**
-
-- `low`: Fast processing, basic enhancement
-- `medium`: Balanced quality and speed (default)
-- `high`: Maximum quality, slower processing
-
-## 💻 Command Examples
-
-### Basic Upscaling
-
-```bash
-# Enable basic 2x upscaling with AI
-snatch download "https://example.com/video" --upscale
-
-# Use specific AI method with 4x upscaling
-snatch download "https://example.com/video" --upscale --upscale-method realesrgan --upscale-factor 4
-
-# Traditional upscaling with high quality
-snatch download "https://example.com/video" --upscale --upscale-method lanczos --upscale-quality high
-```
-
-### Combined Resolution & Upscaling
-
-```bash
-# Download 720p and upscale to 1440p equivalent
-snatch download "https://example.com/video" --resolution 720 --upscale --upscale-factor 2
-
-# Download 1080p and upscale to 4K equivalent
-snatch download "https://example.com/video" --resolution 1080 --upscale --upscale-factor 2
-
-# Download lowest quality and upscale to high quality
-snatch download "https://example.com/video" --resolution 480 --upscale --upscale-factor 4
-```
-
-### Advanced Options
-
-```bash
-# Replace original file after upscaling (saves space)
-snatch download "https://example.com/video" --upscale --replace-original
-
-# Combine with other features
-snatch download "https://example.com/video" --upscale --upscale-method realesrgan --aria2c --stats
-```
-
-## 🎯 CLI Options Reference
-
-### Video Enhancement Options
-
-| Option | Short | Type | Default | Description |
-|--------|-------|------|---------|-------------|
-| `--upscale` | `-u` | Flag | `False` | Enable video upscaling |
-| `--upscale-method` | | String | `lanczos` | Upscaling algorithm |
-| `--upscale-factor` | | Integer | `2` | Scale factor (2x or 4x) |
-| `--upscale-quality` | | String | `medium` | Quality preset |
-| `--replace-original` | | Flag | `False` | Replace source file |
-
-### Fixed Resolution Options
-
-| Option | Short | Type | Description |
-|--------|-------|------|-------------|
-| `--resolution` | `-r` | Integer | Target resolution (now works correctly) |
-
-**Available Resolutions:**
-
-- `2160` - 4K/UHD (3840×2160)
-- `1440` - QHD (2560×1440)
-- `1080` - Full HD (1920×1080)
-- `720` - HD (1280×720)
-- `480` - SD (854×480)
-
-## 🔄 Workflow Examples
-
-### Scenario 1: Enhance Old Videos
-
-```bash
-# Download older content and upscale for modern displays
-snatch download "old-video-url" --upscale --upscale-method realesrgan --upscale-factor 4
-```
-
-### Scenario 2: Save Bandwidth, Enhance Later
-
-```bash
-# Download in lower quality to save bandwidth, then upscale
-snatch download "video-url" --resolution 720 --upscale --upscale-factor 2 --replace-original
-```
-
-### Scenario 3: Maximum Quality
-
-```bash
-# Get highest available resolution and enhance further
-snatch download "video-url" --resolution 2160 --upscale --upscale-quality high
-```
-
-## 🛠️ Technical Implementation
-
-### Architecture
-
-The video upscaling system is implemented through:
-
-1. **VideoUpscaler Class** (`modules/ffmpeg_helper.py`)
- - Handles Real-ESRGAN and traditional upscaling methods
- - Manages upscaling configuration and execution
- - Provides progress tracking and error handling
-
-2. **Pipeline Integration** (`modules/manager.py`)
- - Automatic upscaling detection based on CLI arguments
- - Seamless integration with download workflow
- - File management and cleanup
-
-3. **Configuration System** (`modules/defaults.py`)
- - Predefined upscaling presets and configurations
- - Quality and performance optimization settings
-
-### Dependencies
-
-- **FFmpeg**: Required for all video processing
-- **Real-ESRGAN**: Optional, for AI-powered upscaling (automatically downloaded when needed)
-- **Python 3.8+**: Core system requirements
-
-## 📊 Performance Considerations
-
-### Processing Time
-
-| Resolution | Method | Factor | Estimated Time* |
-|------------|--------|--------|----------------|
-| 720p | Lanczos | 2x | 1-2 minutes |
-| 720p | Real-ESRGAN | 2x | 5-10 minutes |
-| 1080p | Lanczos | 2x | 2-4 minutes |
-| 1080p | Real-ESRGAN | 2x | 10-20 minutes |
-
-*Times vary based on system specifications and video length
-
-### Storage Requirements
-
-- **Temporary Space**: 2-3x the original file size during processing
-- **Final Size**: 2-4x larger than original (depends on upscaling factor)
-- **Use `--replace-original`**: To save space by removing the source file
-
-## 🚀 Getting Started
-
-### Quick Start
-
-1. **Update to latest version:**
-
- ```bash
- git pull
- pip install -e .
- ```
-
-2. **Test resolution selection:**
-
- ```bash
- snatch download "test-url" --resolution 1080
- ```
-
-3. **Try video upscaling:**
-
- ```bash
- snatch download "test-url" --upscale
- ```
-
-### Requirements Check
-
-```bash
-# Verify FFmpeg installation
-ffmpeg -version
-
-# Check system resources
-snatch info
-
-# Test network speed
-snatch speedtest
-```
-
-## 🐛 Troubleshooting
-
-### Common Issues
-
-**Resolution not working:**
-
-- Update to latest version - this bug is now fixed
-- Check available formats: `snatch download "URL" --list-formats`
-
-**Upscaling fails:**
-
-- Ensure FFmpeg is properly installed
-- Check available disk space (need 2-3x file size)
-- Try different upscaling method: `--upscale-method lanczos`
-
-**Real-ESRGAN errors:**
-
-- Allow automatic download of Real-ESRGAN models
-- Check internet connection for model downloads
-- Fall back to traditional methods if needed
-
-### Performance Tips
-
-1. **Use appropriate upscaling methods:**
- - Real-ESRGAN for animated content
- - Lanczos for live action
- - Bilinear for quick processing
-
-2. **Optimize for your system:**
- - Lower quality preset for older hardware
- - Use `--replace-original` to save space
- - Monitor system resources during processing
-
-3. **Batch processing:**
- - Process multiple files sequentially
- - Use lower factors for faster processing
- - Consider processing overnight for large batches
-
-## 📈 Future Enhancements
-
-- Additional AI upscaling models
-- Batch upscaling capabilities
-- GPU acceleration support
-- Custom upscaling profiles
-- Quality comparison tools
-
----
diff --git a/Documentation/FIXES_README.md b/Documentation/FIXES_README.md
deleted file mode 100644
index 424d1c4..0000000
--- a/Documentation/FIXES_README.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# Snatch-DL Fixes
-
-This directory contains various scripts to fix critical issues in the Snatch-DL media downloader.
-
-## Quick Fix Guide
-
-To apply all fixes at once, run:
-
-```bash
-python apply_all_fixes.py
-```
-
-This script will:
-
-1. Fix the interactive mode's `RowDoesNotExist` error
-2. Fix the download command with proper asyncio event loop handling
-3. Ensure proper configuration with automatic directory creation
-
-## Individual Fix Scripts
-
-If you prefer to apply fixes individually:
-
-- **direct_fix.py** - Applies direct patches to fix the most critical issues:
-
- ```bash
- python direct_fix.py
- ```
-
-- **fix_interactive_mode.py** - Specifically fixes interactive mode issues:
-
- ```bash
- python fix_interactive_mode.py
- ```
-
-- **ensure_config.py** - Validates and creates the configuration file:
-
- ```bash
- python ensure_config.py
- ```
-
-## Testing Your Installation
-
-After applying the fixes, you can test if they worked correctly:
-
-```bash
-python test_audio_and_resolution.py
-```
-
-This will test various download options including audio-only and resolution-specific downloads.
-
-## Usage After Fixes
-
-Once the fixes are applied, you can use these commands:
-
-1. Download a video:
-
- ```bash
- snatch download
- ```
-
-2. Download audio only:
-
- ```bash
- snatch download --audio-only
- ```
-
-3. Download at specific resolution:
-
- ```bash
- snatch download --resolution 720
- ```
-
-4. Use interactive mode:
-
- ```bash
- snatch interactive
- ```
-
-## Detailed Fix Information
-
-For detailed technical information about the fixes, please see the [FIXES_SUMMARY.md](./FIXES_SUMMARY.md) file.
diff --git a/Documentation/FIXES_SUMMARY.md b/Documentation/FIXES_SUMMARY.md
deleted file mode 100644
index c0a6874..0000000
--- a/Documentation/FIXES_SUMMARY.md
+++ /dev/null
@@ -1,163 +0,0 @@
-# Snatch-DL Fixes Summary
-
-## Critical Issues Fixed
-
-1. **Interactive Mode RowDoesNotExist Error**
- - Fixed the `get_selected_format` method to handle empty tables and row access errors
- - Added proper error handling and null checks for the table rows
- - Ensured the format selection defaults to "best" when no rows exist or on error
-
-2. **Download Command Not Working**
- - Fixed asyncio event loop handling in the CLI module's download command
- - Ensured proper execution of download commands with flags like `--audio-only` and `--resolution`
- - Improved error handling and recovery when event loop issues occur
-
-3. **Configuration Management**
- - Enhanced config.json validation with automatic directory creation
- - Added robust error handling for missing configuration keys
- - Ensured platform-specific paths are properly handled
-
-4. **Previous Issues Addressed**
- - Fixed duplicate `AudioConversionError` class definition in `manager.py`
- - Fixed incorrect parameter passing to `validate_ffmpeg_installation()`
- - Improved interactive mode with better error handling
-
-## Implementation Details
-
-### AsyncDownloadManager Fixes
-
-The `download_with_options` method in `AsyncDownloadManager` class was fixed to properly handle:
-
-- Audio-only downloads with FFmpeg post-processing
-- Resolution-specific downloads with proper format selection
-- Error handling for missing dependencies
-
-### Interactive Mode Enhancements
-
-- Added proper error handling for `get_selected_format` method
-- Fixed table row access to prevent RowDoesNotExist errors
-- Added safe defaults when format selection fails
-
-### Additional Improvements
-
-- Added robust error handling for FFmpeg-related operations
-- Created comprehensive verification and test scripts
-- Improved configuration management for more reliable operation
-
-## Technical Implementation Details
-
-### Interactive Mode Fix
-
-The critical issue in interactive mode was the `RowDoesNotExist` error in the `get_selected_format` method. The fix adds proper checking and handling:
-
-```python
-def get_selected_format(self) -> Optional[str]:
- """Get the selected format ID"""
- table = self.query_one("#format-table")
-
- # Check if table has any rows
- if not table.row_count:
- return "best"
-
- # Check if a row is selected
- if table.cursor_row is not None:
- try:
- # Access the row safely
- return table.get_row_at(table.cursor_row)[1] # Format ID is in column 1
- except Exception as e:
- logging.warning(f"Error selecting format: {str(e)}")
- return "best"
-
- # Default to best quality if nothing is selected
- return "best"
-```
-
-This ensures that:
-
-1. If there are no rows, it defaults to "best" quality
-2. It safely attempts to access the selected row with proper exception handling
-3. If an exception occurs, it logs the error and defaults to "best" quality
-
-### Download Command Fix
-
-The second critical issue involved the download command not working properly. The fix ensures that async operations are handled correctly:
-
-```python
-# Start the download process
-try:
- # Use loop.run_until_complete to ensure the download completes
- loop = asyncio.get_event_loop()
- loop.run_until_complete(self.run_download(urls, options))
- return 0
-except RuntimeError:
- # If we can't run directly, fall back to previous method
- self.run_async(self.run_download(urls, options))
- return 0
-```
-
-This approach:
-
-1. Attempts to use the current event loop properly
-2. Falls back to an alternative method if runtime errors occur
-3. Ensures that the download process always completes
-
-### Configuration Enhancement
-
-The configuration handling was improved with robust validation and directory creation:
-
-```python
-def validate_config(config):
- """Validate configuration and create any missing directories"""
- required_dirs = [
- "video_output",
- "audio_output",
- "sessions_dir",
- "cache_dir"
- ]
-
- # Check and create required directories
- for dir_key in required_dirs:
- if dir_key in config:
- directory = config[dir_key]
- if not os.path.exists(directory):
- os.makedirs(directory, exist_ok=True)
-```
-
-This ensures that all required directories exist before the application attempts to use them.
-
-### Fix Management Scripts
-
-The fixes are organized into multiple scripts:
-
-1. `apply_all_fixes.py` - The main script that applies all fixes in sequence
-2. `direct_fix.py` - Uses regex to directly patch the affected code
-3. `fix_interactive_mode.py` - Focuses specifically on fixing the interactive mode
-4. `ensure_config.py` - Ensures proper configuration setup
-
-These scripts can be run independently or in sequence through the main `apply_all_fixes.py` script.
-
-## Testing Approach
-
-Created several test scripts:
-
-1. `test_audio_and_resolution.py` - Tests audio-only and resolution downloads
-2. `apply_all_fixes.py` - Applies all fixes in the correct sequence
-3. `direct_fix.py` - Provides direct fixes to the critical issues
-4. `fix_interactive_mode.py` - Specifically fixes the interactive mode issues
-5. `ensure_config.py` - Ensures configuration is properly set up
-
-## Future Enhancements
-
-Consider implementing:
-
-1. More comprehensive progress reporting
-2. Better error messaging for end users
-3. Improved dependency validation at startup
-4. More robust event loop handling for asyncio operations
-
-## Usage Instructions
-
-- For audio downloads: `snatch download --audio-only URL`
-- For video at specific resolution: `snatch download --resolution 720 URL`
-- For interactive mode: `snatch interactive`
-- For comprehensive fixes: `python apply_all_fixes.py`
diff --git a/Documentation/INTERACTIVE_MODE_GUIDE.md b/Documentation/INTERACTIVE_MODE_GUIDE.md
index e3a9a90..f5665cc 100644
--- a/Documentation/INTERACTIVE_MODE_GUIDE.md
+++ b/Documentation/INTERACTIVE_MODE_GUIDE.md
@@ -254,7 +254,7 @@ snatch customize interface --setting high_contrast_mode --value true
```bash
# Check dependencies
-pip install -r setupfiles/requirements.txt
+pip install -e ".[all]"
# Verify installation
snatch --version
diff --git a/Documentation/MODULE_DOCUMENTATION.md b/Documentation/MODULE_DOCUMENTATION.md
index 67f0b0e..d54a58f 100644
--- a/Documentation/MODULE_DOCUMENTATION.md
+++ b/Documentation/MODULE_DOCUMENTATION.md
@@ -2,7 +2,7 @@
## Core Module Analysis
-### modules/manager.py - Download Management System
+### snatch/manager.py - Download Management System
#### Architecture Overview
@@ -130,7 +130,7 @@ The manager implements sophisticated memory management:
---
-### modules/audio_processor.py - Audio Enhancement System
+### snatch/audio_processor.py - Audio Enhancement System
#### Architecture Overview
@@ -232,7 +232,7 @@ await processor.apply_filter_chain([
---
-### modules/p2p.py - Peer-to-Peer System
+### snatch/p2p.py - Peer-to-Peer System
#### Architecture Overview
@@ -351,7 +351,7 @@ p2p.register_discovery_method(CustomDiscovery())
---
-### modules/session.py - Session Management
+### snatch/session.py - Session Management
#### Purpose
@@ -402,7 +402,7 @@ Provides persistent session management for download resumption and state trackin
---
-### modules/config.py - Configuration System
+### snatch/config.py - Configuration System
#### Purpose
@@ -456,7 +456,7 @@ Centralized configuration management with validation, defaults, and environment
---
-### modules/cache.py - Caching System
+### snatch/cache.py - Caching System
#### Purpose
diff --git a/Documentation/PERFORMANCE_OPTIMIZATION_GUIDE.md b/Documentation/PERFORMANCE_OPTIMIZATION_GUIDE.md
index 63dde8d..5f79145 100644
--- a/Documentation/PERFORMANCE_OPTIMIZATION_GUIDE.md
+++ b/Documentation/PERFORMANCE_OPTIMIZATION_GUIDE.md
@@ -2,7 +2,7 @@
## Overview
-This guide provides comprehensive strategies and techniques for optimizing the performance of the Snatch media downloader across different system configurations and use cases, with special focus on the new audio enhancement features introduced in v1.8.0.
+This guide provides comprehensive strategies and techniques for optimizing the performance of the Snatch media downloader across different system configurations and use cases, with special focus on the audio enhancement features.
## Table of Contents
diff --git a/Documentation/PLUGIN_DEVELOPMENT_GUIDE.md b/Documentation/PLUGIN_DEVELOPMENT_GUIDE.md
index 0d2cbbb..aad3abf 100644
--- a/Documentation/PLUGIN_DEVELOPMENT_GUIDE.md
+++ b/Documentation/PLUGIN_DEVELOPMENT_GUIDE.md
@@ -84,7 +84,7 @@ Download hooks allow plugins to interact with the download lifecycle at key poin
#### Interface Definition
```python
-from modules.manager import DownloadHooks, DownloadChunk
+from snatch.manager import DownloadHooks, DownloadChunk
from typing import Dict, Any
class DownloadPlugin(DownloadHooks, PluginInterface):
@@ -143,7 +143,7 @@ import time
import json
from pathlib import Path
from typing import Dict, Any
-from modules.manager import DownloadChunk
+from snatch.manager import DownloadChunk
class AnalyticsPlugin(DownloadPlugin):
"""Plugin that tracks download analytics and performance metrics"""
diff --git a/Documentation/README.md b/Documentation/README.md
deleted file mode 100644
index 9395bc3..0000000
--- a/Documentation/README.md
+++ /dev/null
@@ -1,1058 +0,0 @@
-
-
-
-
-Snatch
-Download Anything, Anywhere, Anytime
-
-
- Features •
- Installation •
- Quick Start •
- Usage •
- Supported Sites •
- Troubleshooting
-
-
-
-
-
-
-
-
-
-
-## What's New in v1.8.0
-
-### 🎯 Major New Features & Enhancements
-
-#### **🎵 Comprehensive Audio Enhancement System**
-
-- **AI-Powered Audio Enhancement**: Advanced noise reduction, frequency extension, and stereo widening
-- **Professional Audio Presets**: 5 curated presets (podcast, music, speech, broadcast, restoration)
-- **Sample Rate Upscaling**: Intelligent upsampling to higher quality audio
-- **Dynamic Range Processing**: Professional loudness normalization and compression
-- **Audio Quality Analysis**: Automatic preset recommendations based on content analysis
-- **CLI Integration**: Complete command-line interface for audio enhancement
-
-#### **🔧 Fixed Resolution Selection Bug**
-
-- **RESOLVED**: Resolution flags (`--resolution`, `-r`) now work correctly
-- **IMPACT**: When requesting 2160p/4K, system now properly selects the highest available quality
-- **IMPROVEMENT**: Enhanced format string generation with proper fallback chains
-
-#### **🚀 AI-Powered Video Upscaling**
-
-- **NEW**: Real-ESRGAN integration for AI-enhanced video quality
-- **METHODS**: Support for both AI (Real-ESRGAN) and traditional (Lanczos, Bicubic) upscaling
-- **FLEXIBLE**: 2x and 4x upscaling factors with quality preservation options
-- **EFFICIENT**: Optimized processing pipeline with progress tracking
-
-#### **🎮 Interactive Mode Enhancements**
-
-- **Enhanced UI**: Modern Textual-based interface with rich components
-- **Audio Processing**: Built-in audio conversion and enhancement tools
-- **File Management**: Advanced file organization and management features
-- **Real-time Processing**: Background audio/video processing with progress tracking
-
-#### **📚 Comprehensive Documentation**
-
-- **[🎯 Features Update Guide](./FEATURES_UPDATE.md)** - Detailed overview of new features
-- **[📖 Usage Guide](./USAGE_GUIDE.md)** - Complete command examples and workflows
-- **[🔧 Technical Documentation](../Documentation/README.md)** - Architecture and implementation details
-
-#### **Quick Examples:**
-
-```bash
-# Fixed resolution selection (now works correctly)
-snatch download "URL" --resolution 2160 # Actually gets 4K!
-
-# AI video upscaling
-snatch download "URL" --upscale --upscale-method realesrgan --upscale-factor 2
-
-# Audio enhancement with presets
-snatch audio enhance myfile.mp3 --preset music
-snatch audio enhance podcast.wav --preset podcast --output enhanced_podcast.wav
-
-# Audio quality analysis and recommendations
-snatch audio analyze myfile.wav
-snatch audio presets --detailed
-
-# Batch audio processing
-snatch audio batch "*.mp3" --preset restoration
-
-# Combine resolution + upscaling for optimal results
-snatch download "URL" --resolution 720 --upscale --upscale-factor 4 --replace-original
-```
-
-### 🎯 Major Architectural Overhaul
-
-#### **Complete Package Refactoring & Modularization**
-
-- **Modular Architecture**: Split monolithic `Snatch.py` into a well-structured package under `modules/`:
- - `cli.py` - Command-line interface and argument parsing
- - `manager.py` - Core download management and orchestration
- - `config.py` - Configuration loading, validation, and management
- - `audio_processor.py` - Advanced audio enhancement and processing
- - `ffmpeg_helper.py` - Video upscaling and FFmpeg processing
- - `p2p.py` - Peer-to-peer networking and file sharing
- - `cache.py` - Intelligent caching and metadata storage
- - `session.py` - Network session management and optimization
- - `progress.py` - Enhanced progress tracking and display
- - `utils.py` - Shared utilities and helper functions
- - `plugins.py` - Plugin system and hook management
- - `logging_config.py` - Comprehensive logging configuration
- - `constants.py` - Application constants and defaults
- - `metadata.py` - Media information extraction and processing
- - `cyberpunk_ui.py` - Cyberpunk-themed UI components
-
-#### **Enhanced Plugin System**
-
-- **Hook-Based Architecture**: Comprehensive plugin system with multiple hook points
-- **Plugin Interfaces**: Support for DownloadHooks, ProcessingPlugin, and UIPlugin
-- **Dynamic Loading**: Automatic plugin discovery and registration
-- **Event System**: Pre/post download hooks, format processing, and UI customization
-
-#### **Advanced Audio Processing**
-
-- **AI-Enhanced Audio**: Intelligent audio enhancement using machine learning algorithms
-- **Multi-Format Support**: Opus, MP3, FLAC, WAV, and M4A with quality optimization
-- **Audio Normalization**: Automatic loudness normalization and dynamic range processing
-- **Surround Sound**: Support for stereo and 7.1 surround sound configurations
-
-#### **Peer-to-Peer Networking**
-
-- **P2P File Sharing**: Share downloaded content directly with other users
-- **Share Code System**: Generate unique codes for easy file sharing
-- **Network Discovery**: Automatic peer discovery and connection management
-- **Distributed Caching**: Leverage peer network for faster downloads
-
-### 🚀 Performance & User Experience Improvements
-
-#### **Smart Performance Optimization**
-
-- **Adaptive Resource Management**: Dynamic chunk sizes based on system resources
-- **Network Speed Testing**: Automatic optimization based on connection speed
-- **Smart Format Selection**: Intelligent format selection without testing all possibilities
-- **Concurrent Processing**: Enhanced multi-threaded download and processing
-
-#### **Enhanced User Interface**
-
-- **Cyberpunk Theme**: Futuristic, neon-styled interface with animations
-- **Interactive Progress**: Real-time progress bars with detailed statistics
-- **Spinner Animations**: Enhanced visual feedback during operations
-- **Rich Console Output**: Color-coded messages and status indicators
-
-#### **Improved Error Handling & Recovery**
-
-- **Intelligent Retry Logic**: Exponential backoff with smart failure recovery
-- **Detailed Error Messages**: Actionable error descriptions with solutions
-- **Advanced Logging**: Comprehensive logging with configurable verbosity levels
-- **Graceful Degradation**: Fallback mechanisms for various failure scenarios
-
-### 📚 Comprehensive Documentation Suite
-
-We've created an extensive documentation ecosystem to support developers and users:
-
-#### **📖 [Technical Documentation](./TECHNICAL_DOCUMENTATION.md)**
-
-- Complete system architecture overview with visual diagrams
-- Component interaction flows and data flow analysis
-- Dependency relationships and module hierarchies
-- Comprehensive file structure documentation
-
-#### **🔧 [Module Documentation](./MODULE_DOCUMENTATION.md)**
-
-- In-depth analysis of all core modules
-- Function signatures, parameters, and return values
-- Usage examples and best practices
-- Module interaction patterns
-
-#### **🔌 [Plugin Development Guide](./PLUGIN_DEVELOPMENT_GUIDE.md)**
-
-- Complete plugin architecture documentation
-- Hook system explanation with practical examples
-- Plugin registration and lifecycle management
-- Sample plugin implementations
-
-#### **📋 [API Reference](./API_REFERENCE.md)**
-
-- Comprehensive API documentation
-- Method signatures with detailed parameters
-- Error handling and return codes
-- Usage examples for all major functions
-
-#### **🚀 [Deployment Guide](./DEPLOYMENT_GUIDE.md)**
-
-- Development environment setup
-- Production deployment strategies
-- Docker containerization
-- Platform-specific installation guides
-- Security considerations and best practices
-
-#### **⚡ [Performance Optimization Guide](./PERFORMANCE_OPTIMIZATION_GUIDE.md)**
-
-- System resource optimization strategies
-- Network performance tuning
-- Memory and CPU optimization techniques
-- Caching strategies and storage optimization
-- Platform-specific performance tips
-
-#### **🔍 [Troubleshooting Guide](./TROUBLESHOOTING_GUIDE.md)**
-
-- Quick diagnostic procedures
-- Common issues and solutions
-- Platform-specific troubleshooting
-- Error codes reference
-- Advanced debugging techniques
-
-#### **🧪 [Integration Testing](./INTEGRATION_TESTING.md)**
-
-- Comprehensive testing strategies
-- Test suite documentation
-- Continuous integration setup
-- Quality assurance procedures
-
-### 🛠️ Technical Improvements
-
-#### **Code Architecture Enhancements**
-
-- **Circular Dependency Resolution**: Eliminated circular dependencies for better stability
-- **Import Optimization**: Improved import hygiene and reduced startup times
-- **Memory Management**: Enhanced memory efficiency and garbage collection
-- **Type Safety**: Comprehensive type hints and validation
-
-#### **Configuration Management**
-
-- **Flexible Configuration**: JSON-based configuration with validation
-- **Environment Variables**: Support for environment-based configuration
-- **Profile System**: Multiple configuration profiles for different use cases
-- **Dynamic Reloading**: Hot-reload configuration changes without restart
-
-#### **Security & Reliability**
-
-- **Input Validation**: Comprehensive input sanitization and validation
-- **Secure Networking**: Enhanced SSL/TLS handling and certificate validation
-- **Rate Limiting**: Intelligent rate limiting to prevent API abuse
-- **Crash Recovery**: Automatic crash detection and recovery mechanisms
-
-## 🚀 Overview
-
-**Snatch** is a powerful and user-friendly media downloader that lets you grab videos, audio, and more from hundreds of websites in various formats and qualities. With its sleek interface, comprehensive customization system, and powerful features, downloading media has never been easier or more satisfying!
-
-✨ Features
-
-### 🎨 **Comprehensive Customization System**
-
-- **8 Built-in Themes** - Default, Dark, Light, High Contrast, Cyberpunk, Minimal, Ocean, and Forest themes
-- **3 Interactive Interfaces** - Enhanced, Modern, and Textual TUI modes with rich interface options
-- **Performance Tuning** - Fine-tune download speeds, connection limits, memory usage, and resource management
-- **Behavior Customization** - Configure confirmations, auto-organization, session management, and error handling
-- **Interface Personalization** - Customize progress styles, keyboard shortcuts, display options, and accessibility features
-- **Command Aliases** - Create custom shortcuts for frequently used commands
-- **Profile System** - Save and switch between different configuration profiles
-- **Import/Export Settings** - Share configurations in YAML, JSON, or TOML formats
-
-### 🖥️ **Enhanced Interactive Modes**
-
-- **Cyberpunk Interface** - Futuristic themed interface with neon aesthetics and advanced features
-- **Modern Interface** - Clean, contemporary design with intuitive controls and beautiful animations
-- **Textual TUI** - Advanced terminal user interface with rich components and responsive design
-- **Enhanced CLI** - Rich-powered command line with syntax highlighting and interactive prompts
-
-### 🎯 **Core Download Features**
-
-- **Fixed Resolution Selection** - Properly working --resolution/-r flags for accurate quality selection
-- **AI Video Upscaling** - Enhance video quality with Real-ESRGAN or traditional upscaling methods (2x/4x)
-- **Dynamic Resource Management** - Adaptive chunk sizes based on your system's resources
-- **Site Explorer** - Browse and search through 1000+ supported sites
-- **Advanced Audio Options** - Choose between Opus (default), MP3, FLAC formats and stereo/surround sound
-- **Smart Conversion** - High-quality audio extraction with format options
-- **Concurrent Downloads** - Download multiple files simultaneously
-- **Quality Selection** - Choose specific video resolutions with reliable format selection
-- **Video Enhancement** - Upscale videos with configurable factors (2x, 4x) and quality preservation
-- **Playlist Support** - Download entire playlists with options to select specific videos
-- **Cache System** - Optimized repeat downloads with smart caching
-- **Error Recovery** - Robust error handling and helpful suggestions
-- **Format Flexibility** - Video, Opus, MP3, FLAC, WAV, and more
-- **Universal Compatibility** - Works on Windows, macOS, and Linux
-- **Automatic File Organization** - Organize downloads based on metadata
-- **Resume Downloads** - Continue interrupted downloads from where they left off
-- **Download Statistics** - Track and display download performance metrics
-- **aria2c Support** - Optional high-speed download engine for better performance
-- **Network Speed Testing** - Automatically optimize settings based on your connection speed
-- **Smart Format Selection** - Intelligently selects best format without testing all possibilities
-- **Temporary File Management** - Advanced handling of temporary files to prevent disk space waste
-
-## 🎨 Customization System
-
-Snatch features a comprehensive customization system that allows you to personalize every aspect of the application to match your preferences and workflow.
-
-### 🎭 Theme Management
-
-**Available Themes:**
-
-- `default` - Standard Snatch appearance
-- `dark` - Dark mode with comfortable contrast
-- `light` - Clean light theme
-- `high_contrast` - Enhanced visibility for accessibility
-- `cyberpunk` - Futuristic neon aesthetics
-- `minimal` - Clean, distraction-free interface
-- `ocean` - Calming blue tones
-- `forest` - Nature-inspired green palette
-
-**Quick Theme Commands:**
-
-```bash
-# View current theme
-snatch customize theme show
-
-# List all available themes
-snatch customize theme list
-
-# Switch themes
-snatch customize theme set --theme cyberpunk
-snatch customize theme set --theme dark
-
-# Create custom theme with JSON colors
-snatch customize theme create --colors '{"primary": "#ff0000", "secondary": "#00ff00"}'
-```
-
-### 🖥️ Interactive Interface Modes
-
-Launch Snatch with different interface experiences:
-
-```bash
-# Enhanced interactive mode (default)
-snatch interactive
-
-# Modern beautiful interface
-snatch modern
-
-# Advanced Textual TUI
-snatch textual
-
-# Direct cyberpunk-themed interface
-snatch # Uses current theme setting
-```
-
-### ⚙️ Performance Customization
-
-Fine-tune performance settings for optimal downloads:
-
-```bash
-# View all performance settings
-snatch customize performance --show
-
-# Adjust concurrent downloads
-snatch customize performance --setting max_concurrent_downloads --value 8
-
-# Set bandwidth limits (0 = unlimited)
-snatch customize performance --setting global_bandwidth_limit --value 1000
-
-# Configure memory usage
-snatch customize performance --setting max_memory_usage_mb --value 1024
-
-# Optimize chunk sizes for your connection
-snatch customize performance --setting chunk_size --value 2097152
-```
-
-### 🎛️ Interface Personalization
-
-Customize the interface to your preferences:
-
-```bash
-# Show interface settings
-snatch customize interface --show
-
-# Enable detailed interface mode
-snatch customize interface --setting interface_mode --value detailed
-
-# Customize progress animations
-snatch customize interface --setting animate_progress --value true
-
-# Set display limits
-snatch customize interface --setting max_display_items --value 100
-```
-
-### 🎯 Behavior Configuration
-
-Control how Snatch behaves during operations:
-
-```bash
-# View behavior settings
-snatch customize behavior --show
-
-# Configure file overwrite confirmations
-snatch customize behavior --setting confirm_file_overwrite --value true
-
-# Auto-organize downloads by type
-snatch customize behavior --setting auto_organize_downloads --value true
-
-# Set large download threshold (MB)
-snatch customize behavior --setting large_download_threshold_mb --value 500
-```
-
-### 🔗 Command Aliases
-
-Create shortcuts for frequently used commands:
-
-```bash
-# List current aliases
-snatch customize alias list
-
-# Add custom aliases
-snatch customize alias add --alias "dl" --command "download"
-snatch customize alias add --alias "4k" --command "download --resolution 2160"
-
-# Remove aliases
-snatch customize alias remove --alias "dl"
-```
-
-### 📁 Profile Management
-
-Save and switch between different configuration profiles:
-
-```bash
-# List available profiles
-snatch customize profile list
-
-# Create new profile
-snatch customize profile create --name "work"
-
-# Load a profile
-snatch customize profile load --name "work"
-
-# Delete profile
-snatch customize profile delete --name "old-profile"
-```
-
-### 📤 Import/Export Settings
-
-Share configurations across devices or backup your settings:
-
-```bash
-# Export settings to YAML
-snatch customize export my-settings.yaml
-
-# Export to JSON format
-snatch customize export my-settings.json --format json
-
-# Import settings from file
-snatch customize import my-settings.yaml
-
-# Reset to defaults
-snatch customize reset
-```
-
-🔧 Installation
-
-### Prerequisites
-
-Before installing Snatch, make sure you have:
-
-1. **Python**: Version 3.8 or higher
-
- ```powershell
- python --version # Should show 3.8 or higher
- ```
-
-2. **Git**: For cloning the repository
-
- ```powershell
- git --version # Should show git version
- ```
-
-3. **FFmpeg**: Required for audio/video processing
- - Windows users can run `setupfiles/setup_ffmpeg.py` after installation
- - Linux/macOS users can use their package manager
-
-### Step-by-Step Installation
-
-1. **Clone the Repository**
-
- ```powershell
- git clone https://github.com/Rashed-alothman/Snatch.git
- cd Snatch
- ```
-
-2. **Create a Virtual Environment**
-
- ```powershell
- # Create a new virtual environment
- python -m venv .venv
-
- # Activate it:
- # On Windows PowerShell:
- .\.venv\Scripts\Activate.ps1
- # On Windows CMD:
- .\.venv\Scripts\activate.bat
- # On Linux/macOS:
- source .venv/bin/activate
- ```
-
-3. **Install Dependencies**
-
- ```powershell
- # Install required packages
- pip install -r setupfiles/requirements.txt
-
- # Install Snatch in development mode
- pip install -e .
- ```
-
-4. **Setup FFmpeg (Windows)**
-
- ```powershell
- # Automatic FFmpeg setup for Windows
- python setupfiles/setup_ffmpeg.py
- ```
-
-5. **Verify Installation**
-
- ```powershell
- snatch --version
- ```
-
-### Quick Start Guide
-
-Once installed, you can use Snatch in several ways:
-
-1. **Interactive Mode (Recommended)**
-
- ```powershell
- snatch
- ```
-
-2. **Direct Download Commands**
-
- ```powershell
- # Download video in best quality
- snatch download "https://youtube.com/watch?v=example"
-
- # Download audio only (Opus format)
- snatch download "https://youtube.com/watch?v=example" --audio-only
-
- # Download with specific format
- snatch download "https://youtube.com/watch?v=example" --audio-only --format mp3
- ```
-
-3. **Common Operations**
-
- ```powershell
- # List supported sites
- snatch sites
-
- # Check system info
- snatch info
-
- # Run speed test
- snatch speedtest
-
- # Show help
- snatch --help
- ```
-
-### Configuration
-
-The default configuration file is created at first run. You can customize it:
-
-```powershell
-# Open config in default editor
-snatch config edit
-
-# Show current config
-snatch config show
-```
-
-### Updating
-
-To update Snatch to the latest version:
-
-```powershell
-git pull
-pip install -e .
-```
-
-💻 Advanced Usage
-
-### Audio Downloads & Enhancement
-
-```powershell
-# Download in Opus format (default, best quality-to-size)
-snatch download "URL" --audio-only
-
-# Download in MP3 format
-snatch download "URL" --audio-only --format mp3
-
-# Download in FLAC format with surround sound
-snatch download "URL" --audio-only --format flac --channels 8
-
-# Download with custom quality
-snatch download "URL" --audio-only --format mp3 --quality 320
-
-# Audio Enhancement Commands (NEW in v1.8.0)
-# Enhance downloaded audio with AI-powered processing
-snatch audio enhance "myfile.mp3" --preset music
-snatch audio enhance "podcast.wav" --preset podcast --output "enhanced_podcast.wav"
-
-# Analyze audio quality and get recommendations
-snatch audio analyze "myfile.wav"
-
-# List available enhancement presets
-snatch audio presets --detailed
-
-# Batch process multiple files
-snatch audio batch "*.mp3" --preset restoration
-
-# Create custom enhancement preset
-snatch audio create-preset "my-preset" "Custom settings for my content"
-```
-
-#### Audio Enhancement Presets
-
-| Preset | Description | Best For |
-|--------|-------------|----------|
-| `podcast` | Speech optimization with noise reduction | Podcasts, interviews, voice recordings |
-| `music` | Stereo enhancement and dynamic preservation | Music tracks, albums |
-| `speech` | Strong noise reduction and clarity | Lectures, audiobooks, presentations |
-| `broadcast` | Professional broadcast standards | Radio shows, professional content |
-| `restoration` | Maximum enhancement for damaged audio | Old recordings, low-quality sources |
-
-### Video Downloads & Enhancement
-
-```powershell
-# Download in best quality
-snatch download "URL"
-
-# Download in specific resolution (FIXED: now works correctly)
-snatch download "URL" --resolution 2160 # 4K/2160p
-snatch download "URL" --resolution 1440 # 1440p
-snatch download "URL" --resolution 1080 # 1080p
-snatch download "URL" --resolution 720 # 720p
-
-# Video upscaling with AI enhancement
-snatch download "URL" --upscale --upscale-method realesrgan --upscale-factor 2
-
-# Traditional upscaling methods
-snatch download "URL" --upscale --upscale-method lanczos --upscale-factor 4
-snatch download "URL" --upscale --upscale-method bicubic --upscale-factor 2
-
-# High-quality upscaling with custom settings
-snatch download "URL" --upscale --upscale-quality high --upscale-factor 2
-
-# Replace original file after upscaling
-snatch download "URL" --upscale --replace-original
-
-# Download with custom format
-snatch download "URL" --format mp4
-
-# Download with subtitles
-snatch download "URL" --subtitles
-```
-
-### Upscaling Methods & Quality Options
-
-| Method | Description | Best For | Performance |
-|--------|-------------|----------|-------------|
-| `realesrgan` | AI-powered upscaling | Anime, cartoons, graphics | Slower, highest quality |
-| `lanczos` | High-quality traditional | Live action, photographs | Medium speed, good quality |
-| `bicubic` | Standard interpolation | General purpose | Fast, decent quality |
-| `bilinear` | Basic interpolation | Quick processing | Fastest, lower quality |
-
-**Upscaling Quality Presets:**
-
-- `low`: Fast processing, basic enhancement
-- `medium`: Balanced quality and speed (default)
-- `high`: Maximum quality, slower processing
-
-**Upscaling Factors:**
-
-- `2`: Double resolution (e.g., 1080p → 2160p)
-- `4`: Quadruple resolution (e.g., 720p → 2880p)
-
-### Advanced Features
-
-```powershell
-# Resume interrupted download
-snatch download "URL" --resume
-
-# Use aria2c for faster downloads
-snatch download "URL" --aria2c
-
-# Show download statistics
-snatch download "URL" --stats
-
-# Save to specific directory
-snatch download "URL" --output "D:\Downloads"
-
-# Combine resolution selection with upscaling
-snatch download "URL" --resolution 720 --upscale --upscale-factor 4
-
-# Batch download from file
-snatch batch urls.txt
-
-# Download playlist with upscaling
-snatch download "PLAYLIST_URL" --playlist --upscale
-
-# Download with Real-ESRGAN upscaling and replace original
-snatch download "URL" --upscale --upscale-method realesrgan --replace-original
-```
-
-### New Video Enhancement CLI Options
-
-| Option | Short | Description | Example |
-|--------|-------|-------------|---------|
-| `--upscale` | `-u` | Enable video upscaling | `--upscale` |
-| `--upscale-method` | | Upscaling method | `--upscale-method realesrgan` |
-| `--upscale-factor` | | Scale factor (2x, 4x) | `--upscale-factor 2` |
-| `--upscale-quality` | | Quality preset | `--upscale-quality high` |
-| `--replace-original` | | Replace source file | `--replace-original` |
-| `--resolution` | `-r` | Target resolution (FIXED) | `--resolution 2160` |
-
-### Interactive Mode Commands
-
-When using interactive mode (`snatch`), you have access to these commands:
-
-| Command | Description | Example |
-|---------|-------------|---------|
-| `download` | Download media | `download https://youtube.com/...` |
-| `queue` | Show active downloads | `queue` |
-| `stats` | Show download statistics | `stats` |
-| `speed` | Run speed test | `speed` |
-| `config` | Show/edit configuration | `config edit` |
-| `clear` | Clear screen | `clear` |
-| `help` | Show help | `help` |
-| `exit` | Exit application | `exit` |
-
-### Environment Variables
-
-Snatch respects these environment variables:
-
-- `SNATCH_CONFIG`: Custom config file location
-- `SNATCH_OUTPUT`: Default output directory
-- `SNATCH_FFMPEG`: FFmpeg binary location
-- `SNATCH_CACHE`: Cache directory location
-- `SNATCH_LOG_LEVEL`: Logging verbosity
-
-Advanced Features
- 1. Playlist Downloads
-
-When downloading a playlist, Snatch will present options to:
-
-- Download the entire playlist
-- Download only the first few videos
-- Select specific videos to download
-
-#### 2. Batch Downloads
-
-Download multiple URLs at once:
-
-```bash
-python Snatch.py "URL1" "URL2" "URL3"
-```
-
-#### 3. Custom Output Directory
-
-```bash
-python Snatch.py "URL" --output-dir "path/to/directory"
-```
-
-#### 4. Format Specification
-
-```bash
-python Snatch.py "URL" --format-id 137+140 # For advanced users
-```
-
-#### 5. Automatic File Organization
-
-Snatch can automatically organize your downloaded files based on metadata extracted from the media. This creates a clean folder structure for your library.
-
-Enable organization with:
-
-```bash
-python Snatch.py --organize URL
-```
-
-Or set it permanently in the configuration:
-
-```bash
-python setup_ffmpeg.py
-```
-
-#### Organization Templates
-
-You can customize how files are organized using templates in the config:
-
-- Audio: `{uploader}/{album}/{title}`
-- Video: `{uploader}/{year}/{title}`
-- Podcast: `Podcasts/{uploader}/{year}-{month}/{title}`
-- Audiobook: `Audiobooks/{uploader}/{title}`
-
-Available variables include:
-
-- `{title}` - Media title
-- `{uploader}` - Channel or uploader name
-- `{album}` - Album name (for music)
-- `{artist}` - Artist name
-- `{year}` - Release year
-- `{month}` - Release month
-- `{day}` - Release day
-- `{track_number}` - Track number
-
-#### 6. Advanced Audio Options
-
-Snatch now offers enhanced audio conversion options:
-
-- **Default Format**: Opus audio format (superior quality-to-size ratio)
-- **Channel Configuration**:
- - Interactive prompt to choose between stereo (2.0) and surround sound (7.1)
- - Command-line option: `--audio-channels 2` (stereo) or `--audio-channels 8` (7.1)
-- **Format Options**:
- - Opus: `--audio-format opus` (default, excellent quality at smaller file sizes)
- - MP3: `--audio-format mp3` (maximum compatibility)
- - FLAC: `--audio-format flac` (lossless audio)
- - WAV: `--audio-format wav` (uncompressed)
- - M4A: `--audio-format m4a` (AAC audio)
-
-```bash
-# Download with 7.1 surround sound in Opus format
-python Snatch.py "URL" --audio-only --audio-channels 8
-
-# Skip interactive prompts (useful for scripting)
-python Snatch.py "URL" --audio-only --non-interactive
-```
-
-#### 7. Advanced Command-line Options
-
-Snatch supports several advanced options for more control over your downloads:
-
-```bash
-# Resume interrupted downloads
-python Snatch.py "URL" --resume
-
-# Show download statistics after completion
-python Snatch.py "URL" --stats
-
-# Display system resource statistics
-python Snatch.py "URL" --system-stats
-
-# Skip using cached media information
-python Snatch.py "URL" --no-cache
-
-# Disable automatic retry logic
-python Snatch.py "URL" --no-retry
-
-# Limit download speed (e.g., 2M = 2MB/s)
-python Snatch.py "URL" --throttle 2M
-
-# Use aria2c as the download engine for better performance
-python Snatch.py "URL" --aria2c
-
-# Enable detailed logging for troubleshooting
-python Snatch.py "URL" --verbose
-
-# Test all available formats for best quality (slower)
-python Snatch.py "URL" --test-formats
-
-# Use fast format selection (default)
-python Snatch.py "URL" --fast
-```
-
-#### 8. Network Speed Testing
-
-Snatch can automatically test your network speed to optimize download settings:
-
-```bash
-# Run a standalone speed test
-python Snatch.py speedtest
-
-# Get detailed speed test results
-python Snatch.py test
-```
-
-## 🏗️ Technical Architecture
-
-- **Modular Design**: Core functionality is split into logical modules for maintainability
- - `common_utils.py`: Shared utilities and helper functions
- - `manager.py`: Download orchestration and resource management
- - `progress.py`: Advanced progress tracking and display
- - `session.py`: Network session handling and speed optimization
- - `metadata.py`: Media information extraction and processing
-
-- **Performance Optimizations**:
- - Smart caching of download information
- - Concurrent downloads with resource monitoring
- - Intelligent format selection without testing all possibilities
- - Network speed-aware chunk size optimization
-
-- **Error Handling**:
- - Graceful recovery from network issues
- - Smart retry logic with exponential backoff
- - Detailed logging for troubleshooting
- - Memory-efficient operation
-
-## 📊 Performance Insights
-
-| Feature | Before | After |
-|---------|---------|--------|
-| Startup Time | ~2.5s | ~0.8s |
-| Memory Usage | 150-200MB | 80-120MB |
-| Download Speed* | 5-10MB/s | 15-25MB/s |
-| CPU Usage | 25-30% | 10-15% |
-
-*With aria2c enabled on a gigabit connection
-
-🌎 Supported Sites
-Snatch supports over 1000 websites including:
-
-- YouTube
-- Vimeo
-- Twitter/X
-- Instagram
-- TikTok
-- Facebook
-- Twitch
-- SoundCloud
-- Reddit
-- Daily Motion
-- And many more!
-
-To see the full list of supported sites:
-
-```bash
-python Snatch.py --list-sites
-```
-
-🔍 Troubleshooting
-
-Common Issues
-
-1. **FFmpeg not found**
-
- ```bash
- python setup_ffmpeg.py # Run this to fix automatically
- ```
-
-2. **SSL Errors**
-
- - Update Python and dependencies:
-
- ```bash
- pip install -U yt-dlp requests
- ```
-
-3. **Permission Errors**
-
- - Run as administrator (Windows)
- - Use sudo (Linux/macOS)
-
-4. **Slow Downloads**
- - Check your internet connection
- - Try with `--aria2c` for faster downloading
- - Use `--http-chunk-size 10485760` for larger chunks
-
-### Need Help?
-
-If you're still having issues:
-
-- Check the logs in download_log.txt
-- Run with `--verbose` for detailed output
-- Try `--system-stats` to check if your system has enough resources
-
- 🤝 Contributing
-Contributions are welcome! Feel free to:
-
-- Report bugs
-- Suggest new features
-- Submit pull requests
-
-## 🗺️ Feature Roadmap
-
-### 📦 Core Architecture & Packaging
-
-- ✅ Modular package structure under `modules/`
-- ✅ `modules/__init__.py` with `__version__` and public API
-- ✅ Basic setup.py configuration
-- ✅ Editable install support
-- ⬜ PyPI packaging and distribution
-
-### 🛠️ Logging & Configuration
-
-- ✅ Root logger with rich formatting
-- ✅ Module-level logging
-- ✅ Color-coded console output
-- ✅ Basic configuration management
-- ⬜ Profile-based configs
-
-### 🎛️ Interactive Experience
-
-- ✅ Modern rich UI interface
-- ✅ Command history
-- ✅ Tab completion
-- ✅ Format selection
-- ✅ Download progress tracking
-- ⬜ Playlist management
-
-### ⚙️ Download Features
-
-- ✅ Audio/video downloads
-- ✅ Format selection
-- ✅ Resolution control
-- ✅ Download resumption
-- ✅ Network optimization
-- ⬜ Batch processing
-
-### 🌐 P2P Capabilities
-
-- ✅ Basic file sharing
-- ✅ Share code generation
-- ✅ File fetching
-- ⬜ DHT implementation
-- ⬜ NAT traversal
-
-### 🔊 Media Processing
-
-- ✅ Audio extraction
-- ✅ Format conversion
-- ✅ Metadata handling
-- ✅ Video upscaling with Real-ESRGAN
-- ✅ Resolution selection fixes
-- ⬜ Audio normalization
-- ⬜ Subtitle support
-
-### 📊 Monitoring
-
-- ✅ Download statistics
-- ✅ Speed testing
-- ✅ System monitoring
-- ⬜ Usage analytics
-
-### Future Plans
-
-- ⬜ GUI interface
-- ⬜ Plugin system
-- ⬜ RSS feed monitoring
-- ⬜ Remote control API
-- ⬜ Docker support
-- ⬜ Auto-update system
-
-## 📜 License
-
-This project is licensed under the MIT License - see the LICENSE file for details.
-
-## 📊 System Requirements
-
-- **Minimum**: 2GB RAM, 1GHz CPU, 100MB free space
-- **Recommended**: 4GB RAM, 2GHz dual-core CPU, 500MB free space
-
-## 🙏 Acknowledgements
-
-- Built with [yt-dlp](https://github.com/yt-dlp)
-- Uses [FFmpeg](https://ffmpeg.org/) for media processing
-
----
-
-
-Made with ❤️ by Rashed Alothman
-
diff --git a/Documentation/TECHNICAL_ARCHITECTURE.md b/Documentation/TECHNICAL_ARCHITECTURE.md
index e4b01cf..185339d 100644
--- a/Documentation/TECHNICAL_ARCHITECTURE.md
+++ b/Documentation/TECHNICAL_ARCHITECTURE.md
@@ -2,7 +2,7 @@
## Overview
-This document provides a comprehensive overview of the Snatch Media Downloader's technical architecture, including the major components, data flow, and system integration patterns introduced in v1.8.0.
+This document provides a comprehensive overview of the Snatch Media Downloader's technical architecture, including the major components, data flow, and system integration patterns as of v2.0.0.
## Table of Contents
@@ -23,7 +23,7 @@ This document provides a comprehensive overview of the Snatch Media Downloader's
```
┌─────────────────────────────────────────────────────────────────┐
-│ Snatch v1.8.0 │
+│ Snatch v2.0.0 │
├─────────────────────────────────────────────────────────────────┤
│ CLI Interface │ Interactive Mode │ Configuration System │
├─────────────────────────────────────────────────────────────────┤
diff --git a/Documentation/TECHNICAL_DOCUMENTATION.md b/Documentation/TECHNICAL_DOCUMENTATION.md
index abefe9e..6347776 100644
--- a/Documentation/TECHNICAL_DOCUMENTATION.md
+++ b/Documentation/TECHNICAL_DOCUMENTATION.md
@@ -75,12 +75,12 @@ Plugin Interface ← Hook Registry → Download Manager
### Entry Points
-#### `modules/__init__.py`
+#### `snatch/__init__.py`
- **Purpose**: Package initialization and public API definition
- **Exports**: `main_app`, `DownloadManager`, `load_config`, `__version__`
- **Integration Points**: Primary entry point for external usage
-#### `modules/cli.py`
+#### `snatch/cli.py`
- **Purpose**: Command-line interface implementation with Rich formatting
- **Key Classes**: `EnhancedCLI`
- **Dependencies**: typer, rich, asyncio
@@ -88,80 +88,80 @@ Plugin Interface ← Hook Registry → Download Manager
### Core Management
-#### `modules/manager.py`
+#### `snatch/manager.py`
- **Purpose**: Central download management with async/sync support
- **Key Classes**: `AsyncDownloadManager`, `DownloadManager`, `DownloadHooks`
- **Features**: Error recovery, retry mechanisms, memory management
- **Plugin Hooks**: Pre/post download, chunk processing, custom processors
-#### `modules/session.py`
+#### `snatch/session.py`
- **Purpose**: Session persistence and state management
- **Key Classes**: `AsyncSessionManager`, `SessionManager`
- **Features**: Download resumption, session tracking, concurrent session handling
### Configuration System
-#### `modules/config.py`
+#### `snatch/config.py`
- **Purpose**: Configuration loading and validation
- **Key Functions**: `load_config`, `initialize_config_async`
- **Integration Points**: All components depend on configuration
-#### `modules/defaults.py`
+#### `snatch/defaults.py`
- **Purpose**: Default values and format presets
- **Constants**: `FORMAT_PRESETS`, `CACHE_DIR`, `MAX_RETRIES`
### Processing & Enhancement
-#### `modules/audio_processor.py`
+#### `snatch/audio_processor.py`
- **Purpose**: Advanced audio processing and enhancement
- **Key Classes**: `AudioProcessor`, `EnhancedAudioProcessor`
- **Features**: Surround sound upmixing, noise reduction, normalization
- **Plugin Integration**: Extensible filter chains, custom processing pipelines
-#### `modules/file_organizer.py`
+#### `snatch/file_organizer.py`
- **Purpose**: File system organization and metadata management
- **Key Classes**: `FileOrganizer`
- **Features**: Smart file organization, metadata extraction
### Networking
-#### `modules/network.py`
+#### `snatch/network.py`
- **Purpose**: Network utilities and connectivity management
- **Key Functions**: `check_internet_connection`, `run_speedtest`
- **Features**: Speed testing, network monitoring
-#### `modules/p2p.py`
+#### `snatch/p2p.py`
- **Purpose**: Peer-to-peer file sharing implementation
- **Features**: DHT support, file sharing, NAT traversal preparation
### User Interface
-#### `modules/cyberpunk_ui.py` & `modules/cyberpunk_interactive.py`
+#### `snatch/theme/cyberpunk_ui.py` & `snatch/theme/cyberpunk_interactive.py`
- **Purpose**: Modern cyberpunk-themed interactive interface
- **Key Classes**: `CyberpunkInteractiveApp`
- **Features**: Rich UI, progress tracking, interactive controls
-#### `modules/textual_interface.py`
+#### `snatch/theme/textual_interface.py`
- **Purpose**: Textual-based terminal user interface
- **Features**: Modern TUI with widgets, responsive design
### Utilities & Infrastructure
-#### `modules/error_handler.py`
+#### `snatch/error_handler.py`
- **Purpose**: Centralized error handling and reporting
- **Key Classes**: `EnhancedErrorHandler`
- **Features**: Error categorization, logging, recovery strategies
-#### `modules/logging_config.py`
+#### `snatch/logging_config.py`
- **Purpose**: Logging configuration and formatting
- **Features**: Rich formatting, module-level logging, colored output
-#### `modules/cache.py`
+#### `snatch/cache.py`
- **Purpose**: Download caching and metadata storage
- **Key Classes**: `DownloadCache`
- **Features**: Resume support, metadata caching
-#### `modules/progress.py`
+#### `snatch/progress.py`
- **Purpose**: Progress tracking and display
- **Key Classes**: `DownloadStats`, `Spinner`
- **Features**: Real-time progress, speed calculation
@@ -215,31 +215,31 @@ class CustomHook(DownloadHooks):
## Module Documentation
-### `modules.cli`
+### `snatch.cli`
- **Responsibility**: Command-line interface and user interaction
- **Public API**: `main()`, `EnhancedCLI`
- **Configuration**: Format presets, output options, UI preferences
- **Plugin Hooks**: Command extensions, custom UI elements
-### `modules.manager`
+### `snatch.manager`
- **Responsibility**: Core download management and coordination
- **Public API**: `AsyncDownloadManager`, `DownloadManager`, `DownloadHooks`
- **Configuration**: Retry settings, memory limits, concurrent downloads
- **Plugin Hooks**: Download lifecycle, custom processors, error handlers
-### `modules.audio_processor`
+### `snatch.audio_processor`
- **Responsibility**: Audio enhancement and processing
- **Public API**: `AudioProcessor`, `EnhancedAudioProcessor`
- **Configuration**: FFmpeg settings, filter presets, quality options
- **Plugin Hooks**: Custom filters, processing pipelines, format converters
-### `modules.session`
+### `snatch.session`
- **Responsibility**: Session persistence and state management
- **Public API**: `AsyncSessionManager`, `SessionManager`
- **Configuration**: Session storage location, cleanup policies
- **Plugin Hooks**: Session lifecycle, custom storage backends
-### `modules.network`
+### `snatch.network`
- **Responsibility**: Network operations and monitoring
- **Public API**: Network utility functions, connectivity checks
- **Configuration**: Timeout settings, proxy configuration
@@ -331,7 +331,7 @@ async def my_custom_processor(file_path: str, options: Dict[str, Any]) -> None:
```python
import logging
from typing import Dict, Any
-from modules.manager import DownloadHooks, DownloadChunk
+from snatch.manager import DownloadHooks, DownloadChunk
logger = logging.getLogger(__name__)
@@ -388,10 +388,10 @@ Plugins can be configured through the main configuration file:
### Public API Overview
-The Snatch public API is exposed through `modules/__init__.py`:
+The Snatch public API is exposed through `snatch/__init__.py`:
```python
-from modules import main_app, DownloadManager, load_config, __version__
+from snatch import main_app, DownloadManager, load_config, __version__
```
### Core Functions
@@ -400,7 +400,7 @@ from modules import main_app, DownloadManager, load_config, __version__
- **Purpose**: Main application entry point
- **Parameters**: None (uses CLI arguments)
- **Returns**: Exit code
-- **Usage**: `python -m modules.cli`
+- **Usage**: `snatch`
#### `load_config(config_path: Optional[str] = None) -> Dict[str, Any]`
- **Purpose**: Load and validate configuration
diff --git a/Documentation/TODO.md b/Documentation/TODO.md
deleted file mode 100644
index e69de29..0000000
diff --git a/Documentation/TROUBLESHOOTING_GUIDE.md b/Documentation/TROUBLESHOOTING_GUIDE.md
index ce8bddc..ea08f46 100644
--- a/Documentation/TROUBLESHOOTING_GUIDE.md
+++ b/Documentation/TROUBLESHOOTING_GUIDE.md
@@ -41,7 +41,7 @@ snatch --verify-deps
| Problem | Quick Fix | Command |
|---------|-----------|---------|
-| FFmpeg not found | Install/configure FFmpeg | `python setupfiles/setup_ffmpeg.py` |
+| FFmpeg not found | Install/configure FFmpeg | `python setup_ffmpeg.py` |
| Permission denied | Run as administrator | Right-click → Run as administrator |
| Network timeout | Check internet connection | `ping google.com` |
| Corrupted cache | Clear cache | `snatch --clear-cache` |
@@ -131,7 +131,7 @@ pip cache purge
```bash
# Windows (automated)
-python setupfiles/setup_ffmpeg.py
+python setup_ffmpeg.py
# Windows (manual)
# 1. Download from https://ffmpeg.org/download.html
@@ -496,8 +496,8 @@ snatch info | grep -i ffmpeg
```bash
# Windows
- python setupfiles/setup_ffmpeg.py
-
+ python setup_ffmpeg.py
+
# Linux
sudo apt-get remove ffmpeg
sudo apt-get install ffmpeg
diff --git a/Documentation/USAGE_GUIDE.md b/Documentation/USAGE_GUIDE.md
index 6dfc479..1b64f3a 100644
--- a/Documentation/USAGE_GUIDE.md
+++ b/Documentation/USAGE_GUIDE.md
@@ -17,7 +17,7 @@ snatch textual
# Direct download with best quality
snatch download "https://example.com/video"
-# Download with specific resolution (FIXED in v1.8.0)
+# Download with specific resolution
snatch download "https://example.com/video" --resolution 1080
# Download with video upscaling
diff --git a/README.md b/README.md
index 409515e..a33cf25 100644
--- a/README.md
+++ b/README.md
@@ -17,14 +17,45 @@
-
+
-## What's New in v1.8.0
+## What's New in v2.0.0
-### 🎯 Major New Features & Enhancements
+### Package Restructuring
+
+- **Renamed package**: `modules/` → `snatch/` with proper Python packaging
+- **Modern packaging**: Added `pyproject.toml` with optional dependency groups (`[audio]`, `[p2p]`, `[video]`)
+- **Console entry point**: `snatch` command available globally after `pip install`
+- **Consolidated config**: Merged 4 config modules into 2 clean ones
+- **Fixed Theme imports**: Moved `Theme/` into `snatch/theme/` sub-package with corrected imports
+- **Unified version**: Single source of truth for version `2.0.0` in `constants.py`
+- **Cleaned CI/CD**: Fully updated GitHub Actions pipeline for new package structure
+- **Test infrastructure**: pytest-based test suite with fixtures for config, cache, CLI
+
+### Installation
+
+```bash
+# Basic install
+pip install -e .
+
+# With all optional features
+pip install -e ".[all]"
+
+# Audio processing features only
+pip install -e ".[audio]"
+
+# Development
+pip install -e ".[dev,all]"
+```
+
+---
+
+## Features
+
+### 🎯 Major Features & Enhancements
#### **🎵 Comprehensive Audio Enhancement System**
@@ -89,22 +120,22 @@ snatch download "URL" --resolution 720 --upscale --upscale-factor 4 --replace-or
#### **Complete Package Refactoring & Modularization**
-- **Modular Architecture**: Split monolithic `Snatch.py` into a well-structured package under `modules/`:
- - `cli.py` - Command-line interface and argument parsing
- - `manager.py` - Core download management and orchestration
+- **Modular Architecture**: Well-structured `snatch/` package:
+ - `cli.py` - Command-line interface and argument parsing (Typer)
+ - `manager.py` - Core download management and orchestration (yt-dlp)
- `config.py` - Configuration loading, validation, and management
+ - `config_manager.py` - Advanced configuration editing, profiles, backup/restore
- `audio_processor.py` - Advanced audio enhancement and processing
- `ffmpeg_helper.py` - Video upscaling and FFmpeg processing
- `p2p.py` - Peer-to-peer networking and file sharing
- `cache.py` - Intelligent caching and metadata storage
- - `session.py` - Network session management and optimization
+ - `session.py` - Download session management and resume
- `progress.py` - Enhanced progress tracking and display
- - `utils.py` - Shared utilities and helper functions
- - `plugins.py` - Plugin system and hook management
+ - `common_utils.py` - Shared utilities and helper functions
- `logging_config.py` - Comprehensive logging configuration
- - `constants.py` - Application constants and defaults
+ - `constants.py` - Application constants, version, and defaults
- `metadata.py` - Media information extraction and processing
- - `cyberpunk_ui.py` - Cyberpunk-themed UI components
+ - `theme/` - Modern TUI interfaces (Textual, Rich)
#### **Enhanced Plugin System**
@@ -206,13 +237,6 @@ We've created an extensive documentation ecosystem to support developers and use
- Error codes reference
- Advanced debugging techniques
-#### **🧪 [Integration Testing](./INTEGRATION_TESTING.md)**
-
-- Comprehensive testing strategies
-- Test suite documentation
-- Continuous integration setup
-- Quality assurance procedures
-
### 🛠️ Technical Improvements
#### **Code Architecture Enhancements**
@@ -451,10 +475,10 @@ snatch customize reset
Before installing Snatch, make sure you have:
-1. **Python**: Version 3.8 or higher
+1. **Python**: Version 3.10 or higher
```powershell
- python --version # Should show 3.8 or higher
+ python --version # Should show 3.10 or higher
```
2. **Git**: For cloning the repository
@@ -464,7 +488,7 @@ Before installing Snatch, make sure you have:
```
3. **FFmpeg**: Required for audio/video processing
- - Windows users can run `setupfiles/setup_ffmpeg.py` after installation
+ - Windows users can run `python setup_ffmpeg.py` after installation
- Linux/macOS users can use their package manager
### Step-by-Step Installation
@@ -494,18 +518,18 @@ Before installing Snatch, make sure you have:
3. **Install Dependencies**
```powershell
- # Install required packages
- pip install -r setupfiles/requirements.txt
-
# Install Snatch in development mode
pip install -e .
+
+ # Or with all optional features
+ pip install -e ".[all]"
```
4. **Setup FFmpeg (Windows)**
```powershell
# Automatic FFmpeg setup for Windows
- python setupfiles/setup_ffmpeg.py
+ python setup_ffmpeg.py
```
5. **Verify Installation**
@@ -591,7 +615,7 @@ snatch download "URL" --audio-only --format flac --channels 8
# Download with custom quality
snatch download "URL" --audio-only --format mp3 --quality 320
-# Audio Enhancement Commands (NEW in v1.8.0)
+# Audio Enhancement Commands
# Enhance downloaded audio with AI-powered processing
snatch audio enhance "myfile.mp3" --preset music
snatch audio enhance "podcast.wav" --preset podcast --output "enhanced_podcast.wav"
@@ -749,19 +773,19 @@ When downloading a playlist, Snatch will present options to:
Download multiple URLs at once:
```bash
-python Snatch.py "URL1" "URL2" "URL3"
+snatch download "URL1" "URL2" "URL3"
```
#### 3. Custom Output Directory
```bash
-python Snatch.py "URL" --output-dir "path/to/directory"
+snatch download "URL" --output "path/to/directory"
```
#### 4. Format Specification
```bash
-python Snatch.py "URL" --format-id 137+140 # For advanced users
+snatch download "URL" --format-id 137+140 # For advanced users
```
#### 5. Automatic File Organization
@@ -771,15 +795,11 @@ Snatch can automatically organize your downloaded files based on metadata extrac
Enable organization with:
```bash
-python Snatch.py --organize URL
+snatch download URL --organize
```
Or set it permanently in the configuration:
-```bash
-python setup_ffmpeg.py
-```
-
#### Organization Templates
You can customize how files are organized using templates in the config:
@@ -817,10 +837,10 @@ Snatch now offers enhanced audio conversion options:
```bash
# Download with 7.1 surround sound in Opus format
-python Snatch.py "URL" --audio-only --audio-channels 8
+snatch "URL" --audio-only --audio-channels 8
# Skip interactive prompts (useful for scripting)
-python Snatch.py "URL" --audio-only --non-interactive
+snatch "URL" --audio-only --non-interactive
```
#### 7. Advanced Command-line Options
@@ -829,34 +849,34 @@ Snatch supports several advanced options for more control over your downloads:
```bash
# Resume interrupted downloads
-python Snatch.py "URL" --resume
+snatch "URL" --resume
# Show download statistics after completion
-python Snatch.py "URL" --stats
+snatch "URL" --stats
# Display system resource statistics
-python Snatch.py "URL" --system-stats
+snatch "URL" --system-stats
# Skip using cached media information
-python Snatch.py "URL" --no-cache
+snatch "URL" --no-cache
# Disable automatic retry logic
-python Snatch.py "URL" --no-retry
+snatch "URL" --no-retry
# Limit download speed (e.g., 2M = 2MB/s)
-python Snatch.py "URL" --throttle 2M
+snatch "URL" --throttle 2M
# Use aria2c as the download engine for better performance
-python Snatch.py "URL" --aria2c
+snatch "URL" --aria2c
# Enable detailed logging for troubleshooting
-python Snatch.py "URL" --verbose
+snatch "URL" --verbose
# Test all available formats for best quality (slower)
-python Snatch.py "URL" --test-formats
+snatch "URL" --test-formats
# Use fast format selection (default)
-python Snatch.py "URL" --fast
+snatch "URL" --fast
```
#### 8. Network Speed Testing
@@ -865,10 +885,10 @@ Snatch can automatically test your network speed to optimize download settings:
```bash
# Run a standalone speed test
-python Snatch.py speedtest
+snatch speedtest
# Get detailed speed test results
-python Snatch.py test
+snatch test
```
## 🏗️ Technical Architecture
@@ -921,7 +941,7 @@ Snatch supports over 1000 websites including:
To see the full list of supported sites:
```bash
-python Snatch.py --list-sites
+snatch --list-sites
```
🔍 Troubleshooting
@@ -971,9 +991,10 @@ Contributions are welcome! Feel free to:
### 📦 Core Architecture & Packaging
-- ✅ Modular package structure under `modules/`
-- ✅ `modules/__init__.py` with `__version__` and public API
-- ✅ Basic setup.py configuration
+- ✅ Modular package structure under `snatch/`
+- ✅ `snatch/__init__.py` with `__version__` and public API
+- ✅ Modern `pyproject.toml` packaging with optional dependency groups
+- ✅ Console entry point (`snatch` command)
- ✅ Editable install support
- ⬜ PyPI packaging and distribution
diff --git a/Snatch.bat b/Snatch.bat
index 9050fac..81f6442 100644
--- a/Snatch.bat
+++ b/Snatch.bat
@@ -17,7 +17,7 @@ set "SCRIPT_DIR=%~dp0"
REM Run as module using pythonpath to ensure imports work correctly
set "PYTHONPATH=%SCRIPT_DIR%;%PYTHONPATH%"
-python -m modules.cli %*
+python -m snatch.cli %*
if %ERRORLEVEL% NEQ 0 (
echo.
diff --git a/Theme/__pycache__/__init__.cpython-312.pyc b/Theme/__pycache__/__init__.cpython-312.pyc
deleted file mode 100644
index 1bb3b05272f44cbaa8123ab7faf5d06ad2e7864c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 801
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
BcRK(8
diff --git a/Theme/__pycache__/textual_interface.cpython-312.pyc b/Theme/__pycache__/textual_interface.cpython-312.pyc
deleted file mode 100644
index 532c52c3615619469108b43700106f68253d663f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 47631
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