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:
parent
e869930315
commit
d94d0ba437
23 changed files with 1257 additions and 581 deletions
|
@ -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
390
objects/functionExec.php
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
@ -10572,4 +10215,5 @@ function checkFileModified($filePath) {
|
|||
require_once __DIR__.'/functionSecurity.php';
|
||||
require_once __DIR__.'/functionMySQL.php';
|
||||
require_once __DIR__.'/functionDocker.php';
|
||||
require_once __DIR__.'/functionImages.php';
|
||||
require_once __DIR__.'/functionImages.php';
|
||||
require_once __DIR__.'/functionExec.php';
|
|
@ -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);
|
||||
|
|
283
plugin/AI/AI.php
283
plugin/AI/AI.php
|
@ -5,11 +5,12 @@ 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';
|
||||
static $typeBasic = 'basic';
|
||||
|
@ -47,41 +48,48 @@ class AI extends PluginAbstract {
|
|||
'uk' => 'Ukrainian',
|
||||
'vi' => 'Vietnamese'
|
||||
];
|
||||
|
||||
|
||||
static $isTest = 0;
|
||||
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;
|
||||
|
@ -237,47 +284,51 @@ class AI extends PluginAbstract {
|
|||
return encryptString(_json_encode($obj));
|
||||
}
|
||||
|
||||
|
||||
static function getTokenFromRequest(){
|
||||
|
||||
if(empty($_REQUEST['token'])){
|
||||
static function getTokenFromRequest()
|
||||
{
|
||||
|
||||
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']);
|
||||
$convert['url'] = str_replace('.mp3', '_Low.mp3', $convert['url']);
|
||||
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);
|
||||
|
@ -306,7 +357,7 @@ class AI extends PluginAbstract {
|
|||
'isValid' => false,
|
||||
'msg' => "Length does not match (Video/MP3) video = {$videoDuration} seconds MP3 = $durationInSeconds seconds",
|
||||
);
|
||||
return $response;
|
||||
return $response;
|
||||
}
|
||||
|
||||
$arrayRegular = array(
|
||||
|
@ -315,9 +366,9 @@ class AI extends PluginAbstract {
|
|||
'durationInSeconds' => $durationInSeconds,
|
||||
'isValid' => !empty($durationInSeconds),
|
||||
);
|
||||
|
||||
$pathsLower = self::getMP3LowerPath($videos_id);
|
||||
if(!empty($pathsLower )){
|
||||
|
||||
$pathsLower = self::getMP3LowerPath($videos_id);
|
||||
if (!empty($pathsLower)) {
|
||||
$duration = getDurationFromFile($pathsLower['path']);
|
||||
$durationInSeconds = durationToSeconds($duration);
|
||||
$arrayLower = array(
|
||||
|
@ -326,24 +377,24 @@ class AI extends PluginAbstract {
|
|||
'durationInSeconds' => $durationInSeconds,
|
||||
'isValid' => !empty($durationInSeconds),
|
||||
);
|
||||
|
||||
$pathsLower = self::getMP3LowerPath($videos_id);
|
||||
|
||||
$pathsLower = self::getMP3LowerPath($videos_id);
|
||||
}
|
||||
}
|
||||
$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,33 +408,35 @@ 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']){
|
||||
ini_set('max_execution_time', 300);
|
||||
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>';
|
||||
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>';
|
||||
}
|
||||
|
||||
public function getVideosManagerListButton()
|
||||
|
@ -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'];
|
||||
}
|
||||
}
|
||||
|
@ -424,16 +478,17 @@ class AI extends PluginAbstract {
|
|||
$global['lastQuery'] = $sql;
|
||||
sqlDAL::writeSql($sql);
|
||||
}
|
||||
|
||||
|
||||
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$/';
|
||||
|
||||
|
||||
if (is_dir($dir) && ($handle = opendir($dir))) {
|
||||
while (false !== ($entry = readdir($handle))) {
|
||||
if (is_file($dir . '/' . $entry) && preg_match($filePattern, $entry, $matches)) {
|
||||
|
@ -442,46 +497,98 @@ class AI extends PluginAbstract {
|
|||
}
|
||||
closedir($handle);
|
||||
}
|
||||
|
||||
|
||||
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}/";
|
||||
|
||||
|
||||
// Find all .vtt files in the directory
|
||||
$vttFiles = glob($dir . "*.vtt");
|
||||
|
||||
|
||||
// Return the array of .vtt files
|
||||
return $vttFiles;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 = ?";
|
||||
|
||||
|
|
72
plugin/AI/Objects/Ai_responses_json.php
Normal file
72
plugin/AI/Objects/Ai_responses_json.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
77
plugin/AI/Objects/Ai_scheduler.php
Normal file
77
plugin/AI/Objects/Ai_scheduler.php
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
@ -69,7 +72,7 @@ if (empty($content)) {
|
|||
$obj = new stdClass();
|
||||
$obj->error = true;
|
||||
$obj->msg = "Could not post to {$aiURLProgress} => {$content}";
|
||||
|
||||
|
||||
die(json_encode($obj));
|
||||
}
|
||||
$jsonProgressDecoded = json_decode($content);
|
||||
|
@ -77,7 +80,7 @@ if (empty($jsonProgressDecoded)) {
|
|||
$obj = new stdClass();
|
||||
$obj->error = true;
|
||||
$obj->msg = "Could not decode => {$content}";
|
||||
|
||||
|
||||
die(json_encode($obj));
|
||||
}
|
||||
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
|
||||
|
@ -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.";;
|
||||
}
|
||||
|
||||
|
|
47
plugin/AI/cutVideo.json.php
Normal file
47
plugin/AI/cutVideo.json.php
Normal 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);
|
|
@ -59,4 +59,34 @@ CREATE TABLE IF NOT EXISTS `ai_transcribe_responses` (
|
|||
REFERENCES `ai_responses` (`id`)
|
||||
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;
|
16
plugin/AI/install/updateV5.0.sql
Normal file
16
plugin/AI/install/updateV5.0.sql
Normal 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;
|
12
plugin/AI/install/updateV6.0.sql
Normal file
12
plugin/AI/install/updateV6.0.sql
Normal 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;
|
|
@ -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>
|
||||
|
|
|
@ -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:
|
||||
|
@ -84,7 +84,7 @@ switch ($_REQUEST['type']) {
|
|||
$o->setPrice_completion_tokens($_REQUEST['price_completion_tokens']);
|
||||
$o->setAi_responses_id($token->ai_responses_id);
|
||||
$jsonDecoded->Ai_metatags_responses = $o->save();
|
||||
|
||||
|
||||
$jsonDecoded->error = false;
|
||||
}
|
||||
break;
|
||||
|
@ -108,29 +108,54 @@ 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);
|
||||
|
||||
echo $r;
|
||||
echo $r;
|
||||
|
|
13
plugin/AI/shorts.php
Normal file
13
plugin/AI/shorts.php
Normal 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();
|
||||
?>
|
|
@ -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>
|
|
@ -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">
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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']);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
@ -1271,7 +1303,7 @@ function _avideoToast(msg, icon) {
|
|||
}
|
||||
$.toast(options);
|
||||
} catch (error) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1363,10 +1395,10 @@ function avideoModalIframeClose() {
|
|||
try {
|
||||
swal.close();
|
||||
} catch (e) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (inIframe()) {
|
||||
window.parent.swal.close();
|
||||
|
@ -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();
|
||||
|
@ -1604,7 +1636,7 @@ function avideoModalIframeWithClassName(url, className, updateURL) {
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
function avideoPushState(url) {
|
||||
function avideoPushState(url) {
|
||||
if (!validURL(url)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -3883,7 +3915,7 @@ function openFullscreenVideo(url, urlBar) {
|
|||
'border': 'none',
|
||||
'background-color': 'black'
|
||||
});
|
||||
|
||||
|
||||
avideoPushState(urlBar);
|
||||
// Append the iframe to the body
|
||||
fullscreenIframe.appendTo('body');
|
||||
|
@ -3930,7 +3962,7 @@ function addCloseButton(elementToAppend) {
|
|||
if (window.self !== window.top) {
|
||||
console.log('close parent iframe');
|
||||
//window.parent.closeFullscreenVideo();
|
||||
window.parent.postMessage('closeFullscreen', '*');
|
||||
window.parent.postMessage('closeFullscreen', '*');
|
||||
} else {
|
||||
console.log('close history.back');
|
||||
window.history.back();
|
||||
|
@ -4070,4 +4102,29 @@ 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue