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

Implement AI image generation feature and enhance image handling in video library

This commit is contained in:
Daniel Neto 2025-06-25 10:52:14 -03:00
parent 0f6b38e7c7
commit 9b438e96e5
17 changed files with 403 additions and 79 deletions

View file

@ -154,7 +154,11 @@ $croppieFilesAdded = 1;
$('#library-btn<?php echo $uid; ?>').off('click'); $('#library-btn<?php echo $uid; ?>').off('click');
$('#library-btn<?php echo $uid; ?>').on('click', function(ev) { $('#library-btn<?php echo $uid; ?>').on('click', function(ev) {
avideoModalIframe(webSiteRootURL + 'view/list-images.php?uid=<?php echo $uid; ?>'); var url = webSiteRootURL + 'view/list-images.php?uid=<?php echo $uid; ?>';
if (typeof mediaId == 'number' && !empty(mediaId)) {
url = addQueryStringParameter(url, 'videos_id', mediaId);
}
avideoModalIframe(url);
}); });
@ -208,4 +212,4 @@ $croppieFilesAdded = 1;
} }
}); });
} }
</script> </script>

View file

@ -7485,6 +7485,80 @@ if (!class_exists('Video')) {
return $result; return $result;
} }
static function saveImageInVideoLib($videos_id, $imageContent, $imageExt = 'png', $prefix = '')
{
global $global;
if (empty($videos_id) || empty($imageContent)) {
return false;
}
$video = Video::getVideoLight($videos_id);
if (empty($video)) {
return false;
}
$imageExt = preg_replace('/[^a-zA-Z0-9]/', '', $imageExt);
$imageName = $prefix .'_'. uniqid() . ".{$imageExt}";
$relativeDir = Video::getVideoLibRelativePath($videos_id);
$path = "{$global['systemRootPath']}{$relativeDir}{$imageName}";
if (_file_put_contents($path, $imageContent)) {
_error_log("Video::saveImageInVideoLib({$videos_id}, {$imageExt}) saved in {$path}");
return true;
} else {
_error_log("Video::saveImageInVideoLib({$videos_id}, {$imageExt}) could not save in {$path}");
return false;
}
}
static function getVideoLibRelativePath($videos_id)
{
if (empty($videos_id)) {
return false;
}
$video = Video::getVideoLight($videos_id);
if (empty($video)) {
return false;
}
$relativeDir = "videos/{$video['filename']}/images/";
return $relativeDir;
}
static function listAllImagesInVideoLib($videos_id)
{
global $global;
if (empty($videos_id)) {
return [];
}
$video = Video::getVideoLight($videos_id);
if (empty($video)) {
return [];
}
$relativeDir = Video::getVideoLibRelativePath($videos_id);
$path = "{$global['systemRootPath']}{$relativeDir}";
if (!is_dir($path)) {
return [];
}
$images = [];
$files = scandir($path);
foreach ($files as $file) {
if (preg_match('/\.(jpg|jpeg|png|gif)$/i', $file)) {
$images[] = [
'name' => $file,
'url' => "{$global['webSiteRootURL']}{$relativeDir}{$file}",
'path' => "{$path}{$file}"
];
}
}
return $images;
}
} }
} }
// Just to convert permalink into clean_title // Just to convert permalink into clean_title

View file

