editia-core
Version:
Core services and utilities for Editia applications - Authentication, Monetization, Video Generation Types, and Database Management
185 lines • 7.94 kB
JavaScript
;
/**
* Unified Video Template Service
* Consolidates video template operations with validation
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.VideoTemplateService = void 0;
const constants_1 = require("./constants");
const validation_1 = require("./validation");
/**
* Service for managing video templates with comprehensive validation
*/
class VideoTemplateService {
/**
* Validates a video template with caption and duration checks
*
* @param scriptText - The script text to validate against
* @param selectedVideos - Videos selected for the template
* @param captionConfig - Caption configuration
* @returns Validation result with warnings and errors
*/
static validateTemplate(scriptText, selectedVideos, captionConfig) {
const warnings = [];
const errors = [];
// Calculate script duration requirements
const scriptLength = scriptText.split(' ').length;
const requiredDuration = scriptLength * constants_1.VIDEO_DURATION_MULTIPLIER;
// Calculate total available video duration
const totalDuration = selectedVideos.reduce((total, video) => {
return total + (video.duration_seconds || 0);
}, 0);
// Validate each video duration individually
const insufficientVideos = selectedVideos.filter(video => {
const videoDuration = video.duration_seconds || 0;
return !(0, validation_1.validateVideoDuration)(scriptLength, videoDuration);
});
// Check overall duration
if (totalDuration < requiredDuration) {
errors.push(`Insufficient video duration. Required: ${Math.round(requiredDuration)}s, Available: ${Math.round(totalDuration)}s`);
}
// Check individual video durations
if (insufficientVideos.length > 0) {
warnings.push(`${insufficientVideos.length} video(s) may be too short for the script length`);
}
// Check caption configuration
if (captionConfig.enabled && !captionConfig.placement) {
warnings.push('Captions enabled but no placement specified');
}
// Check if videos have analysis data for better selection
const videosWithoutAnalysis = selectedVideos.filter(video => !video.analysis_data);
if (videosWithoutAnalysis.length > 0) {
warnings.push(`${videosWithoutAnalysis.length} video(s) lack analysis data for optimal template generation`);
}
return {
isValid: errors.length === 0,
warnings,
errors,
captionsEnabled: captionConfig.enabled,
totalDuration: Math.round(totalDuration),
requiredDuration: Math.round(requiredDuration),
};
}
/**
* Generates a scene plan for video template
* Removes caption elements if captions are disabled
*
* @param scriptText - The script text
* @param selectedVideos - Videos to use in template
* @param captionConfig - Caption configuration
* @returns Array of scene plans
*/
static generateScenePlan(scriptText, selectedVideos, captionConfig) {
const scenePlans = [];
const words = scriptText.split(' ').filter(word => word.trim());
const wordsPerSecond = 2.5; // Conservative speaking rate
let currentTime = 0;
let videoIndex = 0;
// Split script into scenes based on video availability
const wordsPerScene = Math.ceil(words.length / selectedVideos.length);
for (let i = 0; i < words.length; i += wordsPerScene) {
const sceneWords = words.slice(i, i + wordsPerScene);
const sceneDuration = sceneWords.length / wordsPerSecond;
if (videoIndex < selectedVideos.length) {
const video = selectedVideos[videoIndex];
const videoDuration = video.duration_seconds || 0;
// Use the minimum of scene duration or video duration
const actualDuration = Math.min(sceneDuration, videoDuration);
scenePlans.push({
id: `scene-${videoIndex}`,
startTime: currentTime,
endTime: currentTime + actualDuration,
duration: actualDuration,
text: sceneWords.join(' '),
videoId: video.id, // Cast to branded type
});
currentTime += actualDuration;
videoIndex++;
}
}
return scenePlans;
}
/**
* Processes template configuration by removing captions if disabled
*
* @param template - Base template configuration
* @param captionConfig - Caption configuration
* @returns Processed template with captions removed if disabled
*/
static processTemplate(template, captionConfig) {
const processedTemplate = { ...template };
if (!captionConfig.enabled) {
// Remove caption-related elements from template
if (processedTemplate.elements) {
processedTemplate.elements = processedTemplate.elements.filter((element) => element.type !== 'text' && element.type !== 'caption');
}
if (processedTemplate.tracks) {
processedTemplate.tracks = processedTemplate.tracks.filter((track) => track.type !== 'subtitle' && track.type !== 'caption');
}
}
else {
// Apply caption configuration
if (processedTemplate.elements) {
processedTemplate.elements.forEach((element) => {
if (element.type === 'text' || element.type === 'caption') {
// Apply caption styling
if (captionConfig.transcriptColor) {
element.fill = captionConfig.transcriptColor;
}
if (captionConfig.placement) {
element.y = this.getCaptionYPosition(captionConfig.placement);
}
if (captionConfig.transcriptEffect) {
element.effect = captionConfig.transcriptEffect;
}
}
});
}
}
return processedTemplate;
}
/**
* Gets Y position for caption placement
*
* @param placement - Caption placement option
* @returns Y coordinate for caption positioning
*/
static getCaptionYPosition(placement) {
switch (placement) {
case 'top':
return '10%';
case 'center':
return '50%';
case 'bottom':
return '85%';
default:
return '85%'; // Default to bottom
}
}
/**
* Validates script length against available video durations
*
* @param scriptText - The script text
* @param videos - Available videos
* @returns Whether the script can be accommodated by the videos
*/
static canAccommodateScript(scriptText, videos) {
const scriptLength = scriptText.length;
const totalVideoDuration = videos.reduce((total, video) => {
return total + (video.duration_seconds || 0);
}, 0);
const requiredDuration = scriptLength * constants_1.VIDEO_DURATION_MULTIPLIER;
return totalVideoDuration >= requiredDuration;
}
/**
* Gets recommended video duration for a given script
*
* @param scriptText - The script text
* @returns Recommended minimum video duration in seconds
*/
static getRecommendedDuration(scriptText) {
return Math.ceil(scriptText.length * constants_1.VIDEO_DURATION_MULTIPLIER);
}
}
exports.VideoTemplateService = VideoTemplateService;
//# sourceMappingURL=template-service.js.map