Get all youtube videos from a channel (some videos are missing) - php

I'm using the v3 Google API for Youtube:
$url = 'https://www.googleapis.com/youtube/v3/search?part=id&channelId=' . $channelID . '&maxResults=50&order=date&key=' . $API_key;
I've set up a script which should give me all videos from a given channel ID. For some channels I get all videos, for some a few are missing (compared with the number of videos shown directly in Youtube), and for bigger channel I get a max. result of 488 videos despite there are more.
The pageToken is a strange thing. For example a channel has 955 videos. I get 18 pages with 50 items per page (that would be 900 videos). Some of them are playlists but if I subtract the 23 playlists I still have 877 videos. If I remove duplicates I only have 488 results! The totalResults in the JSON output shows me 975 results!?
This is my recursive function:
function fetchAllVideos($parsed_json){
$foundIds = array();
if($parsed_json != ''){
$foundIds = getVideoIds($parsed_json);
$nextPageToken = getNextPageToken($parsed_json);
$prevPageToken = getPrevPageToken($parsed_json);
if($nextPageToken != ''){
$new_parsed_json = getNextPage($nextPageToken);
$foundIds = array_merge($foundIds, fetchAllVideos($new_parsed_json));
}
if($prevPageToken != ''){
$new_parsed_json = getNextPage($prevPageToken);
$foundIds = array_merge($foundIds, fetchAllVideos($new_parsed_json));
}
}
return $foundIds;
}
I call it with $videoIds = fetchAllVideos($parsed_json); and $parsed_json is the result from the first URL which I retrieve. Can you see an error here?
Does anybody know how the number of videos are counted, which are directly shown in Youtube? Has anybody managed to get a full list which correspond to the number in Youtube?

This script goes through selecting a 60 day period at a time and retrieves the results for it, then adds it to the existing data array. By doing this there are no limitations to how many videos are allowed, though it may take some time to trawl larger YouTube channels with a couple thousand videos. Make sure you set the API_KEY, timezone, username, start date (should begin before the first video on the channel), and period (set by default to 60 * 60 * 24 * 60, which is 60 days in seconds. This will need to be lower if the frequency of videos is higher than about 50 for 60 days.) (5184000 seconds).
*All of this is commented within the script.
date_default_timezone_set("TIMEZONE");
//youtube api key
$API_KEY = "YOUR API KEY";
function search($searchTerm,$url){
$url = $url . urlencode($searchTerm);
$result = file_get_contents($url);
if($result !== false){
return json_decode($result, true);
}
return false;
}
function get_user_channel_id($user){
global $API_KEY;
$url = 'https://www.googleapis.com/youtube/v3/channels?key=' . $API_KEY . '&part=id&forUsername=';
return search($user,$url)['items'][0]['id'];
}
function push_data($searchResults){
global $data;
foreach($searchResults['items'] as $item){
$data[] = $item;
}
return $data;
}
function get_url_for_utc_period($channelId, $utc){
//get the API_KEY
global $API_KEY;
//youtube specifies the DateTime to be formatted as RFC 3339 formatted date-time value (1970-01-01T00:00:00Z)
$publishedAfter = date("Y-m-d\TH:i:sP",strval($utc));
//within a 60 day period
$publishedBefore_ = $utc + (60 * 60 * 24 * 60);
$publishedBefore = date("Y-m-d\TH:i:sP",$publishedBefore_);
//develop the URL with the API_KEY, channelId, and the time period specified by publishedBefore & publishedAfter
$url = 'https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&key=' . $API_KEY . '&maxResults=50&channelId=' . $channelId . '&publishedAfter=' . urlencode($publishedAfter) . '&publishedBefore=' . urlencode($publishedBefore);
return array("url"=>$url,"utc"=>$publishedBefore_);
}
//the date that the loop will begin with, have this just before the first videos on the channel.
//this is just an example date
$start_date = "2013-1-1";
$utc = strtotime($start_date);
$username = "CHANNEL USERNAME NOT CHANNEL ID";
//get the channel id for the username
$channelId = get_user_channel_id($username);
while($utc < time()){
$url_utc = get_url_for_utc_period($channelId, $utc);
$searchResults = search("", $url_utc['url']);
$data = push_data($searchResults);
$utc += 60 * 60 * 24 * 60;
}
print "<pre>";
print_r($data);
print "</pre>";
//check that all of the videos have been accounted for (cross-reference this with what it says on their youtube channel)
print count($data);

