mirror of
https://github.com/nicetry247/offlineacademy.git
synced 2026-08-24 09:07:18 +00:00
initial commit
This commit is contained in:
Executable
+85
@@ -0,0 +1,85 @@
|
||||
import { execSync } from 'child_process'
|
||||
import { join, dirname, basename, extname } from 'path'
|
||||
import { existsSync, mkdirSync } from 'fs'
|
||||
|
||||
/**
|
||||
* Get video duration in seconds using ffprobe
|
||||
*/
|
||||
export function getVideoDuration(filePath: string): number | null {
|
||||
try {
|
||||
const output = execSync(
|
||||
`ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${filePath}"`,
|
||||
{ encoding: 'utf-8', timeout: 30000 }
|
||||
)
|
||||
const duration = parseFloat(output.trim())
|
||||
return isNaN(duration) ? null : Math.round(duration)
|
||||
} catch (error) {
|
||||
console.warn(`Failed to get duration for ${filePath}:`, error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate thumbnail from video at specified time (default 10% or 5 seconds)
|
||||
*/
|
||||
export function generateVideoThumbnail(
|
||||
videoPath: string,
|
||||
thumbnailDir: string,
|
||||
timePercent: number = 0.1
|
||||
): string | null {
|
||||
try {
|
||||
// Get video duration first to calculate timestamp
|
||||
const duration = getVideoDuration(videoPath)
|
||||
if (!duration) return null
|
||||
|
||||
const timestamp = Math.min(duration * timePercent, 5) // Max 5 seconds or 10%
|
||||
const timeStr = formatTimeForFfmpeg(timestamp)
|
||||
|
||||
// Create thumbnail directory if it doesn't exist
|
||||
if (!existsSync(thumbnailDir)) {
|
||||
mkdirSync(thumbnailDir, { recursive: true })
|
||||
}
|
||||
|
||||
const videoName = basename(videoPath, extname(videoPath))
|
||||
const thumbnailName = `${videoName}-thumb.jpg`
|
||||
const thumbnailPath = join(thumbnailDir, thumbnailName)
|
||||
|
||||
// Generate thumbnail using ffmpeg
|
||||
execSync(
|
||||
`ffmpeg -y -ss ${timeStr} -i "${videoPath}" -vframes 1 -q:v 2 -vf "scale=320:-1" "${thumbnailPath}"`,
|
||||
{ stdio: 'ignore', timeout: 60000 }
|
||||
)
|
||||
|
||||
if (existsSync(thumbnailPath)) {
|
||||
return thumbnailName
|
||||
}
|
||||
return null
|
||||
} catch (error) {
|
||||
console.warn(`Failed to generate thumbnail for ${videoPath}:`, error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format seconds as HH:MM:SS for ffmpeg -ss parameter
|
||||
*/
|
||||
function formatTimeForFfmpeg(seconds: number): string {
|
||||
const hrs = Math.floor(seconds / 3600)
|
||||
const mins = Math.floor((seconds % 3600) / 60)
|
||||
const secs = Math.floor(seconds % 60)
|
||||
const ms = Math.round((seconds % 1) * 1000)
|
||||
return `${hrs.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}.${ms.toString().padStart(3, '0')}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if ffmpeg/ffprobe are available
|
||||
*/
|
||||
export function checkVideoTools(): boolean {
|
||||
try {
|
||||
execSync('ffprobe -version', { stdio: 'ignore' })
|
||||
execSync('ffmpeg -version', { stdio: 'ignore' })
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user