Updating YouTube PHP Function for V3 API - php

I had been using a function I created after piecing together different bits of code for my Wordpress blog. It allowed me to pass in a shortcode with a value defining the URL of the YouTube video, then it would print the embedded video, the view count and the duration immediately underneath it.
Unfortunately, it appears that the API I was using was depreciated by Google and I can't seem to get the current API (v3) to work with this code, so I'd appreciate some help in trying to make this right. I grabbed a developer key from Google and am able to get the JSON response when I open it directly in my browser but it seems like the calls in the code below doesn't want to match up for some reason. It would help if I still could reference the old API response to compare so I'm sort of flying blind here.
add_shortcode('yt', 'getYoutubeFrame');
// Convert Seconds to Minutes
function sec2hms ($sec, $padHours = false)
{
$hms = "";
$hours = intval(intval($sec) / 3600);
$hms .= ($padHours)
? str_pad($hours, 2, "0", STR_PAD_LEFT). ":"
: $hours. ":";
$minutes = intval(($sec / 60) % 60);
$hms .= str_pad($minutes, 2, "0", STR_PAD_LEFT). ":";
$seconds = intval($sec % 60);
$hms .= str_pad($seconds, 2, "0", STR_PAD_LEFT);
return $hms;
}
function getYoutubeFrame($atts) {
extract(shortcode_atts(array(
'video' => ''
), $atts));
// Get YouTube data via the API
$JSON = file_get_contents("https://gdata.youtube.com/feeds/api/videos?q=$video&alt=json");
$JSON_Data = json_decode($JSON);
$views = $JSON_Data->{'feed'}->{'entry'}[0]->{'yt$statistics'}->{'viewCount'};
$views = number_format($views);
$duration = $JSON_Data->{'feed'}->{'entry'}[0]->{'media$group'}->{'yt$duration'}->{'seconds'};
$s = "<iframe width=\"1280\" height=\"720\" src=\"//www.youtube.com/embed/$video?showinfo=0&rel=0&modestbranding=1&theme=light&iv_load_policy=3&autohide=1&enablejsapi=1\" frameborder=\"0\" allowfullscreen></iframe>";
$s .= "<strong>Views:</strong> $views<br><strong>Duration:</strong> ";
$s .= sec2hms($duration);
$s .= "<br /><br />";
return $s;
}
It appears that the API has changed the way the duration is displayed (using ISO 8601 instead of raw number of seconds). My experience and knowledge of PHP is rather limited otherwise I probably could have solved this in my sleep! Any suggestions for how to resolve this?

You can use the videos:list call on v3 of the YouTube API like this:
$JSON = file_get_contents("https://www.googleapis.com/youtube/v3/videos?part=statistics,contentDetails&id=$videoId&key={YOUR-API-KEY}");
$json_data = json_decode($JSON, true);
$views = $json_data['items'][0]['statistics']['viewCount'];
$duration = $json_data['items'][0]['contentDetails']['duration'];
This will get the number of views and the duration of the video specified in $videoId.
The $duration is going to come back in ISO 8601 format which looks something like PT4H48M18S. You can pass this into a DateInterval class and then format as you wish. For instance:
$dateInterval = new DateInterval($duration);
$formattedDuration = $dateInterval->format("%H:%I:%S");
would take PT4H48M18S and make it look like this 04:48:18.

Related

Display name of the tools provided in the last X days using XPath in PHP

