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
- οΏ½ Content Browser - Browse and search all your content with cover art and details
- π Custom Categories - Create manual or automatic categories with pattern matching
- π± Telegram Notifications - Get notified when new content matches your categories
- οΏ½π³ 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
The Browse feature provides a rich interface to explore all your IPTV content:
- Visual Grid View - Browse movies and series with cover art thumbnails
- Search & Filter - Search by name, filter by group/category
- Content Details - View descriptions, ratings, cast, and episode lists
- Quick Actions - Add items to custom categories directly from the browser
- Pagination - Efficiently browse large catalogs
Access the browser at /browse or click the π Browse Content button on the main page.
Create your own categories to organize content across all sources:
Manually curate collections by adding items one by one:
- Create a category with mode Manual
- Browse content and click the β button to add items
- Items remain in the category until you remove them
Automatically collect content based on search patterns:
- Create a category with mode Automatic
- Define search patterns (contains, starts with, regex, etc.)
- Choose pattern logic: Match ANY (OR) or Match ALL (AND)
- Optionally filter by Recently Added (last 7, 14, 30 days, etc.)
- Enable Apply source filter rules to respect your source filters
Pattern Match Types:
| Type | Description | Example |
|---|---|---|
contains |
Name contains value | Marvel matches "Marvel Avengers" |
starts_with |
Name starts with value | Star Wars matches "Star Wars: Episode IV" |
ends_with |
Name ends with value | 4K matches "Avatar 4K" |
exact |
Exact match | Inception matches only "Inception" |
regex |
Regular expression | ^The.*\(2024\)$ for regex patterns |
not_contains |
Name does NOT contain | XXX excludes adult content |
Example: Create a "New 4K Movies" category:
- Mode: Automatic
- Content Types: VOD only
- Pattern:
containsβ4K - Recently Added: Last 7 days
- Notify Telegram: β
Automatic categories refresh when the cache refreshes, detecting new content automatically.
Get notified on Telegram when automatic categories find new content:
-
Create a Telegram Bot:
- Message @BotFather on Telegram
- Send
/newbotand follow the prompts - Copy the Bot Token (e.g.,
123456789:ABCdefGHI...)
-
Get your Chat ID:
- Message @userinfobot on Telegram
- It will reply with your Chat ID (e.g.,
1234567890) - For groups/channels, add the bot and get the group ID
-
Configure in XtreamFilter:
- Go to the main page and click βοΈ Configure next to "Telegram Notifications"
- Enter your Bot Token and Chat ID
- Enable notifications and click πΎ Save
- Click π Test to verify it works
-
Enable per category:
- Edit an automatic category
- Check π± Send Telegram notification when new items are found
| Scenario | Format |
|---|---|
| Single item with cover | Photo message with movie poster |
| Multiple items with covers | Album (up to 10 photos) with caption |
| Items without covers | Text message with bullet list |
| Endpoint | Method | Description |
|---|---|---|
/api/config/telegram |
GET | Get Telegram settings (token masked) |
/api/config/telegram |
POST | Update Telegram settings |
/api/config/telegram/test |
POST | Send a test notification |
/api/config/telegram/test-diff |
POST | Send a test category update notification |
| 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/categories |
GET | List all categories |
/api/categories |
POST | Create a new category |
/api/categories/<id> |
PUT | Update a category |
/api/categories/<id> |
DELETE | Delete a category |
/api/categories/<id>/items |
POST | Add item to manual category |
/api/categories/<id>/items |
DELETE | Remove item from manual category |
/api/categories/refresh |
POST | Refresh all automatic categories |
| Endpoint | Method | Description |
|---|---|---|
/browse |
GET | Browse content UI |
/api/browse |
GET | Browse content (paginated, with filters) |
| 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
