- Python 61.8%
- JavaScript 28.9%
- CSS 7.9%
- Dockerfile 0.6%
- HTML 0.5%
- Other 0.3%
| backend/xtream_dl_app | ||
| frontend | ||
| tests | ||
| .dockerignore | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
| requirements.txt | ||
| run.ps1 | ||
xtream-dl
LAN-ready Xtream download web app with a FastAPI backend, a static web client, and a sequential Curl-based download queue.
Features
- Setup/config page for one Xtream connection
- App password for LAN access
- Movies and series grouped by category
- Tile view with poster, metadata, and description when provided by the Xtream provider
- Download movies, full series, full seasons, or individual episodes
- Queue with exactly one active download at a time
- Optional global download speed limit in KB/s
- Per-job and global queue controls: pause, resume, stop, and clear queue
- Persistent metadata cache for movie/series categories and lists
- Automatic cache refresh every 6 hours plus manual refresh in the UI
- Curl transport with fixed User-Agent
okhttp/4.9.3 - Resume via
.partfiles and skip already existing target files - Masked logging without plaintext credentials
Start With Docker
The intended deployment is a single Docker container. The container includes Python, FastAPI, the web client, and the required system curl.
docker compose up -d --build
Open the app in your browser:
http://localhost:8081
On first launch, configure the following in the web UI:
- App password for LAN access
- Xtream base URL
- Username
- Password
- Download directory
- Optional download speed limit in KB/s (
0means unlimited)
When running in Docker, set the download directory in the app config to:
/downloads
Data is persisted on the host through volumes:
./data -> /data Config, password hash, queue state, metadata cache
./Downloads -> /downloads Downloaded movies and series
Show container logs:
docker compose logs -f
The app also writes rotating log files to ./data.
server.log and server.err.log each rotate at 50 MB and keep one backup file.
This limits each log stream to about 100 MB maximum.
Stop the container:
docker compose down
Rebuild after code changes
docker compose up -d --build
Local Development Start
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
.\run.ps1
Open the app in your browser:
http://localhost:8081
For LAN access, the app listens on 0.0.0.0:8081. You may need to allow the port in Windows Firewall.
Data
By default, the app writes state and queue data to:
./data/state.json
This file also contains the metadata cache and the last refresh status. The cache includes:
- Movie categories
- Series categories
- All movie list entries
- All series list entries
Individual movie/series detail data is still loaded live when the detail view is opened. The cache refreshes automatically every 6 hours. A manual refresh can be started from the movie/series view.
The download directory is set in the app config. Without changes, the local default is:
./Downloads
Inside the Docker container, the default is set through the environment to /downloads.
Queue Behavior
The queue downloads only one item at a time.
A paused active download terminates the current Curl process in a controlled way and keeps the .part file.
When resumed, the same job is placed back into the queue and Curl continues via resume.
Stop marks jobs as cancelled.
Clear queue removes all non-running entries from the list.
The queue view includes a separate button to remove only completed and skipped jobs.
The optional download speed limit is global and applies to newly started Curl downloads.
Set it to 0 to download without a limit.
If Curl exits with code 18 because the provider closed the transfer early, the job is automatically retried after a 60 second cooldown.
Retries use the existing .part file and Curl resume.
The automatic retry limit is 10 attempts; after that, the job is marked as failed.
Suspiciously tiny provider Content-Length values are ignored for progress display.
Tests
$env:PYTHONPATH = "$PWD\backend"
python -m unittest discover -s tests
Notes
All Xtream API calls and media downloads run server-side through the system curl.
The User-Agent is intentionally fixed to okhttp/4.9.3 because some panels block Python HTTP clients.