Add better display in the m3u table for the user to see what we are doing.

This commit is contained in:
SergeantPanda 2025-05-04 13:26:29 -05:00
parent 4619d09efe
commit d26dd5f6b3
2 changed files with 175 additions and 16 deletions

View file

@ -530,6 +530,7 @@ def refresh_single_m3u_account(account_id):
total_batches = len(batches)
completed_batches = 0
streams_processed = 0 # Track total streams processed
logger.debug(f"Dispatched {len(batches)} parallel tasks for account_id={account_id}.")
# result = task_group.apply_async()
@ -542,18 +543,47 @@ def refresh_single_m3u_account(account_id):
if async_result.ready() and async_result.id not in completed_task_ids: # If the task has completed and we haven't counted it
task_result = async_result.result # The result of the task
logger.debug(f"Task completed with result: {task_result}")
# Extract stream counts from result string if available
if isinstance(task_result, str):
try:
created_match = re.search(r"(\d+) created", task_result)
updated_match = re.search(r"(\d+) updated", task_result)
if created_match and updated_match:
created_count = int(created_match.group(1))
updated_count = int(updated_match.group(1))
streams_processed += created_count + updated_count
except (AttributeError, ValueError):
pass
completed_batches += 1
completed_task_ids.add(async_result.id) # Mark this task as processed
# Calculate progress
progress = int((completed_batches / total_batches) * 100)
# Calculate elapsed time and estimated remaining time
current_elapsed = time.time() - start_time
if progress > 0:
estimated_total = (current_elapsed / progress) * 100
time_remaining = max(0, estimated_total - current_elapsed)
else:
time_remaining = 0
# Send progress update via Channels
# Don't send 100% because we want to clean up after
if progress == 100:
progress = 99
send_m3u_update(account_id, "parsing", progress)
send_m3u_update(
account_id,
"parsing",
progress,
elapsed_time=current_elapsed,
time_remaining=time_remaining,
streams_processed=streams_processed
)
# Optionally remove completed task from the group to prevent processing it again
result.remove(async_result)
@ -567,7 +597,16 @@ def refresh_single_m3u_account(account_id):
# Now run cleanup
cleanup_streams(account_id)
send_m3u_update(account_id, "parsing", 100)
# Send final update with complete metrics
elapsed_time = time.time() - start_time
send_m3u_update(
account_id,
"parsing",
100,
elapsed_time=elapsed_time,
time_remaining=0,
streams_processed=streams_processed
)
end_time = time.time()

View file