@ -16,6 +16,7 @@ class AI extends PluginAbstract
static $typeTranslation = 'translation'; static $typeTranslation = 'translation';
static $typeTranscription = 'transcription'; static $typeTranscription = 'transcription';
static $typeBasic = 'basic'; static $typeBasic = 'basic';
static $typeImage = 'image';
static $typeShorts = 'shorts'; static $typeShorts = 'shorts';
static $typeDubbing = 'dubbing'; static $typeDubbing = 'dubbing';
@ -687,9 +688,9 @@ class AI extends PluginAbstract
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"
role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"> 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>
</div>'; </div>';
@ -776,7 +777,7 @@ class AI extends PluginAbstract
$duration_in_seconds = $video->getDuration_in_seconds(); $duration_in_seconds = $video->getDuration_in_seconds();
if(empty($duration_in_seconds)){ if(empty($duration_in_seconds)){
_error_log("The video {$videos_id} has not duration set, the price will be calculated over 10 minutes", AVideoLog::$ERROR); _error_log("The video {$videos_id} has not duration set, the price will be calculated over 10 minutes", AVideoLog::$ERROR);
$duration_in_seconds = 600; // 10 minutes $duration_in_seconds = 600; // 10 minutes
} }
$price = $obj->priceForDubbing * $duration_in_seconds; $price = $obj->priceForDubbing * $duration_in_seconds;
break; break;
@ -814,8 +815,8 @@ class AI extends PluginAbstract
_error_log("AI:asyncVideosId error the user $users_id has no balance to pay the service $type for videos_id $videos_id "); _error_log("AI:asyncVideosId error the user $users_id has no balance to pay the service $type for videos_id $videos_id ");
$obj = new stdClass(); $obj = new stdClass();
$obj->error = true; $obj->error = true;
$obj->msg = "Transaction failed: Insufficient funds. $obj->msg = "Transaction failed: Insufficient funds.
You currently do not have enough balance in your account to cover the AI-powered video $type service. You currently do not have enough balance in your account to cover the AI-powered video $type service.
Please add funds to proceed. Thank you."; Please add funds to proceed. Thank you.";
return $obj; return $obj;
} }
@ -845,6 +846,10 @@ class AI extends PluginAbstract
_error_log('AI:asyncVideosId ' . basename(__FILE__) . ' line=' . __LINE__); _error_log('AI:asyncVideosId ' . basename(__FILE__) . ' line=' . __LINE__);
$obj = AI::getVideoDubbingMetadata($videos_id, @$_REQUEST['language']); $obj = AI::getVideoDubbingMetadata($videos_id, @$_REQUEST['language']);
break; break;
case AI::$typeImage:
_error_log('AI:asyncVideosId typeImage ' . basename(__FILE__) . ' line=' . __LINE__);
$obj = AI::getVideoBasicMetadata($videos_id);
break;
default: default:
_error_log('AI:asyncVideosId ' . basename(__FILE__) . ' line=' . __LINE__); _error_log('AI:asyncVideosId ' . basename(__FILE__) . ' line=' . __LINE__);
$obj = new stdClass(); $obj = new stdClass();
@ -918,7 +923,7 @@ class AI extends PluginAbstract
if (empty($content)) { if (empty($content)) {
$obj = new stdClass(); $obj = new stdClass();
$obj->error = true; $obj->error = true;
$obj->msg = "Oops! Our system took a bit longer than expected to process your request. $obj->msg = "Oops! Our system took a bit longer than expected to process your request.
Please try again in a few moments. We apologize for any inconvenience and appreciate your patience."; Please try again in a few moments. We apologize for any inconvenience and appreciate your patience.";
return $obj; return $obj;
} }

View file

