shiveshnavin's picture
Fix media
b93e09e
import path from 'path';
import fs from 'fs';
import { Plugin } from './plugin.js';
import { FFMpegUtils } from 'common-utils';
export class CropPlugin extends Plugin {
constructor(name, options) {
super(name, options);
}
mediaPathFlatten(mediaPath) {
return path.join('public', path.basename(mediaPath));
}
async applyPrerender(originalManuscript, jobId) {
const transcript = originalManuscript.transcript || [];
const targetWidth = +this.options.width || +this.options.targetWidth || 1080;
const targetHeight = +this.options.height || +this.options.targetHeight || 1920;
const targetAspect = targetWidth / targetHeight;
for (let item of transcript) {
if (!item.mediaAbsPaths || !item.mediaAbsPaths.length) continue;
for (let mediaObj of item.mediaAbsPaths) {
try {
let mediaPath = mediaObj.path;
if (!mediaPath || !fs.existsSync(mediaPath)) {
const flattenedPath = this.mediaPathFlatten(mediaPath);
if (fs.existsSync(flattenedPath)) {
mediaObj.path = flattenedPath;
mediaPath = flattenedPath;
this.log(`Using flattened media path: ${flattenedPath}`);
} else {
this.log(`Media path does not exist: ${mediaPath}. Trying at flattened path: ${flattenedPath} failed.`);
continue;
}
}
const meta = await FFMpegUtils.getMediaMetadata(mediaPath);
if (!meta || !meta.video || !meta.video.width || !meta.video.height) {
this.log(`No video stream found for ${mediaPath}`);
continue;
}
const srcW = parseInt(meta.video.width);
const srcH = parseInt(meta.video.height);
const srcAspect = srcW / srcH;
let cropW = srcW, cropH = srcH, cropX = 0, cropY = 0;
if (Math.abs(srcAspect - targetAspect) < 0.0001) {
this.log(`Aspect ratio matches target for ${mediaPath}, skipping crop.`);
continue;
}
if (srcAspect > targetAspect) {
// source is too wide -> reduce width, keep height
cropW = Math.round(targetAspect * srcH);
cropX = Math.round((srcW - cropW) / 2);
cropH = srcH;
cropY = 0;
} else {
// source is too tall -> reduce height, keep width
cropH = Math.round(srcW / targetAspect);
cropY = Math.round((srcH - cropH) / 2);
cropW = srcW;
cropX = 0;
}
if (cropW === srcW && cropH === srcH) {
continue;
}
const ext = path.extname(mediaPath);
const base = path.basename(mediaPath, ext);
const outPath = path.join(path.dirname(mediaPath), `${base}.cropped${ext}`);
const cmd = `ffmpeg -i "${mediaPath}" -filter:v "crop=${cropW}:${cropH}:${cropX}:${cropY}" -c:v libx264 -preset veryfast -crf 23 -c:a copy "${outPath}" -y`;
this.log(`Cropping ${mediaPath} -> ${outPath} crop=${cropW}x${cropH}+${cropX}+${cropY}`);
await FFMpegUtils.execute(cmd);
// keep original reference and replace path
mediaObj._originalPath = mediaObj.path;
mediaObj.path = outPath;
} catch (err) {
this.log(`Error cropping media ${mediaObj?.path}: ${err}`);
}
}
}
}
}