From 7a5dba7dbf369cb2ca01588eccda03ec7048387e Mon Sep 17 00:00:00 2001 From: ubuntu Date: Sun, 16 Aug 2026 01:28:31 +0000 Subject: [PATCH] Run Xtream imports in background to avoid long-held HTTP connections dropping silently --- server.py | 36 ++++++++++++++++++++++++++++++++---- static/index.html | 42 +++++++++++++++++++++++++++--------------- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/server.py b/server.py index 7176e75..683aa06 100644 --- a/server.py +++ b/server.py @@ -6,6 +6,7 @@ from pymongo import MongoClient from aiohttp import web import socketio +import asyncio import os, glob import time import json @@ -714,6 +715,19 @@ async def import_xtream_codes(request): ) save_playlist_document(playlist_doc) + asyncio.create_task(run_xtream_import( + id_playlist, import_key, collection_channels, + xtream_server, xtream_username, xtream_password, sidClient + )) + + return web.json_response({'status': 'started', 'id_playlist': id_playlist}) + + +async def run_xtream_import(id_playlist, import_key, collection_channels, + xtream_server, xtream_username, xtream_password, sidClient): + # Runs in the background: the HTTP request already returned, so imports that take + # minutes no longer hold a live connection open (which browsers/networks can silently drop). + # Completion is reported to the client over the existing progress_analysis socketio channel. try: await emit_analysis_progress({'percentage': 0.02}, sidClient) @@ -781,15 +795,29 @@ async def import_xtream_codes(request): bulk_insert_channels(collection_channels, pending_channels, inserted_channels) number_channels = collection_channels.count_documents({"id_playlist": id_playlist}) playlists.update_many({'id_playlist': id_playlist}, {"$set": {"num_channels": number_channels}}) - await emit_analysis_progress({'percentage': 1}, sidClient) - return web.json_response({'num_channel': number_channels, 'id_playlist': id_playlist}) + await emit_analysis_progress({ + 'percentage': 1, + 'completed': True, + 'num_channel': number_channels, + 'id_playlist': id_playlist, + }, sidClient) except requests.RequestException as exc: logger.exception("Xtream import failed") - return web.json_response({'error': f'Xtream request failed: {exc}'}, status=502) + await emit_analysis_progress({ + 'percentage': 1, + 'completed': True, + 'error': f'Xtream request failed: {exc}', + 'id_playlist': id_playlist, + }, sidClient) except Exception as exc: logger.exception("Xtream import failed") - return web.json_response({'error': f'Xtream import failed: {exc}'}, status=500) + await emit_analysis_progress({ + 'percentage': 1, + 'completed': True, + 'error': f'Xtream import failed: {exc}', + 'id_playlist': id_playlist, + }, sidClient) finally: active_xtream_imports.pop(import_key, None) diff --git a/static/index.html b/static/index.html index 803ab94..e5f3c5b 100644 --- a/static/index.html +++ b/static/index.html @@ -1557,6 +1557,28 @@ var displayPercent = Math.round(percentage * 100) $("#progress-bar").val(displayPercent); $("#progress-text").text(displayPercent + "%"); + + if (payload['completed']) { + if (payload['id_playlist']) { + $('#id_playlist_to_action').val(payload['id_playlist']); + } + if (payload['error']) { + $('#add_playlist_alert').html(''); + } else { + var num_channel = payload['num_channel'] || 0; + if (num_channel > 0) { + $('#add_playlist_alert').html(''); + update_list_playlist(); + $('#modal_add_playlist').modal('hide'); + } else { + $('#add_playlist_alert').html(''); + } + } + $("#progress-container").hide(); + playlistImportInFlight = false; + playlistImportProgress = 0; + $('#upload-button').prop('disabled', false).text('Save'); + } } @@ -2850,22 +2872,12 @@ xtream_username: xtreamUsername, xtream_password: xtreamPassword }).done(function(data){ - if (data.status === 'already_running') { - $('#id_playlist_to_action').val(data.id_playlist || $('#id_playlist_to_action').val()); - $('#add_playlist_alert').html(''); - playlistImportInFlight = true; - $('#upload-button').prop('disabled', true).text('Importing...'); - return; - } - var num_channel = data.num_channel || 0; + // The import runs in the background on the server; completion is reported + // via the progress_analysis socket event, not this HTTP response. $('#id_playlist_to_action').val(data.id_playlist || $('#id_playlist_to_action').val()); - if (num_channel > 0) { - $('#add_playlist_alert').html(''); - } else { - $('#add_playlist_alert').html(''); - } - update_list_playlist(); - $('#modal_add_playlist').modal('hide'); + $('#add_playlist_alert').html(''); + playlistImportInFlight = true; + $('#upload-button').prop('disabled', true).text('Importing...'); }).fail(function(xhr){ var message = 'Xtream import failed.'; if (xhr.responseJSON && xhr.responseJSON.error) {