1
0
Fork 0
mirror of https://github.com/DanielnetoDotCom/YouPHPTube synced 2025-10-03 01:39:24 +02:00

AI can now suggest shorts

This commit is contained in:
Daniel Neto 2024-03-05 15:37:54 -03:00
parent e869930315
commit d94d0ba437
23 changed files with 1257 additions and 581 deletions

View file

@ -107,13 +107,7 @@ abstract class ObjectYPT implements ObjectInterface
$res = sqlDAL::readSql($sql);
$fullData = sqlDAL::fetchAllAssoc($res);
sqlDAL::close($res);
$rows = [];
if ($res !== false) {
foreach ($fullData as $row) {
$rows[] = $row;
}
}
return $rows;
return $fullData;
}
public static function getAllActive()
@ -128,13 +122,7 @@ abstract class ObjectYPT implements ObjectInterface
$res = sqlDAL::readSql($sql);
$fullData = sqlDAL::fetchAllAssoc($res);
sqlDAL::close($res);
$rows = [];
if ($res !== false) {
foreach ($fullData as $row) {
$rows[] = $row;
}
}
return $rows;
return $fullData;
}
public static function getTotal()
@ -299,6 +287,7 @@ abstract class ObjectYPT implements ObjectInterface
_error_log("Save error, table " . static::getTableName() . " MySQL Error", AVideoLog::$ERROR);
return false;
}
//_error_log("Save ".static::getTableName().' '.json_encode($fieldsName));
$formats = '';
$values = [];
if (!empty($this->id)) {
@ -475,6 +464,7 @@ abstract class ObjectYPT implements ObjectInterface
'ai_responses',
'ai_metatags_responses',
'ai_transcribe_responses',
'ai_responses_json',
'playlists_schedules'
];
return in_array(static::getTableName(), $ignoreArray);

390
objects/functionExec.php Normal file
View file

@ -0,0 +1,390 @@
<?php
function cutVideoWithFFmpeg($inputFile, $startTimeInSeconds, $endTimeInSeconds, $outputFile)
{
// Ensure start and end times are numeric
$startTimeInSeconds = (int)$startTimeInSeconds;
$endTimeInSeconds = (int)$endTimeInSeconds;
make_path($outputFile);
// Escape arguments to ensure command is safe to execute
$escapedInputFile = escapeshellarg($inputFile);
$escapedOutputFile = escapeshellarg($outputFile);
$escapedStartTime = escapeshellarg($startTimeInSeconds);
$escapedEndTime = escapeshellarg($endTimeInSeconds);
// Construct the FFmpeg command
$cmd = get_ffmpeg() . " -ss {$escapedStartTime} -to {$escapedEndTime} -i {$escapedInputFile} -c copy {$escapedOutputFile}";
$cmd = removeUserAgentIfNotURL($cmd);
// Execute the command
_error_log('cutVideoWithFFmpeg start ' . $cmd);
exec($cmd, $output, $returnVar);
// Check if the command was executed successfully
if ($returnVar === 0) {
_error_log('cutVideoWithFFmpeg success ' . $outputFile);
return true; // Command executed successfully
} else {
_error_log('cutVideoWithFFmpeg error ');
return false; // Command failed
}
}
function getDurationFromFile($file)
{
global $config, $getDurationFromFile;
if (empty($file)) {
return "EE:EE:EE";
}
if (!isset($getDurationFromFile)) {
$getDurationFromFile = [];
}
if (!empty($getDurationFromFile[$file])) {
// I need to check again because I am recreating the file on the AI
//return $getDurationFromFile[$file];
}
$hls = str_replace(".zip", "/index.m3u8", $file);
$file = str_replace(".zip", ".mp4", $file);
// get movie duration HOURS:MM:SS.MICROSECONDS
$videoFile = $file;
if (!file_exists($videoFile)) {
$file_headers = @get_headers($videoFile);
if (!$file_headers || $file_headers[0] == 'HTTP/1.1 404 Not Found') {
error_log('getDurationFromFile try 1, File (' . $videoFile . ') Not Found');
$videoFile = $hls;
}
}
if (!file_exists($videoFile)) {
$file_headers = @get_headers($videoFile);
if (!$file_headers || $file_headers[0] == 'HTTP/1.1 404 Not Found') {
error_log('getDurationFromFile try 2, File (' . $videoFile . ') Not Found');
$videoFile = '';
}
}
if (empty($videoFile)) {
return "EE:EE:EE";
}
$videoFile = escapeshellarg($videoFile);
/**
* @var string $cmd
*/
//$cmd = 'ffprobe -i ' . $file . ' -sexagesimal -show_entries format=duration -v quiet -of csv="p=0"';
eval('$cmd=get_ffprobe()." -i {$videoFile} -sexagesimal -show_entries format=duration -v quiet -of csv=\\"p=0\\"";');
$cmd = removeUserAgentIfNotURL($cmd);
exec($cmd . ' 2>&1', $output, $return_val);
if ($return_val !== 0) {
error_log('{"status":"error", "msg":' . json_encode($output) . ' ,"return_val":' . json_encode($return_val) . ', "where":"getDuration", "cmd":"' . $cmd . '"}');
// fix ffprobe
$duration = "EE:EE:EE";
} else {
preg_match("/([0-9]+:[0-9]+:[0-9]{2})/", $output[0], $match);
if (!empty($match[1])) {
$duration = $match[1];
} else {
error_log('{"status":"error", "msg":' . json_encode($output) . ' ,"match_not_found":' . json_encode($match) . ' ,"return_val":' . json_encode($return_val) . ', "where":"getDuration", "cmd":"' . $cmd . '"}');
$duration = "EE:EE:EE";
}
}
error_log("Duration found: {$duration}");
if ($duration !== 'EE:EE:EE') {
$getDurationFromFile[$file] = $duration;
}
return $duration;
}
function wget($url, $filename, $debug = false)
{
if (empty($url) || $url == "php://input" || !isValidURL($url)) {
return false;
}
if ($lockfilename = wgetIsLocked($url)) {
if ($debug) {
_error_log("wget: ERROR the url is already downloading {$lockfilename} $url, $filename");
}
return false;
}
wgetLock($url);
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$content = @file_get_contents($url);
if (!empty($content) && file_put_contents($filename, $content) > 100) {
wgetRemoveLock($url);
return true;
}
wgetRemoveLock($url);
return false;
}
$filename = escapeshellarg($filename);
$url = escapeshellarg($url);
$cmd = "wget --tries=1 {$url} -O {$filename} --no-check-certificate";
if ($debug) {
_error_log("wget Start ({$cmd}) ");
}
//echo $cmd;
exec($cmd);
wgetRemoveLock($url);
if (!file_exists($filename)) {
_error_log("wget: ERROR the url does not download $url, $filename");
return false;
}
if ($_SERVER['SCRIPT_NAME'] !== '/plugin/Live/m3u8.php' && empty(filesize($filename))) {
_error_log("wget: ERROR the url download but is empty $url, $filename");
return true;
}
return false;
}
function getDirSize($dir, $forceNew = false)
{
global $_getDirSize;
if (!isset($_getDirSize)) {
$_getDirSize = [];
}
if (empty($forceNew) && isset($_getDirSize[$dir])) {
return $_getDirSize[$dir];
}
_error_log("getDirSize: start {$dir}");
if (isWindows()) {
$return = foldersize($dir);
$_getDirSize[$dir] = $return;
return $return;
} else {
$command = "du -sb {$dir}";
exec($command . " < /dev/null 2>&1", $output, $return_val);
if ($return_val !== 0) {
_error_log("getDirSize: ERROR ON Command {$command}");
$return = 0;
$_getDirSize[$dir] = $return;
return $return;
} else {
if (!empty($output[0])) {
preg_match("/^([0-9]+).*/", $output[0], $matches);
}
if (!empty($matches[1])) {
_error_log("getDirSize: found {$matches[1]} from - {$output[0]}");
$return = intval($matches[1]);
$_getDirSize[$dir] = $return;
return $return;
}
_error_log("getDirSize: ERROR on pregmatch {$output[0]}");
$return = 0;
$_getDirSize[$dir] = $return;
return $return;
}
}
}
function convertVideoFileWithFFMPEG($fromFileLocation, $toFileLocation, $logFile = '', $try = 0)
{
$parts = explode('?', $fromFileLocation);
$localFileLock = getCacheDir() . 'convertVideoFileWithFFMPEG_' . md5($parts[0]) . ".lock";
$ageInSeconds = time() - @filemtime($localFileLock);
if ($ageInSeconds > 60) {
_error_log("convertVideoFileWithFFMPEG: age: {$ageInSeconds} too long without change, unlock it " . $fromFileLocation);
@unlink($localFileLock);
} elseif (file_exists($localFileLock)) {
_error_log("convertVideoFileWithFFMPEG: age: {$ageInSeconds} download from CDN There is a process running for " . $fromFileLocation);
return false;
} else {
_error_log("convertVideoFileWithFFMPEG: creating file: localFileLock: {$localFileLock} toFileLocation: {$toFileLocation}");
}
make_path($toFileLocation);
file_put_contents($localFileLock, time());
$fromFileLocationEscaped = escapeshellarg($fromFileLocation);
$toFileLocationEscaped = escapeshellarg($toFileLocation);
$format = pathinfo($toFileLocation, PATHINFO_EXTENSION);
if ($format == 'mp3') {
switch ($try) {
case 0:
$command = get_ffmpeg() . " -i \"{$fromFileLocation}\" -c:a libmp3lame \"{$toFileLocation}\"";
break;
default:
return false;
break;
}
} else {
if ($try === 0 && preg_match('/_offline\.mp4/', $toFileLocation)) {
$try = 'offline';
$fromFileLocationEscaped = "\"$fromFileLocation\"";
$command = get_ffmpeg() . " -i {$fromFileLocationEscaped} -crf 30 {$toFileLocationEscaped}";
} else {
switch ($try) {
case 0:
$command = get_ffmpeg() . " -i {$fromFileLocationEscaped} -c:v libx264 -preset veryfast -crf 23 -c:a aac -b:a 128k {$toFileLocationEscaped}";
break;
case 1:
$command = get_ffmpeg() . " -i {$fromFileLocationEscaped} -c copy {$toFileLocationEscaped}";
break;
case 2:
$command = get_ffmpeg() . " -allowed_extensions ALL -y -i {$fromFileLocationEscaped} -c:v copy -c:a copy -bsf:a aac_adtstoasc -strict -2 {$toFileLocationEscaped}";
break;
case 3:
$command = get_ffmpeg() . " -y -i {$fromFileLocationEscaped} -c:v copy -c:a copy -bsf:a aac_adtstoasc -strict -2 {$toFileLocationEscaped}";
break;
default:
return false;
break;
}
}
}
if (!empty($logFile)) {
$progressFile = getConvertVideoFileWithFFMPEGProgressFilename($toFileLocation);
} else {
$progressFile = $logFile;
}
$progressFileEscaped = escapeshellarg($progressFile);
$command .= " 1> {$progressFileEscaped} 2>&1";
$command = removeUserAgentIfNotURL($command);
_error_log("convertVideoFileWithFFMPEG try[{$try}]: " . $command . ' ' . json_encode(debug_backtrace()));
_session_write_close();
_mysql_close();
exec($command, $output, $return);
_session_start();
_mysql_connect();
_error_log("convertVideoFileWithFFMPEG try[{$try}] output: " . json_encode($output));
unlink($localFileLock);
return ['return' => $return, 'output' => $output, 'command' => $command, 'fromFileLocation' => $fromFileLocation, 'toFileLocation' => $toFileLocation, 'progressFile' => $progressFile];
}
function rrmdirCommandLine($dir, $async = false)
{
if (is_dir($dir)) {
$dir = escapeshellarg($dir);
if (isWindows()) {
$command = ('rd /s /q ' . $dir);
} else {
$command = ('rm -fR ' . $dir);
}
if ($async) {
return execAsync($command);
} else {
return exec($command);
}
}
}
function unzipDirectory($filename, $destination)
{
// Set memory limit and execution time to avoid issues with large files
ini_set('memory_limit', '-1');
set_time_limit(0);
// Escape the input parameters to prevent command injection attacks
$filename = escapeshellarg($filename);
$destination = escapeshellarg($destination);
// Build the command for unzipping the file
$cmd = "unzip -q -o {$filename} -d {$destination} 2>&1";
// Log the command for debugging purposes
_error_log("unzipDirectory: {$cmd}");
// Execute the command and check the return value
exec($cmd, $output, $return_val);
if ($return_val !== 0) {
// If the unzip command fails, try using PHP's ZipArchive class as a fallback
if (class_exists('ZipArchive')) {
$zip = new ZipArchive();
if ($zip->open($filename) === true) {
$zip->extractTo($destination);
$zip->close();
_error_log("unzipDirectory: Success {$destination}");
} else {
_error_log("unzipDirectory: Error opening zip archive: {$filename}");
}
} else {
_error_log("unzipDirectory: Error: ZipArchive class is not available");
}
} else {
_error_log("unzipDirectory: Success {$destination}");
}
// Delete the original zip file
_error_log("unzipDirectory($filename) unlink line=" . __LINE__);
@unlink($filename);
}
function getPIDUsingPort($port)
{
$port = intval($port);
if (empty($port)) {
return false;
}
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$command = 'netstat -ano | findstr ' . $port;
exec($command, $output, $retval);
$pid = 0;
foreach ($output as $value) {
if (preg_match('/LISTENING[^0-9]+([0-9]+)/i', $value, $matches)) {
if (!empty($matches[1])) {
$pid = intval($matches[1]);
return $pid;
}
}
}
} else {
$command = 'lsof -n -i :' . $port . ' | grep LISTEN';
exec($command, $output, $retval);
$pid = 0;
foreach ($output as $value) {
if (preg_match('/[^ ] +([0-9]+).*/i', $value, $matches)) {
if (!empty($matches[1])) {
$pid = intval($matches[1]);
return $pid;
}
} elseif (preg_match('/lsof: not found/i', $value)) {
die('Please install lsof running this command: "sudo apt-get install lsof"');
}
}
}
return false;
}
function execAsync($command)
{
//$command = escapeshellarg($command);
// If windows, else
if (isWindows()) {
//echo $command;
//$pid = system("start /min ".$command. " > NUL");
//$commandString = "start /B " . $command;
//pclose($pid = popen($commandString, "r"));
_error_log($command);
$pid = exec($command, $output, $retval);
_error_log('execAsync Win: ' . json_encode($output) . ' ' . $retval);
} else {
$newCommand = $command . " > /dev/null 2>&1 & echo $!; ";
_error_log('execAsync Linux: ' . $newCommand);
$pid = exec($newCommand);
}
return $pid;
}
function killProcess($pid)
{
$pid = intval($pid);
if (empty($pid)) {
return false;
}
if (isWindows()) {
exec("taskkill /F /PID $pid");
} else {
exec("kill -9 $pid");
}
return true;
}