@ -34,9 +34,9 @@ class Ai_responses extends ObjectYPT
} }
} else { } else {
/** /**
* *
* @var array $global * @var array $global
* @var object $global['mysqli'] * @var object $global['mysqli']
*/ */
_error_log($sql . ' Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error); _error_log($sql . ' Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
} }
@ -74,14 +74,55 @@ class Ai_responses extends ObjectYPT
{ {
return intval($this->videos_id); return intval($this->videos_id);
} }
function setPrice($price) { function setPrice($price)
{
$this->price = floatval($price); $this->price = floatval($price);
} }
function getPrice() { function getPrice()
{
return floatval($this->price); return floatval($this->price);
} }
static function getAllImageFromVideo($videos_id)
{
global $global;
$sql = "SELECT *
FROM ai_responses_json as arj
LEFT JOIN ai_responses as ar ON ar.id = arj.ai_responses_id
WHERE ar.videos_id = ? AND arj.ai_type = ?";
$sql .= self::getSqlFromPost('arj.');
// var_dump($sql, [$videos_id, AI::$typeImage]);
$res = sqlDAL::readSql($sql, 'is', [$videos_id, AI::$typeImage]);
$fullData = sqlDAL::fetchAllAssoc($res);
//var_dump($sql, $fullData);exit;
sqlDAL::close($res);
$rows = array();
if ($res != false) {
foreach ($fullData as $row) {
if (!empty($row['response'])) {
$row['response'] = _json_decode($row['response']);
}
//var_dump($row['response']->data[0]->url);
$row['url'] = '';
if (!empty($row['response']) && !empty($row['response']->data[0]->url)) {
$row['url'] = $row['response']->data[0]->url;
}
$rows[] = $row;
}
} else {
/**
*
* @var array $global
* @var object $global['mysqli']
*/
_error_log($sql . ' Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
}
return $rows;
}
static function getAllBasicFromVideo($videos_id) static function getAllBasicFromVideo($videos_id)
{ {
@ -100,23 +141,23 @@ class Ai_responses extends ObjectYPT
$rows = array(); $rows = array();
if ($res != false) { if ($res != false) {
foreach ($fullData as $row) { foreach ($fullData as $row) {
if(empty($row['videoTitles'])){ if (empty($row['videoTitles'])) {
$row['videoTitles'] = array(); $row['videoTitles'] = array();
}else if(is_string($row['videoTitles'])){ } else if (is_string($row['videoTitles'])) {
$row['videoTitles'] = json_decode($row['videoTitles']); $row['videoTitles'] = json_decode($row['videoTitles']);
} }
if(empty($row['keywords'])){ if (empty($row['keywords'])) {
$row['keywords'] = array(); $row['keywords'] = array();
}else if(is_string($row['keywords'])){ } else if (is_string($row['keywords'])) {
$row['keywords'] = json_decode($row['keywords']); $row['keywords'] = json_decode($row['keywords']);
} }
$rows[] = $row; $rows[] = $row;
} }
} else { } else {
/** /**
* *
* @var array $global * @var array $global
* @var object $global['mysqli'] * @var object $global['mysqli']
*/ */
_error_log($sql . ' Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error); _error_log($sql . ' Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
} }
@ -141,9 +182,9 @@ class Ai_responses extends ObjectYPT
} else { } else {
sqlDAL::close($res); sqlDAL::close($res);
/** /**
* *
* @var array $global * @var array $global
* @var object $global['mysqli'] * @var object $global['mysqli']
*/ */
_error_log($sql . ' Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error); _error_log($sql . ' Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
} }
@ -211,14 +252,14 @@ class Ai_responses extends ObjectYPT
'text'); 'text');
*/ */
foreach ($fullData as $row) { foreach ($fullData as $row) {
if(empty($row['videoTitles'])){ if (empty($row['videoTitles'])) {
$row['videoTitles'] = array(); $row['videoTitles'] = array();
}else if(is_string($row['videoTitles'])){ } else if (is_string($row['videoTitles'])) {
$row['videoTitles'] = json_decode($row['videoTitles']); $row['videoTitles'] = json_decode($row['videoTitles']);
} }
if(empty($row['keywords'])){ if (empty($row['keywords'])) {
$row['keywords'] = array(); $row['keywords'] = array();
}else if(is_string($row['keywords'])){ } else if (is_string($row['keywords'])) {
$row['keywords'] = json_decode($row['keywords']); $row['keywords'] = json_decode($row['keywords']);
} }
foreach ($cleanKeys as $value) { foreach ($cleanKeys as $value) {
@ -228,9 +269,9 @@ class Ai_responses extends ObjectYPT
} }
} else { } else {
/** /**
* *
* @var array $global * @var array $global
* @var object $global['mysqli'] * @var object $global['mysqli']
*/ */
_error_log($sql . ' Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error); _error_log($sql . ' Error : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
} }
@ -240,23 +281,23 @@ class Ai_responses extends ObjectYPT
static function getHistory($videos_id) static function getHistory($videos_id)
{ {
global $global; global $global;
$sql = "SELECT $sql = "SELECT
ar.*, ar.*,
ar.created as sortDate, ar.created as sortDate,
COALESCE(amr.price_prompt_tokens, 0) as price_prompt_tokens, COALESCE(amr.price_prompt_tokens, 0) as price_prompt_tokens,
COALESCE(amr.price_completion_tokens, 0) as price_completion_tokens, COALESCE(amr.price_completion_tokens, 0) as price_completion_tokens,
COALESCE(atr.total_price, 0) as total_price, COALESCE(atr.total_price, 0) as total_price,
(COALESCE(amr.price_prompt_tokens, 0) + COALESCE(amr.price_completion_tokens, 0)) AS total_metadata , (COALESCE(amr.price_prompt_tokens, 0) + COALESCE(amr.price_completion_tokens, 0)) AS total_metadata ,
COALESCE(atr.total_price, 0) AS total_transcription , COALESCE(atr.total_price, 0) AS total_transcription ,
(COALESCE(amr.price_prompt_tokens, 0) + COALESCE(amr.price_completion_tokens, 0) + COALESCE(atr.total_price, 0)) AS total (COALESCE(amr.price_prompt_tokens, 0) + COALESCE(amr.price_completion_tokens, 0) + COALESCE(atr.total_price, 0)) AS total
FROM FROM
ai_responses ar ai_responses ar
LEFT JOIN LEFT JOIN
ai_metatags_responses amr ON ar.id = amr.ai_responses_id ai_metatags_responses amr ON ar.id = amr.ai_responses_id
LEFT JOIN LEFT JOIN
ai_transcribe_responses atr ON ar.id = atr.ai_responses_id ai_transcribe_responses atr ON ar.id = atr.ai_responses_id
WHERE WHERE
ar.videos_id = ? ar.videos_id = ?
ORDER BY sortDate DESC"; ORDER BY sortDate DESC";
//$sql .= self::getSqlFromPost(); //$sql .= self::getSqlFromPost();
@ -264,24 +305,22 @@ class Ai_responses extends ObjectYPT
$res = sqlDAL::readSql($sql, 'i', [$videos_id]); $res = sqlDAL::readSql($sql, 'i', [$videos_id]);
$fullData = sqlDAL::fetchAllAssoc($res); $fullData = sqlDAL::fetchAllAssoc($res);
sqlDAL::close($res); sqlDAL::close($res);
return $fullData ;
return $fullData;
} }
static function getValidTranscriptions($videos_id) static function getValidTranscriptions($videos_id)
{ {
$rows = self::getTranscriptions($videos_id); $rows = self::getTranscriptions($videos_id);
foreach ($rows as $row) { foreach ($rows as $row) {
if(!empty($row['text'])){ if (!empty($row['text'])) {
return $row; return $row;
} }
} }
return false; return false;
} }
static function getTranscriptions($videos_id) static function getTranscriptions($videos_id)
{ {
global $global; global $global;
@ -295,9 +334,8 @@ class Ai_responses extends ObjectYPT
$res = sqlDAL::readSql($sql, 'i', [$videos_id]); $res = sqlDAL::readSql($sql, 'i', [$videos_id]);
$fullData = sqlDAL::fetchAllAssoc($res); $fullData = sqlDAL::fetchAllAssoc($res);
sqlDAL::close($res); sqlDAL::close($res);
return $fullData ;
return $fullData;
} }
static function hasTranscriptions($videos_id) static function hasTranscriptions($videos_id)
@ -309,7 +347,7 @@ class Ai_responses extends ObjectYPT
static function getTranscriptionText($videos_id) static function getTranscriptionText($videos_id)
{ {
$rows = self::getValidTranscriptions($videos_id); $rows = self::getValidTranscriptions($videos_id);
if(!empty($rows)){ if (!empty($rows)) {
return $rows['text']; return $rows['text'];
} }
return ''; return '';
@ -318,25 +356,26 @@ class Ai_responses extends ObjectYPT
static function getTranscriptionVtt($videos_id) static function getTranscriptionVtt($videos_id)
{ {
$rows = self::getValidTranscriptions($videos_id); $rows = self::getValidTranscriptions($videos_id);
if(!empty($rows)){ if (!empty($rows)) {
//_error_log("AI::getTranscriptionVtt($videos_id) ".json_encode($rows['vtt'])); //_error_log("AI::getTranscriptionVtt($videos_id) ".json_encode($rows['vtt']));
return $rows['vtt']; return $rows['vtt'];
} }
return ''; return '';
} }
static function getLatest($videos_id) { static function getLatest($videos_id)
{
global $global; global $global;
$sql = "SELECT tr.*, mr.*, r.* FROM ai_responses r $sql = "SELECT tr.*, mr.*, r.* FROM ai_responses r
LEFT JOIN ai_transcribe_responses tr ON tr.ai_responses_id = r.id LEFT JOIN ai_transcribe_responses tr ON tr.ai_responses_id = r.id
LEFT JOIN ai_metatags_responses mr ON mr.ai_responses_id = r.id LEFT JOIN ai_metatags_responses mr ON mr.ai_responses_id = r.id
WHERE videos_id = ? ORDER BY r.id DESC LIMIT 1"; WHERE videos_id = ? ORDER BY r.id DESC LIMIT 1";
$formats = 'i'; $formats = 'i';
$values = [$videos_id]; $values = [$videos_id];
//var_dump($sql, $values); //var_dump($sql, $values);
$res = sqlDAL::readSql($sql, $formats, $values); $res = sqlDAL::readSql($sql, $formats, $values);
$data = sqlDAL::fetchAssoc($res); $data = sqlDAL::fetchAssoc($res);
sqlDAL::close($res); sqlDAL::close($res);
return $data; return $data;
} }
} }

View file

@ -22,6 +22,11 @@
loadAIShorts(); loadAIShorts();
} }
break; break;
case '<?php echo AI::$typeImage; ?>':
if (typeof loadAIImage == 'function') {
loadAIImage();
}
break;
default: default:
break; break;
} }
@ -30,4 +35,4 @@
} }
avideoToastSuccess('AI ' + json.type + ' received'); avideoToastSuccess('AI ' + json.type + ' received');
} }
</script> </script>