https://gdata.youtube.com/feeds/api/users/USERNAME_HERE/uploads?max-results=50&alt=json&start-index=1 did the trick. It's a JSON feed where you have to loop until you get less than 50 results.
Edit:
This should be the script I used:
ini_set('max_execution_time', 900);
function getVideos($channel){
$ids = array();
$start_index = 1;
$still_have_results = true;
if($channel == ""){
return false;
}
$url = 'https://gdata.youtube.com/feeds/api/users/' . $channel . '/uploads?max-results=50&alt=json&start-index=' . $start_index;
$json = file_get_contents($url);
$obj = json_decode($json);
while($still_have_results){
foreach($obj->feed->entry as $video){
$video_url = $video->id->{'$t'};
$last_pos = strrpos($video_url, '/');
$video_id = substr($video_url, $last_pos+1, strlen($video_url) - $last_pos);
array_push($ids, $video_id);
}
$number_of_items = count($obj->feed->entry);
$start_index += count($obj->feed->entry);
if($number_of_items < 50) {
$still_have_results = false;
}
$url = 'https://gdata.youtube.com/feeds/api/users/' . $channel . '/uploads?max-results=50&alt=json&start-index=' . $start_index;
$json = file_get_contents($url);
$obj = json_decode($json);
}
return $ids;
}
$videoIds = getVideos('youtube');
echo '<pre>';
print_r($videoIds);
echo '</pre>';
Now I made a test, but I didn't gathered 100% of the videos. Nevertheless, the best option I came up with.

Related

How to get AdSchedule object with Google Ads API

I am trying to get all Ad Schedules placed in Google Ads through the Google Ads API and obtain the start and end times (hour and minute) to compare it with some existing values and depending on whether they differ update accordingly.
Here is my code showing where I am iterating over returned Ad Schedules.
foreach($campaigns as $camp) {
// Get restaurant and details
$res = RestaurantsService::getRestaurantByName($camp->getName());
$hours =$res->getHours()->dequeue();
$start = explode("-",$hours)[0];
$end = explode("-",$hours)[1];
// Get current ad schedules as they are now
$campaignAdSchedules = self::getCampaignAdSchedule($campaignCriterionService,$camp->getId());
if ($campaignAdSchedules == null){
$operations = [];
$schedule = new AdSchedule();
$schedule->setDayOfWeek(self::DAYS[date("N")-1]);
$schedule->setStartHour((int)substr($start,0,2));
$schedule->setStartMinute(MinuteOfHour::ZERO);
$schedule->setEndHour((int)substr($end,0,2));
$schedule->setEndMinute(MinuteOfHour::ZERO);
$operation = new CampaignCriterionOperation();
$criterion = new CampaignCriterion();
$criterion->setCampaignId($camp->getId());
$criterion->setCriterion($schedule);
$operation->setOperand($criterion);
$operation->setOperator(Operator::ADD);
$operations[] = $operation;
$campaignCriterionService->mutate($operations);
} else {
foreach($campaignAdSchedules as $adSchedule){
---> $schedule = $adSchedule->getCriterion(); <---
}
}
}
Here the line marked with arrows is the line I am having problems with. The getCriterion() function returns a Criterion object which does not have the methods getStartHour() etc. I have tried casting it but haven't found the correct way.
Help is much appreciated!
Try check the instance:
$result = $campaignCriterionService->get($serviceSelector);
$campaignAdSchedules = $result->getEntries();
foreach ($campaignAdSchedules as $criterion) {
$adSchedule = $criterion->getCriterion();
if ($adSchedule instanceof AdSchedule) {
$adSchedule->getStartHour();
}
}

How to get "metric" and "element" names for PHP - Adobe Analytics

