assets and tier plans revamp

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-08-01 17:44:25 +08:00
parent 39c3c4566b
commit cae958b5d5
41 changed files with 1271 additions and 165 deletions
+3
View File
@@ -23,6 +23,7 @@
* Currently registered:
* - taskOverdue (cron/jobs/task_overdue.cron.js) — settings-backed
* - liftExpiredBans (cron/jobs/lift_expired_bans.cron.js)
* - retryStuckTranscodes (cron/jobs/retry_stuck_transcodes.cron.js)
*
* Author: Kenneth Obsequio (@lash0000)
* Date Created: Jun. 17, 2026
@@ -30,6 +31,7 @@
const cron = require('node-cron');
const taskOverdue = require('./jobs/task_overdue.cron');
const liftExpiredBans = require('./jobs/lift_expired_bans.cron');
const retryStuckTranscodes = require('./jobs/retry_stuck_transcodes.cron');
const { startSettingsBackedJobs } = require('./cronRegistry.util');
// ─── Registry — add future admin-side cron jobs here ─────────────────────────
@@ -40,6 +42,7 @@ const settingsBackedJobs = [
// Plain hardcoded-schedule jobs (not tied to any notification setting).
const plainJobs = [
liftExpiredBans,
retryStuckTranscodes,
];
// ─── Boot all registered admin-side jobs ──────────────────────────────────────
+65
View File
@@ -0,0 +1,65 @@
/***********************************************************************************************************************************************************************
* File Name : retry_stuck_transcodes.cron.js
* Type : Cron Job
* Description : Safety net for the .mov/.mkv -> faststart .mp4 background
* remux (see services/assetTranscode.service.js). Picks up:
* - "pending" — the fire-and-forget call in
* assets.controller.js#finalizeAssetFromStorage
* never actually started (e.g. this process
* crashed between the DB commit and the call).
* - "processing" for over 30 minutes — the job itself was
* running when the process restarted/crashed
* mid-remux and never got to flip the status.
*
* Processes at most 3 per run, sequentially — this runs on a
* small droplet, and remuxing is disk/CPU-bound; no reason to
* pile up concurrent ffmpeg processes for a background sweep.
*
* Schedule : Every 10 minutes ("*\/10 * * * *"). Registered by
* cron/admin.cron.js.
*
* Author: Kenneth Obsequio
* Date Created: Aug. 1, 2026
***********************************************************************************************************************************************************************/
const { Op } = require('sequelize');
const mdl_Assets = require('../../models/assets/assets.mdl');
const { transcodeAsset } = require('../../services/assetTranscode.service');
const MAX_PER_RUN = 3;
const STUCK_PROCESSING_MINUTES = 30;
async function run() {
try {
const stuckSince = new Date(Date.now() - STUCK_PROCESSING_MINUTES * 60 * 1000);
// Demote stale "processing" rows back to "pending" so transcodeAsset()'s
// own claim step (pending/failed -> processing) can pick them up again —
// it never claims an in-progress "processing" row, by design (avoids
// double-processing a job that's actually still running elsewhere).
await mdl_Assets.update(
{ transcode_status: 'pending' },
{ where: { transcode_status: 'processing', updatedAt: { [Op.lt]: stuckSince } } },
);
const candidates = await mdl_Assets.findAll({
where: { deletedAt: null, transcode_status: 'pending' },
limit: MAX_PER_RUN,
});
if (!candidates.length) return;
for (const asset of candidates) {
await transcodeAsset(asset);
}
console.log(`[CRON][RETRY STUCK TRANSCODES] Processed ${candidates.length} asset(s).`);
} catch (err) {
console.error('[CRON][RETRY STUCK TRANSCODES] Failed:', err);
}
}
module.exports = {
name: 'retryStuckTranscodes',
schedule: '*/10 * * * *',
run,
};