@ -43,17 +43,18 @@ const M3UTable = () => {
return buildDownloadingStats(data);
case 'processing_groups':
return 'Processing groups...';
return buildGroupProcessingStats(data);
case 'parsing':
return buildParsingStats(data);
default:
return buildParsingStats(data);
return data.status === 'error' ? buildErrorStats(data) : `${data.action || 'Processing'}...`;
}
};
const buildDownloadingStats = (data) => {
if (data.progress == 100) {
// fetchChannelGroups();
// fetchPlaylists();
return 'Download complete!';
}
@ -61,21 +62,89 @@ const M3UTable = () => {
return 'Downloading...';
}
// Format time remaining in minutes:seconds
const timeRemaining = data.time_remaining ?
`${Math.floor(data.time_remaining / 60)}:${String(Math.floor(data.time_remaining % 60)).padStart(2, '0')}` :
'calculating...';
// Format speed with appropriate unit (KB/s or MB/s)
const speed = data.speed >= 1024 ?
`${(data.speed / 1024).toFixed(2)} MB/s` :
`${Math.round(data.speed)} KB/s`;
return (
<Box>
<Text size="xs">Downloading: {parseInt(data.progress)}%</Text>
{/* <Text size="xs">Speed: {parseInt(data.speed)} KB/s</Text>
<Text size="xs">Time Remaining: {parseInt(data.time_remaining)}</Text> */}
<Flex direction="column" gap="xs">
<Flex justify="space-between" align="center">
<Text size="xs" fw={500}>Downloading:</Text>
<Text size="xs">{parseInt(data.progress)}%</Text>
</Flex>
<Flex justify="space-between" align="center">
<Text size="xs" fw={500}>Speed:</Text>
<Text size="xs">{speed}</Text>
</Flex>
<Flex justify="space-between" align="center">
<Text size="xs" fw={500}>Time left:</Text>
<Text size="xs">{timeRemaining}</Text>
</Flex>
</Flex>
</Box>
);
};
const buildGroupProcessingStats = (data) => {
if (data.progress == 100) {
return 'Groups processed!';
}
if (data.progress == 0) {
return 'Processing groups...';
}
// Format time displays if available
const elapsedTime = data.elapsed_time ?
`${Math.floor(data.elapsed_time / 60)}:${String(Math.floor(data.elapsed_time % 60)).padStart(2, '0')}` :
null;
return (
<Box>
<Flex direction="column" gap="xs">
<Flex justify="space-between" align="center">
<Text size="xs" fw={500}>Processing groups:</Text>
<Text size="xs">{parseInt(data.progress)}%</Text>
</Flex>
{elapsedTime && (
<Flex justify="space-between" align="center">
<Text size="xs" fw={500}>Elapsed:</Text>
<Text size="xs">{elapsedTime}</Text>
</Flex>
)}
{data.groups_processed && (
<Flex justify="space-between" align="center">
<Text size="xs" fw={500}>Groups:</Text>
<Text size="xs">{data.groups_processed}</Text>
</Flex>
)}
</Flex>
</Box>
);
};
const buildErrorStats = (data) => {
return (
<Box>
<Flex direction="column" gap="xs">
<Flex align="center">
<Text size="xs" fw={500} color="red">Error:</Text>
</Flex>
<Text size="xs" color="red">{data.error || "Unknown error occurred"}</Text>
</Flex>
</Box>
);
};
const buildParsingStats = (data) => {
if (data.progress == 100) {
// fetchStreams();
// fetchChannelGroups();
// fetchEPGData();
// fetchPlaylists();
return 'Parsing complete!';
}
@ -83,7 +152,43 @@ const M3UTable = () => {
return 'Parsing...';
}
return `Parsing: ${data.progress}%`;
// Format time displays
const timeRemaining = data.time_remaining ?
`${Math.floor(data.time_remaining / 60)}:${String(Math.floor(data.time_remaining % 60)).padStart(2, '0')}` :
'calculating...';
const elapsedTime = data.elapsed_time ?
`${Math.floor(data.elapsed_time / 60)}:${String(Math.floor(data.elapsed_time % 60)).padStart(2, '0')}` :
'0:00';
return (
<Box>
<Flex direction="column" gap="xs">
<Flex justify="space-between" align="center">
<Text size="xs" fw={500}>Parsing:</Text>
<Text size="xs">{parseInt(data.progress)}%</Text>
</Flex>
{data.elapsed_time && (
<Flex justify="space-between" align="center">
<Text size="xs" fw={500}>Elapsed:</Text>
<Text size="xs">{elapsedTime}</Text>
</Flex>
)}
{data.time_remaining && (
<Flex justify="space-between" align="center">
<Text size="xs" fw={500}>Remaining:</Text>
<Text size="xs">{timeRemaining}</Text>
</Flex>
)}
{data.streams_processed && (
<Flex justify="space-between" align="center">
<Text size="xs" fw={500}>Streams:</Text>
<Text size="xs">{data.streams_processed}</Text>
</Flex>
)}
</Flex>
</Box>
);
};
const toggleActive = async (playlist) => {
@ -143,8 +248,23 @@ const M3UTable = () => {
return generateStatusString(refreshProgress[row.id]);
},
size: 150,
minSize: 80,
size: 180,
minSize: 150,
Cell: ({ cell }) => {
const value = cell.getValue();
// Return a visual component for the status
if (typeof value === 'object') {
return value;
}
// For simple string statuses
return (
<Text size="xs">
{value}
</Text>
);
},
},
{
header: 'Active',