View file

@ -164,6 +164,12 @@ $_page = new Page(['Video Metatags']);
<?php echo __("Basic"); ?> <?php echo __("Basic"); ?>
</a> </a>
</li> </li>
<li>
<a data-toggle="tab" href="#pimage">
<i class="fa-solid fa-image"></i>
<?php echo __("Image"); ?>
</a>
</li>
<li> <li>
<a data-toggle="tab" href="#pShorts"> <a data-toggle="tab" href="#pShorts">
<i class="fa-solid fa-scissors"></i> <i class="fa-solid fa-scissors"></i>
@ -208,6 +214,11 @@ $_page = new Page(['Video Metatags']);
include $global['systemRootPath'] . 'plugin/AI/tabs/basic.php'; include $global['systemRootPath'] . 'plugin/AI/tabs/basic.php';
?> ?>
</div> </div>
<div id="pimage" class="tab-pane fade">
<?php
include $global['systemRootPath'] . 'plugin/AI/tabs/image.php';
?>
</div>
<div id="pShorts" class="tab-pane fade"> <div id="pShorts" class="tab-pane fade">
<?php <?php
include $global['systemRootPath'] . 'plugin/AI/tabs/shorts.php'; include $global['systemRootPath'] . 'plugin/AI/tabs/shorts.php';
@ -537,4 +548,4 @@ $_page = new Page(['Video Metatags']);
</script> </script>
<?php <?php
$_page->print(); $_page->print();
?> ?>

