Source of file YouTubeFeed.php
Size: 12,772 Bytes - Last Modified: 2021-12-23T10:02:23+00:00
/var/www/docs.ssmods.com/process/src/code/Services/YouTubeFeed.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 | <?php /** * Class YouTubeFeed * * Provides YouTube user profile access */ class YouTubeFeed extends Controller { /** * @var Google_Client */ private $client; /** * @var Google_Service_YouTube */ private $service; /** * @var string */ private $stateSessionIdentifier = 'YouTubeFeed_State'; /** * @var array */ private static $allowed_actions = array( 'authenticate' ); /** * Instantiate the Google API and feed provided config values * We require a long-lived access token */ public function __construct() { parent::__construct(); $siteConfig = SiteConfig::current_site_config(); $appID = $siteConfig->YouTubeFeed_AppID; $appSecret = $siteConfig->YouTubeFeed_AppSecret; $this->client = new Google_Client(); $this->client->setScopes('https://www.googleapis.com/auth/youtube'); $this->client->setAccessType('offline'); $this->client->setApprovalPrompt('force'); if (!Director::is_cli()) { $this->client->setRedirectUri(Director::absoluteBaseURL() . 'youtube/authenticate'); } if ($appID && $appSecret) { $this->client->setClientId($appID); $this->client->setClientSecret($appSecret); $this->service = new Google_Service_YouTube($this->client); if ($accessToken = $this->getConfigToken()) { $this->client->setAccessToken($accessToken); } } } /** * Provides an endpoint to complete YouTube OAuth * * @return SS_HTTPResponse|string */ public function authenticate() { $response = $this->getResponse(); if ($code = $this->getRequest()->getVar('code')) { if (strval(Session::get($this->stateSessionIdentifier)) !== strval($this->getRequest()->getVar('state'))) { $response->setStatusCode('400'); $response->setBody('The state did not match'); } else { if ($this->client) { $this->client->authenticate($code); $token = $this->client->getAccessToken(); $this->setConfigToken($token); return $this->redirect(Director::absoluteBaseURL() . 'admin/settings'); } else { $response->setStatusCode(400); $response->setBody('Google account not connected'); } } } else { $response->setStatusCode(400); $response->setBody('Bad request'); } return $response; } /** * Returns a URL the user can visit to grant us permission to access their feed * * @return string */ public function getAuthURL() { $state = mt_rand(); $this->client->setState($state); Session::set($this->stateSessionIdentifier, $state); return $this->client->createAuthUrl(); } /** * Returns true if the user has a valid access token * * @return string */ public function getIsAuthenticated() { return $this->client->getAccessToken(); } /** * Checks the connected YouTube account for new uploads, and calls processVideo() on each one. * Returns an array containing up to $limit YouTubeVideo objects * * @param $limit Int number of results to retrieve * @return array */ public function getRecentUploads($limit = 50) { if ($this->getIsAuthenticated()) { try { // Call the channels.list method to retrieve information about the // currently authenticated user's channel. $channelsResponse = $this->service->channels->listChannels('contentDetails', array( 'mine' => 'true', )); $uploads = array(); foreach ($channelsResponse['items'] as $channel) { // Extract the unique playlist ID that identifies the list of videos // uploaded to the channel, and then call the playlistItems.list method // to retrieve that list. $uploadsListId = $channel['contentDetails']['relatedPlaylists']['uploads']; $playlistItemsResponse = $this->service->playlistItems->listPlaylistItems('snippet', array( 'playlistId' => $uploadsListId, 'maxResults' => $limit )); foreach ($playlistItemsResponse['items'] as $playlistItem) { $videoObject = $this->processVideo($playlistItem); array_push($uploads, $videoObject); } } } catch (Google_Service_Exception $e) { error_log(sprintf('<p>A service error occurred: <code>%s</code></p>', htmlspecialchars($e->getMessage()))); } catch (Google_Exception $e) { error_log(sprintf('<p>An client error occurred: <code>%s</code></p>', htmlspecialchars($e->getMessage()))); } $token = $this->client->getAccessToken(); $this->setConfigToken($token); return isset($uploads) ? $uploads : false; } return false; } /** * Saves a Google_Service_YouTube_PlaylistItem into YouTubeVideo * Overwrites an existing object, or creates a new one. * Returns the YouTubeVideo DataObject. * * @param $video * @return YouTubeVideo */ protected function processVideo($video) { $snippet = $video['snippet']; $privacyStatus = $video['status']['privacyStatus']; // if privacyStatus is set to public, or not privacy status is defined. if ($privacyStatus == 'public' || is_null($privacyStatus)) { $videoFields = array(); // Map response data to columns in our YouTubeVideo table $videoFields['VideoID'] = $snippet['resourceId']['videoId']; $videoFields['Description'] = $snippet['description']; $videoFields['Published'] = strtotime($snippet['publishedAt']); $videoFields['Title'] = $snippet['title']; $videoFields['ChannelTitle'] = $snippet['channelTitle']; $videoFields['ChannelID'] = $snippet['channelId']; $videoFields['PlaylistID'] = $snippet['playlistId']; $videoFields['PlaylistPosition'] = $snippet['position']; // Get the highest res thumbnail available if (isset($snippet['thumbnails']['maxres'])) { $videoFields['ThumbnailURL'] = $snippet['thumbnails']['maxres']['url']; } elseif (isset($snippet['thumbnails']['standard'])) { $videoFields['ThumbnailURL'] = $snippet['thumbnails']['standard']['url']; } elseif (isset($snippet['thumbnails']['high'])) { $videoFields['ThumbnailURL'] = $snippet['thumbnails']['high']['url']; } elseif (isset($snippet['thumbnails']['medium'])) { $videoFields['ThumbnailURL'] = $snippet['thumbnails']['medium']['url']; } elseif (isset($snippet['thumbnails']['default'])) { $videoFields['ThumbnailURL'] = $snippet['thumbnails']['default']['url']; } /** @var YouTubeVideo $videoObject Try retrieve existing YouTubeVideo by Youtube Video ID, create if it doesn't exist */ $videoObject = YouTubeVideo::getExisting($videoFields['VideoID']); if (!$videoObject) { $videoObject = new YouTubeVideo(); $newYouTubeVideo = true; } $videoObject->update($videoFields); if ($videoObject->ThumbnailURL != $videoFields['ThumbnailURL'] || !$videoObject->Thumbnail()) { if ($thumbnail = $this->saveThumbnailFromUrl($videoFields['ThumbnailURL'], $videoFields['Title'], $videoFields['VideoID'] . '.' . $this->getFileExtension($videoFields['ThumbnailURL']))) { $videoObject->ThumbnailID = $thumbnail->ID; } } $videoObject->write(); if (isset($newYouTubeVideo)) { // Allow decoration of YouTubeVideo with onAfterCreate(YouTubeVideo $videoObject) method $this->extend('onAfterCreate', $videoObject); } return $videoObject; } else { $videoObject = YouTubeVideo::getExisting($snippet['resourceId']['videoId']); if($videoObject && $videoObject->exists()) { $videoObject->delete(); } } return null; } /** * Returns the access token from SiteConfig * * @return mixed */ protected function getConfigToken() { return SiteConfig::current_site_config()->YouTubeFeed_Token; } /** * Saves the access token into SiteConfig * * @return void */ protected function setConfigToken($token) { $siteConfig = SiteConfig::current_site_config(); $siteConfig->YouTubeFeed_Token = $token; $siteConfig->write(); } /** * Returns the SS_Datetime a YouTubeVideo was last retrieved from the external service * * @return SS_Datetime */ protected function getTimeLastSaved() { return SiteConfig::current_site_config()->YouTubeFeed_LastSaved; } /** * Checks if it's time to do a video update, or performs one anyway if $force is true * * @param bool $force Force auto update, disregards 'YouTubeFeed_AutoUpdate' property * @throws ValidationException * @throws null */ public function doAutoUpdate($force = false) { $siteConfig = SiteConfig::current_site_config(); if ($force || $siteConfig->YouTubeFeed_AutoUpdate) { $lastUpdated = $siteConfig->YouTubeFeed_LastSaved; $nextUpdateInterval = $siteConfig->YouTubeFeed_UpdateInterval; $nextUpdateIntervalUnit = $siteConfig->YouTubeFeed_UpdateIntervalUnit; if ($lastUpdated) { // Assemble the time another update became required as per SiteConfig options // YouTubeFeed_NextUpdateInterval & ..Unit $minimumUpdateTime = strtotime($lastUpdated . ' +' . $nextUpdateInterval . ' ' . $nextUpdateIntervalUnit); } // If we haven't auto-updated before (fresh install), or an update is due, do update if ($force || !isset($minimumUpdateTime) || $minimumUpdateTime < time()) { $this->getRecentUploads(); // Save the time the update was performed $siteConfig->YouTubeFeed_LastSaved = SS_Datetime::now()->value; $siteConfig->write(); } } } /** * Returns the thumbnail directory, if it does not exist it will * attempt to create it. * * @return string */ public function getBaseThumbnailDir() { $baseFolder = Director::baseFolder(); $assetsFolder = Controller::join_links($baseFolder, ASSETS_DIR); $thumbnailsDir = Controller::join_links($assetsFolder, 'youtube-thumbnails'); if (!is_dir($thumbnailsDir) && !mkdir($thumbnailsDir, 0775)) { user_error( sprintf("Unable to create thumbnail storage directory: %s", $thumbnailsDir), E_USER_ERROR ); } return $thumbnailsDir; } /** * Get the relative thumbnail directory * * @return string */ public function getThumbnailDir() { return Controller::join_links(ASSETS_DIR, 'youtube-thumbnails'); } /** * @param $url * @param $title * @param $filename * @return Image */ public function saveThumbnailFromUrl($url, $title, $filename) { $contents = file_get_contents($url); $outputFilename = Controller::join_links($this->getBaseThumbnailDir(), $filename); $localFilename = Controller::join_links($this->getThumbnailDir(), $filename); file_put_contents($outputFilename, $contents); $image = Image::create(); $image->Filename = $localFilename; $image->Title = $title; $image->write(); return $image; } /** * Returns the extension from a filename. Used for thumbnail saving * * @param $filename * @return mixed */ public function getFileExtension($filename) { return pathinfo(basename($filename), PATHINFO_EXTENSION); } } |