feat: Add comprehensive documentation suite and reorganize project structure
- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
This commit is contained in:
158
f_modules/m_frontend/m_file/thumbnail_preview.php
Normal file
158
f_modules/m_frontend/m_file/thumbnail_preview.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Video thumbnail preview generator
|
||||
*
|
||||
* A simple but effective command-line tool for generating thumbnails of a
|
||||
* video and corresponding VTT file for use within JW Player.
|
||||
*
|
||||
* This script requires that ffmpeg is installed on your system.
|
||||
*
|
||||
* @author Andrew Collington, andy@amnuts.com
|
||||
* @license MIT, http://acollington.mit-license.org/
|
||||
*/
|
||||
|
||||
$params = [
|
||||
'ffmpeg' => 'ffmpeg', // the ffmpeg command - full path if needs be
|
||||
'input' => null, // input video file - specified as command line parameter
|
||||
'output' => __DIR__, // The output directory
|
||||
'timespan' => 3, // seconds between each thumbnail
|
||||
'thumbWidth' => 140, // thumbnail width
|
||||
'spriteWidth' => 10 // number of thumbnails per row in sprite sheet
|
||||
];
|
||||
|
||||
$commands = [
|
||||
'details' => $params['ffmpeg'] . ' -i %s 2>&1',
|
||||
'poster' => $params['ffmpeg'] . ' -ss %d -i %s -y -vframes 1 "%s/poster.jpg" 2>&1',
|
||||
'thumbs' => $params['ffmpeg'] . ' -ss %0.04f -i %s -y -an -sn -vsync 0 -q:v 5 -threads 1 '
|
||||
. '-vf scale=%d:-1,select="not(mod(n\\,%d))" "%s/thumbnails/%s-%%04d.jpg" 2>&1'
|
||||
];
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
exit('Sorry, you can only use this via the command line.');
|
||||
}
|
||||
|
||||
$opts = getopt('i:o:t:w:hvpd');
|
||||
if (isset($opts['h']) || !isset($opts['i'])) {
|
||||
$usage =<<< EOT
|
||||
Usage: php thumbnails.php -i "/input/video.mp4" [-o "/output/directory"] [-t <number>] [-w <number>] [-v] [-p] [-d]
|
||||
|
||||
-i: The input file to be used.
|
||||
-o: The output directory where the thumbnails and vtt file will be saved
|
||||
-t: The time span (in seconds) between each thumbnail (default, {$params['timespan']})
|
||||
-w: The max width of the thumbnail (default, {$params['thumbWidth']})
|
||||
-v: Verbose - don't coalesce the thumbnails into one image
|
||||
-p: Generate poster image from a random frame in the video
|
||||
-d: Delete any previous thumbnails that match before generating new images
|
||||
-h: Show this message
|
||||
|
||||
EOT;
|
||||
exit($usage);
|
||||
}
|
||||
|
||||
// process input parameters
|
||||
|
||||
$params['input'] = escapeshellarg($opts['i']);
|
||||
if (isset($opts['o'])) {
|
||||
$params['output'] = realpath($opts['o']);
|
||||
}
|
||||
if (isset($opts['t']) && (int)$opts['t']) {
|
||||
$params['timespan'] = $opts['t'];
|
||||
}
|
||||
if (isset($opts['w']) && (int)$opts['w']) {
|
||||
$params['thumbWidth'] = $opts['w'];
|
||||
}
|
||||
|
||||
// sanity checks
|
||||
|
||||
if (!is_readable($opts['i'])) {
|
||||
exit("Cannot read the input file '{$opts['i']}'");
|
||||
}
|
||||
if (!is_writable($params['output'])) {
|
||||
exit("Cannot write to output directory '{$opts['o']}'");
|
||||
}
|
||||
if (!file_exists($params['output'] . '/thumbnails')) {
|
||||
if (!mkdir($params['output'] . '/thumbnails')) {
|
||||
exit("Could not create thumbnail output directory '{$params['output']}/thumbnails'");
|
||||
}
|
||||
}
|
||||
$details = shell_exec(sprintf($commands['details'], $params['input']));
|
||||
if ($details === null || !preg_match('/^(?:\s+)?ffmpeg version ([^\s,]*)/i', $details)) {
|
||||
exit("Cannot find ffmpeg - try specifying the path in the \$params variable");
|
||||
}
|
||||
|
||||
// determine some values we need
|
||||
|
||||
$time = $tbr = [];
|
||||
preg_match('/Duration: ((\d+):(\d+):(\d+))\.\d+, start: ([^,]*)/is', $details, $time);
|
||||
preg_match('/\b(\d+(?:\.\d+)?) tbr\b/', $details, $tbr);
|
||||
$duration = ($time[2] * 3600) + ($time[3] * 60) + $time[4];
|
||||
$start = $time[5];
|
||||
$tbr = $tbr[1];
|
||||
|
||||
// generate random poster if required
|
||||
|
||||
if (isset($opts['p'])) {
|
||||
shell_exec(sprintf($commands['poster'], rand(1, $duration - 1), $opts['i'], $params['output']));
|
||||
}
|
||||
|
||||
// generate all thumbnail images
|
||||
|
||||
$name = strtolower(substr(basename($opts['i']), 0, strrpos(basename($opts['i']), '.')));
|
||||
if (isset($opts['d'])) {
|
||||
$files = glob("{$params['output']}/thumbnails/{$name}-*.jpg");
|
||||
foreach ($files as $f) {
|
||||
unlink($f);
|
||||
}
|
||||
}
|
||||
shell_exec(sprintf($commands['thumbs'],
|
||||
$start + .0001, $params['input'], $params['thumbWidth'],
|
||||
$params['timespan'] * $tbr, $params['output'], $name
|
||||
));
|
||||
$files = glob("{$params['output']}/thumbnails/{$name}-*.jpg");
|
||||
if (!($total = count($files))) {
|
||||
exit("Could not find any thumbnails matching '{$params['output']}/thumbnails/{$name}-*.jpg'");
|
||||
}
|
||||
|
||||
// create coalesce image if needs be
|
||||
|
||||
if (!isset($opts['v'])) {
|
||||
$thumbsAcross = min($total, $params['spriteWidth']);
|
||||
$sizes = getimagesize($files[0]);
|
||||
$rows = ceil($total/$thumbsAcross);
|
||||
$w = $sizes[0] * $thumbsAcross;
|
||||
$h = $sizes[1] * $rows;
|
||||
$coalesce = imagecreatetruecolor($w, $h);
|
||||
}
|
||||
|
||||
// generate vtt file, merge thumbnails if needs be
|
||||
|
||||
$vtt = "WEBVTT\n\n";
|
||||
for ($rx = $ry = $s = $f = 0; $f < $total; $f++) {
|
||||
$t1 = sprintf('%02d:%02d:%02d.000', ($s / 3600), ($s / 60 % 60), $s % 60);
|
||||
$s += $params['timespan'];
|
||||
$t2 = sprintf('%02d:%02d:%02d.000', ($s / 3600), ($s / 60 % 60), $s % 60);
|
||||
if (isset($opts['v'])) {
|
||||
$vtt .= "{$t1} --> {$t2}\nthumbnails/" . basename($files[$f]);
|
||||
} else {
|
||||
if ($f && !($f % $thumbsAcross)) {
|
||||
$rx = 0;
|
||||
++$ry;
|
||||
}
|
||||
imagecopymerge($coalesce, imagecreatefromjpeg($files[$f]), $rx * $sizes[0], $ry * $sizes[1], 0, 0, $sizes[0], $sizes[1], 100);
|
||||
$vtt .= sprintf("%s --> %s\nthumbnails.jpg#xywh=%d,%d,%d,%d", $t1, $t2, $rx++ * $sizes[0], $ry * $sizes[1], $sizes[0], $sizes[1]);
|
||||
}
|
||||
$vtt .= "\n\n";
|
||||
}
|
||||
|
||||
// tidy up
|
||||
|
||||
if (!isset($opts['v'])) {
|
||||
imagejpeg($coalesce, "{$params['output']}/thumbnails.jpg", 100);
|
||||
for ($s = 0, $f = 0; $f < $total; $f++) {
|
||||
unlink($files[$f]);
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents("{$params['output']}/thumbnails.vtt", $vtt);
|
||||
exit("Process completed. Check the output directory '{$params['output']}' for VTT file and images");
|
||||
Reference in New Issue
Block a user