feat: AMPP folder sync integration — pre-create folder hierarchy on upload, expose lookup endpoint for Script Task: upload.js
This commit is contained in:
parent
e25e63b3f0
commit
0e36ca9972
1 changed files with 76 additions and 4 deletions
|
|
@ -10,6 +10,7 @@ import {
|
||||||
CompleteMultipartUploadCommand,
|
CompleteMultipartUploadCommand,
|
||||||
AbortMultipartUploadCommand,
|
AbortMultipartUploadCommand,
|
||||||
} from '@aws-sdk/client-s3';
|
} from '@aws-sdk/client-s3';
|
||||||
|
import { getAmppConfig, ensureFolderPath } from '../ampp/client.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
|
@ -30,6 +31,73 @@ const thumbnailQueue = new Queue('thumbnail', {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// AMPP Sync Helpers
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walk up the bins table to build an ordered array of folder name segments
|
||||||
|
* from the root ancestor down to the given binId.
|
||||||
|
*/
|
||||||
|
async function resolveBinPath(binId) {
|
||||||
|
const segments = [];
|
||||||
|
let currentId = binId;
|
||||||
|
while (currentId) {
|
||||||
|
const result = await pool.query(
|
||||||
|
'SELECT id, name, parent_id FROM bins WHERE id = $1',
|
||||||
|
[currentId]
|
||||||
|
);
|
||||||
|
if (result.rows.length === 0) break;
|
||||||
|
const bin = result.rows[0];
|
||||||
|
segments.unshift(bin.name); // prepend → final order is root-to-leaf
|
||||||
|
currentId = bin.parent_id;
|
||||||
|
}
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fire-and-forget: ensure the AMPP folder hierarchy exists for this asset's
|
||||||
|
* project/bin, then persist the resulting folder:id on the asset record so
|
||||||
|
* the AMPP Script Task can look it up and do the final link.
|
||||||
|
*
|
||||||
|
* Never throws — logs failures but does NOT fail the Dragon-Wind upload.
|
||||||
|
*/
|
||||||
|
async function syncToAmpp(assetId, projectId, binId) {
|
||||||
|
try {
|
||||||
|
const config = await getAmppConfig();
|
||||||
|
if (!config) return; // AMPP not configured — skip silently
|
||||||
|
|
||||||
|
// Look up project name
|
||||||
|
const projResult = await pool.query(
|
||||||
|
'SELECT name FROM projects WHERE id = $1',
|
||||||
|
[projectId]
|
||||||
|
);
|
||||||
|
if (projResult.rows.length === 0) return;
|
||||||
|
const projectName = projResult.rows[0].name;
|
||||||
|
|
||||||
|
// Build folder path: ProjectName / [BinAncestors...] / BinName
|
||||||
|
const segments = [projectName];
|
||||||
|
if (binId) {
|
||||||
|
const binSegments = await resolveBinPath(binId);
|
||||||
|
segments.push(...binSegments);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create or verify folder hierarchy in AMPP
|
||||||
|
const folderId = await ensureFolderPath(config, segments);
|
||||||
|
if (!folderId) return;
|
||||||
|
|
||||||
|
// Persist AMPP folder ID on asset so Script Task can look it up by filename
|
||||||
|
await pool.query(
|
||||||
|
'UPDATE assets SET ampp_folder_id = $1, ampp_synced_at = NOW() WHERE id = $2',
|
||||||
|
[folderId, assetId]
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`[AMPP] asset ${assetId} → folder ${folderId} (${segments.join(' / ')})`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[AMPP] sync failed for asset ${assetId}:`, err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// POST /api/v1/upload/init - Initialize a multipart upload
|
// POST /api/v1/upload/init - Initialize a multipart upload
|
||||||
router.post('/init', async (req, res, next) => {
|
router.post('/init', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -164,20 +232,22 @@ router.post('/complete', async (req, res, next) => {
|
||||||
|
|
||||||
const asset = result.rows[0];
|
const asset = result.rows[0];
|
||||||
|
|
||||||
// Queue proxy generation job
|
// Queue proxy and thumbnail generation
|
||||||
await proxyQueue.add('generate', {
|
await proxyQueue.add('generate', {
|
||||||
assetId,
|
assetId,
|
||||||
inputKey: key,
|
inputKey: key,
|
||||||
outputKey: `proxies/${assetId}.mp4`,
|
outputKey: `proxies/${assetId}.mp4`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Queue thumbnail generation job
|
|
||||||
await thumbnailQueue.add('generate', {
|
await thumbnailQueue.add('generate', {
|
||||||
assetId,
|
assetId,
|
||||||
inputKey: `proxies/${assetId}.mp4`,
|
inputKey: `proxies/${assetId}.mp4`,
|
||||||
outputKey: `thumbnails/${assetId}.jpg`,
|
outputKey: `thumbnails/${assetId}.jpg`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Sync AMPP folder structure — non-blocking, never fails the upload
|
||||||
|
syncToAmpp(asset.id, asset.project_id, asset.bin_id);
|
||||||
|
|
||||||
res.json(asset);
|
res.json(asset);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err);
|
||||||
|
|
@ -266,20 +336,22 @@ router.post('/simple', upload.single('file'), async (req, res, next) => {
|
||||||
const result = await pool.query(updateQuery, [assetId]);
|
const result = await pool.query(updateQuery, [assetId]);
|
||||||
const asset = result.rows[0];
|
const asset = result.rows[0];
|
||||||
|
|
||||||
// Queue proxy generation job
|
// Queue proxy and thumbnail generation
|
||||||
await proxyQueue.add('generate', {
|
await proxyQueue.add('generate', {
|
||||||
assetId,
|
assetId,
|
||||||
inputKey: s3Key,
|
inputKey: s3Key,
|
||||||
outputKey: `proxies/${assetId}.mp4`,
|
outputKey: `proxies/${assetId}.mp4`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Queue thumbnail generation job
|
|
||||||
await thumbnailQueue.add('generate', {
|
await thumbnailQueue.add('generate', {
|
||||||
assetId,
|
assetId,
|
||||||
inputKey: `proxies/${assetId}.mp4`,
|
inputKey: `proxies/${assetId}.mp4`,
|
||||||
outputKey: `thumbnails/${assetId}.jpg`,
|
outputKey: `thumbnails/${assetId}.jpg`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Sync AMPP folder structure — non-blocking, never fails the upload
|
||||||
|
syncToAmpp(assetId, projectId, binId || null);
|
||||||
|
|
||||||
res.json(asset);
|
res.json(asset);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
next(err);
|
next(err);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue