YouTube API v3 get last video in playlist - php

Some basic background: I help run a gaming channel on YouTube, and I'm building a utility (using PHP) to integrate the channel's content with a companion website. Our playlists are primarily "let's play" series ordered by publication date that follow chronological progress through various games, and I would like the website to display the "latest episode" from a select number of series.
I know that I can work my way to the last video by chaining calls to the following:
$youtubeService->playlistItems->listPlaylistItems(
"snippet",
array(
"playlistId" => $playlistId
"pageToken" => $nextPageToken
)
)
And simply grab the last item in the response set when $nextPageToken is unset.
However, this strikes me as incredibly inefficient--partly because I believe it eats away at my API request quota, but mostly because it's going to slow down the overall response time of the site. Neither of those are ideal.
It seems like there should be an easier way to grab the "latest" video in a playlist either by changing the order of the response, or with some handy function, but I can't find any documentation on it.
I've looked at using the Search functions over the PlaylistItems, but (according to the documentation), Search only accepts Channel IDs as a parameter and not Playlist IDs, which makes me think that its the wrong direction to head.

The short answer here is that this appears to be impossible under the current version of the API. There is no apparent way to essentially select videos in reverse, but I did make a minor change which resulted in whole process being a tad more efficient.
This is the original code:
$items = $youtube->playlistItems->listPlaylistItems(
"snippet",
array(
"playlistId" => $playlistId,
"maxResults" => 50
)
);
while ($items->nextPageToken) {
$items = $youtube->playlistItems->listPlaylistItems(
"snippet",
array(
"playlistId" => $playlistId,
"maxResults" => 50,
"pageToken" => $items->nextPageToken
)
);
}
if ($items) {
return end($items->getItems());
}
This is the fix:
First, I added an object to assist with caching:
class PlaylistCache {
protected $expirationDate;
protected $playlistId;
protected $latestEpisode;
__construct($playlistId, $latestEpisode) {
$this-playlistId = $playlistId;
$this->latestEpisode = $latestEpisode;
$this->expirationDate = time() + 86400;
// get current time + 24 hours
}
public function getLatestEpisode() {
return $this->latestEpisode;
}
public function getPlaylistId() {
return $this->playlistId;
}
public function isExpired() {
return $this->expirationDate < time();
}
}
Then, before polling the API, I look to see if I have a cached version available, and I only resort to the API if that cached version is expired.
$playlistCache = json_decode(get_option('playlist_cache_' . $playlistId));
if ($playlistCache->isExpired()) {
$items = $youtube->playlistItems->listPlaylistItems(
"id",
array(
"playlistId" => $playlistId,
"maxResults" => 50
)
);
while ($items->nextPageToken) {
$items = $youtube->playlistItems->listPlaylistItems(
"id",
array(
"playlistId" => $playlistId,
"maxResults" => 50,
"pageToken" => $items->nextPageToken
)
);
}
if ($items) {
$videoId = end($items->getItems()[0]->getId());
$video = $youtube->videos->listVideos("snippet", array('id' => $videoId))
$video = $video->getItems()[0];
$playlistCache = new PlaylistCache($playlistId, $video);
update_option('playlist_cache_' . $playlistId, json_encode($playlistCache)));
}
}
return $playlistCache->getLatestEpisode();
The other big change here is that my calls to listPlaylistItems() are requesting the id instead of the snippet.
According to the documentation, the snippet costs 2 units of the API quota while requests for the id are 0. So, I don't need to snag the snippet for every single item on every single page. I only need to grab the snippet of the final video in the results, which I can do with the more refined call to
$youtube->videos->listVideos()
With the addition of the PlaylistCache class I only reach out to the API when the cached version of the Playlist returns true on the $playlistCache->isExpired() call, so I only need to poll the entire playlist one time every 24 hours instead of 1 time every page load for every user.
It's still not exactly ideal, but as far as I can tell, it's the best option available right now.

Firstly, you need to get the channelId for the user via HTTP request:
Sample request:
https://www.googleapis.com/youtube/v3/channels?part=snippet&forUsername={0}&key={1}
where {0} is the USERNAME and key is you API key
Then, get the list of videos by calling 'PlaylistItems:list', it returns a collection of playlist items that match the API request parameters. You can retrieve all of the playlist items in a specified playlist or retrieve one or more playlist items by their unique IDs.
Sample request:
https://www.googleapis.com/youtube/v3/search?order=date&part=snippet&channelId={0}&key={1}
From there, you can create an array to get the last video in the playlist. Include max-results parameter, the max-results specifies the maximum number of results that included in the result set.

