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

Update for publish on Instafgram and Facebook

This commit is contained in:
Daniel Neto 2024-12-17 13:55:57 -03:00
parent 8d809be693
commit 342ee18808
12 changed files with 278 additions and 78 deletions

View file

@ -16,11 +16,11 @@ abstract class ObjectYPT implements ObjectInterface
protected $id; protected $id;
protected $created; protected $created;
public function __construct($id = "") public function __construct($id = "", $refreshCache = false)
{ {
if (!empty($id)) { if (!empty($id)) {
// get data from id // get data from id
$this->load($id); $this->load($id, $refreshCache);
} }
} }
@ -29,9 +29,9 @@ abstract class ObjectYPT implements ObjectInterface
return []; return [];
} }
public function load($id) public function load($id, $refreshCache = false)
{ {
$row = self::getFromDb($id); $row = self::getFromDb($id, $refreshCache);
if (empty($row)) { if (empty($row)) {
return false; return false;
} }

View file

@ -118,7 +118,7 @@ class Category
} }
} }
public function load($id) public function load($id, $refreshCache = false)
{ {
$row = self::getCategory($id); $row = self::getCategory($id);
if (empty($row)) { if (empty($row)) {

View file

@ -67,7 +67,7 @@ class Comment {
return $this->videos_id; return $this->videos_id;
} }
public function load($id) { public function load($id, $refreshCache = false) {
$row = self::getComment($id); $row = self::getComment($id);
if (empty($row)) { if (empty($row)) {
return false; return false;

View file

@ -59,10 +59,10 @@ class AVideoConf extends ObjectYPT{
} }
} }
public function load($id='') public function load($id='', $refreshCache = false)
{ {
global $global; global $global;
return parent::load(1); return parent::load(1, $refreshCache);
} }
public function save(){ public function save(){

View file

@ -39,7 +39,7 @@ class Subscribe extends ObjectYPT{
} }
} }
public function load($id) public function load($id, $refreshCache = false)
{ {
$obj = self::getSubscribe($id); $obj = self::getSubscribe($id);
if (empty($obj)) { if (empty($obj)) {

View file

@ -277,7 +277,7 @@ if (typeof gtag !== \"function\") {
return $eo[$id]; return $eo[$id];
} }
public function load($id) public function load($id, $refreshCache = false)
{ {
$id = intval($id); $id = intval($id);
if (empty($id)) { if (empty($id)) {

View file

@ -23,7 +23,7 @@ class UserGroups{
} }
} }
public function load($id) public function load($id, $refreshCache = false)
{ {
$user = self::getUserGroupsDb($id); $user = self::getUserGroupsDb($id);
if (empty($user)) { if (empty($user)) {

View file

@ -8,6 +8,12 @@ class Publisher_video_publisher_logs extends ObjectYPT
protected $id, $publish_datetimestamp, $status, $details, $videos_id, $users_id, protected $id, $publish_datetimestamp, $status, $details, $videos_id, $users_id,
$publisher_social_medias_id, $timezone; $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() static function getSearchFieldsNames()
{ {
return array('details', 'timezone'); return array('details', 'timezone');
@ -199,6 +205,7 @@ class Publisher_video_publisher_logs extends ObjectYPT
static function getInfo($row) static function getInfo($row)
{ {
global $global;
$row['publish'] = date('Y-m-d H:i:s', $row['publish_datetimestamp']); $row['publish'] = date('Y-m-d H:i:s', $row['publish_datetimestamp']);
$row['json'] = json_decode($row['details']); $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; $link = "https://www.facebook.com/watch/?v=" . $row['json']->response->VideoUploadResponse->id;
$msg[] = "<a href='{$link}' target='_blank'>{$link}</a>"; $msg[] = "<a href='{$link}' target='_blank'>{$link}</a>";
} }
break;
case SocialMediaPublisher::SOCIAL_TYPE_INSTAGRAM["name"]:
if(!empty($row['json']->mediaResponse->permalink)){
$msg[] = "<a href='{$row['json']->mediaResponse->permalink}' target='_blank'>{$row['json']->mediaResponse->permalink}</a>";
}else if ($row['status'] === self::STATUS_UNVERIFIED) {
$msg[] = '<i class="fa fa-spinner fa-spin"></i> <strong>Video is being processed:</strong> Your video is currently being processed for publishing on Instagram. Please wait.';
} elseif ($row['status'] === self::STATUS_VERIFIED) {
$msg[] = '<i class="fa fa-check-circle"></i> <strong>Video successfully published:</strong> Your video has been verified and uploaded to Instagram.';
} else {
$msg[] = '<i class="fa fa-exclamation-triangle"></i> <strong>status:</strong> ' . $row['status'] . '';
}
break; break;
} }
$row['msg'] = implode('<br>', $msg); $row['msg'] = implode('<br>', $msg);
@ -251,4 +270,12 @@ class Publisher_video_publisher_logs extends ObjectYPT
sqlDAL::close($res); sqlDAL::close($res);
return $countRow; return $countRow;
} }
public function save()
{
if (empty($this->status)) {
$this->status = self::STATUS_UNVERIFIED;
}
return parent::save();
}
} }

View file

@ -25,13 +25,16 @@ class SocialUploader
break; break;
case SocialMediaPublisher::SOCIAL_TYPE_INSTAGRAM['name']: case SocialMediaPublisher::SOCIAL_TYPE_INSTAGRAM['name']:
$pub = Publisher_user_preferences::getFromDb($publisher_user_preferences_id); $pub = Publisher_user_preferences::getFromDb($publisher_user_preferences_id);
$broadcaster_id = 0;
if (!empty($pub)) { if (!empty($pub)) {
$json = json_decode($pub['json']); $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)) { 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; $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; break;
case SocialMediaPublisher::SOCIAL_TYPE_TWITCH['name']: case SocialMediaPublisher::SOCIAL_TYPE_TWITCH['name']:
//return SocialUploader::uploadYouTube($accessToken, $videoPath, $title, $description, $visibility, $isShort); //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; $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) static public function getErrorMsg($obj)
@ -846,81 +849,150 @@ class LinkedInUploader
class InstagramUploader class InstagramUploader
{ {
/** /**
* Upload a video to Instagram. * Upload and publish a video to Instagram.
* *
* @param string $accessToken Instagram access token. * @param string $accessToken Instagram user access token.
* @param string $videoPath video file. * @param string $videoUrl Public URL to the video file.
* @param string $caption Caption for the video. * @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. * @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 = [ $return = [
'error' => true, 'error' => true,
'msg' => '', 'msg' => '',
'initResponse' => null, 'containerId' => null,
'publishResponse' => null 'publishResponse' => null
]; ];
$videoUrl = str_replace($global['systemRootPath'], $global['webSiteRootURL'], $videoPath); // Step 1: Create Media Container
$containerResponse = self::createMediaContainer($accessToken, $videoUrl, $caption, $instagramAccountId);
$videoUrl = addQueryStringParameter($videoUrl, 'globalToken', getToken(30)); if ($containerResponse['error']) {
$return['msg'] = 'Error creating media container: ' . $containerResponse['msg'];
// 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'];
return $return; 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']) { if ($publishResponse['error']) {
$return['msg'] = "Error publishing video on Instagram: " . $publishResponse['msg']; $return['msg'] = 'Error publishing media: ' . $publishResponse['msg'];
return $return; return $return;
} }
$return['error'] = false; $return['error'] = false;
$return['msg'] = 'Video uploaded and published successfully!'; $return['msg'] = 'Video uploaded and published successfully!';
$return['publishResponse'] = $publishResponse;
return $return; 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"; $url = "https://graph.facebook.com/{$containerId}?fields=status_code,status,id&access_token={$accessToken}";
$return = [
$data = [ 'error' => true,
'media_type' => 'VIDEO', 'msg' => '',
'video_url' => $videoUrl, 'response' => null,
'caption' => $caption, 'url' => $url,
'access_token' => $accessToken,
]; ];
$response = self::makeCurlRequest($url, $data); $attempts = 0;
//var_dump($url, $data, $response);exit; 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'])) { $attempts++;
return [ } while ($attempts < $maxAttempts);
'error' => true, _error_log("waitForMediaProcessing($accessToken, $containerId, $maxAttempts) " . json_encode($response));
'msg' => $response['response']['error']['message'] ?? 'Failed to initialize upload.' return $return;;
];
}
return [
'error' => false,
'containerId' => $response['response']['id']
];
} }
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 = [ $data = [
'creation_id' => $containerId, 'creation_id' => $containerId,
@ -932,31 +1004,67 @@ class InstagramUploader
if ($response['httpCode'] !== 200) { if ($response['httpCode'] !== 200) {
return [ return [
'error' => true, '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 [ return [
'error' => false, 'httpCode' => $httpCode,
'msg' => 'Video published successfully.', 'response' => json_decode($response, true)
'response' => $response['response']
]; ];
} }
private static function makeCurlRequest($url, $data) static function getInstagramVideoLink($mediaId, $accessToken)
{ {
$ch = curl_init($url); $url = "https://graph.facebook.com/{$mediaId}?fields=permalink&access_token={$accessToken}";
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch); $response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch);
curl_close($ch); curl_close($ch);
return [ if ($response === false) {
'response' => json_decode($response, true), return ['error' => true, 'msg' => 'Error fetching permalink: ' . $error];
'httpCode' => $httpCode }
];
$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.'];
}
} }
} }

View file

@ -50,9 +50,9 @@ class SocialMediaPublisher extends PluginAbstract
self::SOCIAL_TYPE_LINKEDIN['name'] => self::SOCIAL_TYPE_LINKEDIN, 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 = 'http://localhost:81/Restreamer/';
const RESTREAMER_URL = 'https://vlu.me:444/Restreamer/'; //const RESTREAMER_URL = 'https://vlu.me:444/Restreamer/';
public function getTags() public function getTags()
{ {
@ -296,7 +296,7 @@ class SocialMediaPublisher extends PluginAbstract
return $response; 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)) { if (empty($users_id)) {
$users_id = User::getId(); $users_id = User::getId();
@ -346,4 +346,55 @@ class SocialMediaPublisher extends PluginAbstract
return $btn; 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 = "<img src='{$poster}' class='img img-responsive'>";
sendSocketMessageToUsers_id('Video published on instagram<br>' . $img, $row["users_id"], 'avideoToastSuccess');
}
}
return $obj;
}
}
return false;
}
function executeEveryMinute()
{
self::scanInstagam();
}
} }

View file

@ -0,0 +1,14 @@
<?php
require_once __DIR__ . '/../../videos/configuration.php';
header('Content-Type: application/json');
$obj = new stdClass();
$obj->error = true;
$obj->msg = '';
$plugin = AVideoPlugin::loadPluginIfEnabled('SocialMediaPublisher');
$obj = SocialMediaPublisher::scanInstagam();
die(json_encode($obj));

View file

@ -1388,7 +1388,7 @@ function avideoAlertAJAXHTML(url) {
modal.showPleaseWait(); modal.showPleaseWait();
$.ajax({ $.ajax({
url: url, url: url,
success: function (response) { complete: function (response) {
avideoAlertText(response); avideoAlertText(response);
modal.hidePleaseWait(); modal.hidePleaseWait();
} }
@ -1399,7 +1399,7 @@ function avideoAlertAJAX(url) {
modal.showPleaseWait(); modal.showPleaseWait();
$.ajax({ $.ajax({
url: url, url: url,
success: function (response) { complete: function (response) {
avideoResponse(response); avideoResponse(response);
modal.hidePleaseWait(); modal.hidePleaseWait();
} }