I am pulling a report using the Adobe API from Omniture.
Here is the full script :
<?php
include_once('/path/SimpleRestClient.php');
// Date
$end_date = date("Y-m-d",strtotime("-1 days"));
$start_date = date("Y-m-d",strtotime("-8 days"));
// Location of the files exported
$adobe_file = '/path/Adobe_'.$end_date.'.csv';
// List creation that will be updated with the fields and be put into my CSV file
$list = array
(
array('lasttouchchannel', 'product','visits','CTR(Clicks/PageViews)') // headers // ADD or DELETE metrics #
);
function GetAPIData($method, $data)
{
$username = "XXXX";
$shared_secret = "XXXX";
$postURL = "https://api3.omniture.com/admin/1.4/rest/?method=";
// Nonce is a simple unique id to each call to prevent MITM attacks.
$nonce = md5(uniqid(php_uname('n'), true));
// The current timestamp in ISO-8601 format
$nonce_ts = date('c');
/* The Password digest is a concatenation of the nonce, it is timestamp and your password
(from the same location as your username) which runs through SHA1 and then through a base64 encoding */
$digest = base64_encode(sha1($nonce . $nonce_ts . $shared_secret));
$rc = new SimpleRestClient();
$rc -> setOption(CURLOPT_HTTPHEADER, array("X-WSSE: UsernameToken Username=\"$username\", PasswordDigest=\"$digest\", Nonce=\"$nonce\", Created=\"$nonce_ts\""));
//var_dump($o);
$rc -> postWebRequest($postURL .$method, $data);
return $rc;
}
$method = 'Report.Queue';
$data ='
{
"reportDescription":
{
"reportSuiteID":"XXXX",
"dateFrom":"'.$start_date.'",
"dateTo":"'.$end_date.'",
"metrics":[{"id":"visits"},{"id":"instances"},{"id":"pageviews"}],
"elements":[{"id":"lasttouchchannel","top":"50000"}]
}
}';
/*
"date":"'.$date.'",
"dateTo":"'.$date.'",
"dateFrom":"'.$start_date.'",
"dateTo":"'.$end_date.'",
*/
$rc=GetAPIData($method, $data);
if($rc -> getStatusCode() == 200) // status code 200 is for 'ok'
{
$counter = 0;
do
{
if($counter>0){sleep($sleep = 120);}
$return = GetAPIData('Report.Get', $rc->getWebResponse());
$counter++;
}while($return -> getStatusCode() == 400 && json_decode($return->getWebResponse())->error == 'report_not_ready'); // status code 400 is for 'bad request'
//
$json=json_decode($return->getWebResponse());
foreach ($json->report->data as $el)
{
echo $el->name.":".$el->counts[0].":".$el->counts[1]."\n";
// Adding the data in the CSV file without overwriting the previous data
array_push($list, array($el->name, $el->name, $el->counts[0], ($el->counts[1])/($el->counts[2])));
}
}
else
{
echo "Wrong";
}
$fp = fopen($adobe_file, 'w');
foreach ($list as $fields)
{
// Save the data into a CSV file
fputcsv($fp, $fields);
}
fclose($fp);
?>
How can I get the names of the metrics and elements in order to use them in this script? There is no way. I searched with all the possible tags on google and nothing worked !
I need the metrics and elements for this part of the code :
$data ='
{
"reportDescription":
{
"reportSuiteID":"XXXX",
"dateFrom":"'.$start_date.'",
"dateTo":"'.$end_date.'",
"metrics":[{"id":"visits"},{"id":"instances"},{"id":"pageviews"}],
"elements":[{"id":"lasttouchchannel","top":"50000"}]
}
}';
I cannot find 'date' as an element which is crucial. I cannot find all the other metrics as well. In Google Analytics we had this link :
Google Analytics Query
but in Adobe there is not any. I want something like that :
"metrics":[{"id":"instances"},{"id":"impressions"}],
"elements":[{"id":"date","top":"50000"}]
You would json_decode() as $data contains a JSON string. For example:
$data ='
{
"reportDescription":
{
"reportSuiteID":"XXXX",
"dateFrom":"'.$start_date.'",
"dateTo":"'.$end_date.'",
"metrics":[{"id":"visits"},{"id":"instances"},{"id":"pageviews"}],
"elements":[{"id":"lasttouchchannel","top":"50000"}]
}
}';
$json = json_decode($data, true);
echo $json['reportDescription']['dateFrom'];
print_r($json['reportDescription']['metrics']);

