mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-06 03:50:04 +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);
|
$res = sqlDAL::readSql($sql);
|
||||||
$fullData = sqlDAL::fetchAllAssoc($res);
|
$fullData = sqlDAL::fetchAllAssoc($res);
|
||||||
sqlDAL::close($res);
|
sqlDAL::close($res);
|
||||||
$rows = [];
|
return $fullData;
|
||||||
if ($res !== false) {
|
|
||||||
foreach ($fullData as $row) {
|
|
||||||
$rows[] = $row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $rows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getAllActive()
|
public static function getAllActive()
|
||||||
|
@ -128,13 +122,7 @@ abstract class ObjectYPT implements ObjectInterface
|
||||||
$res = sqlDAL::readSql($sql);
|
$res = sqlDAL::readSql($sql);
|
||||||
$fullData = sqlDAL::fetchAllAssoc($res);
|
$fullData = sqlDAL::fetchAllAssoc($res);
|
||||||
sqlDAL::close($res);
|
sqlDAL::close($res);
|
||||||
$rows = [];
|
return $fullData;
|
||||||
if ($res !== false) {
|
|
||||||
foreach ($fullData as $row) {
|
|
||||||
$rows[] = $row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $rows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getTotal()
|
public static function getTotal()
|
||||||
|
@ -299,6 +287,7 @@ abstract class ObjectYPT implements ObjectInterface
|
||||||
_error_log("Save error, table " . static::getTableName() . " MySQL Error", AVideoLog::$ERROR);
|
_error_log("Save error, table " . static::getTableName() . " MySQL Error", AVideoLog::$ERROR);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
//_error_log("Save ".static::getTableName().' '.json_encode($fieldsName));
|
||||||
$formats = '';
|
$formats = '';
|
||||||
$values = [];
|
$values = [];
|
||||||
if (!empty($this->id)) {
|
if (!empty($this->id)) {
|
||||||
|
@ -475,6 +464,7 @@ abstract class ObjectYPT implements ObjectInterface
|
||||||
'ai_responses',
|
'ai_responses',
|
||||||
'ai_metatags_responses',
|
'ai_metatags_responses',
|
||||||
'ai_transcribe_responses',
|
'ai_transcribe_responses',
|
||||||
|
'ai_responses_json',
|
||||||
'playlists_schedules'
|
'playlists_schedules'
|
||||||
];
|
];
|
||||||
return in_array(static::getTableName(), $ignoreArray);
|
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;
|
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)
|
function make_path($path)
|
||||||
{
|
{
|
||||||
$created = false;
|
$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
|
* You can now configure it on the configuration.php
|
||||||
|
@ -4587,50 +4528,6 @@ function getUsageFromURL($url)
|
||||||
return (int) $result;
|
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)
|
function foldersize($path)
|
||||||
{
|
{
|
||||||
$total_size = 0;
|
$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.
|
* Copy remote file over HTTP one small chunk at a time.
|
||||||
*
|
*
|
||||||
|
@ -7250,72 +7105,6 @@ function get_ffprobe() {
|
||||||
return $ffprobe.$complement;
|
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)) {
|
if (!preg_match('/ -i [\'"]?https?:/', $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)
|
function m3u8ToMP4($input)
|
||||||
{
|
{
|
||||||
$videosDir = getVideosDir();
|
$videosDir = getVideosDir();
|
||||||
|
@ -7806,81 +7519,11 @@ function sendSocketMessageToNone($msg, $callbackJSFunction = "")
|
||||||
return sendSocketMessage($msg, $callbackJSFunction, -1);
|
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()
|
function isWindows()
|
||||||
{
|
{
|
||||||
return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
|
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)
|
function isURL200($url, $forceRecheck = false)
|
||||||
{
|
{
|
||||||
global $_isURL200;
|
global $_isURL200;
|
||||||
|
@ -10573,3 +10216,4 @@ require_once __DIR__.'/functionSecurity.php';
|
||||||
require_once __DIR__.'/functionMySQL.php';
|
require_once __DIR__.'/functionMySQL.php';
|
||||||
require_once __DIR__.'/functionDocker.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];
|
return $__getPaths[$videoFilename];
|
||||||
}
|
}
|
||||||
$cleanVideoFilename = self::getCleanFilenameFromFile($videoFilename);
|
$cleanVideoFilename = self::getCleanFilenameFromFile($videoFilename);
|
||||||
|
//var_dump('--'.$cleanVideoFilename, '++'.$videoFilename);
|
||||||
$videosDir = self::getStoragePath();
|
$videosDir = self::getStoragePath();
|
||||||
|
|
||||||
$path = addLastSlash("{$videosDir}{$cleanVideoFilename}");
|
$path = addLastSlash("{$videosDir}{$cleanVideoFilename}");
|
||||||
|
@ -4193,6 +4194,7 @@ if (!class_exists('Video')) {
|
||||||
if (!empty($parts[1]) && $parts[1] == 'index.m3u8') {
|
if (!empty($parts[1]) && $parts[1] == 'index.m3u8') {
|
||||||
$videoFilename = $parts[1];
|
$videoFilename = $parts[1];
|
||||||
}
|
}
|
||||||
|
//var_dump('--'.$videoFilename, $paths);
|
||||||
return "{$paths['url']}{$videoFilename}";
|
return "{$paths['url']}{$videoFilename}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4336,6 +4338,10 @@ if (!class_exists('Video')) {
|
||||||
$path_parts = pathinfo($filename);
|
$path_parts = pathinfo($filename);
|
||||||
if (!empty($path_parts['extension'])) {
|
if (!empty($path_parts['extension'])) {
|
||||||
if ($path_parts['extension'] == 'vtt' || $path_parts['extension'] == 'srt') {
|
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']);
|
$p = explode('.', $path_parts['filename']);
|
||||||
if (count($p) > 1) {
|
if (count($p) > 1) {
|
||||||
array_pop($p);
|
array_pop($p);
|
||||||
|
|
165
plugin/AI/AI.php
165
plugin/AI/AI.php
|
@ -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_responses.php';
|
||||||
require_once $global['systemRootPath'] . 'plugin/AI/Objects/Ai_metatags_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_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 $typeTranslation = 'translation';
|
||||||
static $typeTranscription = 'transcription';
|
static $typeTranscription = 'transcription';
|
||||||
|
@ -52,16 +53,19 @@ class AI extends PluginAbstract {
|
||||||
static $url = 'https://ai.ypt.me/';
|
static $url = 'https://ai.ypt.me/';
|
||||||
static $url_test = 'http://192.168.0.2:81/AI/';
|
static $url_test = 'http://192.168.0.2:81/AI/';
|
||||||
|
|
||||||
static function getMetadataURL(){
|
static function getMetadataURL()
|
||||||
|
{
|
||||||
self::$isTest = ($_SERVER["SERVER_NAME"] == "vlu.me");
|
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(){
|
static function getPricesURL()
|
||||||
|
{
|
||||||
return self::$url . 'prices.json.php';
|
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.";
|
$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>";
|
$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>";
|
||||||
|
|
||||||
|
@ -69,19 +73,23 @@ class AI extends PluginAbstract {
|
||||||
return $desc . $help;
|
return $desc . $help;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName() {
|
public function getName()
|
||||||
|
{
|
||||||
return "AI";
|
return "AI";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUUID() {
|
public function getUUID()
|
||||||
|
{
|
||||||
return "AI-5ee8405eaaa16";
|
return "AI-5ee8405eaaa16";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPluginVersion() {
|
public function getPluginVersion()
|
||||||
return "4.0";
|
{
|
||||||
|
return "6.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getEmptyDataObject() {
|
public function getEmptyDataObject()
|
||||||
|
{
|
||||||
$obj = new stdClass();
|
$obj = new stdClass();
|
||||||
$obj->AccessToken = "";
|
$obj->AccessToken = "";
|
||||||
/*
|
/*
|
||||||
|
@ -102,7 +110,43 @@ class AI extends PluginAbstract {
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getVideoTranslationMetadata($videos_id, $lang, $langName){
|
static function getVideoShortsMetadata($videos_id)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$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;
|
global $global;
|
||||||
$obj = new stdClass();
|
$obj = new stdClass();
|
||||||
$obj->error = true;
|
$obj->error = true;
|
||||||
|
@ -142,7 +186,8 @@ class AI extends PluginAbstract {
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getVideoBasicMetadata($videos_id){
|
static function getVideoBasicMetadata($videos_id)
|
||||||
|
{
|
||||||
global $global;
|
global $global;
|
||||||
$obj = new stdClass();
|
$obj = new stdClass();
|
||||||
$obj->error = true;
|
$obj->error = true;
|
||||||
|
@ -185,7 +230,8 @@ class AI extends PluginAbstract {
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getVideoTranscriptionMetadata($videos_id, $lang){
|
static function getVideoTranscriptionMetadata($videos_id, $lang)
|
||||||
|
{
|
||||||
$obj = new stdClass();
|
$obj = new stdClass();
|
||||||
$obj->error = true;
|
$obj->error = true;
|
||||||
$obj->msg = '';
|
$obj->msg = '';
|
||||||
|
@ -225,7 +271,8 @@ class AI extends PluginAbstract {
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getTokenForVideo($videos_id, $ai_responses_id, $param){
|
static function getTokenForVideo($videos_id, $ai_responses_id, $param)
|
||||||
|
{
|
||||||
global $global;
|
global $global;
|
||||||
$obj = new stdClass();
|
$obj = new stdClass();
|
||||||
$obj->videos_id = $videos_id;
|
$obj->videos_id = $videos_id;
|
||||||
|
@ -238,7 +285,8 @@ class AI extends PluginAbstract {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static function getTokenFromRequest(){
|
static function getTokenFromRequest()
|
||||||
|
{
|
||||||
|
|
||||||
if (empty($_REQUEST['token'])) {
|
if (empty($_REQUEST['token'])) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -259,7 +307,8 @@ class AI extends PluginAbstract {
|
||||||
return $json;
|
return $json;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getMP3Path($videos_id){
|
static function getMP3Path($videos_id)
|
||||||
|
{
|
||||||
$convert = convertVideoToMP3FileIfNotExists($videos_id);
|
$convert = convertVideoToMP3FileIfNotExists($videos_id);
|
||||||
if (empty($convert) || empty($convert['url'])) {
|
if (empty($convert) || empty($convert['url'])) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -267,7 +316,8 @@ class AI extends PluginAbstract {
|
||||||
return $convert;
|
return $convert;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getMP3LowerPath($videos_id){
|
static function getMP3LowerPath($videos_id)
|
||||||
|
{
|
||||||
$convert = self::getMP3Path($videos_id);
|
$convert = self::getMP3Path($videos_id);
|
||||||
if (empty($convert) || empty($convert['url'])) {
|
if (empty($convert) || empty($convert['url'])) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -277,7 +327,8 @@ class AI extends PluginAbstract {
|
||||||
return $convert;
|
return $convert;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getMP3RegularAndLower($videos_id){
|
static function getMP3RegularAndLower($videos_id)
|
||||||
|
{
|
||||||
$arrayRegular = array(
|
$arrayRegular = array(
|
||||||
'paths' => false,
|
'paths' => false,
|
||||||
'duration' => false,
|
'duration' => false,
|
||||||
|
@ -357,7 +408,8 @@ class AI extends PluginAbstract {
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getLowerMP3($videos_id, $try = 0){
|
static function getLowerMP3($videos_id, $try = 0)
|
||||||
|
{
|
||||||
$mp3s = self::getMP3RegularAndLower($videos_id);
|
$mp3s = self::getMP3RegularAndLower($videos_id);
|
||||||
if ($mp3s['regular']['isValid']) {
|
if ($mp3s['regular']['isValid']) {
|
||||||
if (!$mp3s['isValid']) {
|
if (!$mp3s['isValid']) {
|
||||||
|
@ -381,7 +433,8 @@ class AI extends PluginAbstract {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getPluginMenu() {
|
public function getPluginMenu()
|
||||||
|
{
|
||||||
global $global;
|
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>';
|
||||||
}
|
}
|
||||||
|
@ -398,7 +451,8 @@ class AI extends PluginAbstract {
|
||||||
return $btn;
|
return $btn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getTagTypeId() {
|
public static function getTagTypeId()
|
||||||
|
{
|
||||||
$VideoTags = AVideoPlugin::isEnabledByName('VideoTags');
|
$VideoTags = AVideoPlugin::isEnabledByName('VideoTags');
|
||||||
if (empty($VideoTags)) {
|
if (empty($VideoTags)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -428,7 +482,8 @@ class AI extends PluginAbstract {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getVTTLanguageCodes($videos_id) {
|
static function getVTTLanguageCodes($videos_id)
|
||||||
|
{
|
||||||
$video = new Video('', '', $videos_id);
|
$video = new Video('', '', $videos_id);
|
||||||
$dir = getVideosDir() . DIRECTORY_SEPARATOR . $video->getFilename();
|
$dir = getVideosDir() . DIRECTORY_SEPARATOR . $video->getFilename();
|
||||||
$languageCodes = [];
|
$languageCodes = [];
|
||||||
|
@ -446,12 +501,14 @@ class AI extends PluginAbstract {
|
||||||
return array_unique($languageCodes); // Return unique language codes
|
return array_unique($languageCodes); // Return unique language codes
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFooterCode() {
|
public function getFooterCode()
|
||||||
|
{
|
||||||
global $global;
|
global $global;
|
||||||
include $global['systemRootPath'] . 'plugin/AI/footer.php';
|
include $global['systemRootPath'] . 'plugin/AI/footer.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getVTTFiles($videos_id) {
|
static function getVTTFiles($videos_id)
|
||||||
|
{
|
||||||
$video = new Video('', '', $videos_id);
|
$video = new Video('', '', $videos_id);
|
||||||
$filename = $video->getFilename();
|
$filename = $video->getFilename();
|
||||||
$dir = getVideosDir() . "{$filename}/";
|
$dir = getVideosDir() . "{$filename}/";
|
||||||
|
@ -464,7 +521,8 @@ class AI extends PluginAbstract {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static function getFirstVTTFile($videos_id){
|
static function getFirstVTTFile($videos_id)
|
||||||
|
{
|
||||||
$vttFiles = self::getVTTFiles($videos_id);
|
$vttFiles = self::getVTTFiles($videos_id);
|
||||||
if (empty($vttFiles)) {
|
if (empty($vttFiles)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -472,7 +530,8 @@ class AI extends PluginAbstract {
|
||||||
return $vttFiles[0];
|
return $vttFiles[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static function getProgressBarHTML($classname, $text){
|
static function getProgressBarHTML($classname, $text)
|
||||||
|
{
|
||||||
return '
|
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"
|
<div class="progress-bar progress-bar-striped progress-bar-animated"
|
||||||
|
@ -482,6 +541,54 @@ class AI extends PluginAbstract {
|
||||||
</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;
|
global $global;
|
||||||
$sql = "SELECT
|
$sql = "SELECT
|
||||||
|
j.*,
|
||||||
m.*,
|
m.*,
|
||||||
m.id as ai_metatags_responses_id,
|
m.id as ai_metatags_responses_id,
|
||||||
t.*,
|
t.*,
|
||||||
|
@ -166,10 +167,13 @@ class Ai_responses extends ObjectYPT
|
||||||
ai_metatags_responses AS m ON r.id = m.ai_responses_id
|
ai_metatags_responses AS m ON r.id = m.ai_responses_id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
ai_transcribe_responses AS t ON r.id = t.ai_responses_id
|
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
|
WHERE
|
||||||
r.videos_id = ?
|
r.videos_id = ?
|
||||||
UNION
|
UNION
|
||||||
SELECT
|
SELECT
|
||||||
|
j.*,
|
||||||
m.*,
|
m.*,
|
||||||
m.id as ai_metatags_responses_id,
|
m.id as ai_metatags_responses_id,
|
||||||
t.*,
|
t.*,
|
||||||
|
@ -182,6 +186,8 @@ class Ai_responses extends ObjectYPT
|
||||||
ai_metatags_responses AS m ON r.id = m.ai_responses_id
|
ai_metatags_responses AS m ON r.id = m.ai_responses_id
|
||||||
RIGHT JOIN
|
RIGHT JOIN
|
||||||
ai_transcribe_responses AS t ON r.id = t.ai_responses_id
|
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
|
WHERE
|
||||||
r.videos_id = ?";
|
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']);
|
$obj = AI::getVideoTranslationMetadata($videos_id, $_REQUEST['lang'], $_REQUEST['langName']);
|
||||||
$param['lang'] = $_REQUEST['lang'];
|
$param['lang'] = $_REQUEST['lang'];
|
||||||
break;
|
break;
|
||||||
|
case AI::$typeShorts:
|
||||||
|
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
|
||||||
|
$obj = AI::getVideoShortsMetadata($videos_id);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
|
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
|
||||||
forbiddenPage("Undefined type {$_REQUEST['type']}");
|
forbiddenPage("Undefined type {$_REQUEST['type']}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($obj->error) {
|
if ($obj->error) {
|
||||||
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
|
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
|
||||||
forbiddenPage('Something happen: ' . $obj->msg);
|
forbiddenPage('Something happen: ' . $obj->msg);
|
||||||
|
|
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);
|
|
@ -60,3 +60,33 @@ CREATE TABLE IF NOT EXISTS `ai_transcribe_responses` (
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
ON UPDATE CASCADE)
|
ON UPDATE CASCADE)
|
||||||
ENGINE = InnoDB;
|
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();
|
$videos_id = getVideos_id();
|
||||||
|
|
||||||
// each 60 of video will take about 5 minutes to complete
|
// each 60 of video will take about 5 minutes to complete
|
||||||
|
if (!User::isLogged()) {
|
||||||
|
forbiddenPage('You must login first');
|
||||||
|
}
|
||||||
if (empty($videos_id)) {
|
if (empty($videos_id)) {
|
||||||
forbiddenPage('Videos ID is required');
|
forbiddenPage('Videos ID is required');
|
||||||
}
|
}
|
||||||
|
@ -96,7 +98,7 @@ $_page = new Page(['Video Metatags']);
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
<li class="active">
|
<li class="active">
|
||||||
<a data-toggle="tab" href="#pTranscription">
|
<a data-toggle="tab" href="#pTranscription" id="pTranscriptionLink">
|
||||||
<i class="fas fa-microphone-alt"></i>
|
<i class="fas fa-microphone-alt"></i>
|
||||||
<?php echo __("Transcription"); ?>
|
<?php echo __("Transcription"); ?>
|
||||||
</a>
|
</a>
|
||||||
|
@ -116,7 +118,7 @@ $_page = new Page(['Video Metatags']);
|
||||||
<li>
|
<li>
|
||||||
<a data-toggle="tab" href="#pUsage">
|
<a data-toggle="tab" href="#pUsage">
|
||||||
<i class="fas fa-receipt"></i>
|
<i class="fas fa-receipt"></i>
|
||||||
<?php echo __("Usage"); ?>
|
<?php echo __("Usage and details"); ?>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -33,7 +33,7 @@ $jsonDecoded->msg = '';
|
||||||
$jsonDecoded->type = $_REQUEST['type'];
|
$jsonDecoded->type = $_REQUEST['type'];
|
||||||
$jsonDecoded->token = $token;
|
$jsonDecoded->token = $token;
|
||||||
|
|
||||||
_error_log('Start line=' . __LINE__);
|
_error_log('Start line=' . __LINE__ . ' type=' . $_REQUEST['type']);
|
||||||
|
|
||||||
switch ($_REQUEST['type']) {
|
switch ($_REQUEST['type']) {
|
||||||
case AI::$typeTranslation:
|
case AI::$typeTranslation:
|
||||||
|
@ -117,12 +117,37 @@ switch ($_REQUEST['type']) {
|
||||||
$jsonDecoded->error = false;
|
$jsonDecoded->error = false;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
|
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
|
||||||
$jsonDecoded->msg = 'Type not found';
|
$jsonDecoded->msg = 'Type not found';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($jsonDecoded->error) {
|
||||||
|
error_log($global['lastQuery'] . ' Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
|
||||||
|
}
|
||||||
|
|
||||||
if ($jsonDecoded->vttsaved) {
|
if ($jsonDecoded->vttsaved) {
|
||||||
Video::clearCache($token->videos_id);
|
Video::clearCache($token->videos_id);
|
||||||
}
|
}
|
||||||
|
|
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
|
<?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>
|
|
||||||
|
|
||||||
<script>
|
$videos_id = getVideos_id();
|
||||||
async function generateAIIdeas() {
|
|
||||||
await createAISuggestions('<?php echo AI::$typeBasic; ?>');
|
$trascription = Ai_responses::getTranscriptionVtt($videos_id);
|
||||||
loadAIBasic();
|
if (empty($trascription)) {
|
||||||
loadAIUsage();
|
?>
|
||||||
|
<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();
|
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({
|
$.ajax({
|
||||||
url: webSiteRootURL + 'plugin/AI/tabs/basic.json.php',
|
url: webSiteRootURL + 'plugin/AI/cutVideo.json.php', // Replace with your server endpoint
|
||||||
data: {
|
type: 'POST',
|
||||||
videos_id: <?php echo $videos_id; ?>
|
data: formData,
|
||||||
},
|
|
||||||
type: 'post',
|
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
avideoAlertError(response.msg);
|
avideoAlertError(response.msg);
|
||||||
} else {
|
} else {
|
||||||
|
avideoToastSuccess(response.msg);
|
||||||
var columnOrder = [
|
if (typeof response.eval !== 'undefined') {
|
||||||
'videoTitles',
|
eval(response.eval);
|
||||||
'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();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
if (xhr.responseJSON.error) {
|
||||||
|
avideoAlertError(xhr.responseJSON.msg);
|
||||||
|
} else {
|
||||||
|
avideoToastError(xhr.responseJSON.msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
complete: function(response) {
|
||||||
modal.hidePleaseWait();
|
modal.hidePleaseWait();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(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>
|
</script>
|
|
@ -28,9 +28,7 @@ $columnCallbackFunctions = ['text'];
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
|
|
|
@ -32,6 +32,10 @@ foreach ($obj->response as $key => $value) {
|
||||||
$obj->response[$key]['type'] = __('Translation').'/'.__('Transcription')." {$value['language']}";
|
$obj->response[$key]['type'] = __('Translation').'/'.__('Transcription')." {$value['language']}";
|
||||||
}else if(!empty($value['ai_metatags_responses_id'])){
|
}else if(!empty($value['ai_metatags_responses_id'])){
|
||||||
$obj->response[$key]['type'] = __('Basic')." Metatags";
|
$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">
|
<table id="responsesUsage-list" class="table table-bordered table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<!-- Headers will be added here dynamically -->
|
<!-- Headers will be added here dynamically -->
|
||||||
|
@ -9,6 +35,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var modalloadAIUsage = getPleaseWait();
|
var modalloadAIUsage = getPleaseWait();
|
||||||
|
|
||||||
function loadAIUsage() {
|
function loadAIUsage() {
|
||||||
modalloadAIUsage.showPleaseWait();
|
modalloadAIUsage.showPleaseWait();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
|
|
@ -191,6 +191,7 @@ function getVTTChapterTracks($fileName, $returnArray = false)
|
||||||
if (file_exists($defaultFile)) {
|
if (file_exists($defaultFile)) {
|
||||||
$path_parts = pathinfo($defaultFile);
|
$path_parts = pathinfo($defaultFile);
|
||||||
$src = Video::getURLToFile($path_parts['basename']);
|
$src = Video::getURLToFile($path_parts['basename']);
|
||||||
|
//var_dump($src, $path_parts['basename'], $defaultFile);exit;
|
||||||
$obj = new stdClass();
|
$obj = new stdClass();
|
||||||
$obj->srclang = 'en';
|
$obj->srclang = 'en';
|
||||||
$obj->src = $src;
|
$obj->src = $src;
|
||||||
|
|
|
@ -7,22 +7,25 @@ $obj = new stdClass();
|
||||||
$obj->error = true;
|
$obj->error = true;
|
||||||
$obj->msg = "";
|
$obj->msg = "";
|
||||||
|
|
||||||
if(empty($_POST['videos_id'])){
|
if(empty($_REQUEST['videos_id'])){
|
||||||
$_POST['videos_id'] = intval($_POST['videoAutocomplete']);
|
$_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";
|
$obj->msg = "You cant do this";
|
||||||
die(json_encode($obj));
|
die(json_encode($obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
$o = new BookmarkTable(@$_POST['id']);
|
$o = new BookmarkTable(@$_REQUEST['id']);
|
||||||
$o->setName($_POST['name']);
|
$o->setName($_REQUEST['name']);
|
||||||
$o->setTimeInSeconds($_POST['timeInSeconds']);
|
$o->setTimeInSeconds($_REQUEST['timeInSeconds']);
|
||||||
$o->setVideos_id($_POST['videos_id']);
|
$o->setVideos_id($_REQUEST['videos_id']);
|
||||||
|
|
||||||
if($id = $o->save()){
|
if($id = $o->save()){
|
||||||
$obj->error = false;
|
$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 gif = $(this).find(".thumbsGIF");
|
||||||
var jpg = $(this).find(".thumbsJPG");
|
var jpg = $(this).find(".thumbsJPG");
|
||||||
try {
|
try {
|
||||||
gif.lazy({ effect: 'fadeIn',
|
gif.lazy({
|
||||||
|
effect: 'fadeIn',
|
||||||
afterLoad: function (element) {
|
afterLoad: function (element) {
|
||||||
element.removeClass('lazyload');
|
element.removeClass('lazyload');
|
||||||
element.addClass('lazyloadLoaded');
|
element.addClass('lazyloadLoaded');
|
||||||
} });
|
}
|
||||||
|
});
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
gif.height(jpg.height());
|
gif.height(jpg.height());
|
||||||
gif.width(jpg.width());
|
gif.width(jpg.width());
|
||||||
|
@ -794,6 +796,36 @@ async function showMuteTooltip() {
|
||||||
}, 5000);
|
}, 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) {
|
function playerPlayIfAutoPlay(currentTime) {
|
||||||
console.log("playerPlayIfAutoPlay: forceCurrentTime:", currentTime);
|
console.log("playerPlayIfAutoPlay: forceCurrentTime:", currentTime);
|
||||||
if (isWebRTC()) {
|
if (isWebRTC()) {
|
||||||
|
@ -4071,3 +4103,28 @@ function humanFileSize(bytes) {
|
||||||
|
|
||||||
return (bytes / 1000).toFixed(2) + ' KB';
|
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