View file

@ -164,6 +164,26 @@ switch ($_REQUEST['type']) {
//$jsonDecoded->lines[] = __LINE__; //$jsonDecoded->lines[] = __LINE__;
} }
break; break;
case AI::$typeImage:
error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);
if (!empty($_REQUEST['response'])) {
$o = new Ai_responses_json(0);
$o->setResponse($_REQUEST['response']);
$o->setAi_type(AI::$typeImage);
$o->setAi_responses_id($token->ai_responses_id);
if (!empty($_REQUEST['response']['data'][0]['url'])) {
$imageContent = file_get_contents($_REQUEST['response']['data'][0]['url']);
if (empty($imageContent)) {
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__ . ' Error fetching image content');
} else {
Video::saveImageInVideoLib($token->videos_id, $imageContent, 'png', 'ai');
}
}
$jsonDecoded->msg = $_REQUEST['msg'];
$jsonDecoded->Ai_responses_json = $o->save();
$jsonDecoded->error = empty($jsonDecoded->Ai_responses_json);
}
break;
default: default:
_error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__); _error_log('AI: ' . basename(__FILE__) . ' line=' . __LINE__);

View file

@ -1,11 +1,24 @@
<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" style="border-left: 5px solid #31708f; padding-left: 20px;">
<h4><strong>Enhance Your Video SEO with AI</strong></h4> <h4 class="text-primary" style="margin-top: 0;">
<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> <strong><i class="fas fa-rocket"></i> Boost Your Video SEO with AI</strong>
<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> </h4>
<p>Start leveraging the power of AI to make your videos stand out in search results!</p> <p>
We're pleased to introduce an <strong>AI-powered SEO enhancement tool</strong> for your videos.
This new feature intelligently generates optimized:
<em>Titles, Descriptions (Casual & Professional), Meta Descriptions, Keywords, Summaries, Ratings, and Justifications</em>.
</p>
<p>
By analyzing your video's current title and description, our AI delivers tailored SEO suggestions
to maximize discoverability. For the most accurate results, we recommend including a
<i class="fas fa-microphone-alt"></i> <strong><?php echo __("Transcription"); ?></strong> of your video — enabling deeper content understanding.
</p>
<p>
<strong>Start using AI to elevate your content and stand out in search results.</strong>
</p>
</div> </div>
<?php <?php
echo AI::getProgressBarHTML("basic_{$videos_id}", ''); echo AI::getProgressBarHTML("basic_{$videos_id}", '');
?> ?>
@ -24,7 +37,7 @@
<button class="btn btn-success btn-block" onclick="generateAIIdeas()"> <button class="btn btn-success btn-block" onclick="generateAIIdeas()">
<i class="fa-solid fa-lightbulb"></i> <?php echo __('Generate Basic Ideas') ?> <i class="fa-solid fa-lightbulb"></i> <?php echo __('Generate Basic Ideas') ?>
<?php <?php
if(!empty($priceForBasic)){ if (!empty($priceForBasic)) {
echo "<br><span class=\"label label-success\">{$priceForBasicText}</span>"; echo "<br><span class=\"label label-success\">{$priceForBasicText}</span>";
} }
?> ?>
@ -98,4 +111,4 @@
$(document).ready(function() { $(document).ready(function() {
loadAIBasic(); loadAIBasic();
}); });
</script> </script>