Geocoding doesn't work now V3 is in play

I have a internal site which uses php to look through my msql customer database. Find any customers which do not have lat and lng fields filled in. Grab the postcodes and geocode them posting the lat and lng back to my database and plot the customers on the map. This is done by a cron job once a day. This worked fine using v.2 of google api. Since march or april its stopped. Im guessing because of v.3.
Jist my jl_jobscoordinates.cron.php file searches through the database picking up all the postcodes for empty lat and lng fields. Then calls a function from my geocode.class.php called doGeocode which uses xml to put togther and find results and save the lat and lng. Inside the geocodeclass it refers to a m_url which is the googleapi url which is saved inside my config file. I have updated this url to the new v.3 url which is http://maps.googleapis.com/maps/api/geocode/xml?address=%s&sensor=false. My map is back up and running, just nothing will geocode.
I will paste the two files jl_jobscooedinates.cron.php and geocode.class.php. I have commented out the old xml in the geocode which used to work with the old url.
The results of my cron is that it is not getting coordinates. e.g. -- [3-2013] Google could not find this Postcode: [COO041] Test Company Name, Oxfordshire OX26 4SS
jl_jobcoordinates.cron.php
require_once("../includes/config.php");
require_once(_PATH_JMS."/classes/session.class.php");
require_once(_PATH_JMS."/classes/db.class.php");
require_once(_PATH_JMS."/classes/lib.class.php");
require_once(_PATH_JMS."/classes/security.class.php");
require_once(_PATH_JMS."/classes/emails.class.php");
require_once(_PATH_JMS."/classes/geocode.class.php");
require_once(_PATH_JMS."/services/actiontrail.ds.php");
require_once(_PATH_JMS."/services/jobsdue.ds.php");
//-----------------------------------------------------
// Main Object Instances - Initialize what we require
//-----------------------------------------------------
$DB = new DB();
$Security = new Security($DB->i_db_conn);
$Lib = new Lib();
$Session = new Session();
$ActionTrail = new ActionTrail($DB, $Session, $Security);
$JobsDue = new JobsDue($DB, $Session, $Security, $ActionTrail);
$Geocode = new Geocode($Session, $Security);
$Emails = new Emails($DB, $Session, $Security);
//-----------------------------------------------------
// Save as a valid system user
//-----------------------------------------------------
$Session->save('USR_AUTH',_CRON_USER_NAME);
$Session->save('USR_PASS',_CRON_USER_PASS);
$Session->save('USR_IS_EMPLOYED', '1');
$Session->save('CONS',$Session->get('USR_AUTH'));
//-----------------------------------------------------
// Postcodes to Ignore - we cannot geocode these
//-----------------------------------------------------
$m_ignore = array("IRL","IRELAND","IRE","ITA","USA","BEL","EGY","GER","FR","FRA","HOL","POL");
//-----------------------------------------------------
// Get Jobs Due for all consultants for this year and next
//-----------------------------------------------------
$mY = (int) date("Y");
//-----------------------------------------------------
// Find t-cards without lat & lng
//-----------------------------------------------------
$m_errors = array();
for ($y=$mY;$y<=$mY+1;$y++)
{
for ($i=1;$i<=12;$i++)
{
$mM = (int) $i;
//echo "<br> mM =".$mM ." i =".$i;
$mJobs = $JobsDue->getAllJobsDue('%',$mM,$y,'%',NULL,NULL,FALSE); /* DON'T GET MISSED JOBS AS WE WILL START FROM JAN */
//echo "<br>mJobs =".$mJobs;
foreach ($mJobs as $row)
{
$m_postcode = $Lib->lib_str_clean(trim($row->postcode)); //this loops through each of the records and gets the post codes. m_postcodes are the postcodes found
echo "<br>m_postcode =".$m_postcode;
if (($row->latlngexists == 1)||(in_array($m_postcode,$m_ignore))||(in_array($row->card_id,$m_ignore))||(strlen($m_postcode)<=0)) continue;
if ($Lib->lib_ispostcode($m_postcode)) {
$m_coordinates = $Geocode->doGeocode($m_postcode);
echo "<br>m_coords =".$m_coordinates;//nothing displayed
if ($m_coordinates != NULL) {
$DB->setGeoTCard($row->card_id,$m_coordinates['lat'],$m_coordinates['lng']);
} else {
$m_err_desc = sprintf("[%s-%s] Google could not find this Postcode",$mM,$y);
$m_error = array(
"err_desc" => $m_err_desc,
"err_code" => $row->client_code,
"err_comp" => $row->title,
"err_depo" => $row->description,
"err_post" => $m_postcode
);
$m_errors[] = $m_error;
$m_ignore[] = $row->card_id;
}
sleep(_GEOCODE_PAUSE);
} else {
$m_err_desc = sprintf("[%s-%s] Postcode is invalid please check",$mM,$y);
$m_error = array(
"err_desc" => $m_err_desc,
"err_code" => $row->client_code,
"err_comp" => $row->title,
"err_depo" => $row->description,
"err_post" => $m_postcode
);
$m_errors[] = $m_error;
$m_ignore[] = $row->card_id;
}
}
}
}
if (count($m_errors) > 0) {
$Emails->doGeocodeErrNotify($m_errors);
}
geocode.class.php
class Geocode {
private $m_session = NULL;
private $m_security = NULL;
private $m_session_user;
private $m_session_pass;
private $m_key = _GMAP_KEY;
private $m_url = _GMAP_URL;
private $m_res = Array();
public function __construct($p_session,$p_security)
{
$this->m_session = $p_session;
$this->m_security = $p_security;
$this->m_session_user = $this->m_session->get('USR_AUTH');
$this->m_session_pass = $this->m_session->get('USR_PASS');
if ($this->m_security->doLogin($this->m_session_user,$this->m_session_pass) <= 0)
{
return NULL;
die;
}
}
public function doGeocode($p_postcode)
{
try {
// //$xml = new SimpleXMLElement(sprintf($this->m_url,$p_postcode,$this->m_key),0,TRUE); //OLD FOR V.2
$xml = new SimpleXMLElement(sprintf($this->m_url,$p_postcode),0,TRUE);
} catch (Exception $e) {
echo sprintf('Caught exception: %s', $e->getMessage());
return NULL;
die;
}
$st = $xml->Response->Status->code;
if (strcmp($st, "200") == 0)
{
$co = $xml->Response->Placemark->Point->coordinates;
$cs = preg_split("/[\s]*[,][\s]*/", $co);
$this->m_res = Array(
"lng" => $cs[0],
"lat" => $cs[1],
"alt" => $cs[2]
);
return $this->m_res;
} else {
return NULL;
}
}
}
I would really appriciate if someone could help me please. Im guessing its something to do with the new url in my config file and the current xml not set properly for the sensor??
My geocode stuff is still working fine just like this don't forget to use your own personal API key!
/**
* Geocode postcode to get long/lat used when adding suppliers and sites
* #param - $postcode - string - Input post code to geocode
* #return - $lat,$long - array - array containing latitude coords
*/
function geocode($postcode) {
$postcode = urlencode(trim($postcode)); // post code to look up in this case status however can easily be retrieved from a database or a form post
//$request_url = "http://maps.googleapis.com/maps/api/geocode/xml?address=".$postcode."&sensor=false"; // the request URL you'll send to google to get back your XML feed
define("MAPS_HOST", "maps.google.co.uk");
define("KEY", "YOUR API KEY HERE");
$base_url = "http://" . MAPS_HOST . "/maps/geo?output=xml" . "&key=" . KEY;
$request_url = $base_url . "&q=" . $postcode;
$xml = simplexml_load_file($request_url);
$status = $xml->Response->Status->code;
if (strcmp($status, "200") == 0) {
// Successful geocode
$geocode_pending = false;
$coordinates = $xml->Response->Placemark->Point->coordinates;
$coordinatesSplit = explode(",", $coordinates);
// Format: Longitude, Latitude, Altitude
return array("lat"=>$coordinatesSplit[1],"long"=>$coordinatesSplit[0]);
} else {
return array("lat"=>0,"long"=>0);
}
}