Typically, the latest video in a playlist in added to the front, not the end.

Related

Change Variable after each Rest API Call out of a list of Stock symbol names until the list is done in Wordpress?

I'm trying to use the alphavantage REST API to output stock prices through Advanced Custom Fields. My issue right now is that I don't know how I can create a list of stocksymbol names (we have specific stocks we want to check) that changes each call until all stocks have been checked.
This is my current Wordpress Code in functions.php
/* Register Stock Price Custom Post Type */
add_action('init', 'register_aktienpreise_cpt');
function register_aktienpreise_cpt(){
register_post_type('aktienpreise', [
'label' => 'Aktienpreise',
'public' => true,
'capability_type' => 'post'
]);
}
add_action( 'wp_ajax_nopriv_get_aktienpreise_from_api', 'get_aktienpreise_from_api' );
add_action( 'wp_ajax_get_aktienpreise_from_api', 'get_aktienpreise_from_api' );
/* Initialize Function */
function get_aktienpreise_from_api(){
/* Set Log File */
$file = get_stylesheet_directory() . '/report.txt';
/* Declare Current Symbol Variable and check if its not empty */
$current_symbol = ( ! empty($_POST['current_symbol']) ) ? $_POST['current_symbol'] : '';
$aktienpreise = [];
/* Results output for AlphaVantage Rest API + APIKEY - Add Current Symbol Set for each API Call */
$results = wp_remote_retrieve_body( wp_remote_get('https://www.alphavantage.co/query?function=GLOBAL_QUOTE&apikey=demo&symbol=' . $current_symbol_set ));
file_put_contents($file, "Current Symbol: " . $current_symbol. "\n\n", FILE_APPEND);
// turn it into a PHP array from JSON string
$results = json_decode( $results );
// Either the API is down or something else spooky happened. Just be done.
if( ! is_array( $results ) || empty( $results ) ){
return false;
}
/* Change Current Stock Symbol for the next call until empty */
$current_symbol = $current_symbol + $current_symbol_set;
/* Repeat Calls until results are empty */
wp_remote_post( admin_url('admin-ajax.php?action=get_aktienpreise_from_api'), [
'blocking' => false,
'sslverify' => false,
'body' => [
'current_symbol' => $current_symbol
]
] );
}
At this part
/* Change Current Stock Symbol for the next call until empty */
$current_symbol = $current_symbol + $current_symbol_set;
I'd like to have a list of specific stocks I want to check (for example Apple, IBM etc.).
The goal is that on each call the variable changes to the next stock (lets say first call Apple, second IBM), then writes those into the specific ACF Fields that I can showcase in a table on our website.
If the restapi urls would just have number increases it would be easy, but how do I do it with specific terms like "IBM" etc.
Furthermore if there are easier solutions to this please tell me so since I'm fairly new to REST APIs.
The Goal in general is to just display current stockprices of specific stocks inside a table on our website, preferably inside an ACF Field so we can do math formulas on the price (for example price x 2 for another table field).
Thanks for everyone taking their time to help me out!
Why don't you use an array for the stock symbols and do the AlphaVantage API calls inside a loop?
I will give you a quick example:
$my_symbols = [
'IBM',
'Microsoft',
'Tata'
];
foreach( $my_symbols as $current_symbol ) // iterate through the array elements
{
//... make the API calls to AlphaVantage
//... do any other business logic, like storing the API result in file or update the stocks, etc..
}
One thing to keep in mind is that you should make sure the script won't get a timeout. So increase the max_execution_time to a higher value.
Also, you don't have to call your own API here. I mean you don't have to do the wp_remote_post() to your own website.

API pagination, how to implement page token paging method?

I am working on implementing an API interface for my project.
As i know, there are different forms to make pagination through the results, like the following:
https://example.com/api/purchaseorders?page=2&pagesize=25
But, i see many APIs like google use a different approach, in which they use a "pageToken" to let the user move between the pages of results, for example:
https://example.com/api/purchaseorders?pagesize=25&pageToken=ClkKHgoRc291cmNlX2NyZWF0ZWRfYXQSCQjA67Si5sr
So instead of page=2 they used pageToken=[token].
It is not clear for me the idea of pageToken and how to implement it.
It will be helpful if you guide me to any resources so i can get more knowledge.
Thank you.
Here's a very simple standalone example using the filesystem as a keyvalue store (since a filesystem will always be available).
$requestParameters = [];
if (($token = filter_input(INPUT_GET,"pageToken")) && is_readable("/tmp/$token")) {
$requestParameters = file_get_contents("/tmp/$token");
} else {
$requestParameters = [
"q" => filter_input(INPUT_GET,"q"),
"pageSize" => filter_input(INPUT_GET,"pageSize",FILTER_VALIDATE_INT),
"page" => filter_input(INPUT_GET,"page",FILTER_VALIDATE_INT)
];
}
$nextPageRequestParameters = $requestParameters;
$nextPageRequestParameters["page"]++;
$nextPageToken = md5(serialize($nextPageRequestParameters)); //This is not ideal but at least people can't guess it easily.
file_put_contents("/tmp/$nextPageToken", serialize($nextPageRequestParameters));
//Do request using $requestParameters
$result = [ "nextPageToken" => $nextPageToken, "data" => $resultData ];
echo json_encode($result);