View file

@ -0,0 +1,32 @@
<?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 (!AVideoPlugin::isEnabledByName('AI')) {
forbiddenPage('AI plugin is disabled');
}
if(!AI::canUseAI()){
forbiddenPage('You cannot use AI');
}
if (!Video::canEdit($videos_id)) {
forbiddenPage('You cannot edit this video');
}
setRowCount(100);
$video = new Video('', '', $videos_id);
setDefaultSort('created', 'DESC');
$obj = new stdClass();
$obj->msg = '';
$obj->videos_id = $videos_id;
$obj->response = Ai_responses::getAllImageFromVideo($videos_id);
$obj->error = empty($obj->response) && !is_array($obj->response);
$obj->images = Video::listAllImagesInVideoLib($videos_id);
echo _json_encode($obj);

79
plugin/AI/tabs/image.php Normal file
View file

@ -0,0 +1,79 @@
<div class="panel panel-default">
<div class="panel-heading">
<div class="alert alert-info" style="border-left: 5px solid #31708f; padding-left: 20px;">
<p>
<strong><i class="fas fa-image"></i> AI-Generated Image Preview:</strong> We use your video's <strong>title</strong> and <strong>description</strong> to create a unique and visually engaging AI-generated image.
</p>
<p>
These images are designed to enhance your contents visual appeal ideal for use as thumbnails, background posters, or video covers.
</p>
<p>
For the best results, ensure your title and description clearly reflect the main subject or mood of the video.
</p>
</div>
<?php echo AI::getProgressBarHTML("image_{$videos_id}", ''); ?>
</div>
<div class="panel-body">
<div id="ai-images" class="row">
<!-- As imagens serão carregadas aqui -->
</div>
</div>
<div class="panel-footer">
<button class="btn btn-success btn-block" onclick="generateAIImages()">
<i class="fa fa-images"></i> <?php echo __('Generate Image') ?>
<?php
if (!empty($priceForBasic)) {
echo "<br><span class=\"label label-success\">{$priceForBasicText}</span>";
}
?>
</button>
</div>
</div>
<script>
async function generateAIImages() {
await createAISuggestions('<?php echo AI::$typeImage; ?>');
loadAIImage();
loadAIUsage();
}
function loadAIImage() {
modal.showPleaseWait();
$.ajax({
url: webSiteRootURL + 'plugin/AI/tabs/image.json.php',
data: {
videos_id: <?php echo $videos_id; ?>
},
type: 'post',
success: function(response) {
if (response.error) {
avideoAlertError(response.msg);
} else {
const container = $('#ai-images');
container.empty();
response.images.forEach(function(item) {
const imgURL = item.url;
const html = `
<div class="col-xs-12 col-sm-6 col-md-4 text-center" style="margin-bottom: 15px;">
<a href="${imgURL}" target="_blank">
<img src="${imgURL}" class="img img-responsive img-thumbnail" style="margin: 0 auto;"/>
</a>
</div>
`;
container.append(html);
});
}
modal.hidePleaseWait();
}
});
}
$(document).ready(function() {
loadAIImage();
});
</script>

View file

