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: Better buffering with 1MB chunks and optimized settings
- Single Point of Control: All traffic flows through your server
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.
For 4K streams and large catalogs, the application is optimized with:
- Gunicorn workers: 4 workers with 8 threads each
- Chunk size: 1MB for stream proxying
- Timeouts: No timeout for long-running streams
- Keep-alive: 65 seconds for connection reuse
- Direct passthrough: Efficient memory usage 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 flask requests gunicorn
python main.pyThe app will be available at http://localhost:5000
uv run pytest tests/ -v
