Contributing
Thank you for your interest in contributing to Sublarr! This document provides guidelines and instructions for contributing to the project.
Table of Contents
Section titled “Table of Contents”- Getting Started
- Development Setup
- Code Style Guidelines
- Testing
- Pull Request Process
- Reporting Issues
- Adding New Features
- License
Getting Started
Section titled “Getting Started”Sublarr is a standalone subtitle manager and translator built with:
- Backend: Python 3.11, Flask, SQLite
- Frontend: React 18, TypeScript, Tailwind CSS
- Deployment: Docker
Before contributing, please:
- Read the Architecture to understand the system design
- Check existing issues and pull requests to avoid duplicates
- Join discussions in GitHub Issues for major changes
Development Setup
Section titled “Development Setup”Prerequisites
Section titled “Prerequisites”Required Software
- Python 3.11 or higher
- Node.js 20 or higher
- npm or yarn
- Git
- SQLite 3
- ffmpeg (for subtitle processing)
- unrar-free (for archive extraction)
Optional Tools
- Docker and Docker Compose (for containerized development)
- Ollama (for local LLM testing)
- Sonarr/Radarr instances (for integration testing)
Clone the Repository
Section titled “Clone the Repository”git clone https://github.com/yourusername/sublarr.gitcd sublarrBackend Setup
Section titled “Backend Setup”-
Create a virtual environment
Terminal window cd backendpython -m venv venvsource venv/bin/activate # On Windows: venv\Scripts\activate -
Install dependencies
Terminal window pip install -r requirements.txtOder verwende das Setup-Script (empfohlen):
Terminal window # Windows (PowerShell)npm run setup:ps1# Linux/Mac (Bash)./scripts/setup-dev.sh -
Configure environment variables
Terminal window cp ../.env.example .env# Edit .env with boot fundamentals only (DB, paths, port, log level).Sublarr is UI-first: only 11 boot fundamentals are env-readable (see Environment Variables). Translation backends, providers, scanner, etc. configure via the UI on first run. For development, the typical
.envis just:SUBLARR_LOG_LEVEL=DEBUGSUBLARR_LOG_FORMAT=text -
Run the development server
Terminal window npm run dev:backendBackend will be available at
http://localhost:5765(DB is initialized automatically on first start)
Frontend Setup
Section titled “Frontend Setup”-
Install dependencies
Terminal window cd frontendnpm install -
Run the development server
Terminal window npm run devFrontend will be available at
http://localhost:3000with hot module reloading. -
The frontend development server proxies API requests to the backend
- Configured in
vite.config.ts - No CORS issues during development
- Configured in
Docker Setup (Alternative)
Section titled “Docker Setup (Alternative)”For testing the full production build locally:
# Build the imagedocker build -t sublarr:dev .
# Run with docker-composedocker-compose up -d
# View logsdocker-compose logs -f sublarrCode Style Guidelines
Section titled “Code Style Guidelines”Python (Backend)
Section titled “Python (Backend)”General Conventions
- Follow PEP 8 style guide
- Use 4 spaces for indentation (no tabs)
- Maximum line length: 100 characters
- Use type hints for function signatures
- Write docstrings for all public functions and classes
Naming Conventions
- Variables and functions:
snake_case - Classes:
PascalCase - Constants:
UPPER_SNAKE_CASE - Private methods:
_leading_underscore
Example
from typing import Optional, Listfrom pydantic import BaseModel
class SubtitleResult(BaseModel): """Represents a subtitle search result from a provider."""
provider: str language: str format: str score: int download_url: str
def is_ass_format(self) -> bool: """Check if this result is in ASS format.""" return self.format.lower() == "ass"
def search_subtitles(query: str, language: str) -> List[SubtitleResult]: """ Search for subtitles across all enabled providers.
Args: query: Search query (series/movie title) language: ISO 639-1 language code
Returns: List of subtitle results sorted by score """ # Implementation here passImport Order
- Standard library imports
- Third-party imports
- Local application imports
- Separate groups with blank lines
Error Handling
- Use specific exception types, not bare
except: - Log errors with appropriate context
- Raise custom exceptions for business logic errors
TypeScript (Frontend)
Section titled “TypeScript (Frontend)”General Conventions
- Use TypeScript strict mode
- Prefer interfaces over type aliases for object shapes
- Use const assertions for literal types
- No
anytype without explicit comment explaining why
Naming Conventions
- Variables and functions:
camelCase - React components:
PascalCase - Constants:
UPPER_SNAKE_CASE - Types and interfaces:
PascalCase
Example
interface SubtitleResult { provider: string; language: string; format: string; score: number; downloadUrl: string;}
const SubtitleCard: React.FC<{ result: SubtitleResult }> = ({ result }) => { const isAssFormat = result.format.toLowerCase() === 'ass';
return ( <div className="subtitle-card"> <span className={isAssFormat ? 'font-bold' : ''}> {result.provider} - {result.language} </span> </div> );};
export default SubtitleCard;React Component Guidelines
- Use functional components with hooks (no class components)
- Extract complex logic into custom hooks
- Prefer composition over prop drilling
- Use React.memo for expensive renders
- Keep components small and focused (< 200 lines)
Import Order
- React and third-party imports
- Local component imports
- Hook imports
- Utility imports
- Type imports (use
import type)
CSS/Tailwind
Section titled “CSS/Tailwind”Conventions
- Use Tailwind utility classes as primary styling method
- Extract repeated patterns into CSS components (not inline styles)
- Use semantic class names for custom CSS
- Follow mobile-first responsive design
- Use CSS variables for theme colors
Example
// Good: Tailwind utilities<div className="flex items-center justify-between p-4 bg-gray-100 rounded-lg">
// Good: Custom component class for repeated pattern<div className="subtitle-card">
// Avoid: Inline styles<div style={{ padding: '16px', backgroundColor: '#f0f0f0' }}>General Guidelines
Section titled “General Guidelines”Code Quality
- No trailing whitespace
- Files must end with a newline
- Use meaningful variable names (no single letters except loop counters)
- Keep functions short and focused (single responsibility)
- Comment complex logic, not obvious code
Git Commit Messages
- Use imperative mood: “Add feature” not “Added feature”
- First line: 50 characters or less, capitalized
- Blank line after first line if body follows
- Body: wrap at 72 characters, explain what and why (not how)
Example
feat: Add SubDL provider support
Implements SubDL provider with ZIP extraction and API key auth.
Closes #123Commit Prefixes
feat:New featurefix:Bug fixdocs:Documentation onlystyle:Formatting, no code changerefactor:Code restructuring, no behavior changetest:Adding or updating testschore:Maintenance tasks, dependencies
Testing
Section titled “Testing”Backend Tests
Section titled “Backend Tests”Running Tests
cd backendpytestpytest -v # Verbose outputpytest tests/test_providers.py # Specific fileWriting Tests
- Place tests in
backend/tests/directory - Name test files with
test_prefix - Use pytest fixtures for common setup
- Keep provider tests isolated (mock HTTP calls)
Example Test
import pytestfrom providers.opensubtitles import OpenSubtitlesProviderfrom providers.base import VideoQuery
@pytest.fixturedef provider(): return OpenSubtitlesProvider(api_key="test_key")
def test_search_returns_results(provider, mocker): # Mock HTTP call mock_response = {"data": [{"id": "123", "attributes": {...}}]} mocker.patch.object(provider.session, "get", return_value=mock_response)
query = VideoQuery( series="Attack on Titan", season=1, episode=1, language="en" ) results = provider.search(query)
assert len(results) > 0 assert results[0].provider == "opensubtitles"Test Coverage
- Backend: Aim for 80%+ coverage (enforced in CI)
- Frontend: Aim for 70%+ coverage (enforced in CI)
- Mock external services (Ollama, Sonarr, providers)
- Test error handling and edge cases
- Don’t test trivial getters/setters
Coverage Reports:
# Backend coveragecd backendpytest --cov=. --cov-report=html# Open htmlcov/index.html in browser
# Frontend coveragecd frontendnpm run test:coverage# Open coverage/index.html in browserFrontend Tests
Section titled “Frontend Tests”Running Tests
cd frontendnpm testnpm run test:coverageWriting Tests
- Use React Testing Library for component tests
- Test user interactions, not implementation details
- Mock API calls with MSW (Mock Service Worker)
Example Test
import { render, screen, fireEvent } from '@testing-library/react';import SubtitleCard from './SubtitleCard';
test('renders subtitle information', () => { const result = { provider: 'OpenSubtitles', language: 'en', format: 'ass', score: 150, downloadUrl: 'http://example.com/sub.ass' };
render(<SubtitleCard result={result} />);
expect(screen.getByText(/OpenSubtitles/)).toBeInTheDocument(); expect(screen.getByText(/en/)).toBeInTheDocument();});Integration Tests
Section titled “Integration Tests”Provider Testing
- Test against real provider APIs (optional)
- Configure provider keys via Settings → Providers in a dev instance
- Use separate API keys for testing
- Run manually before releases
Manual Testing Checklist
Section titled “Manual Testing Checklist”Before submitting a PR with UI changes:
- Test in Chrome, Firefox, Safari
- Test mobile responsive layout
- Test dark mode (if applicable)
- Test loading and error states
- Test WebSocket disconnection/reconnection
Pull Request Process
Section titled “Pull Request Process”Before Submitting
Section titled “Before Submitting”-
Fork the repository and create a branch from
masterTerminal window git checkout -b feature/my-new-feature -
Make your changes following the code style guidelines
-
Test your changes thoroughly
- Run backend tests:
cd backend && pytest - Run frontend tests:
cd frontend && npm test - Test manually in the browser
- Run backend tests:
-
Update documentation if needed
- Update CHANGELOG.md with your changes
- Add/update docstrings and comments
- Update API.md for new endpoints
-
Commit your changes with clear messages
Terminal window git add .git commit -m "feat: Add new provider support"
Submitting the PR
Section titled “Submitting the PR”-
Push to your fork
Terminal window git push origin feature/my-new-feature -
Create a Pull Request on GitHub
- Use a clear, descriptive title
- Fill out the PR template (if provided)
- Link related issues with “Closes #123”
-
PR Description Should Include
- What changes were made
- Why these changes are needed
- How to test the changes
- Screenshots (for UI changes)
- Breaking changes (if any)
PR Review Process
Section titled “PR Review Process”- Automated Checks: CI/CD runs tests automatically
- Code Review: Maintainer reviews code and provides feedback
- Address Feedback: Make requested changes, push to same branch
- Approval: PR approved when ready to merge
- Merge: Maintainer merges PR to master
PR Guidelines
- One feature/fix per PR (keep it focused)
- Keep PRs small (< 500 lines if possible)
- Respond to feedback within a week
- Rebase on master if conflicts arise
- Squash commits before merge (maintainer will do this)
Reporting Issues
Section titled “Reporting Issues”Bug Reports
Section titled “Bug Reports”Before Reporting
- Search existing issues to avoid duplicates
- Test on the latest version
- Gather logs and error messages
Issue Template
## Bug DescriptionClear description of the bug
## Steps to Reproduce1. Go to '...'2. Click on '...'3. See error
## Expected BehaviorWhat should happen
## Actual BehaviorWhat actually happens
## Environment- Sublarr version:- Docker or local:- OS:- Browser (if frontend issue):
## LogsPaste relevant logs hereFeature Requests
Section titled “Feature Requests”Template
## Feature DescriptionWhat feature would you like to see?
## Use CaseWhy is this feature needed? What problem does it solve?
## Proposed SolutionHow would you implement this?
## Alternatives ConsideredWhat other approaches did you think about?Adding New Features
Section titled “Adding New Features”Adding a New Provider
Section titled “Adding a New Provider”See Plugin Development for detailed instructions on adding subtitle providers.
Quick Steps
- Create
providers/newprovider.pyextendingSubtitleProvider - Implement
search(),download(),health_check()methods - Add config settings to
config.py - Register in
providers/__init__.pyPROVIDER_REGISTRY - Add to
.env.examplewithSUBLARR_prefix - Write tests in
tests/test_providers.py - Update PROVIDERS.md documentation
Adding a New API Endpoint
Section titled “Adding a New API Endpoint”Steps
- Add route handler in a Blueprint under
backend/routes/ - Define Pydantic models for request/response validation
- Update
frontend/src/api/client.tswith TypeScript types - Add React Query hook in
frontend/src/hooks/useApi.ts - Document endpoint in
docs/API.md - Write tests for endpoint
Example
from pydantic import BaseModel
class MyRequest(BaseModel): param: str
class MyResponse(BaseModel): result: str
@api_v1.route("/my-endpoint", methods=["POST"])def my_endpoint(): req = MyRequest(**request.json) # Process request return jsonify(MyResponse(result="success").dict())export const callMyEndpoint = async (param: string): Promise<string> => { const response = await apiClient.post('/my-endpoint', { param }); return response.data.result;};Adding Database Tables
Section titled “Adding Database Tables”Steps
- Define schema in
backend/db/(add table to the relevant domain module) - Add version check and migration logic
- Create model class and helper functions
- Update relevant API endpoints
- Add frontend UI if needed
Migration Pattern
def init_db(): # ... existing tables ...
# Check for new table cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='my_new_table'") if not cursor.fetchone(): cursor.execute(""" CREATE TABLE my_new_table ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_at TEXT NOT NULL, ... ) """) conn.commit()License
Section titled “License”By contributing to Sublarr, you agree that your contributions will be licensed under the GNU General Public License v3.0 (GPL-3.0).
All submitted code must be compatible with GPL-3.0. If you include code from other projects:
- Ensure the original license is compatible with GPL-3.0
- Include proper attribution in comments
- Update ACKNOWLEDGMENTS.md if needed
Questions?
Section titled “Questions?”- Open a GitHub Discussion for general questions
- Join the community chat (if available)
- Tag maintainers in issues for urgent matters
Thank you for contributing to Sublarr!