From 485b809a630720bf09f08e7c48f7016b224a26b0 Mon Sep 17 00:00:00 2001 From: mani Date: Fri, 9 Jan 2026 00:12:50 +0100 Subject: [PATCH] Fix Xbox proxy: Add proper header forwarding and streaming support - Add query parameter forwarding (?api_key=... etc.) - Add X-Forwarded-* headers for client IP tracking - Exclude problematic headers (content-length, transfer-encoding, etc.) - Add streaming support for large files (8KB chunks) - Add root route handler - Properly forward all HTTP methods --- docker/jellyfin-xbox-proxy/xbox-filter.py | 29 +++++++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/docker/jellyfin-xbox-proxy/xbox-filter.py b/docker/jellyfin-xbox-proxy/xbox-filter.py index 29fcf6f..647268b 100644 --- a/docker/jellyfin-xbox-proxy/xbox-filter.py +++ b/docker/jellyfin-xbox-proxy/xbox-filter.py @@ -47,6 +47,7 @@ def filter_codecs(profile): return profile +@app.route('/', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']) @app.route('/', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']) def proxy(path): """Proxy all requests to Jellyfin, filter Xbox responses""" @@ -54,9 +55,23 @@ def proxy(path): is_xbox = 'xbox' in user_agent.lower() is_playback_info = 'PlaybackInfo' in path and request.method == 'POST' - # Forward request to Jellyfin + # Build full URL with query parameters url = f"{JELLYFIN_URL}/{path}" - headers = {key: value for key, value in request.headers if key.lower() != 'host'} + if request.query_string: + url += f"?{request.query_string.decode('utf-8')}" + + # Copy headers, exclude problematic ones + headers = {} + excluded = ['host', 'connection', 'content-length', 'content-encoding', 'transfer-encoding'] + for key, value in request.headers: + if key.lower() not in excluded: + headers[key] = value + + # Add X-Forwarded-* headers for proper client tracking + headers['X-Forwarded-For'] = request.remote_addr + headers['X-Forwarded-Proto'] = request.scheme + headers['X-Forwarded-Host'] = request.host + headers['X-Real-IP'] = request.remote_addr resp = requests.request( method=request.method, @@ -64,12 +79,14 @@ def proxy(path): headers=headers, data=request.get_data(), cookies=request.cookies, - allow_redirects=False + allow_redirects=False, + stream=True # Support large file streaming ) # Filter response for Xbox PlaybackInfo requests if is_xbox and is_playback_info and resp.status_code == 200: try: + # Read full response for JSON manipulation data = resp.json() # Filter DeviceProfile in request body (if sent back) @@ -89,9 +106,11 @@ def proxy(path): response.headers['Content-Type'] = 'application/json' except Exception as e: logger.error(f"Error filtering response: {e}") - response = Response(resp.content, status=resp.status_code) + # Fallback to streaming response + response = Response(resp.iter_content(chunk_size=8192), status=resp.status_code) else: - response = Response(resp.content, status=resp.status_code) + # Stream response for large files (videos, etc.) + response = Response(resp.iter_content(chunk_size=8192), status=resp.status_code) # Copy headers from Jellyfin response excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']