Run Xtream imports in background to avoid long-held HTTP connections dropping silently

This commit is contained in:
ubuntu
2026-08-16 01:28:31 +00:00
parent e34a71b910
commit 7a5dba7dbf
2 changed files with 59 additions and 19 deletions
+32 -4
View File
@@ -6,6 +6,7 @@
from pymongo import MongoClient from pymongo import MongoClient
from aiohttp import web from aiohttp import web
import socketio import socketio
import asyncio
import os, glob import os, glob
import time import time
import json import json
@@ -714,6 +715,19 @@ async def import_xtream_codes(request):
) )
save_playlist_document(playlist_doc) 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: try:
await emit_analysis_progress({'percentage': 0.02}, sidClient) 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) bulk_insert_channels(collection_channels, pending_channels, inserted_channels)
number_channels = collection_channels.count_documents({"id_playlist": id_playlist}) number_channels = collection_channels.count_documents({"id_playlist": id_playlist})
playlists.update_many({'id_playlist': id_playlist}, {"$set": {"num_channels": number_channels}}) playlists.update_many({'id_playlist': id_playlist}, {"$set": {"num_channels": number_channels}})
await emit_analysis_progress({'percentage': 1}, sidClient) await emit_analysis_progress({
return web.json_response({'num_channel': number_channels, 'id_playlist': id_playlist}) 'percentage': 1,
'completed': True,
'num_channel': number_channels,
'id_playlist': id_playlist,
}, sidClient)
except requests.RequestException as exc: except requests.RequestException as exc:
logger.exception("Xtream import failed") 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: except Exception as exc:
logger.exception("Xtream import failed") 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: finally:
active_xtream_imports.pop(import_key, None) active_xtream_imports.pop(import_key, None)
+27 -15
View File
@@ -1557,6 +1557,28 @@
var displayPercent = Math.round(percentage * 100) var displayPercent = Math.round(percentage * 100)
$("#progress-bar").val(displayPercent); $("#progress-bar").val(displayPercent);
$("#progress-text").text(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('<div class="alert alert-danger" role="alert">' + payload['error'] + '</div>');
} else {
var num_channel = payload['num_channel'] || 0;
if (num_channel > 0) {
$('#add_playlist_alert').html('<div class="alert alert-success" role="alert">Imported items: ' + String(num_channel) + '</div>');
update_list_playlist();
$('#modal_add_playlist').modal('hide');
} else {
$('#add_playlist_alert').html('<div class="alert alert-danger" role="alert">No items returned by Xtream Codes.</div>');
}
}
$("#progress-container").hide();
playlistImportInFlight = false;
playlistImportProgress = 0;
$('#upload-button').prop('disabled', false).text('Save');
}
} }
@@ -2850,22 +2872,12 @@
xtream_username: xtreamUsername, xtream_username: xtreamUsername,
xtream_password: xtreamPassword xtream_password: xtreamPassword
}).done(function(data){ }).done(function(data){
if (data.status === 'already_running') { // The import runs in the background on the server; completion is reported
$('#id_playlist_to_action').val(data.id_playlist || $('#id_playlist_to_action').val()); // via the progress_analysis socket event, not this HTTP response.
$('#add_playlist_alert').html('<div class="alert alert-info" role="alert">' + (data.message || 'Xtream import already running...') + '</div>');
playlistImportInFlight = true;
$('#upload-button').prop('disabled', true).text('Importing...');
return;
}
var num_channel = data.num_channel || 0;
$('#id_playlist_to_action').val(data.id_playlist || $('#id_playlist_to_action').val()); $('#id_playlist_to_action').val(data.id_playlist || $('#id_playlist_to_action').val());
if (num_channel > 0) { $('#add_playlist_alert').html('<div class="alert alert-info" role="alert">' + (data.message || 'Importing Xtream Codes playlist...') + '</div>');
$('#add_playlist_alert').html('<div class="alert alert-success" role="alert">Imported items: ' + String(num_channel) + '</div>'); playlistImportInFlight = true;
} else { $('#upload-button').prop('disabled', true).text('Importing...');
$('#add_playlist_alert').html('<div class="alert alert-danger" role="alert">No items returned by Xtream Codes.</div>');
}
update_list_playlist();
$('#modal_add_playlist').modal('hide');
}).fail(function(xhr){ }).fail(function(xhr){
var message = 'Xtream import failed.'; var message = 'Xtream import failed.';
if (xhr.responseJSON && xhr.responseJSON.error) { if (xhr.responseJSON && xhr.responseJSON.error) {