@ -31,9 +31,16 @@ $columnCallbackFunctions = ['text'];
<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" style="border-left: 5px solid #31708f; padding-left: 20px;">
<p><strong>Note:</strong> To ensure accurate transcription, your videos should contain clear speech. Please be aware that videos without any spoken words, or those containing only sounds and instrumental music, cannot be transcribed by our AI system. Make sure your videos have audible and clear speech to take full advantage of this feature.</p> <p>
<strong><i class="fas fa-info-circle"></i> Important:</strong> For accurate transcriptions, your videos must contain clear, audible speech.
Please note that videos with no spoken words or only instrumental music or sound effects cannot be processed by our AI transcription system.
</p>
<p>
Ensure that speech is present and understandable in your content to fully benefit from this feature.
</p>
</div> </div>
<?php <?php
echo AI::getProgressBarHTML("transcription_{$videos_id}", __('Automatic')); echo AI::getProgressBarHTML("transcription_{$videos_id}", __('Automatic'));
foreach (AI::LANGS as $key => $value) { foreach (AI::LANGS as $key => $value) {
@ -258,4 +265,4 @@ $columnCallbackFunctions = ['text'];
loadAITranscriptions(); loadAITranscriptions();
}); });
</script> </script>

View file

@ -40,6 +40,8 @@ foreach ($obj->response as $key => $value) {
}else if(!empty($value['ai_type'])){ }else if(!empty($value['ai_type'])){
if($value['ai_type'] === AI::$typeShorts){ if($value['ai_type'] === AI::$typeShorts){
$obj->response[$key]['type'] = __('Shorts'); $obj->response[$key]['type'] = __('Shorts');
}else if($value['ai_type'] === AI::$typeImage){
$obj->response[$key]['type'] = __('Image');
}else{ }else{
$obj->response[$key]['type'] = "ERROR: {$value['ai_type']} "; $obj->response[$key]['type'] = "ERROR: {$value['ai_type']} ";
} }
@ -47,4 +49,4 @@ foreach ($obj->response as $key => $value) {
} }
$obj->error = empty($obj->response) && !is_array($obj->response); $obj->error = empty($obj->response) && !is_array($obj->response);
echo _json_encode($obj); echo _json_encode($obj);

View file

@ -168,7 +168,7 @@ $bodyClass = '';
if (!empty($_REQUEST['isClosed'])) { if (!empty($_REQUEST['isClosed'])) {
$bodyClass = 'is-closed'; $bodyClass = 'is-closed';
} }
// this is to make sure will not play in fullscreen on ios
$global['overrideNative'] = 1; $global['overrideNative'] = 1;
//var_dump($liveVideo, $video['id'], $poster, $sources);exit; //var_dump($liveVideo, $video['id'], $poster, $sources);exit;
?> ?>

View file

