Bundle server (and mcp entry) into a single JS file with deps inlined, minify UI into one HTML, and ship only dist/ in the runtime image.
156 lines
5.6 KiB
Markdown
156 lines
5.6 KiB
Markdown
# webshare-api
|
|
|
|
Node.js/Express proxy that exposes [Webshare.cz](https://webshare.cz) as protocols *arr apps understand, and as an [MCP](https://modelcontextprotocol.io) server for AI agents.
|
|
|
|
Official Webshare HTTP API documentation: [https://webshare.cz/apidoc/](https://webshare.cz/apidoc/)
|
|
|
|
| Role | Endpoints | Purpose |
|
|
|------|-----------|---------|
|
|
| **Torznab indexer** | `GET /api?t=caps\|search\|tvsearch\|movie` | Search → Torznab RSS/XML |
|
|
| **Fake torrent** | `GET /download/:ident` | Minimal `.torrent` whose webseed points at `/stream/:ident` |
|
|
| **Stream** | `GET /stream/:ident` | Fresh Webshare `file_link` + HTTP 302 to CDN |
|
|
| **Fake qBittorrent** | `/api/v2/*` | Download client API; streams the HTTPS file to disk |
|
|
| **Debug** | `GET /resolve/:ident` | JSON with the resolved CDN URL |
|
|
| **Web UI** | `GET /` | Two-tab UI: search files and add to queue; queue view with progress, speed, and cancel |
|
|
| **MCP server** | `POST /mcp` (Streamable HTTP) | Tools for AI agents: search, resolve links, login check |
|
|
|
|
Webshare download links expire after roughly ten minutes, so the torrent never embeds a CDN URL—only a path on this service that resolves a fresh link at fetch time.
|
|
|
|
## Layout
|
|
|
|
```
|
|
src/ # source (CommonJS modules)
|
|
ui/ # Web UI source (index.html, styles.css, scripts.js)
|
|
scripts/build.mjs # esbuild: single server.js + minified single-file UI
|
|
dist/ # build output (gitignored; produced by npm run build / Docker)
|
|
server.js # one file: app + all npm dependencies
|
|
mcp.js # optional MCP-only entry
|
|
ui/index.html # CSS+JS inlined
|
|
__tests__/
|
|
compose.yaml
|
|
Dockerfile # multi-stage: build with esbuild, runtime = Node + dist only
|
|
```
|
|
|
|
## Build (smaller image / single JS)
|
|
|
|
```bash
|
|
npm install
|
|
npm run build # → dist/server.js (~2 MB, all deps bundled) + dist/ui/index.html
|
|
npm start # node dist/server.js
|
|
npm run start:dev # unbundled src/ for local hacking
|
|
```
|
|
|
|
**esbuild** bundles the server (and optional `mcp` entry) into a **single `.js` file** with
|
|
dependencies inlined — no `node_modules` needed at runtime. The Web UI is minified into
|
|
**one** `dist/ui/index.html` (CSS and JS inlined).
|
|
|
|
Docker uses a multi-stage build: final image copies only `dist/`, not source or
|
|
`node_modules`, which keeps the image much smaller than `npm ci --omit=dev`.
|
|
|
|
## Configuration
|
|
|
|
Environment variables (required unless noted):
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `WEBSHARE_USERNAME` | — | Webshare username or email |
|
|
| `WEBSHARE_PASSWORD` | — | Webshare password (plain; hashed client-side) |
|
|
| `PORT` | `3001` | HTTP listen port |
|
|
| `BASE_URL` | `http://localhost:$PORT` | Public base URL Sonarr must reach (use the Compose service hostname when linking containers) |
|
|
| `DOWNLOAD_PATH` | `/downloads/webshare` | Where the fake qBittorrent writes files |
|
|
| `MEDIA_ROOT` | `/data` | Media root (used for path reporting) |
|
|
| `MAX_CONCURRENT_DOWNLOADS` | `2` | Parallel stream downloads |
|
|
| `SONARR_URL` | `http://sonarr:8989` | Optional; Sonarr base URL |
|
|
| `SONARR_API_KEY` | _(empty)_ | Optional; enables periodic missing-episode search |
|
|
| `SONARR_SEARCH_INTERVAL_HOURS` | `2` | Interval for that search |
|
|
| `DOWNLOAD_HOST_PATH` | `./data/webshare` | Host path mounted at `DOWNLOAD_PATH` in Compose |
|
|
| `MCP_ENABLED` | `true` | Set to `0`/`false` to disable the MCP HTTP endpoint |
|
|
| `MCP_PATH` | `/mcp` | HTTP path for the Streamable HTTP MCP endpoint |
|
|
|
|
Copy `.env.example` and fill in credentials. Do not commit a filled `.env`.
|
|
|
|
## Run with Compose
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
# set WEBSHARE_USERNAME and WEBSHARE_PASSWORD
|
|
docker compose up -d --build
|
|
```
|
|
|
|
See `compose.yaml`. Override ports, `BASE_URL`, Sonarr settings, and volume mounts via `.env` as needed.
|
|
|
|
## Run without Compose
|
|
|
|
```bash
|
|
npm install
|
|
npm run build
|
|
export WEBSHARE_USERNAME=...
|
|
export WEBSHARE_PASSWORD=...
|
|
npm start
|
|
# or: npm run start:dev # no build step, uses src/
|
|
```
|
|
|
|
## Sonarr / Radarr
|
|
|
|
1. **Indexer** (Torznab): `http://<host>:3001/api`
|
|
2. **Download client** (qBittorrent): host/port of this service; any username/password (login accepts all).
|
|
|
|
When both run on the same Compose network, use the service name, e.g. `http://webshare-api:3001`.
|
|
|
|
Sonarr hands `.torrent` files to the fake qBittorrent API. This service extracts the webseed, streams the file into `DOWNLOAD_PATH`, and reports progress via `/api/v2/torrents/*`.
|
|
|
|
## MCP (AI agents)
|
|
|
|
Exposes Webshare to MCP-compatible clients over **Streamable HTTP** (not stdio).
|
|
|
|
With `npm start` (or Compose), MCP is available on the same process/port:
|
|
|
|
```
|
|
POST http://<host>:3001/mcp
|
|
```
|
|
|
|
Optional MCP-only process (no Torznab/qBittorrent):
|
|
|
|
```bash
|
|
export WEBSHARE_USERNAME=...
|
|
export WEBSHARE_PASSWORD=...
|
|
npm run mcp # listens on PORT, serves POST /mcp
|
|
```
|
|
|
|
### Tools
|
|
|
|
| Tool | Description |
|
|
|------|-------------|
|
|
| `webshare_search` | Search files (`query`, optional `limit` / `offset` / `sort` / `category`) |
|
|
| `webshare_get_link` | Resolve a temporary CDN URL for a file `ident` |
|
|
| `webshare_login_check` | Verify credentials against the Webshare API |
|
|
|
|
### Client configuration example
|
|
|
|
**HTTP / Streamable HTTP** (preferred):
|
|
|
|
```toml
|
|
[mcp_servers.webshare]
|
|
url = "http://127.0.0.1:3001/mcp"
|
|
```
|
|
|
|
```bash
|
|
grok mcp add --transport http webshare http://127.0.0.1:3001/mcp
|
|
```
|
|
|
|
Credentials stay on the server process (`WEBSHARE_*` env); clients only need the URL.
|
|
|
|
The transport is **stateless** Streamable HTTP (POST only).
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
npm test
|
|
```
|
|
|
|
## License
|
|
|
|
[MIT](LICENSE)
|
|
|
|
You need a valid Webshare account. This project only speaks their documented HTTP API ([apidoc](https://webshare.cz/apidoc/)). Use at your own risk.
|