A Docker-based Xtream Codes proxy that filters IPTV content (Live TV, Movies, Series) from multiple sources with per-source dedicated routes, merged playlists, stream proxying, and advanced filtering.
- 📺 Full Xtream Codes API Proxy - Works with any Xtream-compatible player (TiviMate, XCIPTV, etc.)
- 🔗 Multi-Source Support - Configure multiple Xtream providers with dedicated routes per source
- 🎬 Merged Playlist - Combine all sources into a single unified endpoint with virtual IDs
- 🛡️ Stream Proxy - Optionally proxy all streams through the server (hides upstream URLs, better for 4K)
- 🛣️ Per-Source Routing - Each source has its own URL path to avoid ID conflicts
- 🎬 Live TV, Movies & Series - Filter all content types independently
- 🔧 Web-based Configuration - Easy UI to manage sources, settings and filters
- 🎯 Advanced Filtering - Include/exclude filters with multiple match types per source
- 🚫 Exclude All - Start with empty and whitelist only what you want
- 🏷️ Source Prefixing - Optionally prefix group names to identify content origin
- 🔄 Smart Caching - Background refresh with configurable TTL and real-time progress bar
- 💾 Persistent Cache - Survives container restarts
- 🐳 Docker Ready - Easy deployment with docker-compose
docker run -d \
--name xtreamfilter \
-p 8080:5000 \
-v ./data:/data \
--restart unless-stopped \
spanishst/xtreamfilter:latestOr with docker-compose, create a docker-compose.yml:
version: '3'
services:
xtreamfilter:
image: spanishst/xtreamfilter:latest
container_name: xtreamfilter
ports:
- "8080:5000"
volumes:
- ./data:/data
restart: unless-stoppedThen run:
docker-compose up -d- Clone the repository:
git clone https://github.com/spanishst/xtreamfilter.git
cd xtreamfilter- Build and run:
docker-compose up --build -d- Open the web UI:
http://localhost:8080
-
Add your Xtream source(s) in the Sources section:
- Name, Host, Username, Password
- Dedicated Route (required): URL path for this source (e.g.,
myprovider)
-
Configure your filters for each source - Live TV, VOD, and Series
-
Connect your IPTV player using one of the connection URLs shown in the UI
XtreamFilter provides multiple ways to connect your IPTV player:
Combines all sources into a single playlist with virtual IDs that prevent conflicts:
Server: http://YOUR_SERVER_IP:8080/merged
Username: proxy
Password: proxy
- All sources appear in one unified playlist
- Virtual IDs ensure no conflicts between sources (each source gets a 10M ID range)
- Filters from each source are applied
Each source has its own dedicated endpoint:
Filtered endpoint (with your filter rules applied):
Server: http://YOUR_SERVER_IP:8080/<route>
Username: (from your provider)
Password: (from your provider)
Unfiltered endpoint (full catalog from this source):
Server: http://YOUR_SERVER_IP:8080/<route>/full
Username: (from your provider)
Password: (from your provider)
Generate M3U playlists for players that don't support Xtream API:
| URL | Description |
|---|---|
/playlist.m3u |
All sources combined (merged with virtual IDs) |
/<route>/playlist.m3u |
Single source filtered |
/<route>/full/playlist.m3u |
Single source unfiltered |
When enabled, all streams are proxied through the XtreamFilter server instead of redirecting clients directly to upstream servers.
- Privacy: Upstream server URLs are hidden from clients
- 4K Performance: Adaptive buffering optimized for 4K streams (up to 25 Mbps)
- Freeze Prevention: Smart pre-buffering and dynamic buffer sizing
- Single Point of Control: All traffic flows through your server
The proxy includes an intelligent buffering system designed to prevent freezes when upstream servers have issues:
| Feature | Value | Description |
|---|---|---|
| Pre-buffer | 4 MB | Initial buffer filled before streaming starts (~1.3s at 25 Mbps) |
| Min buffer | 1 MB | Minimum adaptive buffer size |
| Max buffer | 16 MB | Maximum buffer (~5s runway at 25 Mbps for 4K) |
| Chunk size | 512 KB | Read chunk size for efficient memory usage |
How it works:
- Pre-buffering: Before sending any data to the client, the proxy fills a 4 MB buffer to ensure smooth playback start
- Throughput monitoring: Measures upstream speed every 2 seconds, keeping 10 samples for stability
- Slowdown detection: If current throughput drops below 60% of average:
- Buffer doubles (e.g., 1 MB → 2 MB)
- On consecutive slowdowns, buffer increases 4x for faster response
- Stable recovery: After 4 stable periods (~20s), buffer reduces by 25% to optimize memory
- Logging: All buffer adjustments are logged at INFO level for monitoring
Example log output:
Pre-buffer filled: 4096.0KB in 0.34s (11993.5 KB/s)
Upstream slowdown detected (2285 KB/s < 11950 KB/s avg), increasing buffer: 1024KB -> 2048KB
Upstream slowdown detected (1781 KB/s < 7832 KB/s avg), increasing buffer: 2048KB -> 8192KB
Upstream stable (2033 KB/s), reducing buffer: 16384KB -> 12288KB
Enable/disable via the web UI in the Connection URLs card, or via API:
# Enable proxy
curl -X POST http://localhost:8080/api/proxy/enable
# Disable proxy
curl -X POST http://localhost:8080/api/proxy/disable
# Check status
curl http://localhost:8080/api/proxy/statusWhen disabled, clients receive a 302 redirect to the upstream URL.
- Open the web UI and go to the Sources section
- Click Add Source to add a new provider
- Enter the source details:
- Name: Friendly name for the source (e.g., "Provider A")
- Host: The Xtream server URL (e.g.,
http://provider.example.com) - Username/Password: Your credentials for this provider
- Prefix (optional): Text to prepend to group names (e.g.,
[ProvA]) - Dedicated Route (required): URL path for this source (e.g.,
providera) - Enabled: Toggle to enable/disable this source
Each source must have a dedicated route. This ensures:
- No ID conflicts between providers (two sources may have series with the same ID)
- Clean separation of content per source
- Correct playback for all content types
Example with two sources:
- Source "Smarters" with route
smarters:- Filtered:
http://YOUR_SERVER_IP:8080/smarters - Unfiltered:
http://YOUR_SERVER_IP:8080/smarters/full
- Filtered:
- Source "Strong" with route
strong:- Filtered:
http://YOUR_SERVER_IP:8080/strong - Unfiltered:
http://YOUR_SERVER_IP:8080/strong/full
- Filtered:
The Prefix option prepends text to all group names from a source, helping identify content origin when viewing in your player.
Example:
- Source with prefix
[US]→ Groups become[US] Sports,[US] Movies, etc.
Each source has its own independent filter configuration:
- Filters are applied per-source
- You can have different include/exclude rules per provider
- Select a source in the filter dropdown to edit its specific filters
- Live TV - Television channels
- VOD - Movies/Films
- Series - TV series
- Include - Only keep matching items (whitelist mode)
- Exclude - Remove matching items (blacklist mode)
| Mode | Description | Example |
|---|---|---|
starts_with |
Matches if name starts with value | FR| matches "FR| TF1" but NOT "ABC FR| News" |
ends_with |
Matches if name ends with value | HD matches "Canal+ HD" |
contains |
Matches if name contains value anywhere | Sports matches "beIN Sports 1" |
not_contains |
Matches if name does NOT contain value | XXX excludes adult content |
exact |
Exact match only (case insensitive by default) | TF1 matches only "TF1" |
regex |
Regular expression pattern | ^FR|.* for regex patterns |
all |
Matches everything | Use with "Exclude All" to start fresh |
The Exclude All option lets you start with a clean slate by excluding everything, then adding include rules to whitelist specific content:
- Click "Exclude All Groups" or "Exclude All Channels"
- Add "Include" filters for the specific content you want to keep
This is useful when you only want a small subset of content from a large catalog.
Include only French content (Live TV):
- Type:
include, Match:starts_with, Value:FR|
Exclude adult content (all categories):
- Type:
exclude, Match:contains, Value:XXX
Include specific streaming services:
- Type:
include, Match:exact, Value:NETFLIX SERIES - Type:
include, Match:exact, Value:DISNEY+ MOVIES
Start fresh and whitelist:
- Add: Type:
exclude, Match:all, Value:* - Add: Type:
include, Match:starts_with, Value:FR|
The proxy caches all data from upstream servers for fast responses:
- Default TTL: 1 hour (3600 seconds)
- Background Refresh: Automatic refresh before cache expires
- Real-time Progress: Visual progress bar with step-by-step updates
- Cross-Worker Sync: Progress is visible even when page is reloaded
- Disk Persistence: Cache survives container restarts
- Per-Source Caching: Each source is cached independently
- Cancel Button: Abort stuck refreshes if needed
The web UI shows:
- Total Live Streams, Movies, and Series counts
- Cache validity status (✅ valid,
⚠️ expired, 🔄 refreshing) - Last refresh time
- Progress bar during refresh with current step and source
| Endpoint | Description |
|---|---|
/merged/player_api.php |
Unified Xtream API with all sources merged |
/merged/live/{user}/{pass}/{id} |
Live stream (virtual ID decoded automatically) |
/merged/movie/{user}/{pass}/{id} |
Movie stream (virtual ID decoded automatically) |
/merged/series/{user}/{pass}/{id} |
Series stream (virtual ID decoded automatically) |
Each source with a dedicated route exposes these endpoints:
| Endpoint | Description |
|---|---|
/<route>/player_api.php |
Filtered Xtream API for this source |
/<route>/full/player_api.php |
Unfiltered Xtream API for this source |
/<route>/live/{user}/{pass}/{id} |
Live stream |
/<route>/movie/{user}/{pass}/{id} |
Movie stream |
/<route>/series/{user}/{pass}/{id} |
Series stream |
/<route>/full/live/{user}/{pass}/{id} |
Live stream (unfiltered path) |
/<route>/full/movie/{user}/{pass}/{id} |
Movie stream (unfiltered path) |
/<route>/full/series/{user}/{pass}/{id} |
Series stream (unfiltered path) |
| Endpoint | Description |
|---|---|
/playlist.m3u |
Merged playlist from all sources (virtual IDs) |
/<route>/playlist.m3u |
Filtered playlist for single source |
/<route>/full/playlist.m3u |
Unfiltered playlist for single source |
| Endpoint | Description |
|---|---|
/player_api.php |
Serves first configured source (or redirects) |
/full/player_api.php |
Serves first source unfiltered |
/live/{user}/{pass}/{id} |
Live stream (auto-routes to correct source) |
/movie/{user}/{pass}/{id} |
Movie stream (auto-routes to correct source) |
/series/{user}/{pass}/{id} |
Series stream (auto-routes to correct source) |
| Endpoint | Description |
|---|---|
/ |
Web configuration UI |
/health |
Health check |
| Endpoint | Method | Description |
|---|---|---|
/api/sources |
GET | List all sources |
/api/sources |
POST | Add a new source |
/api/sources/<id> |
PUT | Update a source |
/api/sources/<id> |
DELETE | Delete a source |
/api/sources/<id>/filters |
GET | Get filters for a source |
/api/sources/<id>/filters |
POST | Update all filters for a source |
| Endpoint | Method | Description |
|---|---|---|
/api/cache/status |
GET | Cache status, stats, and refresh progress |
/api/cache/refresh |
POST | Trigger background cache refresh |
/api/cache/cancel-refresh |
POST | Cancel/clear stuck refresh state |
/api/cache/clear |
POST | Clear all cached data |
| Endpoint | Method | Description |
|---|---|---|
/api/proxy/status |
GET | Check if stream proxy is enabled |
/api/proxy/enable |
POST | Enable stream proxying |
/api/proxy/disable |
POST | Disable stream proxying (use redirects) |
Configuration is stored in data/config.json:
{
"sources": [
{
"id": "abc123",
"name": "My Provider",
"host": "http://provider.example.com",
"username": "myuser",
"password": "mypass",
"enabled": true,
"prefix": "",
"route": "myprovider",
"filters": {
"live": { "groups": [], "channels": [] },
"vod": { "groups": [], "channels": [] },
"series": { "groups": [], "channels": [] }
}
}
],
"content_types": {
"live": true,
"vod": true,
"series": true
},
"options": {
"proxy_streams": false
}
}Cache is stored in data/api_cache.json and automatically rebuilt on startup.
XtreamFilter is built on a modern async Python stack for maximum performance:
| Component | Technology | Purpose |
|---|---|---|
| Web Framework | FastAPI 0.115+ | High-performance async API framework |
| ASGI Server | Uvicorn | Lightning-fast async server |
| HTTP Client | httpx | Async HTTP with connection pooling |
| Runtime | Python 3.13+ | Latest Python with performance improvements |
For 4K streams and large catalogs, the application is optimized with:
- Fully async: All I/O operations are non-blocking for high concurrency
- Connection pooling: Reuses connections to upstream servers (100 max, 20 per host)
- Adaptive buffering: Dynamic buffer sizing from 1 MB to 16 MB based on upstream speed
- Pre-buffering: 4 MB initial buffer before streaming starts
- Smart timeouts: 30s connect, 600s read for large streams
- Retry mechanism: Automatic retry with exponential backoff on upstream failures
- Keep-alive: 65 seconds for connection reuse
- Streaming response: Memory-efficient async generators for large streams
version: '3'
services:
xtreamfilter:
build: .
container_name: xtreamfilter
ports:
- "8080:5000"
volumes:
- ./data:/data
restart: unless-stoppedRun locally without Docker:
cd app
pip install fastapi uvicorn httpx jinja2 aiofiles
uvicorn main:app --host 0.0.0.0 --port 5000 --reloadThe app will be available at http://localhost:5000
| Package | Version | Purpose |
|---|---|---|
| fastapi | ≥0.115.0 | Async web framework |
| uvicorn | ≥0.34.0 | ASGI server |
| httpx | ≥0.28.0 | Async HTTP client |
| jinja2 | ≥3.1.0 | Template engine |
| aiofiles | ≥24.0 | Async file operations |
uv run pytest tests/ -v
