diff --git a/objects/Object.php b/objects/Object.php
index 84a0dfee2f..6fcf27710f 100644
--- a/objects/Object.php
+++ b/objects/Object.php
@@ -16,11 +16,11 @@ abstract class ObjectYPT implements ObjectInterface
protected $id;
protected $created;
- public function __construct($id = "")
+ public function __construct($id = "", $refreshCache = false)
{
if (!empty($id)) {
// get data from id
- $this->load($id);
+ $this->load($id, $refreshCache);
}
}
@@ -29,9 +29,9 @@ abstract class ObjectYPT implements ObjectInterface
return [];
}
- public function load($id)
+ public function load($id, $refreshCache = false)
{
- $row = self::getFromDb($id);
+ $row = self::getFromDb($id, $refreshCache);
if (empty($row)) {
return false;
}
diff --git a/objects/category.php b/objects/category.php
index 9f439903de..86aa041773 100644
--- a/objects/category.php
+++ b/objects/category.php
@@ -118,7 +118,7 @@ class Category
}
}
- public function load($id)
+ public function load($id, $refreshCache = false)
{
$row = self::getCategory($id);
if (empty($row)) {
diff --git a/objects/comment.php b/objects/comment.php
index f48332bd28..fbd0ce71eb 100644
--- a/objects/comment.php
+++ b/objects/comment.php
@@ -67,7 +67,7 @@ class Comment {
return $this->videos_id;
}
- public function load($id) {
+ public function load($id, $refreshCache = false) {
$row = self::getComment($id);
if (empty($row)) {
return false;
diff --git a/objects/configuration.php b/objects/configuration.php
index 86e1a2f91b..2417aed7de 100644
--- a/objects/configuration.php
+++ b/objects/configuration.php
@@ -59,10 +59,10 @@ class AVideoConf extends ObjectYPT{
}
}
- public function load($id='')
+ public function load($id='', $refreshCache = false)
{
global $global;
- return parent::load(1);
+ return parent::load(1, $refreshCache);
}
public function save(){
diff --git a/objects/subscribe.php b/objects/subscribe.php
index c3417e738d..edbf41002d 100644
--- a/objects/subscribe.php
+++ b/objects/subscribe.php
@@ -39,7 +39,7 @@ class Subscribe extends ObjectYPT{
}
}
- public function load($id)
+ public function load($id, $refreshCache = false)
{
$obj = self::getSubscribe($id);
if (empty($obj)) {
diff --git a/objects/user.php b/objects/user.php
index bd24f6a7d2..7997032d31 100644
--- a/objects/user.php
+++ b/objects/user.php
@@ -277,7 +277,7 @@ if (typeof gtag !== \"function\") {
return $eo[$id];
}
- public function load($id)
+ public function load($id, $refreshCache = false)
{
$id = intval($id);
if (empty($id)) {
diff --git a/objects/userGroups.php b/objects/userGroups.php
index ba7072363a..6167ae8dc8 100644
--- a/objects/userGroups.php
+++ b/objects/userGroups.php
@@ -23,7 +23,7 @@ class UserGroups{
}
}
- public function load($id)
+ public function load($id, $refreshCache = false)
{
$user = self::getUserGroupsDb($id);
if (empty($user)) {
diff --git a/plugin/SocialMediaPublisher/Objects/Publisher_video_publisher_logs.php b/plugin/SocialMediaPublisher/Objects/Publisher_video_publisher_logs.php
index 7f184ff8fe..5541037262 100644
--- a/plugin/SocialMediaPublisher/Objects/Publisher_video_publisher_logs.php
+++ b/plugin/SocialMediaPublisher/Objects/Publisher_video_publisher_logs.php
@@ -8,6 +8,12 @@ class Publisher_video_publisher_logs extends ObjectYPT
protected $id, $publish_datetimestamp, $status, $details, $videos_id, $users_id,
$publisher_social_medias_id, $timezone;
+ const STATUS_UNVERIFIED = 'u';
+ const STATUS_VERIFIED = 'v';
+ const STATUS_ACTIVE = 'a';
+ const STATUS_INACTIVE = 'i';
+ const STATUS_PROCESSING = 'p';
+
static function getSearchFieldsNames()
{
return array('details', 'timezone');
@@ -199,6 +205,7 @@ class Publisher_video_publisher_logs extends ObjectYPT
static function getInfo($row)
{
+ global $global;
$row['publish'] = date('Y-m-d H:i:s', $row['publish_datetimestamp']);
$row['json'] = json_decode($row['details']);
@@ -229,6 +236,18 @@ class Publisher_video_publisher_logs extends ObjectYPT
$link = "https://www.facebook.com/watch/?v=" . $row['json']->response->VideoUploadResponse->id;
$msg[] = "{$link}";
}
+ break;
+ case SocialMediaPublisher::SOCIAL_TYPE_INSTAGRAM["name"]:
+ if(!empty($row['json']->mediaResponse->permalink)){
+ $msg[] = "mediaResponse->permalink}' target='_blank'>{$row['json']->mediaResponse->permalink}";
+ }else if ($row['status'] === self::STATUS_UNVERIFIED) {
+ $msg[] = ' Video is being processed: Your video is currently being processed for publishing on Instagram. Please wait.';
+ } elseif ($row['status'] === self::STATUS_VERIFIED) {
+ $msg[] = ' Video successfully published: Your video has been verified and uploaded to Instagram.';
+ } else {
+ $msg[] = ' status: ' . $row['status'] . '';
+ }
+
break;
}
$row['msg'] = implode('
', $msg);
@@ -251,4 +270,12 @@ class Publisher_video_publisher_logs extends ObjectYPT
sqlDAL::close($res);
return $countRow;
}
+
+ public function save()
+ {
+ if (empty($this->status)) {
+ $this->status = self::STATUS_UNVERIFIED;
+ }
+ return parent::save();
+ }
}
diff --git a/plugin/SocialMediaPublisher/Objects/SocialUploader.php b/plugin/SocialMediaPublisher/Objects/SocialUploader.php
index 35c482269e..9ae9a126c9 100644
--- a/plugin/SocialMediaPublisher/Objects/SocialUploader.php
+++ b/plugin/SocialMediaPublisher/Objects/SocialUploader.php
@@ -25,13 +25,16 @@ class SocialUploader
break;
case SocialMediaPublisher::SOCIAL_TYPE_INSTAGRAM['name']:
$pub = Publisher_user_preferences::getFromDb($publisher_user_preferences_id);
+ $broadcaster_id = 0;
if (!empty($pub)) {
$json = json_decode($pub['json']);
+ //var_dump($json);
if (!empty($json) && !empty($json->{"restream.ypt.me"}->instagram) && !empty($json->{"restream.ypt.me"}->instagram->access_token)) {
$accessToken = $json->{"restream.ypt.me"}->instagram->access_token;
+ $broadcaster_id = $json->{"restream.ypt.me"}->instagram->broadcaster_id;
}
}
- return SocialUploader::uploadInstagram($accessToken, $videoPath, $title, $description, $isShort);
+ return SocialUploader::uploadInstagram($accessToken, $videoPath, $title, $description, $broadcaster_id);
break;
case SocialMediaPublisher::SOCIAL_TYPE_TWITCH['name']:
//return SocialUploader::uploadYouTube($accessToken, $videoPath, $title, $description, $visibility, $isShort);
@@ -151,10 +154,10 @@ class SocialUploader
}
}
- private static function uploadInstagram($accessToken, $videoPath, $title, $description, $isShort = false)
+ private static function uploadInstagram($accessToken, $videoPath, $title, $description, $broadcaster_id)
{
$caption = $title . PHP_EOL . PHP_EOL . $description;
- return InstagramUploader::upload($accessToken, $videoPath, $caption, $isShort);
+ return InstagramUploader::upload($accessToken, $videoPath, $caption, $broadcaster_id);
}
static public function getErrorMsg($obj)
@@ -846,81 +849,150 @@ class LinkedInUploader
class InstagramUploader
{
/**
- * Upload a video to Instagram.
+ * Upload and publish a video to Instagram.
*
- * @param string $accessToken Instagram access token.
- * @param string $videoPath video file.
+ * @param string $accessToken Instagram user access token.
+ * @param string $videoUrl Public URL to the video file.
* @param string $caption Caption for the video.
- * @param string $userId Instagram user ID.
+ * @param string $instagramAccountId Instagram Business Account ID.
* @return array Response from the Instagram API.
*/
- public static function upload($accessToken, $videoPath, $caption, $userId)
+ public static function upload($accessToken, $videoUrl, $caption, $instagramAccountId)
{
- global $global;
$return = [
'error' => true,
'msg' => '',
- 'initResponse' => null,
+ 'containerId' => null,
'publishResponse' => null
];
- $videoUrl = str_replace($global['systemRootPath'], $global['webSiteRootURL'], $videoPath);
-
- $videoUrl = addQueryStringParameter($videoUrl, 'globalToken', getToken(30));
-
- // Step 1: Initialize the upload with video_url
- $initResponse = self::initializeInstagramUpload($accessToken, $userId, $videoUrl, $caption);
- $return['initResponse'] = $initResponse;
-
- if ($initResponse['error']) {
- $return['msg'] = "Failed to initialize Instagram upload: " . $initResponse['msg'];
+ // Step 1: Create Media Container
+ $containerResponse = self::createMediaContainer($accessToken, $videoUrl, $caption, $instagramAccountId);
+ if ($containerResponse['error']) {
+ $return['msg'] = 'Error creating media container: ' . $containerResponse['msg'];
return $return;
}
- $containerId = $initResponse['containerId'];
+ $containerId = $containerResponse['id'];
+ $return['accessToken'] = $accessToken;
+ $return['containerId'] = $containerId;
+ $return['containerResponse'] = $containerResponse;
+ $return['instagramAccountId'] = $instagramAccountId;
+
+ if (!empty($return['containerId'])) {
+ $return['error'] = false;
+ }
+
+ $waitForMediaProcessing = self::waitForMediaProcessing($accessToken, $containerId);
+ $return['waitForMediaProcessing'] = $waitForMediaProcessing;
+ return $return;
+ }
+
+ private static function createMediaContainer($accessToken, $videoUrl, $caption, $instagramAccountId)
+ {
+ global $global;
+ $url = "https://graph.facebook.com/{$instagramAccountId}/media";
+
+ $videoUrl = str_replace($global['systemRootPath'], $global['webSiteRootURL'], $videoUrl);
+ $data = [
+ 'media_type' => 'REELS',
+ 'video_url' => $videoUrl,
+ 'is_carousel_item' => false,
+ 'caption' => $caption,
+ 'access_token' => $accessToken
+ ];
+
+ $response = self::makeCurlRequest($url, $data);
+
+ if ($response['httpCode'] !== 200 || empty($response['response']['id'])) {
+ return [
+ 'error' => true,
+ 'msg' => $response['response']['error']['message'] ?? 'Failed to create media container.',
+ 'url' => $url,
+ 'data' => $data,
+ 'response' => $response
+ ];
+ }
+
+ return ['error' => false, 'id' => $response['response']['id'], 'url' => $url, 'data' => $data, 'response' => $response];
+ }
+
+
+ public static function publishMediaIfIsReady($accessToken, $containerId, $instagramAccountId)
+ {
+
+ $return = [
+ 'error' => true,
+ 'msg' => '',
+ 'containerId' => null,
+ 'publishResponse' => null
+ ];
+ $return['accessToken'] = $accessToken;
+ $return['containerId'] = $containerId;
+ $return['instagramAccountId'] = $instagramAccountId;
+
+ $waitForMediaProcessing = self::waitForMediaProcessing($accessToken, $containerId);
+ $return['waitForMediaProcessing'] = $waitForMediaProcessing;
+ //var_dump($isReady);exit;
+
+ if(empty($waitForMediaProcessing['error']) || $waitForMediaProcessing["response"]["status_code"] === "PUBLISHED"){
+ // Step 3: Publish Media
+ $publishResponse = self::publishMedia($accessToken, $containerId, $instagramAccountId);
+
+ $mediaResponse = self::getInstagramVideoLink($publishResponse['id'], $accessToken);
+
+ $return['publishResponse'] = $publishResponse;
+ $return['mediaResponse'] = $mediaResponse;
+ }
+
+ if ($waitForMediaProcessing['error']) {
+ $return['msg'] = $waitForMediaProcessing['msg'];
+ return $return;
+ }
- // Step 2: Publish the video
- $publishResponse = self::publishInstagramVideo($accessToken, $containerId, $userId);
- $return['publishResponse'] = $publishResponse;
if ($publishResponse['error']) {
- $return['msg'] = "Error publishing video on Instagram: " . $publishResponse['msg'];
+ $return['msg'] = 'Error publishing media: ' . $publishResponse['msg'];
return $return;
}
$return['error'] = false;
$return['msg'] = 'Video uploaded and published successfully!';
+ $return['publishResponse'] = $publishResponse;
return $return;
}
- private static function initializeInstagramUpload($accessToken, $userId, $videoUrl, $caption)
+ private static function waitForMediaProcessing($accessToken, $containerId, $maxAttempts = 1)
{
- $url = "https://graph.facebook.com/$userId/media";
-
- $data = [
- 'media_type' => 'VIDEO',
- 'video_url' => $videoUrl,
- 'caption' => $caption,
- 'access_token' => $accessToken,
+ $url = "https://graph.facebook.com/{$containerId}?fields=status_code,status,id&access_token={$accessToken}";
+ $return = [
+ 'error' => true,
+ 'msg' => '',
+ 'response' => null,
+ 'url' => $url,
];
- $response = self::makeCurlRequest($url, $data);
- //var_dump($url, $data, $response);exit;
+ $attempts = 0;
+ do {
+ sleep(5); // Wait for 5 seconds
+ $response = self::makeCurlRequest($url);
+ $status = $response['response']['status_code'] ?? null;
+ $return['response'] = $response['response'];
+ if ($status === 'FINISHED') {
+ $return['error'] = false;
+ return $return;
+ }
+ if (!empty($return['response']['error']) && !empty($return['response']['error']['message'])) {
+ $return['msg'] = $return['response']['error']['message'];
+ }
- if ($response['httpCode'] !== 200 || empty($response['response']['id'])) {
- return [
- 'error' => true,
- 'msg' => $response['response']['error']['message'] ?? 'Failed to initialize upload.'
- ];
- }
-
- return [
- 'error' => false,
- 'containerId' => $response['response']['id']
- ];
+ $attempts++;
+ } while ($attempts < $maxAttempts);
+ _error_log("waitForMediaProcessing($accessToken, $containerId, $maxAttempts) " . json_encode($response));
+ return $return;;
}
- private static function publishInstagramVideo($accessToken, $containerId, $userId)
+ private static function publishMedia($accessToken, $containerId, $instagramAccountId)
{
- $url = "https://graph.facebook.com/v17.0/$userId/media_publish";
+ $url = "https://graph.facebook.com/v17.0/{$instagramAccountId}/media_publish";
$data = [
'creation_id' => $containerId,
@@ -932,31 +1004,67 @@ class InstagramUploader
if ($response['httpCode'] !== 200) {
return [
'error' => true,
- 'msg' => $response['response']['error']['message'] ?? 'Failed to publish video.'
+ 'msg' => $response['response']['error']['message'] ?? 'Failed to publish media.'
+ ];
+ }
+
+ return ['error' => false, 'id' => $response['response']['id']];
+ }
+
+ private static function makeCurlRequest($url, $data = [])
+ {
+ $ch = curl_init($url);
+
+ if (!empty($data)) {
+ // POST request if $data is not empty
+ curl_setopt($ch, CURLOPT_POST, true);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+ } else {
+ // GET request if $data is empty
+ curl_setopt($ch, CURLOPT_HTTPGET, true);
+ }
+
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 30); // Optional: Timeout after 30 seconds
+
+ $response = curl_exec($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $error = curl_error($ch);
+ curl_close($ch);
+
+ if ($response === false) {
+ return [
+ 'httpCode' => $httpCode,
+ 'response' => ['error' => ['message' => $error]]
];
}
return [
- 'error' => false,
- 'msg' => 'Video published successfully.',
- 'response' => $response['response']
+ 'httpCode' => $httpCode,
+ 'response' => json_decode($response, true)
];
}
- private static function makeCurlRequest($url, $data)
+ static function getInstagramVideoLink($mediaId, $accessToken)
{
- $ch = curl_init($url);
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ $url = "https://graph.facebook.com/{$mediaId}?fields=permalink&access_token={$accessToken}";
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
- $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $error = curl_error($ch);
curl_close($ch);
- return [
- 'response' => json_decode($response, true),
- 'httpCode' => $httpCode
- ];
+ if ($response === false) {
+ return ['error' => true, 'msg' => 'Error fetching permalink: ' . $error];
+ }
+
+ $data = json_decode($response, true);
+
+ if (isset($data['permalink'])) {
+ return ['error' => false, 'permalink' => $data['permalink']];
+ } else {
+ return ['error' => true, 'msg' => 'Failed to retrieve video link.'];
+ }
}
}
diff --git a/plugin/SocialMediaPublisher/SocialMediaPublisher.php b/plugin/SocialMediaPublisher/SocialMediaPublisher.php
index 6272e32410..e6abc42317 100644
--- a/plugin/SocialMediaPublisher/SocialMediaPublisher.php
+++ b/plugin/SocialMediaPublisher/SocialMediaPublisher.php
@@ -50,9 +50,9 @@ class SocialMediaPublisher extends PluginAbstract
self::SOCIAL_TYPE_LINKEDIN['name'] => self::SOCIAL_TYPE_LINKEDIN,
);
- //const RESTREAMER_URL = 'https://restream.ypt.me/';
+ const RESTREAMER_URL = 'https://restream.ypt.me/';
//const RESTREAMER_URL = 'http://localhost:81/Restreamer/';
- const RESTREAMER_URL = 'https://vlu.me:444/Restreamer/';
+ //const RESTREAMER_URL = 'https://vlu.me:444/Restreamer/';
public function getTags()
{
@@ -296,7 +296,7 @@ class SocialMediaPublisher extends PluginAbstract
return $response;
}
- private static function saveLog($publisher_social_medias_id, $videos_id, $details, $users_id = 0, $status = '')
+ private static function saveLog($publisher_social_medias_id, $videos_id, $details, $users_id = 0, $status = Publisher_video_publisher_logs::STATUS_UNVERIFIED)
{
if (empty($users_id)) {
$users_id = User::getId();
@@ -346,4 +346,55 @@ class SocialMediaPublisher extends PluginAbstract
return $btn;
}
+
+ static function scanInstagam()
+ {
+ global $global;
+ $sql = "SELECT psm.*, pvpl.* FROM publisher_video_publisher_logs pvpl LEFT JOIN publisher_social_medias psm ON publisher_social_medias_id = psm.id
+ WHERE pvpl.status='" . Publisher_video_publisher_logs::STATUS_UNVERIFIED . "'
+ AND name = '" . SocialMediaPublisher::SOCIAL_TYPE_INSTAGRAM["name"] . "' ORDER BY pvpl.id DESC LIMIT 100 ";
+
+ $res = sqlDAL::readSql($sql);
+ $fullData = sqlDAL::fetchAllAssoc($res);
+ sqlDAL::close($res);
+ //var_dump($sql, $fullData);
+ if (!empty($fullData)) {
+ foreach ($fullData as $key => $row) {
+ $json = json_decode($row["details"]);
+
+ $accessToken = $json->response->accessToken;
+ $containerId = $json->response->containerId;
+ $instagramAccountId = $json->response->instagramAccountId;
+
+ $obj = InstagramUploader::publishMediaIfIsReady($accessToken, $containerId, $instagramAccountId);
+
+ if ((isset($obj['error']) && $obj['error'] === false) || $obj["waitForMediaProcessing"]["response"]["status_code"] === "PUBLISHED") {
+
+ //var_dump($obj);
+ if (!empty($obj['publishResponse'])) {
+ $json->publishResponse = $obj['publishResponse'];
+ }
+ if (!empty($obj['mediaResponse'])) {
+ $json->mediaResponse = $obj['mediaResponse'];
+ }
+
+ $smp = new Publisher_video_publisher_logs($row['id'], true);
+ $smp->setDetails($json);
+ $smp->setStatus(Publisher_video_publisher_logs::STATUS_VERIFIED);
+ if ($smp->save()) {
+ $poster = Video::getPoster($row["videos_id"],);
+ $img = "
";
+ sendSocketMessageToUsers_id('Video published on instagram
' . $img, $row["users_id"], 'avideoToastSuccess');
+ }
+ }
+ return $obj;
+ }
+ }
+ return false;
+ }
+
+ function executeEveryMinute()
+ {
+ self::scanInstagam();
+ }
}
diff --git a/plugin/SocialMediaPublisher/publishInstagram.json.php b/plugin/SocialMediaPublisher/publishInstagram.json.php
new file mode 100644
index 0000000000..c3f68ebcd2
--- /dev/null
+++ b/plugin/SocialMediaPublisher/publishInstagram.json.php
@@ -0,0 +1,14 @@
+error = true;
+$obj->msg = '';
+
+$plugin = AVideoPlugin::loadPluginIfEnabled('SocialMediaPublisher');
+
+$obj = SocialMediaPublisher::scanInstagam();
+
+die(json_encode($obj));
diff --git a/view/js/script.js b/view/js/script.js
index a23d165897..7ddc798fbc 100644
--- a/view/js/script.js
+++ b/view/js/script.js
@@ -1388,7 +1388,7 @@ function avideoAlertAJAXHTML(url) {
modal.showPleaseWait();
$.ajax({
url: url,
- success: function (response) {
+ complete: function (response) {
avideoAlertText(response);
modal.hidePleaseWait();
}
@@ -1399,7 +1399,7 @@ function avideoAlertAJAX(url) {
modal.showPleaseWait();
$.ajax({
url: url,
- success: function (response) {
+ complete: function (response) {
avideoResponse(response);
modal.hidePleaseWait();
}