Contributing to ListSync
October 13, 2025 · View on GitHub
We welcome contributions to ListSync! This guide will help you get started with development and explain our contribution process.
Table of Contents
- Development Setup
- Project Structure
- Development Workflow
- Code Standards
- Testing
- Documentation
- Pull Request Process
- Release Process
Development Setup
Prerequisites
- Python 3.9+ with Poetry for dependency management
- Node.js 18+ with npm for frontend development
- Docker & Docker Compose for containerized development
- Git for version control
- Chrome/Chromium for Selenium testing
Local Development Environment
-
Clone the Repository
git clone https://github.com/your-repo/list-sync.git cd list-sync -
Backend Setup (Python)
# Install Poetry if not already installed pip install poetry==1.8.3 # Install Python dependencies poetry install # Install additional API dependencies poetry run pip install -r api_requirements.txt # Activate virtual environment poetry shell -
Frontend Setup (Nuxt 3)
cd listsync-nuxt npm install -
Environment Configuration
# Copy example environment file cp envsample.txt .env # Edit .env with your development settings nano .env -
Database Setup
# Database will be automatically initialized on first run python -m list_sync --setup
Development Docker Setup
For containerized development:
# Build development image
docker-compose -f docker-compose.local.yml build
# Start development environment
docker-compose -f docker-compose.local.yml up -d
# View logs
docker-compose -f docker-compose.local.yml logs -f
IDE Configuration
Recommended VS Code Extensions:
- Python
- Pylance
- Black Formatter
- ES7+ React/Redux/React-Native snippets
- TypeScript and JavaScript Language Features
- Tailwind CSS IntelliSense
- Docker
Python Settings (settings.json):
{
"python.defaultInterpreterPath": ".venv/bin/python",
"python.formatting.provider": "black",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.linting.flake8Enabled": true
}
Project Structure
imdb-overseerr/
├── list_sync/ # Core Python application
│ ├── api/ # API integrations
│ │ └── overseerr.py # Overseerr API client
│ ├── providers/ # List provider implementations
│ │ ├── imdb.py # IMDb provider
│ │ ├── trakt.py # Trakt provider
│ │ ├── letterboxd.py # Letterboxd provider
│ │ ├── mdblist.py # MDBList provider
│ │ └── stevenlu.py # Steven Lu provider
│ ├── notifications/ # Notification services
│ │ └── discord.py # Discord notifications
│ ├── ui/ # CLI interface components
│ │ ├── cli.py # CLI implementation
│ │ └── display.py # Display utilities
│ ├── utils/ # Utility modules
│ │ ├── helpers.py # Helper functions
│ │ ├── logger.py # Logging utilities
│ │ └── timezone_utils.py # Timezone handling
│ ├── __init__.py
│ ├── __main__.py # Entry point
│ ├── main.py # Main application logic
│ ├── config.py # Configuration management
│ └── database.py # Database operations
├── listsync-nuxt/ # Nuxt 3 frontend application
│ ├── app/ # Nuxt 3 app directory
│ ├── components/ # Vue components
│ │ ├── dashboard/ # Dashboard components
│ │ ├── history/ # History components
│ │ ├── lists/ # List management components
│ │ ├── settings/ # Settings components
│ │ ├── sync/ # Sync components
│ │ └── ui/ # UI components
│ ├── composables/ # Vue composables
│ │ ├── useApi.ts
│ │ ├── useApiService.ts
│ │ ├── useSyncMonitor.ts
│ │ └── useTheme.ts
│ ├── pages/ # Nuxt pages
│ ├── services/ # Service layer
│ │ └── api.ts
│ ├── stores/ # Pinia stores
│ ├── types/ # TypeScript types
│ ├── public/ # Static assets
│ ├── nuxt.config.ts # Nuxt configuration
│ └── package.json # Node.js dependencies
├── docs/ # Documentation
├── development-files/ # Development utilities
│ ├── analysis/ # Analysis scripts
│ ├── documentation/ # Additional docs
│ ├── scripts/ # Helper scripts
│ └── testing/ # Test files
├── api_server.py # FastAPI backend server
├── start_api.py # API startup script
├── pyproject.toml # Python project configuration (Poetry)
├── requirements.txt # Python dependencies
├── Dockerfile # Multi-stage container build
├── Dockerfile.core # Core-only container
├── docker-compose.yml # Production deployment
├── docker-compose.local.yml # Local development
└── docker-compose.core.yml # Core-only deployment
Key Components
Backend (Python):
- Core Application -
list_sync/- Main sync logic and providers - API Server -
api_server.py- FastAPI REST API - Database - SQLite with automatic migrations
- Providers - Modular list source implementations
Frontend (Nuxt 3):
- Pages Directory - Nuxt 3 file-based routing
- Components - Reusable Vue 3 components with Radix Vue
- Composables - Custom Vue composables for API integration
- Stores - Pinia stores for state management
- Types - TypeScript interfaces and types
Development Workflow
Branch Strategy
- main - Production-ready code
- develop - Integration branch for features
- feature/ - Individual feature branches
- bugfix/ - Bug fix branches
- hotfix/ - Critical production fixes
Feature Development
-
Create Feature Branch
git checkout develop git pull origin develop git checkout -b feature/your-feature-name -
Development Process
- Write code following our standards
- Add tests for new functionality
- Update documentation as needed
- Test thoroughly in development environment
-
Commit Guidelines
# Use conventional commit format git commit -m "feat: add new list provider support" git commit -m "fix: resolve sync timeout issue" git commit -m "docs: update API documentation" -
Push and Create PR
git push origin feature/your-feature-name # Create pull request via GitHub/GitLab interface
Local Testing
Backend Testing:
# Run Python tests
poetry run pytest
# Run with coverage
poetry run pytest --cov=list_sync
# Run specific test file
poetry run pytest tests/test_providers.py
Frontend Testing:
cd listsync-nuxt
# Run Nuxt tests
npm test
# Run with coverage
npm run test:coverage
# Run E2E tests (if configured)
npm run test:e2e
Integration Testing:
# Start full stack locally
docker-compose -f docker-compose.local.yml up
# Run integration tests
npm run test:integration
Code Standards
Python Code Standards
Style Guidelines:
- PEP 8 compliance with Black formatting
- Type hints for all function parameters and returns
- Docstrings for all classes and functions
- Error handling with specific exception types
Example:
from typing import List, Optional, Dict
import logging
def sync_list(
list_id: str,
list_type: str,
limit: Optional[int] = None
) -> Dict[str, int]:
"""
Synchronize items from a specific list.
Args:
list_id: Identifier for the list to sync
list_type: Type of list provider (imdb, trakt, etc.)
limit: Maximum number of items to sync
Returns:
Dictionary containing sync results and statistics
Raises:
ValueError: If list_id or list_type is invalid
ConnectionError: If provider is unreachable
"""
try:
# Implementation here
return {"requested": 10, "failed": 0}
except Exception as e:
logging.error(f"Sync failed for {list_type} list {list_id}: {e}")
raise
Linting Configuration:
# pyproject.toml
[tool.black]
line-length = 88
target-version = ['py39']
[tool.pylint.messages_control]
disable = ["C0114", "C0116"]
[tool.flake8]
max-line-length = 88
extend-ignore = ["E203", "W503"]
TypeScript/Vue Standards
Component Structure:
<!-- components/sync/SyncStatus.vue -->
<script setup lang="ts">
import { computed } from 'vue'
import { cn } from '@/lib/utils'
interface Props {
isActive: boolean
progress?: number
className?: string
}
const props = withDefaults(defineProps<Props>(), {
progress: 0,
className: ''
})
const statusClass = computed(() => cn('sync-status', props.className))
</script>
<template>
<div :class="statusClass">
<!-- Component implementation -->
</div>
</template>
Composable Pattern:
// composables/useSync.ts
import { ref, computed } from 'vue'
interface SyncState {
isLoading: boolean
progress: number
error?: string
}
export function useSync(listId?: string) {
const state = ref<SyncState>({
isLoading: false,
progress: 0,
})
const startSync = async () => {
// Implementation
}
return {
state,
isLoading: computed(() => state.value.isLoading),
progress: computed(() => state.value.progress),
startSync
}
}
Naming Conventions:
- Components - PascalCase (
SyncStatus.vue) - Composables - camelCase with
useprefix (useSync) - Utilities - camelCase (
formatTime) - Constants - UPPER_SNAKE_CASE (
API_BASE_URL) - Stores - camelCase (
useSyncStore)
API Standards
FastAPI Endpoint Structure:
from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
from typing import List, Optional
router = APIRouter(prefix="/api/lists", tags=["lists"])
class ListCreateRequest(BaseModel):
list_type: str
list_id: str
auto_sync: bool = True
class ListResponse(BaseModel):
id: int
list_type: str
list_id: str
list_url: Optional[str]
item_count: int
last_synced: Optional[str]
@router.post("/", response_model=ListResponse)
async def create_list(request: ListCreateRequest):
"""Create a new list configuration."""
try:
# Implementation
return ListResponse(...)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
Testing
Test Structure
tests/
├── unit/ # Unit tests
│ ├── test_providers.py # Provider functionality
│ ├── test_database.py # Database operations
│ └── test_config.py # Configuration management
├── integration/ # Integration tests
│ ├── test_sync_flow.py # End-to-end sync testing
│ └── test_api.py # API endpoint testing
└── fixtures/ # Test data and fixtures
├── sample_lists.json
└── mock_responses.py
Test Examples
Unit Test:
# tests/unit/test_providers.py
import pytest
from unittest.mock import Mock, patch
from list_sync.providers.imdb import IMDbProvider
class TestIMDbProvider:
def test_construct_list_url_chart(self):
provider = IMDbProvider()
url = provider.construct_list_url("top")
assert url == "https://www.imdb.com/chart/top/"
def test_construct_list_url_user_list(self):
provider = IMDbProvider()
url = provider.construct_list_url("ls123456789")
assert url == "https://www.imdb.com/list/ls123456789/"
@patch('list_sync.providers.imdb.requests.get')
def test_fetch_list_items(self, mock_get):
mock_response = Mock()
mock_response.status_code = 200
mock_response.text = "<html>Mock HTML</html>"
mock_get.return_value = mock_response
provider = IMDbProvider()
items = provider.fetch_list_items("top")
assert isinstance(items, list)
mock_get.assert_called_once()
Integration Test:
# tests/integration/test_sync_flow.py
import pytest
from fastapi.testclient import TestClient
from api_server import app
client = TestClient(app)
class TestSyncFlow:
def test_full_sync_workflow(self):
# Create list
response = client.post("/api/lists/", json={
"list_type": "imdb",
"list_id": "top",
"auto_sync": True
})
assert response.status_code == 200
list_data = response.json()
# Trigger sync
response = client.post(f"/api/sync/lists/{list_data['id']}")
assert response.status_code == 200
# Check sync results
response = client.get(f"/api/lists/{list_data['id']}")
assert response.status_code == 200
updated_list = response.json()
assert updated_list["item_count"] > 0
Frontend Testing
Component Test:
// __tests__/components/SyncStatus.test.ts
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import SyncStatus from '@/components/sync/SyncStatus.vue'
describe('SyncStatus', () => {
it('renders inactive state correctly', () => {
const wrapper = mount(SyncStatus, {
props: { isActive: false }
})
expect(wrapper.text()).toContain('inactive')
})
it('shows progress when active', () => {
const wrapper = mount(SyncStatus, {
props: { isActive: true, progress: 50 }
})
expect(wrapper.text()).toContain('50%')
})
})
Documentation
Code Documentation
Python Docstrings:
def sync_items(items: List[Dict], overseerr_api: str) -> SyncResults:
"""
Synchronize media items with Overseerr.
This function processes a list of media items and requests them
via the Overseerr API. It handles duplicate detection, error
recovery, and progress tracking.
Args:
items: List of media items with title, year, and type
overseerr_api: Base URL for Overseerr API
Returns:
SyncResults object containing statistics and failed items
Raises:
ConnectionError: When Overseerr API is unreachable
ValueError: When items list is empty or malformed
Example:
>>> items = [{"title": "Movie", "year": 2023, "type": "movie"}]
>>> results = sync_items(items, "http://overseerr:5055")
>>> print(f"Requested: {results.requested_count}")
"""
TypeScript JSDoc:
/**
* Custom composable for managing sync operations
*
* @param listId - Optional list ID to sync specific list
* @returns Object containing sync state and control functions
*
* @example
* ```vue
* <script setup>
* const { isLoading, startSync, progress } = useSync("imdb-top")
* </script>
*
* <template>
* <button @click="startSync" :disabled="isLoading">
* {{ isLoading ? `${progress}%` : 'Start Sync' }}
* </button>
* </template>
* ```
*/
export function useSync(listId?: string) {
// Implementation
}
Documentation Files
When contributing, update relevant documentation:
- README.md - Overview and quick start
- docs/api.md - API endpoint documentation
- docs/configuration.md - Configuration options
- docs/troubleshooting.md - Common issues and solutions
Pull Request Process
PR Requirements
- Code follows style guidelines
- Tests added for new functionality
- Documentation updated
- All tests passing
- No linting errors
- PR description explains changes
PR Template
## Description
Brief description of the changes made.
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Manual testing completed
## Checklist
- [ ] Code follows the style guidelines
- [ ] Self-review of code completed
- [ ] Documentation updated
- [ ] No new warnings introduced
Review Process
- Automated Checks - CI/CD pipeline runs tests and linting
- Code Review - Maintainer reviews code quality and design
- Testing - Manual testing of functionality
- Approval - PR approved by maintainer
- Merge - PR merged to develop branch
Release Process
Version Management
We use semantic versioning (SemVer):
- MAJOR - Breaking changes
- MINOR - New features (backward compatible)
- PATCH - Bug fixes (backward compatible)
Release Workflow
- Feature Freeze - No new features for release
- Testing - Comprehensive testing of release candidate
- Documentation - Update changelog and documentation
- Tag Release - Create git tag with version number
- Build Images - Build and push Docker images
- Deploy - Deploy to production environment
Contributing Guidelines
- Start Small - Begin with bug fixes or small features
- Discuss First - Open an issue for major changes
- Follow Standards - Adhere to code and documentation standards
- Be Patient - Review process may take time
- Stay Engaged - Respond to review feedback promptly
Getting Help
- GitHub Issues - Report bugs or request features
- Discussions - Ask questions or discuss ideas
- Discord - Join our community chat (if available)
- Email - Contact maintainers directly
Thank you for contributing to ListSync! Your contributions make this project better for everyone.