updated docker for ffmpeg

This commit is contained in:
Dmitri 2026-04-13 19:17:18 +04:00
parent 540b7b6aa7
commit b98b5106d9
4 changed files with 106 additions and 4 deletions

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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;
}
}
/**