Ctrl K
ComunidadGuías…

CONTRIBUTING.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Contributing to AniKoKo

Thanks for wanting to contribute. This is a small project with a narrow scope — a
Hindi-dub anime aggregator — so a few ground rules keep it maintainable.

## What this project is

- **Backend** (`backend/`) — FastAPI service. Scrapes anime metadata and stream
  sources from source sites, normalizes them into one API, proxies media.
- **Frontend** (`frontend/`) — Zero-build single-page client. Plain HTML, CSS,
  and vanilla JavaScript. Talks to the backend over JSON.

No frameworks on the frontend. No ORM, no database. Everything is in-memory or
ephemeral. Keep it that way unless there's a strong reason.

## Getting set up

### Backend

```bash
cd backend
python3 -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
python3 main.py
```

### Frontend

```bash
cd frontend
python3 -m http.server 5500
```

Open `http://localhost:5500`.

## Reporting bugs

Open an issue with:

- what you did
- what you expected
- what actually happened
- browser / OS / Python version
- relevant console output (browser DevTools or server terminal)

If a scraper broke because a source site changed its HTML, include the exact
series or episode URL and — if you can — a `curl` output of the page.

## Suggesting features

Open an issue first. Small, focused suggestions are easier to accept than broad
rewrites. If your idea touches more than one subsystem, describe the shape
before writing code.

## Adding a provider

Providers are the main extension point. To add one:

1. **Probe the source site.** Figure out:
   - How to search (`?s=` or a REST endpoint)
   - How to list latest / popular
   - How a series page links to its episodes
   - Where the stream sources live (m3u8? iframe? base64 blob?)

2. **Create `backend/scrapers/<slug>.py`.** Subclass `BaseScraper`:

   ```python
   from scrapers.base import BaseScraper, AnimeCard, Episode, StreamSource
   from config import PROVIDERS

   class MyProviderScraper(BaseScraper):
       slug = "myprovider"
       base = PROVIDERS[slug]["base"]

       async def search(self, query: str) -> list[AnimeCard]: ...
       async def latest(self, page: int = 1) -> list[AnimeCard]: ...
       async def anime_detail(self, url: str) -> dict: ...
       async def episode_sources(self, url: str) -> list[StreamSource]: ...
   ```

3. **Register it** in `backend/scrapers/registry.py` and `backend/config.py`:

   ```python
   # registry.py
   _SCRAPERS = {
       "desidubanime": DesiDubAnimeScraper(),
       "myprovider":   MyProviderScraper(),
   }

   # config.py
   PROVIDERS = {
       "desidubanime": {"name": "DesiDubAnime", "base": "...", "lang": "Hindi"},
       "myprovider":   {"name": "MyProvider",   "base": "...", "lang": "Hindi"},
   }
   ```

4. **Test it** against real URLs before opening a PR. Confirm:
   - `GET /api/latest?provider=myprovider`
   - `GET /api/search?q=some%20title&provider=myprovider`
   - `GET /api/anime?url=<series>&provider=myprovider` → has episodes
   - `GET /api/sources?url=<episode>&provider=myprovider` → has at least one source

## Adding an embed resolver

If a source site uses a third-party player you want to turn into a direct m3u8:

1. Fetch an example embed page and inspect what it returns (raw HTML, JSON,
   base64 blob, packed JS).
2. Create `backend/resolvers/<host>.py` subclassing `BaseResolver`.
3. Register it in `backend/resolvers/__init__.py`.
4. Test with `GET /api/resolve?url=<embed_url>` — it should return
   `{"ok": true, "resolved": "https://.../something.m3u8"}`.

If the resolver needs to execute JavaScript (obfuscated crypto, token derivation),
**don't guess**. Document what you found and leave it as a plain embed — a working
iframe is better than a broken native player.

## Code style

### Python

- 4-space indent
- Type hints on public functions
- Prefer `async`/`await` throughout — this is an I/O-bound service
- No blocking calls in request paths
- Keep imports flat; no lazy imports unless avoiding a cycle

### JavaScript

- 2-space indent
- `const` and `let`, never `var`
- Prefer template literals over concatenation
- Every user-facing string goes through the escape helper before HTML insertion
- No frameworks, no build step — plain DOM APIs

### CSS

- 2-space indent
- CSS custom properties for anything reused
- Mobile-first — the base styles should work at 360px; widen upward
- No `!important` except for the global `[hidden]` rule

## Commit style

Conventional-ish, short, imperative:

```
scraper: handle new episode URL shape on desidubanime
frontend: add keyboard shortcut for search focus
resolver: fix vidmoly m3u8 extraction when folder changes
api: return 404 instead of 500 for unknown provider
```

Prefixes in use: `scraper:`, `resolver:`, `api:`, `frontend:`, `style:`,
`docs:`, `fix:`, `chore:`.

One change per commit. Don't mix a scraper fix with a CSS tweak.

## Pull requests

- Keep PRs focused — one feature or one fix
- Reference the issue it closes: `Closes #12`
- Include a short "what changed" and "how to test" section
- If you touched a scraper, say which URLs you tested against
- Screenshots help for frontend changes

Before submitting:

- `python3 -c "import main"` in `backend/` should not error
- Hard-refresh the frontend and click through the flow you changed
- No new warnings in the browser console

## What we don't want

- **Don't add telemetry.** No analytics, no trackers, no external beacons.
- **Don't add a build step.** The frontend is deliberately three files.
- **Don't gate features behind accounts.** This is a personal tool.
- **Don't rewrite working scrapers "for cleanliness."** Site HTML is fragile;
  the working scraper is the spec.
- **Don't commit `.env`, `venv/`, `__pycache__/`, or anything under `.gitignore`.**
  If a file feels like it shouldn't be tracked, it probably shouldn't.

## Legal note

This project scrapes publicly available pages. Only contribute scrapers for
sources you're comfortable handling personally. Don't add DRM circumvention,
paid-content bypasses, or anything that impersonates a logged-in user.

## Questions

Open an issue. If it's small, ask in the issue itself — no need for a separate
discussion thread.