Stuck with paginating youtube api playlist results, trying to calculate tha sum duration of all the videos in a playlist

I would like to make a little php snippet for my drupal site, which counts all the durations of all the videos in a youtube playlist.
I managed to find a good starting point here at this site, I made some changes, and it is almost good:
<?php
$playlist_id = "266DBEDBE6892C11";
$url = "https://gdata.youtube.com/feeds/api/playlists/".$playlist_id."?v=2&alt=json&start-index=1&max-results=50";
$data = json_decode(file_get_contents($url),true);
$info = $data["feed"];
$video = $info["entry"];
$nVideo = count($video);
$length = 0;
echo "Playlist Name: ".$info["title"]['$t'].'<br/>';
echo "Number of Videos (".$nVideo."):<br/>";
for($i=0;$i<200;$i++){
$temporary_length = $video[$i]['media$group']['yt$duration']['seconds'];
$length += $temporary_length;
echo "Lenght: ". $temporary_length ."<br/>";
}
echo "Length: " . $length ;
?>
My problem is, that I can't paginate, youtube only gives me maximum 50 results.
I tried with the start-index parameter, but that did not work for me.
I search through the youtube api pages, but I have no clue hot to do it. I am no programmer, this is what I could come up with with my limited programming knowledge.
What should I add to the code, to count all the videos in a playlist? Or If someone could help me with another snippet, that would be perfect also.
Thank you!
Sorry can't test this here, but taking into account what error you are getting I think you first need to check what data you have receieved back from youtube.
I also put in a friendly way for you to test the current page and requests per page.
<?php
//Configurable
$playlist_id = "266DBEDBE6892C11";
$results_per_request = 50;
$current_page = 1;
$start_index = $request_per_page * ($current_page - 1) + (($current_page > 1) ? 1 : 0);
$url = "https://gdata.youtube.com/feeds/api/playlists/".$playlist_id."?v=2&alt=json&start-index=".$start_index."&max-results=".$results_per_request;
$data = json_decode(file_get_contents($url),true);
if (is_array($data) && count($data) > 0)
{
$info = $data["feed"];
$video = $info["entry"];
$nVideo = count($video);
$length = 0;
echo "Playlist Name: ".$info["title"]['$t'].'<br/>';
echo "Number of Videos (".$nVideo."):<br/>";
for($i=0;$i<200;$i++){
$temporary_length = $video[$i]['media$group']['yt$duration']['seconds'];
$length += $temporary_length;
echo "Lenght: ". $temporary_length ."<br/>";
}
echo "Length: " . $length ;
}
else
{
echo "Youtube did not return any more results."
}
?>

