From b61324024725e2f0d846563fc5da97a7437013d9 Mon Sep 17 00:00:00 2001 From: Daniel Neto Date: Mon, 16 Dec 2024 14:16:03 -0300 Subject: [PATCH] Update --- objects/category.php | 47 ++-- .../Publisher_video_publisher_logs.php | 6 + .../Objects/SocialUploader.php | 253 ++++++++++++++++-- .../SocialMediaPublisher.php | 16 +- 4 files changed, 274 insertions(+), 48 deletions(-) diff --git a/objects/category.php b/objects/category.php index 3e5d7bcdb7e8..7a49343e5019 100644 --- a/objects/category.php +++ b/objects/category.php @@ -381,13 +381,13 @@ public static function getAllCategories($filterCanAddVideoOnly = false, $onlyWit if (!empty($_REQUEST['doNotShowCats'])) { $doNotShowCats = $_REQUEST['doNotShowCats']; - if(!is_array($_REQUEST['doNotShowCats'])){ + if (!is_array($_REQUEST['doNotShowCats'])) { $doNotShowCats = array($_REQUEST['doNotShowCats']); } foreach ($doNotShowCats as $key => $value) { $doNotShowCats[$key] = str_replace("'", '', $value); } - $sql .= " AND (c.clean_name NOT IN ('".implode("', '", $doNotShowCats)."') )"; + $sql .= " AND (c.clean_name NOT IN ('" . implode("', '", $doNotShowCats) . "') )"; } if ($filterCanAddVideoOnly && !User::isAdmin()) { @@ -403,8 +403,8 @@ public static function getAllCategories($filterCanAddVideoOnly = false, $onlyWit } if ($onlyWithVideos) { - $sql .= " AND ((SELECT count(*) FROM videos v where 1=1 "; - if(isForKidsSet()){ + $sql .= " AND ((SELECT count(*) FROM videos v where 1=1 "; + if (isForKidsSet()) { $sql .= " AND v.made_for_kids = 1 "; } $sql .= " AND v.categories_id = c.id OR categories_id IN (SELECT id from categories where parentId = c.id AND id != c.id)) > 0 "; @@ -425,7 +425,7 @@ public static function getAllCategories($filterCanAddVideoOnly = false, $onlyWit } $sql .= ")"; } - + if ($sameUserGroupAsMe) { //_error_log('getAllCategories getUserGroups'); $users_groups = UserGroups::getUserGroups($sameUserGroupAsMe); @@ -453,9 +453,9 @@ public static function getAllCategories($filterCanAddVideoOnly = false, $onlyWit } $sql .= BootGrid::getSqlFromPost(['name'], "", " ORDER BY `order`, name ASC "); - if(!empty($_GET['debug'])){ + if (!empty($_GET['debug'])) { //echo $sql;exit; - } + } $timeLogName = TimeLogStart("getAllCategories"); $cacheSuffix = md5($sql); @@ -674,10 +674,10 @@ public static function getTotalFromCategory($categories_id, $showUnlisted = fals { global $global; $cacheSuffix = "getTotalFromCategory_{$categories_id}_" . intval($showUnlisted) . intval($getAllVideos); - if(isset($global[$cacheSuffix])){ + if (isset($global[$cacheSuffix])) { return $global[$cacheSuffix]; } - + $timeLogName = TimeLogStart($cacheSuffix); $cacheHandler = new CategoryCacheHandler(0); TimeLogEnd($timeLogName, __LINE__); @@ -785,7 +785,7 @@ public static function getTotalVideosFromCategory($categories_id, $showUnlisted $cacheHandler = new CategoryCacheHandler($categories_id); $suffix = "totalVideos_" . intval($showUnlisted) . "_" . intval($getAllVideos); - + $timeLogName = TimeLogStart($suffix); $total = $cacheHandler->getCache($suffix); TimeLogEnd($timeLogName, __LINE__); @@ -867,38 +867,35 @@ public static function getTotalLivesFromCategory($categories_id, $showUnlisted = $cacheHandler = new CategoryCacheHandler($categories_id); $suffix = "totalLives_" . intval($showUnlisted); $total = $cacheHandler->getCache($suffix); - //$renew = true; + if ($renew || (empty($total) && $total !== 0)) { - $sql = "SELECT count(id) as total FROM live_transmitions v WHERE 1=1 AND categories_id = ? "; + $sql = " + SELECT COUNT(DISTINCT v.id) AS total + FROM live_transmitions v + INNER JOIN live_transmitions_history h ON v.id = h.key + WHERE v.categories_id = ? "; if (empty($showUnlisted)) { - $sql .= " AND public = 1 "; + $sql .= " AND v.public = 1 "; } - //echo $categories_id, $sql;exit; $res = sqlDAL::readSql($sql, "i", [$categories_id]); $fullResult = sqlDAL::fetchAllAssoc($res); sqlDAL::close($res); $total = empty($fullResult[0]['total']) ? 0 : intval($fullResult[0]['total']); + $rows = self::getChildCategories($categories_id); foreach ($rows as $value) { $total += self::getTotalLivesFromCategory($value['id'], $showUnlisted, $renew); } $cacheHandler->setCache(intval($total)); - /* - AVideoPlugin::getEnd(); - echo PHP_EOL.PHP_EOL.PHP_EOL.'---------'.PHP_EOL.PHP_EOL; - $total2 = $cacheHandler->getCache($suffix); - echo 'total='; - var_dump($total, $total2, $suffix); - echo PHP_EOL.PHP_EOL.PHP_EOL.'---------'.PHP_EOL.PHP_EOL; - exit; - */ } + return intval($total); } + public static function clearCacheCount($categories_id = 0) { // clear category count cache @@ -965,8 +962,8 @@ public static function getTotalCategories($filterCanAddVideoOnly = false, $onlyW $sql .= "AND parentId = 0 OR parentId = -1 "; } if ($onlyWithVideos) { - $sql .= " AND ((SELECT count(*) FROM videos v where 1=1 "; - if(isForKidsSet()){ + $sql .= " AND ((SELECT count(*) FROM videos v where 1=1 "; + if (isForKidsSet()) { $sql .= " AND v.made_for_kids = 1 "; } $sql .= " AND v.categories_id = c.id OR categories_id IN (SELECT id from categories where parentId = c.id AND id != c.id) ) > 0 "; diff --git a/plugin/SocialMediaPublisher/Objects/Publisher_video_publisher_logs.php b/plugin/SocialMediaPublisher/Objects/Publisher_video_publisher_logs.php index 42c23ade5447..7f184ff8fe51 100644 --- a/plugin/SocialMediaPublisher/Objects/Publisher_video_publisher_logs.php +++ b/plugin/SocialMediaPublisher/Objects/Publisher_video_publisher_logs.php @@ -224,6 +224,12 @@ static function getInfo($row) $msg[] = "{$link}"; } break; + case SocialMediaPublisher::SOCIAL_TYPE_FACEBOOK["name"]: + if (!empty($row['json']->response->VideoUploadResponse) && !empty($row['json']->response->VideoUploadResponse->id)) { + $link = "https://www.facebook.com/watch/?v=" . $row['json']->response->VideoUploadResponse->id; + $msg[] = "{$link}"; + } + break; } $row['msg'] = implode('
', $msg); } diff --git a/plugin/SocialMediaPublisher/Objects/SocialUploader.php b/plugin/SocialMediaPublisher/Objects/SocialUploader.php index c9f26a4f0f23..2e8e25cece4e 100644 --- a/plugin/SocialMediaPublisher/Objects/SocialUploader.php +++ b/plugin/SocialMediaPublisher/Objects/SocialUploader.php @@ -11,7 +11,7 @@ public static function upload($publisher_user_preferences_id, $videoPath, $title return array('error' => true, 'msg' => 'Empty access token', 'line' => __LINE__); } $pub = new Publisher_user_preferences($publisher_user_preferences_id); - _error_log("SocialMediaPublisher::upload provider=".$pub->getProviderName()); + _error_log("SocialMediaPublisher::upload provider=" . $pub->getProviderName()); try { switch ($pub->getProviderName()) { case SocialMediaPublisher::SOCIAL_TYPE_YOUTUBE['name']: @@ -20,11 +20,11 @@ public static function upload($publisher_user_preferences_id, $videoPath, $title case SocialMediaPublisher::SOCIAL_TYPE_FACEBOOK['name']: $json = json_decode($pub->getJson()); $pageId = $json->{'restream.ypt.me'}->facebook->broadcaster_id; - _error_log("SocialMediaPublisher::upload json=".json_encode($json)); + _error_log("SocialMediaPublisher::upload json=" . json_encode($json)); return SocialUploader::uploadFacebook($accessToken, $pageId, $videoPath, $title, $description, $visibility, $isShort); break; case SocialMediaPublisher::SOCIAL_TYPE_INSTAGRAM['name']: - //return SocialUploader::uploadYouTube($accessToken, $videoPath, $title, $description, $visibility, $isShort); + return SocialUploader::uploadInstagram($accessToken, $videoPath, $title, $description, $isShort); break; case SocialMediaPublisher::SOCIAL_TYPE_TWITCH['name']: //return SocialUploader::uploadYouTube($accessToken, $videoPath, $title, $description, $visibility, $isShort); @@ -80,6 +80,7 @@ private static function uploadLinkedIn($accessToken, $authorUrn, $id, $videoPath return $uploadResult; } + private static function uploadYouTube($accessToken, $videoPath, $title, $description, $visibility = 'public', $isShort = false) { $title = _substr($title, 0, 90); @@ -143,6 +144,11 @@ private static function uploadFacebook($accessToken, $pageId, $videoPath, $title } } + private static function uploadInstagram($accessToken, $videoPath, $title, $description, $isShort = false) + { + $caption = $title . PHP_EOL . PHP_EOL . $description; + return InstagramUploader::upload($accessToken, $videoPath, $caption, $isShort); + } static public function getErrorMsg($obj) { @@ -171,14 +177,18 @@ static public function getErrorMsg($obj) } + class FacebookUploader { static function uploadFacebookVideo($accessToken, $pageId, $videoPath, $title, $description) { global $global; // Upload a local video directly - $videoUrl = str_replace(getVideosDir(), $global['webSiteRootURL'], $videoPath); - $VideoUploadResponse = FacebookUploader::uploadDirectHostedVideo($pageId, $accessToken, $videoUrl, $title,PHP_EOL.$description); + $videoUrl = str_replace($global['systemRootPath'], $global['webSiteRootURL'], $videoPath); + + $videoUrl = addQueryStringParameter($videoUrl, 'globalToken', getToken(9930)); + + $VideoUploadResponse = FacebookUploader::uploadDirectHostedVideo($pageId, $accessToken, $videoUrl, $title, PHP_EOL . $description); //$VideoUploadResponse = FacebookUploader::uploadDirectLocalVideo($pageId, $accessToken, $videoPath); $return = [ @@ -189,13 +199,16 @@ static function uploadFacebookVideo($accessToken, $pageId, $videoPath, $title, $ $return['videoUrl'] = $videoUrl; $return['VideoUploadResponse'] = $VideoUploadResponse; - if (isset($VideoUploadResponse['success']) && $VideoUploadResponse['success']) { + if (isset($VideoUploadResponse['id'])) { $return['line'][] = __LINE__; $return['error'] = false; return $return; } else { $return['line'][] = __LINE__; $return['msg'] = isset($VideoUploadResponse['error']) ? $VideoUploadResponse['error'] : 'Unknown error occurred during video upload.'; + if (!empty($return['VideoUploadResponse']) && !empty($return['VideoUploadResponse']['error']) && !empty($return['VideoUploadResponse']['error']['error_user_msg'])) { + $return['msg'] = $return['VideoUploadResponse']['error']['error_user_msg']; + } return $return; } } @@ -258,7 +271,9 @@ static function initializeFacebookUploadSession($pageId, $accessToken) static function uploadLocalVideoToFacebook($videoId, $accessToken, $filePath) { - $url = "https://rupload.facebook.com/video-upload/v19.0/{$videoId}"; + $url = "https://upload.facebook.com/video-upload/v19.0/{$videoId}"; + //$url = "https://graph.facebook.com/{$pageId}/videos"; + $fileSize = filesize($filePath); $ch = curl_init(); @@ -284,8 +299,7 @@ static function uploadLocalVideoToFacebook($videoId, $accessToken, $filePath) static function uploadHostedVideoToFacebook($videoId, $accessToken, $fileUrl) { - $url = "https://rupload.facebook.com/video-upload/v19.0/{$videoId}"; - + $url = "https://upload.facebook.com/video-upload/v19.0/{$videoId}"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, [ @@ -339,30 +353,83 @@ static function uploadDirectLocalVideo($pageId, $accessToken, $filePath, $descri return json_decode($response, true); } + // Fetch Page Access Token + static function getPageAccessToken($accessToken, $pageId) + { + $url = "https://graph.facebook.com/me/accounts?access_token={$accessToken}"; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $response = curl_exec($ch); + $error = curl_error($ch); + curl_close($ch); + + if ($response === false) { + $return = [ + 'error' => true, + 'msg' => 'Error fetching pages: ' . $error, + 'line' => array(__LINE__) + ]; + die(json_encode($return)); + } + + $data = json_decode($response, true); + if (isset($data['data'])) { + foreach ($data['data'] as $page) { + if ($page['id'] == $pageId && isset($page['access_token'])) { + _error_log("SocialMediaPublisher::getPageAccessToken access_token found tp pageId=$pageId " . json_encode($page['access_token'])); + return $page['access_token']; + } + } + } else if (isset($data['error'])) { + $return = [ + 'error' => true, + 'msg' => "Failed to fetch page access token with error for pageId={$pageId} {$data['error']['message']}", + 'line' => array(__LINE__), + 'url' => $url, + ]; + die(json_encode($return)); + } + + // Handle unexpected case where no page access token is found + $msg = "Unexpected failure to fetch page access token for pageId={$pageId}."; + $msg .= "
This might happen if you are trying to publish to a personal profile. Please delete the connection and connect to a Facebook Page."; + + $return = [ + 'error' => true, + 'msg' => $msg, + 'line' => array(__LINE__), + 'url' => $url, + ]; + die(json_encode($return)); + } + + static function uploadDirectHostedVideo($pageId, $accessToken, $videoUrl, $description = '') { + $pageAccessToken = self::getPageAccessToken($accessToken, $pageId); + $url = "https://graph.facebook.com/{$pageId}/videos"; + $postData = [ 'description' => $description, - 'access_token' => $accessToken, + 'access_token' => $pageAccessToken, 'file_url' => $videoUrl ]; - _error_log("SocialMediaPublisher::uploadDirectHostedVideo $url ".json_encode($postData)); + _error_log("SocialMediaPublisher::uploadDirectHostedVideo $url " . json_encode($postData)); $ch = curl_init(); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Content-Type: application/json', - "Authorization: Bearer {$accessToken}" - ]); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 0); // No timeout + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300); // 300 seconds $response = curl_exec($ch); if (curl_errno($ch)) { - return json_encode(['error' => curl_error($ch)]); + return json_encode(['error' => curl_error($ch), 'errno' => curl_errno($ch)]); } curl_close($ch); @@ -769,3 +836,157 @@ static function publishVideo($accessToken, $authorUrn, $videoUrn, $title, $descr } } } + +class InstagramUploader +{ + /** + * Upload a video to Instagram. + * + * @param string $accessToken Instagram access token. + * @param string $videoPath Local path to the video file. + * @param string $caption Caption for the video. + * @param bool $isReel Whether the upload is for Instagram Reels. + * @return array Response from the Instagram API. + */ + public static function upload($accessToken, $videoPath, $caption, $isReel = false) + { + $return = [ + 'error' => true, + 'msg' => '', + 'initResponse' => null, + 'uploadResponse' => null, + 'publishResponse' => null + ]; + + // Step 1: Initialize the upload + $initResponse = self::initializeInstagramUpload($accessToken, $isReel); + $return['initResponse'] = $initResponse; + + if ($initResponse['error']) { + $return['msg'] = "Failed to initialize Instagram upload: " . $initResponse['message']; + return $return; + } + + $uploadUrl = $initResponse['uploadUrl']; + $containerId = $initResponse['containerId']; + + // Step 2: Upload the video file + $uploadResponse = self::uploadVideoToInstagram($uploadUrl, $videoPath); + $return['uploadResponse'] = $uploadResponse; + + if ($uploadResponse['error']) { + $return['msg'] = "Error uploading video to Instagram: " . $uploadResponse['msg']; + return $return; + } + + // Step 3: Publish the video + $publishResponse = self::publishInstagramVideo($accessToken, $containerId, $caption); + $return['publishResponse'] = $publishResponse; + + if ($publishResponse['error']) { + $return['msg'] = "Error publishing video on Instagram: " . $publishResponse['msg']; + return $return; + } + + $return['error'] = false; + $return['msg'] = 'Video uploaded and published successfully!'; + return $return; + } + + private static function initializeInstagramUpload($accessToken, $isReel) + { + $url = $isReel + ? "https://graph.facebook.com/v17.0/me/media?media_type=VIDEO" + : "https://graph.instagram.com/v17.0/me/media?media_type=VIDEO"; + + $data = [ + 'access_token' => $accessToken, + ]; + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + $responseArray = json_decode($response, true); + + if ($httpCode !== 200 || empty($responseArray['id'])) { + return [ + 'error' => true, + 'message' => $responseArray['error']['message'] ?? 'Failed to initialize upload.' + ]; + } + + return [ + 'error' => false, + 'containerId' => $responseArray['id'], + 'uploadUrl' => $responseArray['video_url'] + ]; + } + + private static function uploadVideoToInstagram($uploadUrl, $videoPath) + { + $fileSize = filesize($videoPath); + + $ch = curl_init($uploadUrl); + curl_setopt($ch, CURLOPT_PUT, true); + curl_setopt($ch, CURLOPT_INFILE, fopen($videoPath, 'r')); + curl_setopt($ch, CURLOPT_INFILESIZE, $fileSize); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($httpCode !== 200) { + return [ + 'error' => true, + 'msg' => 'Failed to upload video file.' + ]; + } + + return [ + 'error' => false, + 'msg' => 'Video uploaded successfully.' + ]; + } + + private static function publishInstagramVideo($accessToken, $containerId, $caption) + { + $url = "https://graph.facebook.com/v17.0/me/media_publish"; + + $data = [ + 'access_token' => $accessToken, + 'creation_id' => $containerId, + 'caption' => $caption + ]; + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + $responseArray = json_decode($response, true); + + if ($httpCode !== 200) { + return [ + 'error' => true, + 'msg' => $responseArray['error']['message'] ?? 'Failed to publish video.' + ]; + } + + return [ + 'error' => false, + 'msg' => 'Video published successfully.', + 'response' => $responseArray + ]; + } +} diff --git a/plugin/SocialMediaPublisher/SocialMediaPublisher.php b/plugin/SocialMediaPublisher/SocialMediaPublisher.php index 96f0b3249ec8..3ff92f644d45 100644 --- a/plugin/SocialMediaPublisher/SocialMediaPublisher.php +++ b/plugin/SocialMediaPublisher/SocialMediaPublisher.php @@ -235,12 +235,14 @@ static function revalidateTokenAndSave($publisher_user_preferences_id, $onlyIfIs public static function upload($publisher_user_preferences_id, $videos_id) { $video = new Video('', '', $videos_id); - $paths = Video::getFirstSource($video->getFilename()); - if (empty($paths)) { + //$paths = Video::getFirstSource($video->getFilename()); + $paths = getVideosURLMP4Only($video->getFilename()); + //var_dump($paths['mp4']);exit; + if (empty($paths['mp4'])) { _error_log("SocialMediaPublisher::upload($publisher_user_preferences_id, $videos_id) video paths are empty"); return false; } - if (!file_exists($paths['path'])) { + if (!file_exists($paths['mp4']['path'])) { _error_log("SocialMediaPublisher::upload($publisher_user_preferences_id, $videos_id) video path does not exist"); return false; } @@ -255,11 +257,11 @@ public static function upload($publisher_user_preferences_id, $videos_id) $providerName = $o->getProviderName(); - $fromFileLocation = $paths['url']; - if (!isDummyFile($paths['path'])) { - $videoPath = $paths['path']; + $fromFileLocation = $paths['mp4']['url']; + if (!isDummyFile($paths['mp4']['path'])) { + $videoPath = $paths['mp4']['path']; } else { - $videoPathToYouTube = $videoPath = ($paths['path'] . '.toYouTube.mp4'); + $videoPathToYouTube = $videoPath = ($paths['mp4']['path'] . '.toYouTube.mp4'); } if (!file_exists($videoPath)) { _error_log("SocialMediaPublisher::upload($publisher_user_preferences_id, $videos_id) $providerName start conversion ");