@ -10,7 +10,13 @@ if (!User::isLogged()) {
} }
$userId = User::getId(); $userId = User::getId();
$relativeDir = "videos/userPhoto/Live/user_{$userId}/";
if (!empty($_REQUEST['videos_id'])) {
$relativeDir = Video::getVideoLibRelativePath($_REQUEST['videos_id']);
} else {
$relativeDir = "videos/userPhoto/Live/user_{$userId}/";
}
$absoluteDir = realpath(__DIR__ . "/../{$relativeDir}"); $absoluteDir = realpath(__DIR__ . "/../{$relativeDir}");
if (!is_dir($absoluteDir)) { if (!is_dir($absoluteDir)) {

View file

@ -6,11 +6,18 @@ if (!User::isLogged()) {
} }
$userId = User::getId(); $userId = User::getId();
$videos_id = getVideos_id();
// List of relative directories (must end with slash) // List of relative directories (must end with slash)
$relativeDirs = [ if($videos_id){
"videos/userPhoto/Live/user_{$userId}/", $relativeDirs = [
]; Video::getVideoLibRelativePath($videos_id),
];
}else{
$relativeDirs = [
"videos/userPhoto/Live/user_{$userId}/",
];
}
$allowed_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp']; $allowed_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$images = []; $images = [];
@ -19,7 +26,7 @@ foreach ($relativeDirs as $relativeDir) {
$absoluteDir = realpath(__DIR__ . "/../{$relativeDir}"); $absoluteDir = realpath(__DIR__ . "/../{$relativeDir}");
// Security check: must be valid and inside videos folder // Security check: must be valid and inside videos folder
if (!$absoluteDir || strpos($absoluteDir, realpath(__DIR__ . '/../videos/userPhoto/Live/')) !== 0) { if (!$absoluteDir || strpos($absoluteDir, realpath(__DIR__ . '/../videos/')) !== 0) {
continue; continue;
} }

View file

@ -1,6 +1,7 @@
<?php <?php
global $global, $config; global $global, $config;
require_once __DIR__ . '/../videos/configuration.php'; require_once __DIR__ . '/../videos/configuration.php';
$videos_id = getVideos_id();
$_page = new Page(array('List Categories')); $_page = new Page(array('List Categories'));
?> ?>
<link href="<?php echo getURL('view/mini-upload-form/assets/css/style.css'); ?>" rel="stylesheet" /> <link href="<?php echo getURL('view/mini-upload-form/assets/css/style.css'); ?>" rel="stylesheet" />
@ -9,6 +10,7 @@ $_page = new Page(array('List Categories'));
.image-col { .image-col {
margin-bottom: 15px; margin-bottom: 15px;
} }
.image-col img { .image-col img {
margin: 0 !important; margin: 0 !important;
} }
@ -31,6 +33,7 @@ $_page = new Page(array('List Categories'));
<div id="drop"> <div id="drop">
<a><?php echo __("Browse files"); ?></a> <a><?php echo __("Browse files"); ?></a>
<input type="file" name="upl" id="fileInput" multiple accept="image/*" /> <input type="file" name="upl" id="fileInput" multiple accept="image/*" />
<input type="hidden" name="videos_id" value="<?php echo $videos_id; ?>" />
</div> </div>
<ul> <ul>
<!-- Upload progress shown here --> <!-- Upload progress shown here -->
@ -49,8 +52,15 @@ $_page = new Page(array('List Categories'));
<script src="<?php echo getURL('view/mini-upload-form/assets/js/jquery.fileupload.js'); ?>"></script> <script src="<?php echo getURL('view/mini-upload-form/assets/js/jquery.fileupload.js'); ?>"></script>
<script> <script>
var videos_id = <?php echo json_encode($videos_id ?: null); ?>;
function loadImages() { function loadImages() {
$.getJSON(webSiteRootURL + 'view/list-images.json.php', function(images) { const url = webSiteRootURL + 'view/list-images.json.php';
const query = videos_id ? {
videos_id: videos_id
} : {};
$.getJSON(url, query, function(images) {
$('#imageGrid').empty(); $('#imageGrid').empty();
images.forEach(function(img) { images.forEach(function(img) {
const col = $('<div class="col-xs-6 col-sm-4 col-md-3 text-center image-col"></div>'); const col = $('<div class="col-xs-6 col-sm-4 col-md-3 text-center image-col"></div>');
@ -62,7 +72,8 @@ $_page = new Page(array('List Categories'));
avideoConfirm(__('Are you sure you want to delete this image?')).then(function(response) { avideoConfirm(__('Are you sure you want to delete this image?')).then(function(response) {
if (response) { if (response) {
$.post(webSiteRootURL + 'view/list-images.delete.json.php', { $.post(webSiteRootURL + 'view/list-images.delete.json.php', {
filename: img.filename filename: img.filename,
videos_id: videos_id
}, function(response) { }, function(response) {
if (!response.error) { if (!response.error) {
col.remove(); col.remove();
@ -81,7 +92,8 @@ $_page = new Page(array('List Categories'));
const uid = new URLSearchParams(window.location.search).get('uid'); const uid = new URLSearchParams(window.location.search).get('uid');
window.parent.postMessage({ window.parent.postMessage({
selectedImageURL: image.data('src'), selectedImageURL: image.data('src'),
croppieUID: uid croppieUID: uid,
videos_id: videos_id
}, '*'); }, '*');
}); });
@ -91,6 +103,7 @@ $_page = new Page(array('List Categories'));
}); });
} }
$(document).ready(function() { $(document).ready(function() {
$('#drop a').click(function() { $('#drop a').click(function() {
$(this).parent().find('input').click(); $(this).parent().find('input').click();

View file

@ -13,7 +13,14 @@ if (!User::isLogged()) {
} }
$userId = User::getId(); $userId = User::getId();
$relativeDir = "videos/userPhoto/Live/user_{$userId}/";
if (!empty($_REQUEST['videos_id'])) {
$relativeDir = Video::getVideoLibRelativePath($_REQUEST['videos_id']);
} else {
$relativeDir = "videos/userPhoto/Live/user_{$userId}/";
}
$absoluteDir = __DIR__ . '/../' . $relativeDir; $absoluteDir = __DIR__ . '/../' . $relativeDir;
make_path($relativeDir); make_path($relativeDir);