Looping through pages from API

I'm trying to get all the gameids for a particular player. The API I am working with only returns 24 games per page. I just cant figure out how to loop though the pages.
When a player no longer has anymore pages $mPages will be equal to false.
The problem is adding 1 to $iPages so it can get the next page..
My current script:
<?php
//incude sql database connection
include_once('sql.php');
//include api key
include_once('api.php');
//gamertag
$gamertag = "jam1efoster";
//variant- Valid values are "Campaign", "Firefight", "Competitive", "Arena", "Invasion", "Custom", "Unknown". "Unknown" returns all games.
$variant = "Unknown";
//page number 0 = most recent
$iPage = 0;
while(!$endPages == "stop"){
$iPage = $iPage++;
$GetGameHistory = "http://www.bungie.net/api/reach/reachapijson.svc/player/gamehistory/".$apiKey."/".rawurlencode($gamertag)."/".$variant."/".$iPage;
$output = file_get_contents($GetGameHistory);
$obj = json_decode($output);
echo $output;
$mPages = $obj->HasMorePages;
if($mPages == false){$endPages = "stop";}
foreach($obj->RecentGames as $recentgames) {
$gameid = $recentgames->GameId;
echo $gameid."<br/>";
}
}
?>
Perhaps a while loop?
while (!endOfPages) {
getMoreGames();
}

Categories