How to use pagination with alaouy/youtube laravel package?

I'm using alaouy/youtube package for one of my projects, Its working just fine, But with this method I can't use pagination! Is there any way or I've to write my own code? There is a vendor folder in my resources with pagination folder but I can't get work with it!
// List videos in a given channel, return an array of PHP objects
$videoList = Youtube::listChannelVideos('UCk1SpWNzOs4MYmr0uICEntg', 40);
I'm the creator of that package, happy that it was helpful for you and sorry that the documentation wasn't clear enough.
The listChannelVideos is based on the searchAdvanced method, so you can paginate the channel videos like this :
$params = array(
'channelId' => 'UCk1SpWNzOs4MYmr0uICEntg',
'type' => 'video',
'part' => 'id, snippet',
'maxResults' => 40);
// Make intial call. with second argument to reveal page info such as page tokens
$search = Youtube::searchAdvanced($params, true);
print_r($search); // First page results
// Check if we have a pageToken
if (isset($search['info']['nextPageToken'])) {
$params['pageToken'] = $search['info']['nextPageToken'];
}
// Make another call and repeat
$search = Youtube::searchAdvanced($params, true);
print_r($search); // Second page results
Hope this will help you!

How to retrieve all my active Facebook ads?

I'm creating a dashboard for myself that helps me keep track of the Facebook ads I'm running.
What I've not been able to figure out is:
How can I retrieve an array of ad IDs for all ads that are active or could soon be active after no further action on my part?
In other words, I want all ads that I've set to Active and that exist within Adsets and Campaigns that are active (and therefore these ads are live right now)... plus all the ads that from my perspective are Active but that Facebook has set to another status such as Pending Review (and will soon set back to Active).
I have some code below, but the problem is that it also accidentally includes Pending ads that--once reviewed and approved by Facebook--will be inactive rather than active (because I've set them that way). And I do NOT want this type of ad to be included in my report.
My report should only show me ones where I'm actively spending money or have the potential to spend money as soon as FB approves them.
I think I understand the difference between configured_status and effective_status in AbstractArchivableCrudObjectFields, but I don't know that it's enough to help me because I have lots of ads set to Active that are within Adsets that are Inactive, and I don't want to see those listed in my report.
Any recommendations?
public function getActiveAdIds() {
$key = 'activeAdIds';
$adIdsJson = Cache::get($key);
if ($adIdsJson) {
$adIds = json_decode($adIdsJson);
} else {
$adsResponse = $this->getAdsByStatus([ArchivableCrudObjectEffectiveStatuses::ACTIVE, ArchivableCrudObjectEffectiveStatuses::PENDING_REVIEW]);
$ads = $adsResponse->data;
$adIds = [];
foreach ($ads as $ad) {
$adIds[] = $ad->id;
}
$adIdsJson = json_encode($adIds);
Cache::put($key, $adIdsJson, 1);
}
return $adIds;
}
public function getAdsByStatus($statuses) {
$params = [\FacebookAds\Object\Fields\AbstractArchivableCrudObjectFields::EFFECTIVE_STATUS => $statuses];
$adAccount = new AdAccount(self::ACT_PREPEND . $this->fbConfig['account_id']);
$cursor = $adAccount->getAds([], $params);
$response = $cursor->getResponse();
$jsonString = $response->getBody();
return json_decode($jsonString);
}
I get stats based on assets for my active campaigns. I have 119 ad accounts. This is php code which I used it for this purpose (any suggestion to improve it will be appreciated):
$fields = array(AdsInsightsFields::ACCOUNT_NAME,AdsInsightsFields::CAMPAIGN_ID,
AdsInsightsFields::CAMPAIGN_NAME, AdsInsightsFields::ADSET_ID,
AdsInsightsFields::ADSET_NAME,AdsInsightsFields::DATE_START,
AdsInsightsFields::DATE_STOP,AdsInsightsFields::REACH,
AdsInsightsFields::SPEND, AdsInsightsFields::IMPRESSIONS,
AdsInsightsFields::CLICKS, AdsInsightsFields::WEBSITE_CLICKS,
AdsInsightsFields::CALL_TO_ACTION_CLICKS,AdsInsightsFields::ACTIONS,
AdsInsightsFields::TOTAL_ACTIONS,AdsInsightsFields::CPC,
AdsInsightsFields::CPM,AdsInsightsFields::CPP,
AdsInsightsFields::CTR,AdsInsightsFields::OBJECTIVE,);
$params_c['date_preset'] = AdDatePresetValues::YESTERDAY;
$params_c['time_increment'] = 1;
$params_c['action_attribution_windows'] = array('1d_view', '28d_click');
$params_c['effective_status'] = AdStatusValues::ACTIVE;
$params_c['level'] = AdsInsightsLevelValues::ADSET;
$params_c['filtering'] = [array("field"=>"campaign.delivery_info",
"operator"=>"IN",
"value"=>array("active"))];
$params_c['fields']= $fields;
try{
// Initialize a new Session and instanciate an Api object
Api::init(self::api_key, self::secret_token, self::extended_token)->getHttpClient()->setCaBundlePath( $this->path_cert);
// The Api object is now available trough singleton
$api = Api::instance();
$user = new \FacebookAds\Object\Business($business_id);
$user->read(array(BusinessFields::ID));
//get all ad_account from Business
$accounts = $user->getAssignedAdAccounts(
array(
AdAccountFields::ID,
),
array('limit'=>1000,)
);
} catch (FacebookAds\Exception\Exception $ex) {
return $ex->getMessage();
}
if(isset($accounts) && ($accounts->count() > 0)):
do{
$ad_account = $accounts->current();
$adset_insights = $ad_account->getInsights($fields,$params_c);
do {
$adset_insights->fetchAfter();
} while ($adset_insights->getNext());
$adsets = $adset_insights->getArrayCopy(true);
}
while ($accounts->current());
endif;
If you include the adset{end_time} field in the query for the ad, you can assume that ad is not actually running if the end_time was in the past. This is how we get a base list of ads to query on.
The next step we take (which probably won't help you, unfortunately, but may help others) is building a batch of simple requests (one per ad) to see if there are any insights data for that day. If the response is an empty 'data' array, we can remove that ID from the ad list.
After we've reduced the size of the ad list with those two steps we can then make requests to run all of our breakdown reports. This method almost cut our API requests in half.
I have yet to find a way to do a "give me all ads that are for sure running this day" query in one step.
Edit:
I just found a better way to do this.... :
curl -G \
-d 'access_token=<ACCESS_TOKEN>' \
-d 'level=campaign' \
-d 'filtering=[{field:"ad.impressions",operator:"GREATER_THAN",value:0}]' \
'https://graph.facebook.com/v2.7/act_<ACCOUNT_ID>/insights'

Is there a limit to wp_remote_get and/or simplexml_load_string from Vimeo?

I know *wp_remote_get* is a WordPress function, and that I should be posting this in wordpress.stackexchange, however, I am almost certain my issue lies more on the general PHP side.
The problem: I need to retrieve all Vimeo videos found within an album, yet I am only getting 20.
The approach:
$vmg_feed_url = 'http://vimeo.com/api/v2/';
$vmg_user = '2212323';
if($vmg_type == 'user'){ /** just an input variable checking whether the function should search for user or for album. in our case, it searches for album **/
$vmg_type_url = '';
} else {
$vmg_type_url = $vmg_type . '/';
}
$vmg_videos_url = $vmg_feed_url . $vmg_type_url . $vmg_user . '/videos.xml';
$videos_result = wp_remote_get($vmg_videos_url);
$vmg_videos = simplexml_load_string($videos_result['body']);
The resulting XML is http://vimeo.com/api/v2/album/2212323/videos.xml - and as you can see, I am only retrieving 20 videos.
The question: Am I missing something? Is there a function/variable that sets a limit to the amount of videos I can retrieve? I know that wp_remote_get gives me these attributes (from the WordPress Codex):
$url
(string) (required) Universal Resource Locator (URL).
- Default: None
$args
(array) (optional)
- Default: method: GET, timeout: 5, redirection: 5, httpversion: 1.0, blocking: true, headers: array(), body: null, cookies: array()
Any help is truly appreciated. Please let me know if I forgot about any details!
Yor problem is not in wordpress or PHP. It is API limits:
http://developer.vimeo.com/apis/simple
Simple API responses include up to 20 items per page.
You can get more by adding ?page parameter in next requests.

Categories