# Contributing to Goon ## Development setup Goon backend is Python 3.12+, FastAPI + SQLAlchemy + APScheduler + Postgres. Mobile client is React Native + Expo. ### Backend ```bash # Create virtualenv python -m venv .venv . .venv/bin/activate # or .venv\Scripts\activate on Windows # Install with dev extras pip install -e .[dev] # Bring up postgres (or use docker-compose; see README) # Adjust DATABASE_URL in .env if needed cp .env.example .env # Run migrations alembic upgrade head # Run API uvicorn app.main:app --reload --port 8000 # Run worker (separate terminal) python -m app.scheduler.worker # full scheduler python -m app.scheduler.worker --once --source=tpdb --limit=50 # one-shot ingest ``` ### Mobile ```bash cd mobile npm install npm start # opens Expo dev server ``` ## Tests ```bash pytest # full suite (~70 tests, <5s) pytest tests/test_resolve_*.py -v ruff check app/ mypy app/ # optional, CI-only ``` PRs must pass `pytest` + `ruff check`. Run them locally before pushing. ## Code style - **Formatting**: ruff (config in `pyproject.toml`). Line length 100. - **Type hints**: required on public functions. `from __future__ import annotations` in every module. - **Docstrings**: write the **why**, not the **what**. Reference real bugs/incidents when explaining non-obvious code paths. - **Comments**: only when the code can't speak for itself. Prefer renaming a variable over adding a comment that explains it. - **No dead code, no commented-out code, no TODO without an issue link.** - **Polish or English in comments**: existing code is mostly Polish in comments and English in code (function/class/var names). New code can be either, but be consistent within a file. ## Adding a new tube extractor / scraper If you want Goon to support an additional adult tube site: 1. **Stream extractor** (`app/extractors/tubes/`): given a scene page URL, return a list of `StreamSource` (m3u8/mp4 URLs with quality labels). - Mainstream tubes: try `_ytdlp.extract` (yt-dlp covers ~30 tubes out of the box — just register the sitetag in `app/extractors/__init__.py`). - WordPress-like tubes with embed iframe: register `_embed_iframe.extract`. - Custom player / signed URLs / token rotation: write your own per-tube module (see `hqporner.py`, `eporner.py`, `sxyprn.py` as references). 2. **Discovery scraper** (`app/connectors/direct_scrapers/`): subclass `BaseSearchScraper`, set `sitetag`, `_search_url_template`, `_scene_url_re`. Most aggregator tubes can fit in 10-20 lines (see `xmoviesforyou.py`). 3. **Register** the scraper class in `ALL_DIRECT_SCRAPERS` in `app/connectors/direct_scrapers/__init__.py`. 4. **Test** with one performer name that you know has scenes on that tube: ```bash python -m app.scheduler.worker --once --strategy=performer-driven \ --performers="Some Performer" --sitetags= ``` ## Database migrations Use Alembic: ```bash alembic revision -m "describe change" # new migration alembic upgrade head # apply alembic downgrade -1 # roll back one ``` Every migration must have a working `downgrade()`. We don't ship squashed migrations — full history is the source of truth. ## What we won't merge - **Adult-content moderation features** (auto-tagging by detected acts, content filtering by performer attributes, etc.) — out of scope. - **Hardcoded credentials, API keys, or device IDs** in source — must be env-driven. - **Bypassing tube paywalls / DRM / auth** — Goon only scrapes publicly accessible search pages. - **Telemetry or analytics that report user activity to third parties**. Sentry is opt-in (`SENTRY_DSN` empty by default). - **Public deployment recipes** (e.g. nginx config for an open instance). Goon is self-hosted only — see [DISCLAIMER.md](./DISCLAIMER.md). ## License By contributing, you agree your contributions are licensed under the MIT License (see [LICENSE](./LICENSE)).