updated docker for ffmpeg
This commit is contained in:
parent
540b7b6aa7
commit
b98b5106d9
@ -9,6 +9,8 @@ RUN yarn build
|
||||
|
||||
|
||||
FROM node:20.15.1-alpine
|
||||
# Install FFmpeg for video processing (reversed video generation)
|
||||
RUN apk add --no-cache ffmpeg
|
||||
WORKDIR /app
|
||||
COPY backend/package.json backend/yarn.lock ./
|
||||
RUN yarn install --pure-lockfile
|
||||
|
||||
@ -21,6 +21,8 @@ RUN yarn install --pure-lockfile
|
||||
FROM node:20.15.1-alpine AS build
|
||||
RUN apk add --no-cache git nginx curl
|
||||
RUN apk add --no-cache lsof procps
|
||||
# Install FFmpeg for video processing (reversed video generation)
|
||||
RUN apk add --no-cache ffmpeg
|
||||
RUN yarn global add concurrently
|
||||
|
||||
RUN apk add --no-cache \
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
FROM node:20.15.1-alpine
|
||||
|
||||
RUN apk update && apk add bash
|
||||
# Install bash and FFmpeg for video processing (reversed video generation)
|
||||
RUN apk update && apk add --no-cache bash ffmpeg
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
const AssetsDBApi = require('../db/api/assets');
|
||||
const Asset_variantsDBApi = require('../db/api/asset_variants');
|
||||
const { createEntityService } = require('../factories/service.factory');
|
||||
const ValidationError = require('./notifications/errors/validation');
|
||||
const { downloadToBuffer, uploadBuffer } = require('./file');
|
||||
const videoProcessing = require('./videoProcessing');
|
||||
const { logger } = require('../utils/logger');
|
||||
|
||||
/**
|
||||
* Valid MIME type patterns for each asset type
|
||||
@ -68,11 +72,11 @@ const BaseService = createEntityService(AssetsDBApi, {
|
||||
});
|
||||
|
||||
/**
|
||||
* Assets Service with validation
|
||||
* Assets Service with validation and video pre-processing
|
||||
*/
|
||||
class AssetsService extends BaseService {
|
||||
/**
|
||||
* Create asset with MIME type validation
|
||||
* Create asset with MIME type validation and video pre-processing
|
||||
*/
|
||||
static async create(data, currentUser) {
|
||||
// Validate asset_type and mime_type match
|
||||
@ -85,7 +89,99 @@ class AssetsService extends BaseService {
|
||||
}
|
||||
|
||||
// Call parent create
|
||||
return super.create(data, currentUser);
|
||||
const asset = await super.create(data, currentUser);
|
||||
|
||||
// Pre-generate reversed video for video assets (async, doesn't block response)
|
||||
if (assetType === 'video' && data.storage_key) {
|
||||
AssetsService.preGenerateReversedVideo(asset, currentUser).catch((err) => {
|
||||
logger.error(
|
||||
{ err, assetId: asset.id },
|
||||
'Failed to pre-generate reversed video (non-blocking)',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-generate reversed video variant for a video asset.
|
||||
* Runs asynchronously after asset creation - doesn't block the upload response.
|
||||
* This ensures reversed videos are ready for instant use in transitions.
|
||||
*
|
||||
* @param {Object} asset - Created asset record
|
||||
* @param {Object} currentUser - Current user context
|
||||
*/
|
||||
static async preGenerateReversedVideo(asset, currentUser) {
|
||||
const log = logger.child({
|
||||
assetId: asset.id,
|
||||
operation: 'preGenerateReversed',
|
||||
});
|
||||
|
||||
log.info('Starting pre-generation of reversed video');
|
||||
|
||||
try {
|
||||
// Check if FFmpeg is available
|
||||
const ffmpegAvailable = await videoProcessing.isFFmpegAvailable();
|
||||
if (!ffmpegAvailable) {
|
||||
log.warn('FFmpeg not available, skipping pre-generation');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if reversed variant already exists (shouldn't happen on create, but safety check)
|
||||
const existingAsset = await AssetsDBApi.findBy({ id: asset.id });
|
||||
const variants = existingAsset?.asset_variants_asset || [];
|
||||
const existingReversed = variants.find((v) => v.variant_type === 'reversed');
|
||||
|
||||
if (existingReversed) {
|
||||
log.debug('Reversed variant already exists');
|
||||
return existingReversed.storage_key;
|
||||
}
|
||||
|
||||
// Download original video to buffer
|
||||
const storageKey = asset.storage_key;
|
||||
log.info({ storageKey }, 'Downloading original video');
|
||||
|
||||
const originalBuffer = await downloadToBuffer(storageKey);
|
||||
|
||||
// Generate reversed video
|
||||
log.info('Generating reversed video with FFmpeg');
|
||||
const reversedBuffer = await videoProcessing.reverseVideo(
|
||||
originalBuffer,
|
||||
asset.original_file_name || 'video.mp4',
|
||||
);
|
||||
|
||||
// Upload reversed video to storage
|
||||
const reversedKey = `assets/${asset.id}/reversed.mp4`;
|
||||
log.info({ reversedKey }, 'Uploading reversed video');
|
||||
|
||||
const result = await uploadBuffer(reversedKey, reversedBuffer, {
|
||||
contentType: 'video/mp4',
|
||||
});
|
||||
|
||||
// Create variant record
|
||||
await Asset_variantsDBApi.create(
|
||||
{
|
||||
assetId: asset.id,
|
||||
variant_type: 'reversed',
|
||||
cdn_url: result.url,
|
||||
storage_key: reversedKey,
|
||||
size_mb: reversedBuffer.length / (1024 * 1024),
|
||||
},
|
||||
{ currentUser },
|
||||
);
|
||||
|
||||
log.info(
|
||||
{ reversedKey, sizeMb: (reversedBuffer.length / (1024 * 1024)).toFixed(2) },
|
||||
'Pre-generated reversed video successfully',
|
||||
);
|
||||
|
||||
return reversedKey;
|
||||
} catch (err) {
|
||||
log.error({ err }, 'Failed to pre-generate reversed video');
|
||||
// Don't throw - this is a background operation
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user