Back at it again with another XML/PHP problem. :)
On my webpage I want to display the names of the tools provided in the last X days. X is a number that will be entered by the user in a textbox, and after clicking the submit button, the names of the tools that have been provided in those last X days will appear.
This will be done by comparing the X value the user enters with the dates in my XML file, and to find the tools that match.
In my XML file, I have a "dateentered" node that stores a random date that I entered:
<tools>
<tool type="..." web-based="..." free="...">
<name>Facebook</name>
<description>...</description>
<url>...</url>
<subjects>...</subjects>
<creators>...</creators>
<category>...</category>
<price>...</price>
<dateentered>2020-12-01</dateentered>
</tool>
</tools>
Next, I created a function in PHP that basically converts the 'Y-M-D' format into days by subtracting the current date from whatever date you enter:
function time2string($timeline) {
$periods = array('day' => 86400);
$ret = '';
foreach($periods AS $name => $seconds){
$num = floor($timeline / $seconds);
$timeline -= ($num * $seconds);
$ret .= $num;
}
return trim($ret);
}
Then, I loaded my xml file using simpleXML:
$xml = simplexml_load_file('tools.xml');
So for example, using the XML code sample above and doing
$days = $xml->xpath("//tool/dateentered");
foreach ($days as $day) {
print (time2string(time()-strtotime($day)));
}
this converts '2020-12-02' to '1' and therefore outputs '1', meaning that the function works as it should.
With XPath, What I want to do is, I want to compare the value the user enters in the textbox with the converted 'dateentered' from my xml, and if they match, then I want to display the tool name.
So something like:
if(isset($_REQUEST["submit"])) {
// time2string function
$f_day = $_REQUEST["days"]; // getting the value the user enters in the textbox
$xml = simplexml_load_file('tools.xml');
// do something here
}
So let's say, using the xml sample I provided above, if the user enters 1 in the textbox, the output should be:
Facebook
How can I solve this?
I'm also open for different approaches besides having to create a function, this is just what I came up with.
Turns out, like #CBroe has said, I don't even need a function that converts date to days, instead, I can take advantage of PHP's date() and strtotime() functions as follows:
<?php
if(isset($_REQUEST["submit"])) {
$xml = simplexml_load_file('tools.xml');
$f_days = $_REQUEST["days"];
$days = date("Y-m-d", strtotime("-$f_days days"));
$xdays = $xml->xpath("//tool[dateentered = '$days']/name");
foreach ($xdays as $name) {
echo "<h1 align='center'>".$name."</h1><br>";
}
}
?>
And this will output:
Facebook

DateTime with Timezone in php and Microsoft Graph API

