Fixed queue items counter

This commit is contained in:
Xoconoch
2025-08-02 10:04:58 -06:00
parent 5cb442244c
commit 54e6592de8
4 changed files with 94 additions and 16 deletions

View File

@@ -27,8 +27,31 @@ ACTIVE_TASK_STATES = {
ProgressState.TRACK_PROGRESS, # "track_progress" - real-time track progress
ProgressState.REAL_TIME, # "real_time" - real-time download progress
ProgressState.RETRYING, # "retrying" - task is retrying after error
"real-time", # "real-time" - real-time download progress (hyphenated version)
}
def get_task_status_from_last_status(last_status):
"""
Extract the task status from last_status, checking both possible locations.
Args:
last_status: The last status dict from get_last_task_status()
Returns:
str: The task status string
"""
if not last_status:
return "unknown"
# Check for status in nested status_info (for real-time downloads)
status_info = last_status.get("status_info", {})
if isinstance(status_info, dict) and "status" in status_info:
return status_info["status"]
# Fall back to top-level status (for other task types)
return last_status.get("status", "unknown")
def is_task_active(task_status):
"""
Determine if a task is currently active (working/processing).
@@ -138,7 +161,7 @@ def _build_task_response(task_info, last_status, task_id, current_time):
# Determine last_line content
if last_status and "raw_callback" in last_status:
last_line_content = last_status["raw_callback"]
elif last_status and last_status.get("status") == "error":
elif last_status and get_task_status_from_last_status(last_status) == "error":
last_line_content = _build_error_callback_object(last_status)
else:
last_line_content = last_status
@@ -191,7 +214,7 @@ def get_paginated_tasks(page=1, limit=20, active_only=False):
continue
last_status = get_last_task_status(task_id)
task_status = last_status.get("status") if last_status else "unknown"
task_status = get_task_status_from_last_status(last_status)
is_active_task = is_task_active(task_status)
# Categorize tasks by status using ProgressState constants
@@ -321,7 +344,7 @@ def get_task_details(task_id):
# Determine last_line content
if last_status and "raw_callback" in last_status:
last_line_content = last_status["raw_callback"]
elif last_status and last_status.get("status") == "error":
elif last_status and get_task_status_from_last_status(last_status) == "error":
last_line_content = _build_error_callback_object(last_status)
else:
# Fallback for non-error, no raw_callback, or if last_status is None
@@ -405,7 +428,7 @@ def list_tasks():
continue
last_status = get_last_task_status(task_id)
task_status = last_status.get("status") if last_status else "unknown"
task_status = get_task_status_from_last_status(last_status)
is_active_task = is_task_active(task_status)
# Categorize tasks by status using ProgressState constants
@@ -621,7 +644,7 @@ def get_task_updates():
task_timestamp = last_status.get("timestamp") if last_status else task_info.get("created_at", 0)
# Determine task status and categorize
task_status = last_status.get("status") if last_status else "unknown"
task_status = get_task_status_from_last_status(last_status)
is_active_task = is_task_active(task_status)
# Categorize tasks by status using ProgressState constants

View File

@@ -510,7 +510,13 @@ export const Queue = () => {
if (!context) return null;
if (!isVisible) return null;
const hasActive = items.some((item) => !isTerminalStatus(item.status));
const hasActive = items.some((item) => {
// Check for status in both possible locations (nested status_info for real-time, or top-level for others)
const actualStatus = (item.last_line?.status_info?.status as QueueStatus) ||
(item.last_line?.status as QueueStatus) ||
item.status;
return isActiveTaskStatus(actualStatus);
});
const hasFinished = items.some((item) => isTerminalStatus(item.status));
// Handle mobile swipe-to-dismiss

View File

@@ -55,9 +55,15 @@ export function QueueProvider({ children }: { children: ReactNode }) {
const [totalTasks, setTotalTasks] = useState(0);
const pageSize = 20; // Number of non-active tasks per page
// Calculate active downloads count
// Calculate active downloads count (active + queued)
const activeCount = useMemo(() => {
return items.filter(item => !isTerminalStatus(item.status)).length;
return items.filter(item => {
// Check for status in both possible locations (nested status_info for real-time, or top-level for others)
const actualStatus = (item.last_line?.status_info?.status as QueueStatus) ||
(item.last_line?.status as QueueStatus) ||
item.status;
return isActiveTaskStatus(actualStatus);
}).length;
}, [items]);
const stopPolling = useCallback((internalId: string) => {
@@ -156,15 +162,27 @@ export function QueueProvider({ children }: { children: ReactNode }) {
total_tasks: number;
active_tasks: number;
updated_count: number;
task_counts?: {
active: number;
queued: number;
retrying: number;
completed: number;
error: number;
cancelled: number;
skipped: number;
};
}>(`/prgs/updates?since=${lastUpdateTimestamp.current}&active_only=true`);
const { tasks: updatedTasks, current_timestamp, total_tasks } = response.data;
const { tasks: updatedTasks, current_timestamp, total_tasks, task_counts } = response.data;
// Update the last timestamp for next poll
lastUpdateTimestamp.current = current_timestamp;
// Update total tasks count
setTotalTasks(total_tasks || 0);
// Update total tasks count - use active + queued if task_counts available
const calculatedTotal = task_counts ?
(task_counts.active + task_counts.queued) :
(total_tasks || 0);
setTotalTasks(calculatedTotal);
if (updatedTasks.length > 0) {
console.log(`Smart polling: ${updatedTasks.length} tasks updated (${response.data.active_tasks} active) out of ${response.data.total_tasks} total`);
@@ -236,6 +254,15 @@ export function QueueProvider({ children }: { children: ReactNode }) {
pagination: {
has_more: boolean;
};
task_counts?: {
active: number;
queued: number;
retrying: number;
completed: number;
error: number;
cancelled: number;
skipped: number;
};
}>(`/prgs/list?page=${nextPage}&limit=${pageSize}`);
const { tasks: newTasks, pagination } = response.data;
@@ -302,9 +329,18 @@ export function QueueProvider({ children }: { children: ReactNode }) {
};
total_tasks: number;
timestamp: number;
task_counts?: {
active: number;
queued: number;
retrying: number;
completed: number;
error: number;
cancelled: number;
skipped: number;
};
}>(`/prgs/list?page=1&limit=${pageSize}`);
const { tasks, pagination, total_tasks, timestamp } = response.data;
const { tasks, pagination, total_tasks, timestamp, task_counts } = response.data;
const backendItems = tasks
.filter((task: any) => {
@@ -329,7 +365,12 @@ export function QueueProvider({ children }: { children: ReactNode }) {
setItems(backendItems);
setHasMore(pagination.has_more);
setTotalTasks(total_tasks || 0);
// Update total tasks count - use active + queued if task_counts available
const calculatedTotal = task_counts ?
(task_counts.active + task_counts.queued) :
(total_tasks || 0);
setTotalTasks(calculatedTotal);
// Set initial timestamp to current time
lastUpdateTimestamp.current = timestamp;
@@ -489,7 +530,14 @@ export function QueueProvider({ children }: { children: ReactNode }) {
}, []);
const cancelAll = useCallback(async () => {
const activeItems = items.filter((item) => item.taskId && !isTerminalStatus(item.status));
const activeItems = items.filter((item) => {
if (!item.taskId) return false;
// Check for status in both possible locations (nested status_info for real-time, or top-level for others)
const actualStatus = (item.last_line?.status_info?.status as QueueStatus) ||
(item.last_line?.status as QueueStatus) ||
item.status;
return isActiveTaskStatus(actualStatus);
});
if (activeItems.length === 0) {
toast.info("No active downloads to cancel.");
return;

View File

@@ -18,8 +18,8 @@ export type QueueStatus =
| "progress"
| "track_progress";
// Active task statuses - tasks that are currently working/processing
// This matches the ACTIVE_TASK_STATES constant in the backend
// Active task statuses - tasks that are currently working/processing or queued
// This matches the ACTIVE_TASK_STATES constant in the backend plus queued tasks
export const ACTIVE_TASK_STATUSES: Set<QueueStatus> = new Set([
"initializing", // task is starting up
"processing", // task is being processed
@@ -28,6 +28,7 @@ export const ACTIVE_TASK_STATUSES: Set<QueueStatus> = new Set([
"track_progress", // real-time track progress
"real-time", // real-time download progress
"retrying", // task is retrying after error
"queued", // task is queued and waiting to start
]);
/**