16 KiB
Contributing to YTPTube
Thank you for your interest in contributing to YTPTube! We welcome contributions from the community to help improve this project.
This document provides guidelines and instructions for contributing to the project.
Table of Contents
- Contributing to YTPTube
Code of Conduct
Please be respectful and constructive in all interactions. We aim to maintain a welcoming and inclusive community.
Getting Started
Prerequisites
Before you begin, ensure you have the following tools installed on your system:
Backend Requirements
- Python 3.13+: YTPTube requires Python 3.13 or higher
- uv: Modern Python package manager and virtual environment tool
Frontend Requirements
- Node.js 18+: Required for the Nuxt/Vue frontend
- pnpm: Fast, disk space efficient package manager
Optional Tools
- Git: Version control
- Docker: For containerized development (optional)
- ffmpeg: Required for video player functionality
Installing Required Package Managers
Installing uv (Python Package Manager)
uv is a fast Python package installer and resolver written in Rust. It's used for managing Python dependencies and virtual environments.
On Linux/macOS:
curl -LsSf https://astral.sh/uv/install.sh | sh
On Windows (PowerShell):
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
Using pip:
pip install uv
Verify installation:
uv --version
For more information, visit: https://github.com/astral-sh/uv
Installing pnpm (Node.js Package Manager)
pnpm is a fast, disk space efficient package manager for Node.js.
Using npm:
npm install -g pnpm
Using Homebrew (macOS):
brew install pnpm
Using Scoop (Windows):
scoop install pnpm
Using standalone script:
curl -fsSL https://get.pnpm.io/install.sh | sh -
Verify installation:
pnpm --version
For more information, visit: https://pnpm.io/installation
Cloning the Repository
Clone the repository to your local machine:
git clone https://github.com/arabcoders/ytptube.git
cd ytptube
Important: Always work on the dev branch, not master/main:
git checkout dev
Development Setup
Backend Setup (Python)
- Navigate to the project root:
cd ytptube
- Install Python dependencies using uv:
uv sync
This will:
- Create a virtual environment (
.venv/) - Install all dependencies from
pyproject.toml - Install development dependencies
- Install development dependencies:
Development dependencies are defined in pyproject.toml under [dependency-groups]:
uv sync --group dev
- Verify installation:
uv run python --version
uv run pytest --version
- Run the backend development server:
uv run app/main.py
The backend API will be available at http://localhost:8081
Frontend Setup (Node.js)
- Navigate to the UI directory:
cd ui
- Install Node.js dependencies using pnpm:
pnpm install
Note
Create a
.envfile in theui/directory based on.env.exampleto configure environment variables. to link the frontend to the backend API.
- Run the frontend development server:
pnpm dev
The frontend will be available at http://localhost:8082 (or the port shown in the terminal)
- Verify the setup:
pnpm run typecheck
pnpm run lint
Development Workflow
Working on a Feature or Bug Fix
- Ensure you're on the
devbranch:
git checkout dev
git pull origin dev
- Create a feature branch:
git checkout -b feature/your-feature-name
# or
git checkout -b fix/bug-description
-
Make your changes
-
Test your changes (see Testing Requirements)
-
Run code quality checks (see Code Quality and Linting)
-
Commit your changes:
git add .
git commit -m "feat: add new feature description"
# or
git commit -m "fix: fix bug description"
- Push to your fork:
git push origin feature/your-feature-name
- Create a Pull Request targeting the
devbranch
Branching Strategy
YTPTube uses a two-branch strategy:
master: Production-ready code. Only maintainers merge to this branch.dev: Development branch. All contributions must target this branch.
Important Rules:
- ✅ DO: Create feature branches from
dev - ✅ DO: Submit pull requests to
dev - ❌ DON'T: Submit pull requests to
master/main - ❌ DON'T: Commit directly to
dev(use feature branches)
Branch Naming Conventions:
feature/description- For new featuresfix/description- For bug fixesrefactor/description- For code refactoringdocs/description- For documentation updatestest/description- For test additions/improvements
Testing Requirements
All code changes must include appropriate tests. This ensures code quality and prevents regressions.
Backend Testing (Python)
Tests are located in app/tests/ and use pytest.
Running tests:
# Run all tests
uv run pytest
# Run specific test file
uv run pytest app/tests/test_download.py
# Run with verbose output
uv run pytest -v
# Run with coverage
uv run pytest --cov=app --cov-report=html
Creating new tests:
- Create a test file in
app/tests/with the prefixtest_ - Use pytest conventions:
import pytest
from app.library.YourModule import YourClass
class TestYourClass:
def test_feature_name(self):
"""Test description."""
# Arrange
instance = YourClass()
# Act
result = instance.method()
# Assert
assert result == expected_value
@pytest.mark.asyncio
async def test_async_feature(self):
"""Test async functionality."""
result = await async_function()
assert result is not None
Testing Singleton Classes:
When testing singleton classes, always reset the singleton instance:
from app.library.Presets import Presets
class TestPresets:
def setup_method(self):
"""Reset singleton before each test."""
Presets._reset_singleton()
def teardown_method(self):
"""Reset singleton after each test."""
Presets._reset_singleton()
Frontend Testing (TypeScript/Vue)
Tests are located in ui/tests/ and use Vitest.
Running tests:
cd ui
# Run all tests
pnpm test
# Run in watch mode
pnpm test:watch
# Run with coverage
pnpm run test --coverage
Creating new tests:
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import YourComponent from '~/components/YourComponent.vue'
describe('YourComponent', () => {
it('renders correctly', () => {
const wrapper = mount(YourComponent)
expect(wrapper.exists()).toBe(true)
})
it('handles user interaction', async () => {
const wrapper = mount(YourComponent)
await wrapper.find('button').trigger('click')
expect(wrapper.emitted('event-name')).toBeTruthy()
})
})
Test Coverage Expectations
- New features: Must include comprehensive tests
- Bug fixes: Must include a test that reproduces the bug
- Refactoring: Existing tests should still pass
- Coverage goal: Aim for >80% code coverage on new code
Code Quality and Linting
Backend (Python)
YTPTube uses Ruff for linting and formatting Python code.
Running Ruff:
# Check for linting issues
uv run ruff check app/
# Auto-fix linting issues
uv run ruff check --fix app/
# Format code
uv run ruff format app/
# Check specific file
uv run ruff check app/library/Download.py
uv run ruff check --fix app/library/Download.py
Configuration:
Ruff configuration is in pyproject.toml under [tool.ruff].
Frontend (TypeScript/Vue)
The frontend uses ESLint for linting and TypeScript for type checking.
Running checks:
cd ui
# Type checking
pnpm run typecheck
# Linting
pnpm run lint
# Auto-fix linting issues
pnpm run lint:fix
Code Quality Checklist
Before committing, ensure:
- ✅ All tests pass (
uv run pytestandpnpm test) - ✅ No linting errors (
ruff checkandpnpm run lint) - ✅ TypeScript compilation succeeds (
pnpm run typecheck) - ✅ Code is formatted properly
- ✅ No unused imports or variables
- ✅ Type hints are present (Python 3.13+)
Pre-commit Hooks
YTPTube uses a custom shell script-based pre-commit hook that runs all checks in parallel for faster execution.
Setting Up Pre-commit Hooks
- Create the pre-commit hook script:
Create a file at .git/hooks/pre-commit with the following content:
#!/bin/sh
set -eu
REPO_ROOT=$(git rev-parse --show-toplevel)
cd "$REPO_ROOT" || exit 1
PIDS_FILE=$(mktemp)
trap 'rm -f "$PIDS_FILE"' EXIT
export CI=1
run_step() {
local label="$1"
shift
echo "🔹 Starting $label..." >&2
(
if ! "$@"; then
echo "❌ [$label] failed. Commit aborted." >&2
exit 1
else
echo "✅ [$label] passed." >&2
fi
) &
echo $! >>"$PIDS_FILE"
}
run_step "Ruff (Python linter)" uv run ruff check app/
run_step "PyTest" uv run pytest app/ -q
run_step "ESLint" pnpm --dir ./ui run lint
run_step "Vitest" pnpm --dir ./ui run test
fail=0
while read -r pid; do
if ! wait "$pid"; then
fail=1
fi
done <"$PIDS_FILE"
if [ "$fail" -eq 1 ]; then
echo "❌ One or more checks failed. Commit aborted." >&2
exit 1
fi
echo "✅ All checks passed. Commit allowed."
exit 0
- Make the hook executable:
chmod +x .git/hooks/pre-commit
- Test the hook:
# Test manually
.git/hooks/pre-commit
# Or make a test commit
git commit --allow-empty -m "test: verify pre-commit hook"
Before Submitting
✅ Checklist:
- Code is on a feature branch created from
dev - All tests pass locally
- Code passes linting and formatting checks
- TypeScript types are properly defined (no
any) without justification - New features include tests
- Documentation is updated (if applicable)
- Commit messages are clear and descriptive
- Pre-commit hooks pass
Pull Request Guidelines
Creating a Pull Request
- Push your branch to GitHub:
git push origin feature/your-feature-name
-
Open a Pull Request on GitHub
-
Target the
devbranch (notmaster) -
Fill out the PR template with:
- Description of changes
- Related issue numbers (if applicable)
- Testing performed
- Screenshots (for UI changes)
- Breaking changes (if any)
PR Review Process
- Automated Checks: CI pipeline runs tests and linting
- Code Review: Maintainers review your code
- Feedback: Address any requested changes
- Approval: Once approved, maintainers will merge
After Your PR is Merged
- Delete your feature branch (optional but recommended)
- Pull the latest
devbranch:
git checkout dev
git pull origin dev
- Celebrate! Thank you for contributing!
Code Style
Python Code Style
- Line length: 120 characters
- Indentation: 4 spaces
- Quotes: Double quotes for strings
- Type hints: Use Python 3.13+ type hints
- Imports: Absolute imports from
app.library - Comparison: Use Yoda comparisons for literals (
"value" == variable)
Example:
from app.library.Download import Download
from app.library.ItemDTO import ItemDTO
async def process_download(item: ItemDTO) -> bool:
"""Process a download item."""
try:
if "pending" == item.status: # Yoda comparison
result = await Download.start(item)
return result.success
except Exception as e:
LOG.error(f"Download failed: {e}")
return False
TypeScript/Vue Code Style
- Type safety: No
anytypes without justification - Explicit imports: Import all Vue functions explicitly
- Component structure: Use
<script setup lang="ts"> - Props/Emits: Always type-define
Example:
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import type { DownloadItem } from '~/types/download'
const props = defineProps<{
items: Array<DownloadItem>
}>()
const emit = defineEmits<{
(e: 'itemSelected', item: DownloadItem): void
}>()
const selectedItem = ref<DownloadItem | null>(null)
</script>
Getting Help
Resources
- API Docs: See API.md for API reference
- FAQ: Check FAQ.md for common questions
- Issues: Browse existing GitHub Issues
Ask Questions
- GitHub Discussions: For general questions and discussions
- GitHub Issues: For bug reports and feature requests
- Pull Requests: For code review and implementation questions
- Discord: Join our community on Discord (link in README)
Reporting Bugs
When reporting bugs, please include:
- Description: Clear description of the issue
- Steps to Reproduce: Detailed steps to reproduce the bug
- Expected Behavior: What you expected to happen
- Actual Behavior: What actually happened
- Environment: OS, Python version, Node version, etc.
- Logs: Relevant error messages or logs
- Screenshots: If applicable
License
By contributing to YTPTube, you agree that your contributions will be licensed under the MIT License.
Thank you for contributing to YTPTube! Your efforts help make this project better for everyone.