View file

@ -1896,48 +1896,6 @@ function decideMoveUploadedToVideos($tmp_name, $filename, $type = "video")
return $destinationFile;
}
function unzipDirectory($filename, $destination)
{
// Set memory limit and execution time to avoid issues with large files
ini_set('memory_limit', '-1');
set_time_limit(0);
// Escape the input parameters to prevent command injection attacks
$filename = escapeshellarg($filename);
$destination = escapeshellarg($destination);
// Build the command for unzipping the file
$cmd = "unzip -q -o {$filename} -d {$destination} 2>&1";
// Log the command for debugging purposes
_error_log("unzipDirectory: {$cmd}");
// Execute the command and check the return value
exec($cmd, $output, $return_val);
if ($return_val !== 0) {
// If the unzip command fails, try using PHP's ZipArchive class as a fallback
if (class_exists('ZipArchive')) {
$zip = new ZipArchive();
if ($zip->open($filename) === true) {
$zip->extractTo($destination);
$zip->close();
_error_log("unzipDirectory: Success {$destination}");
} else {
_error_log("unzipDirectory: Error opening zip archive: {$filename}");
}
} else {
_error_log("unzipDirectory: Error: ZipArchive class is not available");
}
} else {
_error_log("unzipDirectory: Success {$destination}");
}
// Delete the original zip file
_error_log("unzipDirectory($filename) unlink line=".__LINE__);
@unlink($filename);
}
function make_path($path)
{
$created = false;
@ -3485,23 +3443,6 @@ function rrmdir($dir)
}
}
function rrmdirCommandLine($dir, $async = false)
{
if (is_dir($dir)) {
$dir = escapeshellarg($dir);
if (isWindows()) {
$command = ('rd /s /q ' . $dir);
} else {
$command = ('rm -fR ' . $dir);
}
if ($async) {
return execAsync($command);
} else {
return exec($command);
}
}
}
/**
* You can now configure it on the configuration.php
@ -4587,50 +4528,6 @@ function getUsageFromURL($url)
return (int) $result;
}
function getDirSize($dir, $forceNew = false)
{
global $_getDirSize;
if (!isset($_getDirSize)) {
$_getDirSize = [];
}
if (empty($forceNew) && isset($_getDirSize[$dir])) {
return $_getDirSize[$dir];
}
_error_log("getDirSize: start {$dir}");
if (isWindows()) {
$return = foldersize($dir);
$_getDirSize[$dir] = $return;
return $return;
} else {
$command = "du -sb {$dir}";
exec($command . " < /dev/null 2>&1", $output, $return_val);
if ($return_val !== 0) {
_error_log("getDirSize: ERROR ON Command {$command}");
$return = 0;
$_getDirSize[$dir] = $return;
return $return;
} else {
if (!empty($output[0])) {
preg_match("/^([0-9]+).*/", $output[0], $matches);
}
if (!empty($matches[1])) {
_error_log("getDirSize: found {$matches[1]} from - {$output[0]}");
$return = intval($matches[1]);
$_getDirSize[$dir] = $return;
return $return;
}
_error_log("getDirSize: ERROR on pregmatch {$output[0]}");
$return = 0;
$_getDirSize[$dir] = $return;
return $return;
}
}
}
function foldersize($path)
{
$total_size = 0;
@ -5784,48 +5681,6 @@ function reloadSearchVar()
}
}
function wget($url, $filename, $debug = false)
{
if (empty($url) || $url == "php://input" || !isValidURL($url)) {
return false;
}
if ($lockfilename = wgetIsLocked($url)) {
if ($debug) {
_error_log("wget: ERROR the url is already downloading {$lockfilename} $url, $filename");
}
return false;
}
wgetLock($url);
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$content = @file_get_contents($url);
if (!empty($content) && file_put_contents($filename, $content) > 100) {
wgetRemoveLock($url);
return true;
}
wgetRemoveLock($url);
return false;
}
$filename = escapeshellarg($filename);
$url = escapeshellarg($url);
$cmd = "wget --tries=1 {$url} -O {$filename} --no-check-certificate";
if ($debug) {
_error_log("wget Start ({$cmd}) ");
}
//echo $cmd;
exec($cmd);
wgetRemoveLock($url);
if (!file_exists($filename)) {
_error_log("wget: ERROR the url does not download $url, $filename");
return false;
}
if ($_SERVER['SCRIPT_NAME'] !== '/plugin/Live/m3u8.php' && empty(filesize($filename))) {
_error_log("wget: ERROR the url download but is empty $url, $filename");
return true;
}
return false;
}
/**
* Copy remote file over HTTP one small chunk at a time.
*
@ -7250,73 +7105,7 @@ function get_ffprobe() {
return $ffprobe.$complement;
}
function getDurationFromFile($file)
{
global $config, $getDurationFromFile;
if (empty($file)) {
return "EE:EE:EE";
}
if (!isset($getDurationFromFile)) {
$getDurationFromFile = [];
}
if (!empty($getDurationFromFile[$file])) {
// I need to check again because I am recreating the file on the AI
//return $getDurationFromFile[$file];
}
$hls = str_replace(".zip", "/index.m3u8", $file);
$file = str_replace(".zip", ".mp4", $file);
// get movie duration HOURS:MM:SS.MICROSECONDS
$videoFile = $file;
if (!file_exists($videoFile)) {
$file_headers = @get_headers($videoFile);
if (!$file_headers || $file_headers[0] == 'HTTP/1.1 404 Not Found') {
error_log('getDurationFromFile try 1, File (' . $videoFile . ') Not Found');
$videoFile = $hls;
}
}
if (!file_exists($videoFile)) {
$file_headers = @get_headers($videoFile);
if (!$file_headers || $file_headers[0] == 'HTTP/1.1 404 Not Found') {
error_log('getDurationFromFile try 2, File (' . $videoFile . ') Not Found');
$videoFile = '';
}
}
if (empty($videoFile)) {
return "EE:EE:EE";
}
$videoFile = escapeshellarg($videoFile);
/**
* @var string $cmd
*/
//$cmd = 'ffprobe -i ' . $file . ' -sexagesimal -show_entries format=duration -v quiet -of csv="p=0"';
eval('$cmd=get_ffprobe()." -i {$videoFile} -sexagesimal -show_entries format=duration -v quiet -of csv=\\"p=0\\"";');
$cmd = removeUserAgentIfNotURL($cmd);
exec($cmd . ' 2>&1', $output, $return_val);
if ($return_val !== 0) {
error_log('{"status":"error", "msg":' . json_encode($output) . ' ,"return_val":' . json_encode($return_val) . ', "where":"getDuration", "cmd":"' . $cmd . '"}');
// fix ffprobe
$duration = "EE:EE:EE";
} else {
preg_match("/([0-9]+:[0-9]+:[0-9]{2})/", $output[0], $match);
if (!empty($match[1])) {
$duration = $match[1];
} else {
error_log('{"status":"error", "msg":' . json_encode($output) . ' ,"match_not_found":' . json_encode($match) . ' ,"return_val":' . json_encode($return_val) . ', "where":"getDuration", "cmd":"' . $cmd . '"}');
$duration = "EE:EE:EE";
}
}
error_log("Duration found: {$duration}");
if ($duration !== 'EE:EE:EE') {
$getDurationFromFile[$file] = $duration;
}
return $duration;
}
function removeUserAgentIfNotURL($cmd)
function removeUserAgentIfNotURL($cmd)
{
if (!preg_match('/ -i [\'"]?https?:/', $cmd)) {
$cmd = preg_replace('/-user_agent "[^"]+"/', '', $cmd);
@ -7360,82 +7149,6 @@ function convertVideoToMP3FileIfNotExists($videos_id)
}
}
function convertVideoFileWithFFMPEG($fromFileLocation, $toFileLocation, $logFile = '', $try = 0)
{
$parts = explode('?', $fromFileLocation);
$localFileLock = getCacheDir() . 'convertVideoFileWithFFMPEG_' . md5($parts[0]) . ".lock";
$ageInSeconds = time() - @filemtime($localFileLock);
if ($ageInSeconds > 60) {
_error_log("convertVideoFileWithFFMPEG: age: {$ageInSeconds} too long without change, unlock it " . $fromFileLocation);
@unlink($localFileLock);
} elseif (file_exists($localFileLock)) {
_error_log("convertVideoFileWithFFMPEG: age: {$ageInSeconds} download from CDN There is a process running for " . $fromFileLocation);
return false;
} else {
_error_log("convertVideoFileWithFFMPEG: creating file: localFileLock: {$localFileLock} toFileLocation: {$toFileLocation}");
}
make_path($toFileLocation);
file_put_contents($localFileLock, time());
$fromFileLocationEscaped = escapeshellarg($fromFileLocation);
$toFileLocationEscaped = escapeshellarg($toFileLocation);
$format = pathinfo($toFileLocation, PATHINFO_EXTENSION);
if ($format == 'mp3') {
switch ($try) {
case 0:
$command = get_ffmpeg() . " -i \"{$fromFileLocation}\" -c:a libmp3lame \"{$toFileLocation}\"";
break;
default:
return false;
break;
}
} else {
if ($try === 0 && preg_match('/_offline\.mp4/', $toFileLocation)) {
$try = 'offline';
$fromFileLocationEscaped = "\"$fromFileLocation\"";
$command = get_ffmpeg() . " -i {$fromFileLocationEscaped} -crf 30 {$toFileLocationEscaped}";
} else {
switch ($try) {
case 0:
$command = get_ffmpeg() . " -i {$fromFileLocationEscaped} -c:v libx264 -preset veryfast -crf 23 -c:a aac -b:a 128k {$toFileLocationEscaped}";
break;
case 1:
$command = get_ffmpeg() . " -i {$fromFileLocationEscaped} -c copy {$toFileLocationEscaped}";
break;
case 2:
$command = get_ffmpeg() . " -allowed_extensions ALL -y -i {$fromFileLocationEscaped} -c:v copy -c:a copy -bsf:a aac_adtstoasc -strict -2 {$toFileLocationEscaped}";
break;
case 3:
$command = get_ffmpeg() . " -y -i {$fromFileLocationEscaped} -c:v copy -c:a copy -bsf:a aac_adtstoasc -strict -2 {$toFileLocationEscaped}";
break;
default:
return false;
break;
}
}
}
if(!empty($logFile)){
$progressFile = getConvertVideoFileWithFFMPEGProgressFilename($toFileLocation);
}else{
$progressFile = $logFile;
}
$progressFileEscaped = escapeshellarg($progressFile);
$command .= " 1> {$progressFileEscaped} 2>&1";
$command = removeUserAgentIfNotURL($command);
_error_log("convertVideoFileWithFFMPEG try[{$try}]: " . $command . ' '.json_encode(debug_backtrace()));
_session_write_close();
_mysql_close();
exec($command, $output, $return);
_session_start();
_mysql_connect();
_error_log("convertVideoFileWithFFMPEG try[{$try}] output: " . json_encode($output));
unlink($localFileLock);
return ['return' => $return, 'output' => $output, 'command' => $command, 'fromFileLocation' => $fromFileLocation, 'toFileLocation' => $toFileLocation, 'progressFile' => $progressFile];
}
function m3u8ToMP4($input)
{
$videosDir = getVideosDir();
@ -7806,81 +7519,11 @@ function sendSocketMessageToNone($msg, $callbackJSFunction = "")
return sendSocketMessage($msg, $callbackJSFunction, -1);
}
function execAsync($command)
{
//$command = escapeshellarg($command);
// If windows, else
if (isWindows()) {
//echo $command;
//$pid = system("start /min ".$command. " > NUL");
//$commandString = "start /B " . $command;
//pclose($pid = popen($commandString, "r"));
_error_log($command);
$pid = exec($command, $output, $retval);
_error_log('execAsync Win: ' . json_encode($output) . ' ' . $retval);
} else {
$newCommand = $command . " > /dev/null 2>&1 & echo $!; ";
_error_log('execAsync Linux: ' . $newCommand);
$pid = exec($newCommand);
}
return $pid;
}
function killProcess($pid)
{
$pid = intval($pid);
if (empty($pid)) {
return false;
}
if (isWindows()) {
exec("taskkill /F /PID $pid");
} else {
exec("kill -9 $pid");
}
return true;
}
function isWindows()
{
return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
}
function getPIDUsingPort($port)
{
$port = intval($port);
if (empty($port)) {
return false;
}
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$command = 'netstat -ano | findstr ' . $port;
exec($command, $output, $retval);
$pid = 0;
foreach ($output as $value) {
if (preg_match('/LISTENING[^0-9]+([0-9]+)/i', $value, $matches)) {
if (!empty($matches[1])) {
$pid = intval($matches[1]);
return $pid;
}
}
}
} else {
$command = 'lsof -n -i :' . $port . ' | grep LISTEN';
exec($command, $output, $retval);
$pid = 0;
foreach ($output as $value) {
if (preg_match('/[^ ] +([0-9]+).*/i', $value, $matches)) {
if (!empty($matches[1])) {
$pid = intval($matches[1]);
return $pid;
}
} elseif (preg_match('/lsof: not found/i', $value)) {
die('Please install lsof running this command: "sudo apt-get install lsof"');
}
}
}
return false;
}
function isURL200($url, $forceRecheck = false)
{
global $_isURL200;
@ -10573,3 +10216,4 @@ require_once __DIR__.'/functionSecurity.php';
require_once __DIR__.'/functionMySQL.php';
require_once __DIR__.'/functionDocker.php';
require_once __DIR__.'/functionImages.php';
require_once __DIR__.'/functionExec.php';

View file

@ -4147,6 +4147,7 @@ if (!class_exists('Video')) {
return $__getPaths[$videoFilename];
}
$cleanVideoFilename = self::getCleanFilenameFromFile($videoFilename);
//var_dump('--'.$cleanVideoFilename, '++'.$videoFilename);
$videosDir = self::getStoragePath();
$path = addLastSlash("{$videosDir}{$cleanVideoFilename}");
@ -4193,6 +4194,7 @@ if (!class_exists('Video')) {
if (!empty($parts[1]) && $parts[1] == 'index.m3u8') {
$videoFilename = $parts[1];
}
//var_dump('--'.$videoFilename, $paths);
return "{$paths['url']}{$videoFilename}";
}
@ -4336,6 +4338,10 @@ if (!class_exists('Video')) {
$path_parts = pathinfo($filename);
if (!empty($path_parts['extension'])) {
if ($path_parts['extension'] == 'vtt' || $path_parts['extension'] == 'srt') {
$search = ['.Chapters'];
foreach ($search as $value) {
$path_parts['filename'] = str_ireplace($value, '', $path_parts['filename']);
}
$p = explode('.', $path_parts['filename']);
if (count($p) > 1) {
array_pop($p);

View file

@ -5,10 +5,11 @@ require_once $global['systemRootPath'] . 'plugin/Plugin.abstract.php';
require_once $global['systemRootPath'] . 'plugin/AI/Objects/Ai_responses.php';
require_once $global['systemRootPath'] . 'plugin/AI/Objects/Ai_metatags_responses.php';
require_once $global['systemRootPath'] . 'plugin/AI/Objects/Ai_transcribe_responses.php';
require_once $global['systemRootPath'] . 'plugin/AI/Objects/Ai_responses_json.php';
require_once $global['systemRootPath'] . 'plugin/AI/Objects/Ai_scheduler.php';
class AI extends PluginAbstract {
class AI extends PluginAbstract
{
static $typeTranslation = 'translation';
static $typeTranscription = 'transcription';
@ -52,36 +53,43 @@ class AI extends PluginAbstract {
static $url = 'https://ai.ypt.me/';
static $url_test = 'http://192.168.0.2:81/AI/';
static function getMetadataURL(){
static function getMetadataURL()
{
self::$isTest = ($_SERVER["SERVER_NAME"] == "vlu.me");
return self::$isTest?self::$url_test:self::$url;
return self::$isTest ? self::$url_test : self::$url;
}
static function getPricesURL(){
return self::$url.'prices.json.php';
static function getPricesURL()
{
return self::$url . 'prices.json.php';
}
public function getDescription() {
public function getDescription()
{
$desc = "Optimize video visibility with AI-driven meta-tag suggestions and automatic transcription for enhanced SEO performance.";
$help = "<br><small><a href='https://github.com/WWBN/AVideo/wiki/AI-Plugin' target='_blank'><i class='fas fa-question-circle'></i> Help</a></small>";
//$desc .= $this->isReadyLabel(array('YPTWallet'));
return $desc.$help;
return $desc . $help;
}
public function getName() {
public function getName()
{
return "AI";
}
public function getUUID() {
public function getUUID()
{
return "AI-5ee8405eaaa16";
}
public function getPluginVersion() {
return "4.0";
public function getPluginVersion()
{
return "6.0";
}
public function getEmptyDataObject() {
public function getEmptyDataObject()
{
$obj = new stdClass();
$obj->AccessToken = "";
/*
@ -102,7 +110,8 @@ class AI extends PluginAbstract {
return $obj;
}
static function getVideoTranslationMetadata($videos_id, $lang, $langName){
static function getVideoShortsMetadata($videos_id)
{
global $global;
$obj = new stdClass();
$obj->error = true;
@ -114,7 +123,42 @@ class AI extends PluginAbstract {
return $obj;
}
if(!Video::canEdit($videos_id)){
if (!Video::canEdit($videos_id)) {
$obj->msg = 'you cannot edit this video';
return $obj;
}
$trascription = Ai_responses::getTranscriptionVtt($videos_id);
if (empty($trascription)) {
$obj->msg = 'empty transcription';
return $obj;
}
//var_dump($paths);exit;
$obj->response = array(
'type' => AI::$typeShorts,
'transcription' => $trascription,
);
$obj->error = false;
return $obj;
}
static function getVideoTranslationMetadata($videos_id, $lang, $langName)
{
global $global;
$obj = new stdClass();
$obj->error = true;
$obj->msg = '';
$obj->response = array();
if (empty($videos_id)) {
$obj->msg = 'empty videos id';
return $obj;
}
if (!Video::canEdit($videos_id)) {
$obj->msg = 'you cannot edit this video';
return $obj;
}
@ -122,12 +166,12 @@ class AI extends PluginAbstract {
$video = new Video('', '', $videos_id);
$filename = $video->getFilename();
if(AVideoPlugin::isEnabledByName('SubtitleSwitcher')){
if (AVideoPlugin::isEnabledByName('SubtitleSwitcher')) {
SubtitleSwitcher::transcribe($videos_id, false);
}
$firstVTTPath = AI::getFirstVTTFile($videos_id);
$vttURL = str_replace($global['systemRootPath'], $global['webSiteRootURL'],$firstVTTPath);
$vttURL = str_replace($global['systemRootPath'], $global['webSiteRootURL'], $firstVTTPath);
//var_dump($paths);exit;
$obj->response = array(
@ -142,7 +186,8 @@ class AI extends PluginAbstract {
return $obj;
}
static function getVideoBasicMetadata($videos_id){
static function getVideoBasicMetadata($videos_id)
{
global $global;
$obj = new stdClass();
$obj->error = true;
@ -154,7 +199,7 @@ class AI extends PluginAbstract {
return $obj;
}
if(!Video::canEdit($videos_id)){
if (!Video::canEdit($videos_id)) {
$obj->msg = 'you cannot edit this video';
return $obj;
}
@ -162,13 +207,13 @@ class AI extends PluginAbstract {
$video = new Video('', '', $videos_id);
$filename = $video->getFilename();
if(AVideoPlugin::isEnabledByName('SubtitleSwitcher')){
if (AVideoPlugin::isEnabledByName('SubtitleSwitcher')) {
SubtitleSwitcher::transcribe($videos_id, false);
}
/*
*/
$firstVTTPath = AI::getFirstVTTFile($videos_id);
$vttURL = str_replace(getVideosDir(), $global['webSiteRootURL'],$firstVTTPath);
$vttURL = str_replace(getVideosDir(), $global['webSiteRootURL'], $firstVTTPath);
//var_dump($paths);exit;
$obj->response = array(
'type' => AI::$typeBasic,
@ -185,7 +230,8 @@ class AI extends PluginAbstract {
return $obj;
}
static function getVideoTranscriptionMetadata($videos_id, $lang){
static function getVideoTranscriptionMetadata($videos_id, $lang)
{
$obj = new stdClass();
$obj->error = true;
$obj->msg = '';
@ -196,7 +242,7 @@ class AI extends PluginAbstract {
return $obj;
}
if(!Video::canEdit($videos_id)){
if (!Video::canEdit($videos_id)) {
$obj->msg = 'you cannot edit this video';
return $obj;
}
@ -205,7 +251,7 @@ class AI extends PluginAbstract {
$mp3 = false;
$mp3s = self::getLowerMP3($videos_id);
$fsize = 0;
if($mp3s['isValid']){
if ($mp3s['isValid']) {
$mp3 = $mp3s['lower']['paths']['url'];
$fsize = filesize($mp3s['lower']['paths']['path']);
}
@ -225,7 +271,8 @@ class AI extends PluginAbstract {
return $obj;
}
static function getTokenForVideo($videos_id, $ai_responses_id, $param){
static function getTokenForVideo($videos_id, $ai_responses_id, $param)
{
global $global;
$obj = new stdClass();
$obj->videos_id = $videos_id;
@ -238,38 +285,41 @@ class AI extends PluginAbstract {
}
static function getTokenFromRequest(){
static function getTokenFromRequest()
{
if(empty($_REQUEST['token'])){
if (empty($_REQUEST['token'])) {
return false;
}
$string = decryptString($_REQUEST['token']);
if(empty($string)){
if (empty($string)) {
return false;
}
$json = _json_decode($string);
if(empty($json)){
if (empty($json)) {
return false;
}
return $json;
}
static function getMP3Path($videos_id){
static function getMP3Path($videos_id)
{
$convert = convertVideoToMP3FileIfNotExists($videos_id);
if(empty($convert) || empty($convert['url'])){
if (empty($convert) || empty($convert['url'])) {
return false;
}
return $convert;
}
static function getMP3LowerPath($videos_id){
static function getMP3LowerPath($videos_id)
{
$convert = self::getMP3Path($videos_id);
if(empty($convert) || empty($convert['url'])){
if (empty($convert) || empty($convert['url'])) {
return false;
}
$convert['path'] = str_replace('.mp3', '_Low.mp3', $convert['path']);
@ -277,7 +327,8 @@ class AI extends PluginAbstract {
return $convert;
}
static function getMP3RegularAndLower($videos_id){
static function getMP3RegularAndLower($videos_id)
{
$arrayRegular = array(
'paths' => false,
'duration' => false,
@ -292,7 +343,7 @@ class AI extends PluginAbstract {
);
$paths = self::getMP3Path($videos_id);
if(!empty($paths)){
if (!empty($paths)) {
$duration = getDurationFromFile($paths['path']);
$durationInSeconds = durationToSeconds($duration);
$video = new Video('', '', $videos_id);
@ -317,7 +368,7 @@ class AI extends PluginAbstract {
);
$pathsLower = self::getMP3LowerPath($videos_id);
if(!empty($pathsLower )){
if (!empty($pathsLower)) {
$duration = getDurationFromFile($pathsLower['path']);
$durationInSeconds = durationToSeconds($duration);
$arrayLower = array(
@ -332,18 +383,18 @@ class AI extends PluginAbstract {
}
$msg = '';
$isValid = false;
if($arrayRegular['isValid'] && $arrayLower['isValid']){
if ($arrayRegular['isValid'] && $arrayLower['isValid']) {
$diff = abs($arrayRegular['durationInSeconds'] - $arrayLower['durationInSeconds']);
if ($diff <= 2) {
$isValid = true;
}else{
} else {
$msg = "durationInSeconds are not the same regular={$arrayRegular['durationInSeconds']} lower={$arrayLower['durationInSeconds']}";
}
}else{
if(!$arrayRegular['isValid']){
} else {
if (!$arrayRegular['isValid']) {
$msg = 'Regular MP3 is invalid';
}
if(!$arrayRegular['isValid']){
if (!$arrayRegular['isValid']) {
$msg .= ' Lower MP3 is invalid';
}
}
@ -357,31 +408,33 @@ class AI extends PluginAbstract {
return $response;
}
static function getLowerMP3($videos_id, $try = 0){
static function getLowerMP3($videos_id, $try = 0)
{
$mp3s = self::getMP3RegularAndLower($videos_id);
if($mp3s['regular']['isValid']){
if(!$mp3s['isValid']){
if ($mp3s['regular']['isValid']) {
if (!$mp3s['isValid']) {
ini_set('max_execution_time', 300);
set_time_limit(300);
if(file_exists($mp3s['lower']['paths']['path'])){
if (file_exists($mp3s['lower']['paths']['path'])) {
unlink($mp3s['lower']['paths']['path']);
}
$fromFileLocationEscaped = escapeshellarg($mp3s['regular']['paths']['path']);
$toFileLocationEscaped = escapeshellarg($mp3s['lower']['paths']['path']);
$command = get_ffmpeg()." -i {$fromFileLocationEscaped} -ar 16000 -ac 1 -b:a 16k {$toFileLocationEscaped}";
$command = get_ffmpeg() . " -i {$fromFileLocationEscaped} -ar 16000 -ac 1 -b:a 16k {$toFileLocationEscaped}";
$command = removeUserAgentIfNotURL($command);
exec($command, $output);
_error_log('getLowerMP3: '.json_encode($output));
_error_log('getLowerMP3: ' . json_encode($output));
return self::getMP3RegularAndLower($videos_id);
}
}else{
} else {
return $mp3s;
}
return $mp3s;
}
public function getPluginMenu() {
public function getPluginMenu()
{
global $global;
return '<button onclick="avideoModalIframeLarge(webSiteRootURL+\'plugin/AI/View/editor.php\')" class="btn btn-primary btn-xs btn-block"><i class="fa fa-edit"></i> Edit</button>';
}
@ -398,18 +451,19 @@ class AI extends PluginAbstract {
return $btn;
}
public static function getTagTypeId() {
public static function getTagTypeId()
{
$VideoTags = AVideoPlugin::isEnabledByName('VideoTags');
if(empty($VideoTags)){
if (empty($VideoTags)) {
return false;
}
$typeName = 'Keywords';
$row = TagsTypes::getFromName($typeName);
if(empty($row)){
if (empty($row)) {
$tagType = new TagsTypes(0);
$tagType->setName($typeName );
$tagType->setName($typeName);
return $tagType->save();
}else{
} else {
return $row['id'];
}
}
@ -428,9 +482,10 @@ class AI extends PluginAbstract {
return true;
}
static function getVTTLanguageCodes($videos_id) {
static function getVTTLanguageCodes($videos_id)
{
$video = new Video('', '', $videos_id);
$dir = getVideosDir().DIRECTORY_SEPARATOR.$video->getFilename();
$dir = getVideosDir() . DIRECTORY_SEPARATOR . $video->getFilename();
$languageCodes = [];
$filePattern = '/video_[\w\d]+\.([\w\d_]+)\.vtt$/';
@ -446,12 +501,14 @@ class AI extends PluginAbstract {
return array_unique($languageCodes); // Return unique language codes
}
public function getFooterCode() {
public function getFooterCode()
{
global $global;
include $global['systemRootPath'] . 'plugin/AI/footer.php';
}
static function getVTTFiles($videos_id) {
static function getVTTFiles($videos_id)
{
$video = new Video('', '', $videos_id);
$filename = $video->getFilename();
$dir = getVideosDir() . "{$filename}/";
@ -464,24 +521,74 @@ class AI extends PluginAbstract {
}
static function getFirstVTTFile($videos_id){
static function getFirstVTTFile($videos_id)
{
$vttFiles = self::getVTTFiles($videos_id);
if(empty($vttFiles)){
if (empty($vttFiles)) {
return false;
}
return $vttFiles[0];
}
static function getProgressBarHTML($classname, $text){
static function getProgressBarHTML($classname, $text)
{
return '
<div class="progress progressAI '.$classname.'" style="display:none;">
<div class="progress progressAI ' . $classname . '" style="display:none;">
<div class="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
<strong class="progressAITitle">'.$text.'</strong>
<strong class="progressAITitle">' . $text . '</strong>
</div>
</div>';
}
function executeEveryMinute()
{
$rows = Ai_scheduler::getAllActive();
if (!empty($rows) && is_array($rows)) {
foreach ($rows as $row) {
$ai = new Ai_scheduler($row['id']);
$ai->setStatus(Ai_scheduler::$statusExecuted);
$ai->save();
if ($ai->getAi_scheduler_type() === Ai_scheduler::$typeCutVideo) {
$obj = _json_decode($ai->getJson());
_error_log('AI:videoCut start '.$ai->getJson());
$vid = Video::getVideoLight($obj->videos_id);
$sources = getVideosURLOnly($vid['filename'], false);
$source = end($sources);
if (!empty($source)) {
_error_log('AI:videoCut source found '.$source["url"]);
$duration_in_seconds = $obj->endTimeInSeconds - $obj->startTimeInSeconds;
$date = date('YMDHis');
$videoFileName = "cut{$obj->videos_id}_{$date}_{$obj->startTimeInSeconds}{$obj->endTimeInSeconds}";
$video = new Video($obj->title, $videoFileName);
$video->setDescription($obj->description);
$video->setUsers_id($obj->users_id);
$video->setDuration_in_seconds($duration_in_seconds);
$video->setDuration(secondsToDuration($duration_in_seconds));
$newVideos_id = $video->save(false, true);
if (!empty($newVideos_id)) {
_error_log('AI:videoCut new video saved videos_id='.$newVideos_id);
$outputFile = Video::getPathToFile("{$videoFileName}.mp4");
cutVideoWithFFmpeg($source['url'], $obj->startTimeInSeconds, $obj->endTimeInSeconds, $outputFile);
$video = new Video('', '', $newVideos_id);
if(file_exists($outputFile)){
$video->setAutoStatus(Video::$statusActive);
AVideoPlugin::onUploadIsDone($newVideos_id);
AVideoPlugin::afterNewVideo($newVideos_id);
_error_log('AI:videoCut create file success '.$outputFile);
$url = Video::getURL($newVideos_id);
$obj->socketResponse = sendSocketSuccessMessageToUsers_id("<a href='$url'>Video cutted</a>", $obj->users_id);
}else{
$video->delete(true);
_error_log('AI:videoCut error on create file '.$outputFile);
}
}
//cutVideoWithFFmpeg($source['url'], $startTimeInSeconds, $endTimeInSeconds, $outputFile);
}
}
}
}
}
}

View file

@ -154,6 +154,7 @@ class Ai_responses extends ObjectYPT
{
global $global;
$sql = "SELECT
j.*,
m.*,
m.id as ai_metatags_responses_id,
t.*,
@ -166,10 +167,13 @@ class Ai_responses extends ObjectYPT
ai_metatags_responses AS m ON r.id = m.ai_responses_id
LEFT JOIN
ai_transcribe_responses AS t ON r.id = t.ai_responses_id
LEFT JOIN
ai_responses_json AS j ON r.id = j.ai_responses_id
WHERE
r.videos_id = ?
UNION
SELECT
j.*,
m.*,
m.id as ai_metatags_responses_id,
t.*,
@ -182,6 +186,8 @@ class Ai_responses extends ObjectYPT
ai_metatags_responses AS m ON r.id = m.ai_responses_id
RIGHT JOIN
ai_transcribe_responses AS t ON r.id = t.ai_responses_id
RIGHT JOIN
ai_responses_json AS j ON r.id = j.ai_responses_id
WHERE
r.videos_id = ?";

View file

@ -0,0 +1,72 @@
<?php
require_once dirname(__FILE__) . '/../../../videos/configuration.php';
class Ai_responses_json extends ObjectYPT {
protected $id,$response,$ai_type,$ai_responses_id;
static function getSearchFieldsNames() {
return array('response','ai_type');
}
static function getTableName() {
return 'ai_responses_json';
}
function setId($id) {
$this->id = intval($id);
}
function setResponse($response) {
if(!is_string($response)){
$response = json_encode($response);
}
$this->response = $response;
}
function setAi_type($ai_type) {
$this->ai_type = $ai_type;
}
function setAi_responses_id($ai_responses_id) {
$this->ai_responses_id = intval($ai_responses_id);
}
function getId() {
return intval($this->id);
}
function getResponse() {
return $this->response;
}
function getAi_type() {
return $this->ai_type;
}
function getAi_responses_id() {
return intval($this->ai_responses_id);
}
static function getAllFromAIType($type, $videos_id = 0) {
if (!static::isTableInstalled()) {
return false;
}
$sql = "SELECT * FROM ai_responses_json arj LEFT JOIN ai_responses ar ON ar.id = arj.ai_responses_id WHERE ai_type = ? ";
$format = 's';
$values = array($type);
if(!empty($videos_id)){
$sql .= " AND videos_id = ? ";
$format .= 'i';
$values[] = $videos_id;
}
$sql .= self::getSqlFromPost();
$res = sqlDAL::readSql($sql, $format, $values);
$fullData = sqlDAL::fetchAllAssoc($res);
sqlDAL::close($res);
return $fullData;
}
}

View file

@ -0,0 +1,77 @@
<?php
require_once dirname(__FILE__) . '/../../../videos/configuration.php';
class Ai_scheduler extends ObjectYPT {
static $statusActive = 'a';
static $statusInactive = 'i';
static $statusExecuted = 'e';
static $typeCutVideo = 'cutVideo';
protected $id,$json,$status,$ai_scheduler_type,$created_php_time,$modified_php_time;
static function getSearchFieldsNames() {
return array('ai_scheduler_type');
}
static function getTableName() {
return 'ai_scheduler';
}
function setId($id) {
$this->id = intval($id);
}
function setJson($json) {
if(!is_string($json)){
$json = _json_encode($json);
}
$this->json = $json;
}
function setStatus($status) {
$this->status = $status;
}
function setAi_scheduler_type($ai_scheduler_type) {
$this->ai_scheduler_type = $ai_scheduler_type;
}
function setCreated_php_time($created_php_time) {
$this->created_php_time = $created_php_time;
}
function setModified_php_time($modified_php_time) {
$this->modified_php_time = $modified_php_time;
}
function getId() {
return intval($this->id);
}
function getJson() {
return $this->json;
}
function getStatus() {
return $this->status;
}
function getAi_scheduler_type() {
return $this->ai_scheduler_type;
}
function getCreated_php_time() {
return $this->created_php_time;
}
function getModified_php_time() {
return $this->modified_php_time;
}
}

View file

@ -41,12 +41,15 @@ switch ($_REQUEST['type']) {
$obj = AI::getVideoTranslationMetadata($videos_id, $_REQUEST['lang'], $_REQUEST['langName']);
$param['lang'] = $_REQUEST['lang'];
break;
case AI::$typeShorts:
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
$obj = AI::getVideoShortsMetadata($videos_id);
break;
default:
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
forbiddenPage("Undefined type {$_REQUEST['type']}");
break;
}
if ($obj->error) {
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
forbiddenPage('Something happen: ' . $obj->msg);
@ -86,7 +89,7 @@ if (empty($jsonProgressDecoded->canRequestNew)) {
$obj->error = true;
$obj->msg = $jsonProgressDecoded->msg;
$obj->jsonProgressDecoded = $jsonProgressDecoded;
if(empty($obj->msg)){
if (empty($obj->msg)) {
$obj->msg = "A process for Video ID {$videos_id} is currently active and in progress.";;
}

View file

@ -0,0 +1,47 @@
<?php
require_once '../../videos/configuration.php';
header('Content-Type: application/json');
$videos_id = getVideos_id();
if (empty($videos_id)) {
forbiddenPage('Videos ID is required');
}
if (!isset($_REQUEST['startTimeInSeconds'])) {
forbiddenPage('startTimeInSeconds is required');
}
if (!isset($_REQUEST['endTimeInSeconds'])) {
forbiddenPage('endTimeInSeconds is required');
}
if (!AVideoPlugin::isEnabledByName('AI')) {
forbiddenPage('AI plugin is disabled');
}
if (!Video::canEdit($videos_id)) {
forbiddenPage('You cannot edit this video');
}
$objSchedule = AVideoPlugin::getDataObjectIfEnabled('Scheduler');
if (empty($objSchedule)) {
forbiddenPage('Scheduler is required');
}
$obj = new stdClass();
$obj->videos_id = $videos_id;
$obj->startTimeInSeconds = $_REQUEST['startTimeInSeconds'];
$obj->endTimeInSeconds = $_REQUEST['endTimeInSeconds'];
$obj->users_id = User::getId();
$obj->title = $_REQUEST['title'];
$obj->description = $_REQUEST['description'];
$ai = new Ai_scheduler(0);
$ai->setAi_scheduler_type(Ai_scheduler::$typeCutVideo);
$ai->setJson($obj);
$ai->setStatus(Ai_scheduler::$statusActive);
$obj->schedulerSaved = $ai->save();
echo json_encode($obj);

View file

@ -60,3 +60,33 @@ CREATE TABLE IF NOT EXISTS `ai_transcribe_responses` (
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `ai_responses_json` (
`id` INT NOT NULL AUTO_INCREMENT,
`response` MEDIUMTEXT NOT NULL,
`created` DATETIME NULL,
`modified` DATETIME NULL,
`ai_type` VARCHAR(45) NOT NULL,
`ai_responses_id` INT NOT NULL,
PRIMARY KEY (`id`),
INDEX `typeAiIndex` (`ai_type` ASC) ,
INDEX `fk_ai_responses_json_ai_responses1_idx` (`ai_responses_id` ASC) ,
CONSTRAINT `fk_ai_responses_json_ai_responses1`
FOREIGN KEY (`ai_responses_id`)
REFERENCES `ai_responses` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `ai_scheduler` (
`id` INT NOT NULL AUTO_INCREMENT,
`json` MEDIUMTEXT NOT NULL,
`status` CHAR(1) NOT NULL DEFAULT 'a',
`ai_scheduler_type` VARCHAR(45) NOT NULL,
`created` DATETIME NULL,
`modified` DATETIME NULL,
`created_php_time` BIGINT NULL,
`modified_php_time` BIGINT NULL,
PRIMARY KEY (`id`),
INDEX `status_ai_schedler_index` (`status` ASC))
ENGINE = InnoDB;

View file

@ -0,0 +1,16 @@
CREATE TABLE IF NOT EXISTS `ai_responses_json` (
`id` INT NOT NULL AUTO_INCREMENT,
`response` MEDIUMTEXT NOT NULL,
`created` DATETIME NULL,
`modified` DATETIME NULL,
`ai_type` VARCHAR(45) NOT NULL,
`ai_responses_id` INT NOT NULL,
PRIMARY KEY (`id`),
INDEX `typeAiIndex` (`ai_type` ASC) ,
INDEX `fk_ai_responses_json_ai_responses1_idx` (`ai_responses_id` ASC) ,
CONSTRAINT `fk_ai_responses_json_ai_responses1`
FOREIGN KEY (`ai_responses_id`)
REFERENCES `ai_responses` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;

View file

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS `ai_scheduler` (
`id` INT NOT NULL AUTO_INCREMENT,
`json` MEDIUMTEXT NOT NULL,
`status` CHAR(1) NOT NULL DEFAULT 'a',
`ai_scheduler_type` VARCHAR(45) NOT NULL,
`created` DATETIME NULL,
`modified` DATETIME NULL,
`created_php_time` BIGINT NULL,
`modified_php_time` BIGINT NULL,
PRIMARY KEY (`id`),
INDEX `status_ai_schedler_index` (`status` ASC))
ENGINE = InnoDB;

View file

@ -4,7 +4,9 @@ require_once '../../videos/configuration.php';
$videos_id = getVideos_id();
// each 60 of video will take about 5 minutes to complete
if (!User::isLogged()) {
forbiddenPage('You must login first');
}
if (empty($videos_id)) {
forbiddenPage('Videos ID is required');
}
@ -96,7 +98,7 @@ $_page = new Page(['Video Metatags']);
<div class="panel-heading">
<ul class="nav nav-tabs">
<li class="active">
<a data-toggle="tab" href="#pTranscription">
<a data-toggle="tab" href="#pTranscription" id="pTranscriptionLink">
<i class="fas fa-microphone-alt"></i>
<?php echo __("Transcription"); ?>
</a>
@ -116,7 +118,7 @@ $_page = new Page(['Video Metatags']);
<li>
<a data-toggle="tab" href="#pUsage">
<i class="fas fa-receipt"></i>
<?php echo __("Usage"); ?>
<?php echo __("Usage and details"); ?>
</a>
</li>
<li>

View file

@ -33,7 +33,7 @@ $jsonDecoded->msg = '';
$jsonDecoded->type = $_REQUEST['type'];
$jsonDecoded->token = $token;
_error_log('Start line=' . __LINE__);
_error_log('Start line=' . __LINE__ . ' type=' . $_REQUEST['type']);
switch ($_REQUEST['type']) {
case AI::$typeTranslation:
@ -108,27 +108,52 @@ switch ($_REQUEST['type']) {
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
//$jsonDecoded->lines[] = __LINE__;
$paths = Ai_transcribe_responses::getVTTPaths($token->videos_id, $_REQUEST['response']['language']);
if(!empty($paths['path'])){
$jsonDecoded->vttsaved = file_put_contents($paths['path'],$_REQUEST['response']['vtt']);
}else{
_error_log("VTTFile Path is empty videos_id={$token->videos_id}, language={$_REQUEST['response']['language']} ".json_encode($paths));
if (!empty($paths['path'])) {
$jsonDecoded->vttsaved = file_put_contents($paths['path'], $_REQUEST['response']['vtt']);
} else {
_error_log("VTTFile Path is empty videos_id={$token->videos_id}, language={$_REQUEST['response']['language']} " . json_encode($paths));
}
}
$jsonDecoded->error = false;
}
break;
case AI::$typeShorts:
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
if (!empty($_REQUEST['response']) && !empty($_REQUEST['response']['shorts'])) {
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__ . json_encode($_REQUEST['response']));
$shorts = $_REQUEST['response']['shorts'];
if (!empty($shorts)) {
$o = new Ai_responses_json(0);
$o->setResponse($shorts);
$o->setAi_type(AI::$typeShorts);
$o->setAi_responses_id($token->ai_responses_id);
$jsonDecoded->shorts = $shorts;
$jsonDecoded->Ai_responses_json = $o->save();
$jsonDecoded->error = empty($jsonDecoded->Ai_responses_json);
}else{
_error_log('AI: shorts ERROR' . basename(__FILE__) . ' line=' . __LINE__);
}
}else{
_error_log('AI: ERROR ' . basename(__FILE__) . ' line=' . __LINE__ . json_encode($_REQUEST));
}
break;
default:
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
$jsonDecoded->msg = 'Type not found';
break;
}
if($jsonDecoded->vttsaved){
if ($jsonDecoded->error) {
error_log($global['lastQuery'] . ' Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
if ($jsonDecoded->vttsaved) {
Video::clearCache($token->videos_id);
}
_error_log('You received a new translation ' . json_encode(debug_backtrace()));
sendSocketMessageToUsers_id(['type'=>$_REQUEST['type']], $token->users_id, 'aiSocketMessage');
sendSocketMessageToUsers_id(['type' => $_REQUEST['type']], $token->users_id, 'aiSocketMessage');
$r = json_encode($jsonDecoded);
_error_log($r);

13
plugin/AI/shorts.php Normal file
View file

@ -0,0 +1,13 @@
<?php
require_once '../../videos/configuration.php';
$_page = new Page(['Shorts']);
?>
<div class="container-fluid">
<?php
$doNotGetShorts = true;
require_once $global['systemRootPath'] . 'plugin/AI/tabs/shorts.php';
?>
</div>
<?php
$_page->print();
?>

View file

@ -1,96 +1,242 @@
<div class="panel panel-default">
<div class="panel-heading">
<div class="alert alert-info">
<h4><strong>Enhance Your Video SEO with AI</strong></h4>
<p>We're excited to announce a new AI-driven feature to enhance your video SEO! This tool will automatically suggest optimized <strong>Titles, Casual Descriptions, Professional Descriptions, Meta Descriptions, Keywords, Summaries, Ratings, and Rating Justifications</strong> for your videos.</p>
<p>Our AI analyzes your video's existing title and description to generate these SEO elements. For even more precise and tailored suggestions, we recommend providing a <i class="fas fa-microphone-alt"></i> <?php echo __("Transcription"); ?> of your video. This additional information allows our AI to better understand and optimize your content for search engines, boosting your video's visibility and reach.</p>
<p>Start leveraging the power of AI to make your videos stand out in search results!</p>
</div>
<?php
echo AI::getProgressBarHTML("basic_{$videos_id}", '');
?>
</div>
<div class="panel-body">
<table id="responses-list" class="table table-bordered table-hover">
<thead>
<!-- Headers will be added here dynamically -->
</thead>
<tbody>
<!-- Rows will be added here dynamically -->
</tbody>
</table>
</div>
<div class="panel-footer">
<button class="btn btn-success btn-block" onclick="generateAIIdeas()">
<i class="fa-solid fa-lightbulb"></i> <?php echo __('Generate Basic Ideas') ?>
</button>
</div>
</div>
<?php
<script>
async function generateAIIdeas() {
await createAISuggestions('<?php echo AI::$typeBasic; ?>');
loadAIBasic();
loadAIUsage();
$videos_id = getVideos_id();
$trascription = Ai_responses::getTranscriptionVtt($videos_id);
if (empty($trascription)) {
?>
<a href="#" onclick="$('#pTranscriptionLink').click();">
<div class="alert alert-info" role="alert">
<strong>Transcription Needed!</strong>
A transcription is required to create the short suggestions with AI.
Please ensure the text is accurately transcribed for the best results.
</div>
</a>
<?php
return false;
}
$video = Video::getVideoLight($videos_id);
$rows = Ai_responses_json::getAllFromAIType(AI::$typeShorts, $videos_id);
$responses = array();
foreach ($rows as $key => $value) {
if (!empty($value['response'])) {
$response = json_decode($value['response']);
foreach ($response as $key2 => $shorts) {
foreach ($shorts as $key3 => $short) {
$responses[] = $short;
}
}
}
}
$bookmark = AVideoPlugin::isEnabledByName('Bookmark');
?>
<style>
#shortsPlayer.panel {
height: 100vh;
/* Make the panel take the full viewport height */
display: flex;
flex-direction: column;
}
function loadAIBasic() {
#shortsPlayer .panel-body {
overflow-y: auto;
flex-grow: 1;
}
#shortsPlayer .showMoreButton.collapsed .fa-minus,
#shortsPlayer .showMoreButton .fa-plus {
display: none;
}
#shortsPlayer .showMoreButton.collapsed .fa-plus {
display: inline-block;
}
</style>
<link href="<?php echo getURL('node_modules/video.js/dist/video-js.min.css'); ?>" rel="stylesheet" type="text/css" />
<div class="panel panel-default" id="<?php echo empty($responses) ? '' : 'shortsPlayer'; ?>">
<div class="panel-heading">
<div class="row">
<div class="col-sm-2 col-lg-3"></div>
<div class="col-sm-8 col-lg-6">
<?php
echo PlayerSkins::getMediaTag($video['filename']);
?>
</div>
</div>
</div>
<div class="panel-body">
<?php
if (empty($responses)) {
?>
<div class="alert alert-info">
Currently, there are no AI-generated video shorts suggestions available for this video.
</div>
<?php
} else {
?>
<div class="row">
<?php
usort($responses, function ($a, $b) {
return $a->startTimeInSeconds - $b->startTimeInSeconds;
});
foreach ($responses as $key => $value) {
$buttons = array();
$buttons[] = '<button class="btn btn-primary" onclick="playVideoSegmentFromIndex(' . $key . ');" data-toggle="tooltip" title="' . __('Play') . '"><i class="fa-solid fa-play"></i></button>';
$buttons[] = '<button class="btn btn-success" onclick="submitVideoForm(' . $key . ');" data-toggle="tooltip" title="' . __('Save Cut') . '"><i class="fa-solid fa-scissors"></i></button>';
if ($bookmark) {
$buttons[] = '<button class="btn btn-warning" onclick="bookmarkFromIndex(' . $key . ');" data-toggle="tooltip" title="' . __('Bookmark') . '"><i class="fa-solid fa-bookmark"></i></button>';
}
?>
<div class="col-sm-3">
<div class="panel panel-default">
<div class="panel-heading clearfix">
<h3 class="panel-title">
<strong>
<?= htmlspecialchars($value->shortTitle) ?>
</strong>
</h3>
<div class="clearfix"></div>
<div class="row">
<div class="col-sm-6">
<input id="startTimeInSeconds<?= $key ?>" class="form-control" value="<?= secondsToDuration($value->startTimeInSeconds) ?>" />
</div>
<div class="col-sm-6">
<input id="endTimeInSeconds<?= $key ?>" class="form-control" value="<?= secondsToDuration($value->endTimeInSeconds) ?>" />
</div>
</div>
</div>
<div class="panel-body">
<button type="button" class="pull-right btn btn-default btn-xs showMoreButton collapsed" data-toggle="collapse" data-target="#collapseBody<?= $key ?>" aria-expanded="false">
<i class="fa-solid fa-plus"></i>
<i class="fa-solid fa-minus"></i>
</button>
<div id="collapseBody<?= $key ?>" class="collapse"> <!-- Make sure this ID matches the button's data-target -->
<p><?= htmlspecialchars($value->shortDescription) ?></p>
</div>
</div>
<div class="panel-footer">
<div class="btn-group btn-group-justified">
<?php
foreach ($buttons as $btn) {
echo $btn;
}
?>
</div>
</div>
<form id="cutVideoForm<?= $key ?>" class="hidden" style="display: none;">
<textarea name="title"><?= $value->shortTitle ?></textarea>
<textarea name="videos_id"><?= $videos_id ?></textarea>
<textarea name="startTimeInSeconds"><?= $value->startTimeInSeconds ?></textarea>
<textarea name="endTimeInSeconds"><?= $value->endTimeInSeconds ?></textarea>
<textarea name="description"><?= htmlspecialchars($value->shortDescription) ?></textarea>
</form>
</div>
</div>
<?php
if (($key + 1) % 4 == 0) {
echo '<div class="clearfix"></div>';
}
}
?>
</div>
<?php
}
?>
</div>
<?php
if (empty($doNotGetShorts)) {
?>
<div class="panel-footer">
<button class="btn btn-success btn-block" onclick="suggestShorts()">
<i class="fa-solid fa-lightbulb"></i> <?php echo __('Get shorts suggestions') ?>
</button>
</div>
<?php
}
?>
</div>
<?php
include $global['systemRootPath'] . 'view/include/video.min.js.php';
?>
<?php
echo AVideoPlugin::afterVideoJS();
?>
<script>
function playVideoSegmentFromIndex(index) {
var startTimeInSeconds = durationToSeconds($('#startTimeInSeconds' + index).val());
var endTimeInSeconds = durationToSeconds($('#endTimeInSeconds' + index).val());
playVideoSegment(startTimeInSeconds, endTimeInSeconds);
}
function bookmarkFromIndex(index) {
var startTimeInSeconds = durationToSeconds($('#startTimeInSeconds' + index).val());
var url = webSiteRootURL+"plugin/Bookmark/page/bookmarkSave.json.php";
avideoAjax(url, {
name: $('#cutVideoForm'+index+' textarea[name="title"]').text(),
timeInSeconds: startTimeInSeconds,
videos_id: <?= $videos_id ?>
});
}
function submitVideoForm(index) {
modal.showPleaseWait();
var startTimeInSeconds = durationToSeconds($('#startTimeInSeconds' + index).val());
var endTimeInSeconds = durationToSeconds($('#endTimeInSeconds' + index).val());
$('#cutVideoForm'+index+' textarea[name="startTimeInSeconds"]').text(startTimeInSeconds);
$('#cutVideoForm'+index+' textarea[name="endTimeInSeconds"]').text(endTimeInSeconds);
var formData = $('#cutVideoForm' + index).serialize(); // Serialize the form data
// Perform the AJAX request
$.ajax({
url: webSiteRootURL + 'plugin/AI/tabs/basic.json.php',
data: {
videos_id: <?php echo $videos_id; ?>
},
type: 'post',
url: webSiteRootURL + 'plugin/AI/cutVideo.json.php', // Replace with your server endpoint
type: 'POST',
data: formData,
success: function(response) {
if (response.error) {
avideoAlertError(response.msg);
} else {
var columnOrder = [
'videoTitles',
'casualDescription',
'professionalDescription',
'metaDescription',
'keywords',
'shortSummary',
'rrating',
'rratingJustification',
];
var columnHeaders = {
'casualDescription': 'Casual Description',
'professionalDescription': 'Professional Description',
'metaDescription': 'Meta Description',
'rrating': 'Rating',
'rratingJustification': 'Rating Justification',
'shortSummary': 'Summary',
'keywords': 'Keywords',
'videoTitles': 'Titles'
};
var columnCallbackFunctions = [
'videoTitles',
'casualDescription',
'professionalDescription',
'metaDescription',
'keywords',
'shortSummary',
'rrating',
];
var selector = '#responses-list';
//console.log(selector, response);
processAIResponse(selector, response, columnOrder, columnHeaders, columnCallbackFunctions);
loadTitleDescription();
avideoToastSuccess(response.msg);
if (typeof response.eval !== 'undefined') {
eval(response.eval);
}
}
},
error: function(xhr, status, error) {
if (xhr.responseJSON.error) {
avideoAlertError(xhr.responseJSON.msg);
} else {
avideoToastError(xhr.responseJSON.msg);
}
},
complete: function(response) {
modal.hidePleaseWait();
}
});
}
$(document).ready(function() {
loadAIBasic();
$('#shortsPlayer [data-toggle="collapse"]').click(function() {
var target = $(this).attr('data-target');
$(target).collapse('toggle'); // Toggle the collapse state
// Optionally, toggle aria-expanded attribute for accessibility
var isExpanded = $(this).attr('aria-expanded') === 'true';
$(this).attr('aria-expanded', !isExpanded);
});
});
<?php
if (empty($doNotGetShorts)) {
?>
async function suggestShorts() {
await createAISuggestions('<?php echo AI::$typeShorts; ?>');
loadAIUsage();
}
<?php
}
?>
</script>

View file

@ -28,9 +28,7 @@ $columnCallbackFunctions = ['text'];
}
</style>
<div class="row">
<div class="col-sm-8">
<div class="panel panel-default">
<div class="panel-heading">
<div class="alert alert-info">

View file

@ -32,6 +32,10 @@ foreach ($obj->response as $key => $value) {
$obj->response[$key]['type'] = __('Translation').'/'.__('Transcription')." {$value['language']}";
}else if(!empty($value['ai_metatags_responses_id'])){
$obj->response[$key]['type'] = __('Basic')." Metatags";
}else if(!empty($value['ai_type'])){
if($value['ai_type'] !== AI::$typeShorts){
$obj->response[$key]['type'] = __('Shorts');
}
}
}

View file

@ -1,3 +1,29 @@
<h2><?php echo __('MP3 Files details'); ?></h2>
<div class="list-group">
<?php
// Assuming $mp3s is your array variable
foreach ($mp3s as $quality => $mp3Info) {
if ($quality !== 'isValid' && $quality !== 'msg') { // Skip non-array entries
$path = $mp3Info['paths']['path'];
$url = $mp3Info['paths']['url'];
$duration = $mp3Info['duration'];
$isValid = $mp3Info['isValid'] ? 'Yes' : 'No';
$fileSize = filesize($path); // Get file size in bytes
// Convert file size to human-readable format
$formattedSize = humanFileSize($fileSize);
echo "<a href='{$url}' class='list-group-item'>";
echo "<h4 class='list-group-item-heading'>File: " . basename($path) . "</h4>";
echo "<p class='list-group-item-text'>Duration: $duration</p>";
echo "<p class='list-group-item-text'>File Size: $formattedSize</p>";
echo "<p class='list-group-item-text'>Valid: $isValid</p>";
echo "</a>";
}
}
?>
</div>
<h2><?php echo __('Usage'); ?></h2>
<table id="responsesUsage-list" class="table table-bordered table-hover">
<thead>
<!-- Headers will be added here dynamically -->
@ -9,6 +35,7 @@
<script>
var modalloadAIUsage = getPleaseWait();
function loadAIUsage() {
modalloadAIUsage.showPleaseWait();
$.ajax({

View file

@ -191,6 +191,7 @@ function getVTTChapterTracks($fileName, $returnArray = false)
if (file_exists($defaultFile)) {
$path_parts = pathinfo($defaultFile);
$src = Video::getURLToFile($path_parts['basename']);
//var_dump($src, $path_parts['basename'], $defaultFile);exit;
$obj = new stdClass();
$obj->srclang = 'en';
$obj->src = $src;

View file

@ -7,22 +7,25 @@ $obj = new stdClass();
$obj->error = true;
$obj->msg = "";
if(empty($_POST['videos_id'])){
$_POST['videos_id'] = intval($_POST['videoAutocomplete']);
if(empty($_REQUEST['videos_id'])){
$_REQUEST['videos_id'] = intval($_REQUEST['videoAutocomplete']);
}
$obj->videos_id = $_REQUEST['videos_id'];
if(!User::isAdmin() && !Video::canEdit($_POST['videos_id'])){
if(!User::isAdmin() && !Video::canEdit($_REQUEST['videos_id'])){
$obj->msg = "You cant do this";
die(json_encode($obj));
}
$o = new BookmarkTable(@$_POST['id']);
$o->setName($_POST['name']);
$o->setTimeInSeconds($_POST['timeInSeconds']);
$o->setVideos_id($_POST['videos_id']);
$o = new BookmarkTable(@$_REQUEST['id']);
$o->setName($_REQUEST['name']);
$o->setTimeInSeconds($_REQUEST['timeInSeconds']);
$o->setVideos_id($_REQUEST['videos_id']);
if($id = $o->save()){
$obj->error = false;
$obj->msg = __('Saved');
$obj->bookmarks = BookmarkTable::getAllFromVideo($_REQUEST['videos_id']);
}

View file

@ -470,11 +470,13 @@ async function mouseEffect() {
var gif = $(this).find(".thumbsGIF");
var jpg = $(this).find(".thumbsJPG");
try {
gif.lazy({ effect: 'fadeIn',
afterLoad: function (element) {
element.removeClass('lazyload');
element.addClass('lazyloadLoaded');
} });
gif.lazy({
effect: 'fadeIn',
afterLoad: function (element) {
element.removeClass('lazyload');
element.addClass('lazyloadLoaded');
}
});
setTimeout(function () {
gif.height(jpg.height());
gif.width(jpg.width());
@ -794,6 +796,36 @@ async function showMuteTooltip() {
}, 5000);
}
function playVideoSegment(startTime, endTime) {
// Ensure only one 'timeupdate' event listener is active
// Remove the previous listener if exists
if (typeof player._timeUpdateHandler === 'function') {
player.off('timeupdate', player._timeUpdateHandler);
}
// Define a new 'timeupdate' handler
player._timeUpdateHandler = function () {
if (player.currentTime() >= endTime) {
console.log('playVideoSegment endTime', endTime);
player.pause();
// Optionally, remove the listener to prevent potential memory leaks
player.off('timeupdate', player._timeUpdateHandler);
player._timeUpdateHandler = null;
}
};
// Attach the new 'timeupdate' event listener
player.on('timeupdate', player._timeUpdateHandler);
forceCurrentTime = startTime;
console.log('playVideoSegment startTime', startTime);
// Start playing the video at the specified start time
player.currentTime(startTime);
player.play();
}
function playerPlayIfAutoPlay(currentTime) {
console.log("playerPlayIfAutoPlay: forceCurrentTime:", currentTime);
if (isWebRTC()) {
@ -1253,7 +1285,7 @@ function avideoAlertOnceForceConfirm(title, msg, type) {
}
function _avideoToast(msg, icon) {
if(empty(msg)){
if (empty(msg)) {
msg = '';
}
try {
@ -1385,7 +1417,7 @@ function closeFullscreenVideo() {
}
// Listen for messages from child frames
window.addEventListener('message', function(event) {
window.addEventListener('message', function (event) {
if (event.data === 'closeFullscreen') {
// Call the function to close fullscreen video
closeFullscreenVideo();
@ -1614,9 +1646,9 @@ function avideoPushState(url) {
parent.updatePageSRC(url);
}
// Then we set up the popstate event listener
window.onpopstate = function(event) {
window.onpopstate = function (event) {
avideoModalIframeClose();
};
};
}
function checkIframeLoaded(id) {
@ -2827,7 +2859,7 @@ $(document).ready(function () {
setInterval(function () {// check for the carousel
checkDescriptionArea();
}, 3000);
if(typeof Cookies != 'undefined'){
if (typeof Cookies != 'undefined') {
Cookies.set('timezone', timezone, {
path: '/',
expires: 365
@ -4071,3 +4103,28 @@ function humanFileSize(bytes) {
return (bytes / 1000).toFixed(2) + ' KB';
}
function durationToSeconds(duration) {
// Split the duration by the colon
const parts = duration.split(':');
// Calculate hours, minutes, and seconds
let hours = 0, minutes = 0, seconds = 0;
// Based on the parts length, calculate the duration in seconds
if (parts.length === 3) {
hours = parseInt(parts[0], 10);
minutes = parseInt(parts[1], 10);
seconds = parseInt(parts[2], 10);
} else if (parts.length === 2) {
// If the duration is in MM:SS format
minutes = parseInt(parts[0], 10);
seconds = parseInt(parts[1], 10);
} else if (parts.length === 1) {
// If the duration is in SS format
seconds = parseInt(parts[0], 10);
}
// Convert everything to seconds
return (hours * 3600) + (minutes * 60) + seconds;
}