()
| 209 | } |
| 210 | |
| 211 | async terminateInProgressJobs() { |
| 212 | // Change the status of "In Progress" jobs to "Terminated" and recover/cleanup videos |
| 213 | for (let jobId in this.jobs) { |
| 214 | if (this.jobs[jobId].status === 'In Progress') { |
| 215 | logger.info({ jobId }, 'Recovering job after server restart'); |
| 216 | |
| 217 | let recoveredCount = 0; |
| 218 | let outputMessage = 'Job terminated due to server restart'; |
| 219 | |
| 220 | try { |
| 221 | // Step 1: Recover completed videos from JobVideoDownload table |
| 222 | recoveredCount = await this.recoverCompletedVideos(jobId); |
| 223 | |
| 224 | // Step 2: Clean up in-progress videos from disk |
| 225 | try { |
| 226 | await downloadCleanup.cleanupInProgressVideos(jobId); |
| 227 | } catch (cleanupErr) { |
| 228 | logger.error({ err: cleanupErr, jobId }, 'Error cleaning up in-progress videos for job'); |
| 229 | } |
| 230 | |
| 231 | // Step 3: Set appropriate output message |
| 232 | if (recoveredCount > 0) { |
| 233 | outputMessage = `${recoveredCount} video${recoveredCount === 1 ? '' : 's'} completed (recovered after server restart)`; |
| 234 | } |
| 235 | |
| 236 | // Step 4: Clean up all JobVideoDownload entries for this job |
| 237 | try { |
| 238 | const deletedCount = await JobVideoDownload.destroy({ |
| 239 | where: { job_id: jobId } |
| 240 | }); |
| 241 | if (deletedCount > 0) { |
| 242 | logger.info({ jobId, deletedCount }, 'Cleaned up JobVideoDownload tracking entries for job'); |
| 243 | } |
| 244 | } catch (deleteErr) { |
| 245 | logger.error({ err: deleteErr, jobId }, 'Error deleting JobVideoDownload entries for job'); |
| 246 | } |
| 247 | } catch (err) { |
| 248 | logger.error({ err, jobId }, 'Error during recovery for job'); |
| 249 | outputMessage = `Job terminated with errors: ${err.message}`; |
| 250 | } |
| 251 | |
| 252 | // Step 5: Reload job videos from database into in-memory structure |
| 253 | // This ensures Download History shows the correct count |
| 254 | try { |
| 255 | const jobVideos = await JobVideo.findAll({ |
| 256 | where: { job_id: jobId } |
| 257 | }); |
| 258 | |
| 259 | const videos = []; |
| 260 | for (const jobVideo of jobVideos) { |
| 261 | const video = await Video.findOne({ where: { id: jobVideo.video_id } }); |
| 262 | if (video) { |
| 263 | videos.push(video.dataValues); |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | if (!this.jobs[jobId].data) { |
| 268 | this.jobs[jobId].data = {}; |
no test coverage detected