I'm trying to read the calendar entries of rooms and output the next three events on their calendar. However I am testing first to see if I can get just the first 3 events of the day, but it seems the timezone or something else is causing it to not show events correctly.
This is a function I wrote:
function mb_get_meetings($mb_email = null, $mb_proxy = false, $mb_datetime_start = null, $mb_datetime_finish = null)
{
date_default_timezone_set('Australia/Melbourne');
// get the Microsoft Open Graph API
$mb_msgraph = json_decode(mb_microsoft_opengraph($mb_proxy), true); // custom function to get Beaker Token + $mb_proxy for internal proxy on or off for dev testing
$mb_msgraph_token = $mb_msgraph['access_token'];
$mb_datetimenow = new DateTime();
$mb_datetimezone = new DateTimeZone('Australia/Melbourne');
$mb_datetimenow->setTimezone($mb_datetimezone);
$mb_datetime_start = new DateTime($mb_datetime_start, $mb_datetimezone);
$mb_datetime_finish = new DateTime($mb_datetime_finish, $mb_datetimezone);
$mb_datetime_start = $mb_datetime_start->format('Y-m-d\TH:i:s.u');
$mb_datetime_finish = $mb_datetime_finish->format('Y-m-d\TH:i:s.u');
$mb_url_string = 'https://graph.microsoft.com/v1.0/users/' . $mb_email . '/calendar/calendarView?startDateTime=' . $mb_datetime_start . '&endDateTime=' . $mb_datetime_finish;
$mb_args = array(
'headers' => array(
'Authorization' => 'Bearer ' . $mb_msgraph_token,
'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8',
'Prefer' => 'outlook.timezone="Australia/Melbourne"'
),
'httpversion' => '1.1'
);
$mb_output = wp_remote_get($mb_url_string, $mb_args);
$mb_output = wp_remote_retrieve_body($mb_output);
return $mb_output;
}
I am using Wordpress as the backend and it does retrieve the body.
In my frontend page I call:
$mbroom = (mb_get_meetings('email#domain.tld', true, 'today 7am', 'today 7pm'));
$mbroom = json_decode($mbroom, true);
$mbroom = $mbroom['value'];
foreach ($mbroom as $k => $v) {
// get the first 3 entries
if ($k < 3) {
print_r($k);
print_r($v['subject']);
print_r(date('g:i', strtotime($v['start']['dateTime']));
print_r(date('g:i', strtotime($v['end']['dateTime']));
print_r($v['organizer']['emailAddress']['name']);
echo '<hr>';
}
}
In the results, I will get sometimes no calendar entries while other times I might get entries for 2pm but not anything from 8am. I have tried changing it to hardcoded YYYY-MM-DDD 08:00 and YYYY-MM-DD 20:00 but to no avail. I have also tried yesterday and tomorrow but none/incorrect results.
I also tried booking an entire day with 30-minute meetings and that too wasn't working.
Am I doing something wrong?
I suggest sending the startDateTime and endDateTime parameters in UTC, formatted as ISO 8601 as described in the docs (https://learn.microsoft.com/en-us/graph/api/user-list-calendarview?view=graph-rest-1.0&tabs=http), which you have done. However, I'd suggest using a PHP constant, since it's less error-prone (https://www.php.net/manual/en/class.datetimeinterface.php#datetime.constants.iso8601). Doing something like the following for those parameters:
$startDateTime = new DateTime(date("Y-m-d H:i:s"));
$startDateTime = $startDateTime->format(DATE_ISO8601);
$startDateTime = substr($startDateTime, 0, strpos($startDateTime, '+'));
Edit:
Using substring to remove the locale part of the DateTime string was probably a naïve thing to do, but it does serve the purpose.

Get duration using YouTube API

I need to get the video duration using Youtube API V3. My application was working fine with API V3 but now it doesn't work.
I found a working example and it works:
$dur = file_get_contents("https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=[VIDOID]&key=[API KEY]");
$duration =json_decode($dur, true);
foreach ($duration['items'] as $vidTime) {
$vTime= $vidTime['contentDetails']['duration'];
}
Credits: Youtube API v3 , how to get video durations?
This will return the time format something like this.
PT24M30S
How can I convent this to a readable time. Like 24:30?
I can't believe it, anyone have used DateInterval, why? It's just like this:
$duration = new DateInterval('PT24M30S');
print $duration->format('%H:%i:%s'); // outputs: 00:24:30
Helpful links:
The DateInterval class
DateInterval::format
One possible way is to use the function str_replace();
$stamp = "PT24M30S";
$formated_stamp = str_replace(array("PT","M","S"), array("",":",""),$stamp);
echo $formated_stamp; //should give "24:30"
Bonus content - leading zeros
In order to add leading zeros one must first split the string up with explode(), then format the numbers idividually with sprintf(); and finally add it all together again.
$exploded_string = explode(":",$formated_stamp);
$new_formated_stamp = sprintf("%02d", $exploded_string[0]).":".sprintf("%02d", $exploded_string[1]);
$time_format = "PT24M30S ";
preg_match_all('/(\d+)/',$time_format,$parts);
$hours = floor($parts[0][0]/60);
$minutes = $parts[0][0]%60;
$seconds = $parts[0][1];
echo $hours . ": ". $minutes .":". $seconds;
video resource contains a duration field which is a string of the following format. It will be up to you the developer to reformat it as necessary for your application and needs.
contentDetails.duration
The length of the video. The tag value is an ISO 8601 duration. For
example, for a video that is at least one minute long and less than
one hour long, the duration is in the format PT#M#S, in which the
letters PT indicate that the value specifies a period of time, and the
letters M and S refer to length in minutes and seconds, respectively.
The # characters preceding the M and S letters are both integers that
specify the number of minutes (or seconds) of the video. For example,
a value of PT15M33S indicates that the video is 15 minutes and 33
seconds long.
If the video is at least one hour long, the duration is in the format
PT#H#M#S, in which the # preceding the letter H specifies the length
of the video in hours and all of the other details are the same as
described above. If the video is at least one day long, the letters P
and T are separated, and the value's format is P#DT#H#M#S. Please
refer to the ISO 8601 specification for complete details.
As to how to do it I would have to say remove the PT and the S and replace the M with a :. It will probably require some testing on your part depending on what happens when a value is null. I would do some research into ISO-8061
I use the following function to convert the YouTube duration. Simply replace $yt with the format YouTube provided you and it'll print it out nice and readable.
function ConvertYoutubeVideoDuration($yt){
$yt=str_replace(['P','T'],'',$yt);
foreach(['D','H','M','S'] as $a){
$pos=strpos($yt,$a);
if($pos!==false) ${$a}=substr($yt,0,$pos); else { ${$a}=0; continue; }
$yt=substr($yt,$pos+1);
}
if($D>0){
$M=str_pad($M,2,'0',STR_PAD_LEFT);
$S=str_pad($S,2,'0',STR_PAD_LEFT);
return ($H+(24*$D)).":$M:$S"; // add days to hours
} elseif($H>0){
$M=str_pad($M,2,'0',STR_PAD_LEFT);
$S=str_pad($S,2,'0',STR_PAD_LEFT);
return "$H:$M:$S";
} else {
$S=str_pad($S,2,'0',STR_PAD_LEFT);
return "$M:$S";
}
}
Solution PHP
function covtime($youtube_time){
preg_match_all('/(\d+)/',$youtube_time,$parts);
$hours = $parts[0][0];
$minutes = $parts[0][1];
$seconds = $parts[0][2];
if($seconds != 0)
return $hours.':'.$minutes.':'.$seconds;
else
return $hours.':'.$minutes;
}

php RRD Graph floating values instead of integers

I am producing an rrd graph and I am facing 2 problems.
Problem 1: The numbers that I am printing they are integers without decimals, although when they are printed decimals appear. It is really confusing. So I looked online on the rrdgraph_graph and although that I am using the correct syntax and I am not applying any calculations I still do get floating values instead of integers.
According to the official website: %s place this after %le, %lf or %lg. This will be replaced by the appropriate SI magnitude unit and the value will be scaled accordingly (123456 -> 123.456 k).
I have attached a photo as sample of the output. I have also provide a working example code so if anyone understands the RRD's can view possible the error.
Problem 2: I was trying to add on my graph the VRULE:time#color[:legend][:dashes[=on_s[,off_s[,on_s,off_s]...]][:dash-offset=offset]] function and based on the online instructions I can supply the time. Since my graph is shifting I was planning to do time (value) - 1800 sec. I wanted to place a vertical line in the middle of the graph so I could view approximately an average on 30 minutes values. When I am applying such a format I get this error:
<b>Graph error: </b>parameter '1400274668-1800' does not represent time in line VRULE:1400274668-1800#0000CD:Half way values
When I remove the subtraction everything work fine. Is there a way to produce this line in the middle of my graph?
<?php
$file = "snmp";
$rrdFile = dirname(__FILE__) . "/".$file.".rrd";
$in = "losses";
$png = "/home/linux/Desktop/";
$in_min = "vdef_in_min";
$in_max = "vdef_in_max";
$in_lst = "vdef_in_lst";
$in_av = "vdef_in_av";
$title = "Losses RRD::Graph";
$output = array("1h","1d");
$step = 5;
$heartbeat = 2 * $step;
while (1) {
sleep (1);
$options = array(
"--start","now -15s",
"--step", "".$step."",
"DS:".$in.":GAUGE:".$heartbeat.":0:U",
"RRA:LAST:0.5:1:3600",
"RRA:MIN:0.5:1:3600",
"RRA:MAX:0.5:1:3600",
"RRA:AVERAGE:0.5:6:600",
"RRA:LAST:0.5:300:288",
"RRA:MIN:0.5:300:288",
"RRA:MAX:0.5:300:288`",
"RRA:AVERAGE:0.5:600:144"
);
if ( !isset( $create ) ) {
$create = rrd_create(
"".$rrdFile."",
$options
);
if ( $create === FALSE ) {
echo "Creation error: ".rrd_error()."\n";
}
}
$t = time();
$losses = rand(0, 150);
$update = rrd_update(
"".$rrdFile."",
array(
"".$t.":".$losses.""
)
);
if ($update === FALSE) {
echo "Update error: ".rrd_error()."\n";
}
date_default_timezone_set('Europe/Stockholm');
$timezone = new DateTime(NULL, new DateTimeZone('Europe/Stockholm'));
$date = date('l jS \of F Y h\\:i\\:s A' , $timezone->format('U'));
$comment = "RRD last updated:".$date;
$comment = str_replace( ":", "\\:", $comment );
$graph = "Graph last updated:".$date;
$graph = str_replace( ":", "\\:", $graph );
foreach ($output as $test) {
$final = array(
"--start","end - ".$test."",
"--end", "".$t."",
"--title=".$file." RRD::Graph with - ".$test." Periods",
"--vertical-label=Bytes(s)/sec",
"--right-axis-label=latency(ms)",
"--alt-y-grid", "--rigid",
"--width", "800", "--height", "500",
"--lower-limit=0",
"--no-gridfit",
"--slope-mode",
"DEF:".$in."_def=".$file.".rrd:".$in.":LAST",
"CDEF:inbytes=".$in."_def",
"VDEF:".$in_lst."=inbytes,LAST",
"VDEF:".$in_min."=inbytes,MINIMUM",
"VDEF:".$in_max."=inbytes,MAXIMUM",
"VDEF:".$in_av."=inbytes,AVERAGE",
"COMMENT:\\n",
"LINE2:".$in."_def#FF0000:".$in."",
"GPRINT:".$in_min.": Minimum\:%6.2lf %S",
"GPRINT:".$in_max.":Maximum\:%6.2lf %S",
"GPRINT:".$in_lst.":Last\:%6.2lf %s",
"GPRINT:".$in_av.":Average\:%6.2lf %s",
"COMMENT:\\n",
"VRULE:".$t."#0000CD:Half way values",
"COMMENT:\\n",
"HRULE:50#FFFF00:Maximum value",
"COMMENT:\\n",
"COMMENT: ",
"COMMENT:\\n",
"COMMENT:".$comment."\\r",
"COMMENT:".$graph."\\r"
);
$outputPngFile = rrd_graph(
"".$png."".$test.".png",
$final
);
if ($outputPngFile === FALSE) {
echo "<b>Graph error: </b>".rrd_error()."\n";
}
}
$debug = rrd_lastupdate (
"".$rrdFile.""
);
if ($debug === FALSE) {
echo "<b>Graph result error: </b>".rrd_error()."\n";
}
var_dump ($debug);
}
?>
The answer to your first problem is almost certainly Data Normalisation. Since you are not updating the RRD precisely on the Step boundary every time, the submitted data values are normalised to a step boundary, resulting in the decimal values. To understand this, read Alex van den Bogeardt's excellent article on the subject.
Your second problem is that you simply cannot use the VRULE declaration in that way. The first parameter to VRULE may be either a number or a VDEF variable, but it cannot be a formula. Therefore, VRULE:12345678#0000CD:Foo is fine, as is VRULE:vdefname#FF00FF:Bar. However you may not use VRULE:123456-123#0000CD:No. Do the calculation before, like this:
"VRULE:".($t-1800)."#0000CD:Half way values",
... and this should result in a valid syntax.

How to convert Facebook time zone to a custom string

I am working on facebook integration in my application.
Facebook returns users timezone in the format like for India its
5.5
But the existing timezones are saved in database in format
+05:30
Any functions or algorithm suggestions for this conversion.
thanks .
This one is a bit long but it adds necessary preceding zeros etc if that's a requirement.
$num = -5.5;
$res = ($num < 0 ? '-' : '+') . (abs($num) < 10 ? '0' : '') . abs((int)$num) . ':';
// --> -05:
$mins = round((abs($num) - abs((int)$num)) * 60);
// --> 30
$res .= ($mins < 10 ? '0' : '') . $mins;
echo $res;
// -05:30
Easy peasy (assume $a = 5.5):
$tz = ($a > 0 ? '+' : '-') . ((int) $a) . ':' . (($a - (int)$a)*60)
First bit gets you the - or + sign (left/right timezones), secondly casting $a to (int) you lose the fractional part and the third bit first gets the fractional part (I'm sure there's a php function for this but I come from C so basically: 5.5 - (int)5.5 = 5.5 - 5 = 0.5). The fractional part is expressed in hours (5.5 hours is 5 hours and 30 minutes) so basically 0.5 * 60 gets you the fractional part in minutes.
EDIT: Here's a cleaner (one-liner) version:
$time = sprintf("%+03d:%2d", (int)$a, abs($a-(int)$a)*60);
This one is pretty much the easiest. Thanks inhan for pointing out the abs() detail :)

Categories