|
|
@@ -1,4 +1,5 @@
|
|
|
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
|
|
+import * as fs from 'fs';
|
|
|
import { ConfigService } from './config.service';
|
|
|
import { DbService } from './db.service';
|
|
|
import { EventsGateway } from './events.gateway';
|
|
|
@@ -83,7 +84,8 @@ export class TaskQueueService implements OnModuleInit {
|
|
|
}
|
|
|
|
|
|
onModuleInit() {
|
|
|
- this.startProcessing();
|
|
|
+ // Don't start processing automatically - wait for explicit start command
|
|
|
+ this.logger.log('Task queue initialized - processing stopped by default');
|
|
|
}
|
|
|
|
|
|
startProcessing() {
|
|
|
@@ -106,6 +108,17 @@ export class TaskQueueService implements OnModuleInit {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Public API methods
|
|
|
+ start() {
|
|
|
+ this.startProcessing();
|
|
|
+ return { started: true };
|
|
|
+ }
|
|
|
+
|
|
|
+ stop() {
|
|
|
+ this.stopProcessing();
|
|
|
+ return { stopped: true };
|
|
|
+ }
|
|
|
+
|
|
|
private async processPendingTasks() {
|
|
|
if (this.isProcessing) {
|
|
|
return; // Already processing
|
|
|
@@ -126,14 +139,10 @@ export class TaskQueueService implements OnModuleInit {
|
|
|
return; // No tasks to process
|
|
|
}
|
|
|
|
|
|
- // Process tasks up to concurrency limit
|
|
|
+ // Process tasks up to batch size, respecting concurrency limit
|
|
|
const processingPromises: Promise<void>[] = [];
|
|
|
- const tasksToProcess = pendingTasks.slice(
|
|
|
- 0,
|
|
|
- this.queueSettings.concurrency,
|
|
|
- );
|
|
|
|
|
|
- for (const task of tasksToProcess) {
|
|
|
+ for (const task of pendingTasks) {
|
|
|
if (this.activeTasks.size >= this.queueSettings.concurrency) {
|
|
|
break; // Respect concurrency limit
|
|
|
}
|
|
|
@@ -221,6 +230,42 @@ export class TaskQueueService implements OnModuleInit {
|
|
|
|
|
|
private async processTask(task: Task): Promise<void> {
|
|
|
try {
|
|
|
+ // Check if output file already exists and skip if it does (unless requeued)
|
|
|
+ if (task.output && fs.existsSync(task.output)) {
|
|
|
+ // Check if this task was requeued (has retry_count > 0 or was manually requeued)
|
|
|
+ const wasRequeued =
|
|
|
+ (task.retry_count || 0) > 0 || task.status === 'requeued';
|
|
|
+
|
|
|
+ if (!wasRequeued) {
|
|
|
+ // Skip processing - file already exists
|
|
|
+ this.db.updateTask(task.id, { status: 'skipped', progress: 100 });
|
|
|
+
|
|
|
+ // Update file status if it exists
|
|
|
+ if (task.dataset) {
|
|
|
+ this.db.setFile(task.dataset, task.input!, {
|
|
|
+ status: 'success',
|
|
|
+ date: new Date().toISOString(),
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // Emit skipped event
|
|
|
+ this.eventsGateway.emitTaskUpdate({
|
|
|
+ type: 'skipped',
|
|
|
+ taskId: task.id,
|
|
|
+ task: 'handbrake',
|
|
|
+ input: task.input,
|
|
|
+ output: task.output,
|
|
|
+ preset: task.preset,
|
|
|
+ success: true,
|
|
|
+ });
|
|
|
+
|
|
|
+ this.logger.log(
|
|
|
+ `Task ${task.id} skipped - output file already exists: ${task.output}`,
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Process the file
|
|
|
const success = await this.handbrake.processWithHandbrake(
|
|
|
task.input!,
|
|
|
@@ -365,4 +410,18 @@ export class TaskQueueService implements OnModuleInit {
|
|
|
settings: this.getQueueSettings(),
|
|
|
};
|
|
|
}
|
|
|
+
|
|
|
+ // Get task by input file
|
|
|
+ getTaskByInput(input: string): Task | undefined {
|
|
|
+ return this.db.getTaskByInput(input) as Task | undefined;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Update task status
|
|
|
+ updateTaskStatus(taskId: number, status: string, errorMessage?: string) {
|
|
|
+ const updateData: any = { status };
|
|
|
+ if (errorMessage) {
|
|
|
+ updateData.error_message = errorMessage;
|
|
|
+ }
|
|
|
+ return this.db.updateTask(taskId, updateData);
|
|
|
+ }
|
|
|
}
|