Convert youtube Api v3 video duration in php
Asked Answered
A

15

21

how do i convert PT2H34M25S to 2:34:25

I searched and used this code. I'm new to regex can someone explain this ? and help me with my issue

function covtime($youtube_time){
        preg_match_all('/(\d+)/',$youtube_time,$parts);
        $hours = floor($parts[0][0]/60);
        $minutes = $parts[0][0]%60;
        $seconds = $parts[0][1];
        if($hours != 0)
            return $hours.':'.$minutes.':'.$seconds;
        else
            return $minutes.':'.$seconds;
    }   

but this code only give me HH:MM

so dumb found the solution :

   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;
        }
Ammon answered 24/6, 2014 at 17:54 Comment(6)
Are you sure it's not giving you MM:SS (the second case in your conditional)? I don't see a code path that could output HH:MM.Inoculation
it is giving me MM:SS but not HH:MM:SSAmmon
And are you sure you tried it with a video over an hour long? Also, the input format example you give in your question clearly has 3 separate digit sequences, so why does your code only consider the first 2?Inoculation
just found the solution have posted it above thanks for your helpAmmon
@DanielEuchar, I edited my answer with a revised function for you to look at. There is still more validation that can be done. Also it looks like someone removed the edit you put into your question.Breannebrear
solution fails on videos that report a duration of 1:00:01. see my answer: stackoverflow.com/a/35836604Haya
S
37

Using DateTime class

You could use PHP's DateTime class to achieve this. This solution is much simpler, and will work even if the format of the quantities in the string are in different order. A regex solution will (most likely) break in such cases.

In PHP, PT2H34M25S is a valid date period string, and is understood by the DateTime parser. We then make use of this fact, and just add it to the Unix epoch using add() method. Then you can format the resulting date to obtain the required results:

function covtime($youtube_time){
    if($youtube_time) {
        $start = new DateTime('@0'); // Unix epoch
        $start->add(new DateInterval($youtube_time));
        $youtube_time = $start->format('H:i:s');
    }
    
    return $youtube_time;
}   

echo covtime('PT2H34M25S');

Demo

Stans answered 24/6, 2014 at 18:28 Comment(12)
wow this is so much better but in my wamp server this gave me a error Uncaught exception 'Exception' with message 'DateInterval::__construct() [<a href='dateinterval.--construct'>dateinterval.--construct</a>]: Unknown or bad format ()Ammon
@DanielEuchar: Can you add var_dump($youtube_time); inside the function, and tell me what it outputs when you try it with your time string?Stans
sometimes i get PT2H34M25S and sometimes PT3M22S depends on the video lengthAmmon
try to define if it has hours:minutes:seconds then use this function instead you can always do a switch case and in each of your cases you do: e.g return $start->format('g:i:s'); //for hours:minutes:secondsGilbertine
@JefferyThaGintoki: Yup, this can be improved in many ways. I leave it to the user to figure out how to modify the function with respect to their requirements :)Stans
By the way use return $start->format('H:i:s'); instead of g put a capital H this will convert the time perfectly even if u pass only minutes or secondsGilbertine
@JefferyThaGintoki: Not sure I understand. Could you give a sample input for which H:i:s would work but g:i:s won't?Stans
<?php /** * Return the youtube video duration * @param $youtube_time * @return time */ function covtime($youtube_time){ $start = new DateTime('@0'); // Unix epoch $start->add(new DateInterval($youtube_time)); return $start->format('H:i:s'); } echo covtime('PT34M25S'); as you can see no hours passedGilbertine
@JefferyThaGintoki: Could you give a sample input for which H:i:s would work but g:i:s won't?Stans
'g:i:s' won't work perfectly if we pass youtube time without hours or minutes, just try it and you will see what i meanGilbertine
@JefferyThaGintoki: Wow, thanks. Don't know how I missed that until now. Thank you, I've now updated the answer to reflect this :)Stans
Some styling for those extra zeros: ``if ( substr( $start, 0, 5 ) === '00:00' ) { $start = substr( $start, 4 ); } else if ( substr( $start, 0, 3 ) === '00:' ) { $start = substr( $start, 3 ); } return $start;```Truck
R
12

Both Amal's and Pferate's solutions are great! However, Amal solution keep giving me 1Hr extra. Below is my solution which is same with Amal but difference approach and this work for me.

$date = new DateTime('1970-01-01');
$date->add(new DateInterval('PT2H34M25S'));
echo $date->format('H:i:s')

datetime->add() reference

Rodmun answered 24/6, 2014 at 17:54 Comment(1)
It works. The Date part is not important. So maybe you could use $date = new DateTime('00:00');Charleycharlie
H
6

Here's my solution which handles missing H, M or S (test a video with a reported length of 1:00:01 and you'll see the problem with other answers). Seconds alone are presented as 0:01, but if you want to change that just change the last else to elseif($M>0) and add an else for seconds e.g. else { return ":$S" }

// convert youtube v3 api duration e.g. PT1M3S to HH:MM:SS
// updated to handle videos days long e.g. P1DT1M3S
function covtime($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";
    }
}
Haya answered 7/3, 2016 at 5:12 Comment(2)
Best solution to the "YouTube API duration timestamp" conundrum. Tested and works in all cases.Oliveolivegreen
this fails for video id: WBu2pJpgKEo. contentDetails.duration = P1DT3H8S from the data api docs: "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." so, here's an amendment: $yt = str_replace('P', '', $yt); $yt = str_replace('T', '', $yt); nice solution otherwise! edit: additionally, handling the D case is necessary, but trivial to implement with the current setup of the function.Revisory
B
4

You should take a look at your $parts variable after it's created.

var_dump($parts);

Compare that output with how you are defining your variables. It should stand out pretty obviously after that.

I guess my next question after that is what variations of the input time string are you expecting and what validation will you be doing? The variations of the input format you want to handle will affect the complexity of the actual code written.

Edit: Here is an updated function to handle missing numeric values (if the hours or minutes is omitted) and seconds/hours over 60 (not sure if this will ever happen). This doesn't validate the label for the numbers:

  • 1 number: assume it is seconds
  • 2 numbers: assume it is minutes, seconds
  • 3 or more numbers: assume first 3 numbers are hours, minutes, seconds (ignoring another numbers)

More validation can be added to look into validating the input string.

<?php
function covtime($youtube_time) {
    preg_match_all('/(\d+)/',$youtube_time,$parts);

    // Put in zeros if we have less than 3 numbers.
    if (count($parts[0]) == 1) {
        array_unshift($parts[0], "0", "0");
    } elseif (count($parts[0]) == 2) {
        array_unshift($parts[0], "0");
    }

    $sec_init = $parts[0][2];
    $seconds = $sec_init%60;
    $seconds_overflow = floor($sec_init/60);

    $min_init = $parts[0][1] + $seconds_overflow;
    $minutes = ($min_init)%60;
    $minutes_overflow = floor(($min_init)/60);

    $hours = $parts[0][0] + $minutes_overflow;

    if($hours != 0)
        return $hours.':'.$minutes.':'.$seconds;
    else
        return $minutes.':'.$seconds;
}
Breannebrear answered 24/6, 2014 at 18:18 Comment(1)
this fails on videos that report a duration of 1:00:01. see my answer: stackoverflow.com/a/35836604Haya
R
4

This is my solution, test completed!

preg_match_all("/PT(\d+H)?(\d+M)?(\d+S)?/", $duration, $matches);

$hours   = strlen($matches[1][0]) == 0 ? 0 : substr($matches[1][0], 0, strlen($matches[1][0]) - 1);
$minutes = strlen($matches[2][0]) == 0 ? 0 : substr($matches[2][0], 0, strlen($matches[2][0]) - 1);
$seconds = strlen($matches[3][0]) == 0 ? 0 : substr($matches[3][0], 0, strlen($matches[3][0]) - 1);

return 3600 * $hours + 60 * $minutes + $seconds;
Rapid answered 9/3, 2016 at 13:35 Comment(0)
H
2

I use two classes, 1 to call YouTube API, and one to convert the time. here it is simplified.

As you can see, YouTubeGet is a static class, but it does not have to be. YouTubeDateInterval however has to be an instance due to the way that DateInterval works (which is being extended).

class YouTubeGet{
static public function get_duration($video_id, $api_key){
        // $youtube_api_key
        $url = "https://www.googleapis.com/youtube/v3/videos?id={$video_id}&part=contentDetails&key={$api_key}";
        // Get and convert. This example uses wordpress wp_remote_get()
        // this could be replaced by file_get_contents($url);
        $response = wp_remote_get($url);

        // if using file_get_contents(), then remove ['body']
        $result = json_decode($response['body'], true);
        // I've converted to an array, but this works fine as an object
        if(isset($result['items'][0]['contentDetails']['duration'])){
            $duration = $result['items'][0]['contentDetails']['duration'];
            return self::convtime($duration);
        }
        return false;
    }

    static protected function convtime($youtube_time){
        $time = new YouTubeDateInterval($youtube_time);        
        return $time->to_seconds();
    }   

}

class YouTubeDateInterval extends DateInterval {
    public function to_seconds() 
      { 
        return ($this->y * 365 * 24 * 60 * 60) + 
               ($this->m * 30 * 24 * 60 * 60) + 
               ($this->d * 24 * 60 * 60) + 
               ($this->h * 60 * 60) + 
               ($this->i * 60) + 
               $this->s; 
      } 
}

usage is either about passing a iso 8601 date to YouTubeDateInterval.

$time = new YouTubeDateInterval('PT2H34M25S');
$duration_in_seconds = $time->to_seconds();

or passing the YouTubeGet the api and video id key

$duration_in_seconds = YouTubeGet::get_duration('videoIdString','YouTubeApiKeyString');
Housley answered 3/7, 2018 at 12:13 Comment(0)
T
1

Here is the complete code to get, convert and display the video duration

$vidkey = "           " ; // for example: cHPMH26sw2f
$apikey = "xxxxxxxxxxxx" ;

$dur = file_get_contents("https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=$vidkey&key=$apikey");
$VidDuration =json_decode($dur, true);
foreach ($VidDuration['items'] as $vidTime) 
{
$VidDuration= $vidTime['contentDetails']['duration'];
}
// convert duration from ISO to M:S
$date = new DateTime('2000-01-01');
$date->add(new DateInterval($VidDuration));
echo $date->format('i:s') ;

Replace xxxxxxx with your API Key Results: 13:07

Telfore answered 20/4, 2015 at 17:28 Comment(0)
I
1

try this will give you duration by seconds

function video_length($youtube_time)
{
    $duration = new \DateInterval($youtube_time);
    return $duration->h  * 3600 + $duration->i  * 60  + $duration->s;
}
Incubator answered 8/4, 2018 at 5:37 Comment(0)
O
1
$duration = new \DateInterval($data['items'][0]['contentDetails']['duration']);
echo $duration->format('%H:%i:%s');
Oligarch answered 26/1, 2020 at 22:17 Comment(0)
A
0
function getDuration($duration): string
{
    preg_match_all("/P(\d+D)?T(\d+H)?(\d+M)?(\d+S)?/", $duration, $m);

    $days = empty($m[1][0]) ? 0 : substr($m[1][0], 0, -1);
    $hours = empty($m[2][0]) ? 0 : substr($m[2][0], 0, -1);
    $minutes = empty($m[3][0]) ? 0 : substr($m[3][0], 0, -1);
    $seconds = empty($m[4][0]) ? 0 : substr($m[4][0], 0, -1);

    $hours += ($days * 24);

    $hours = sprintf("%02d", $hours);
    $minutes = sprintf("%02d", $minutes);
    $seconds = sprintf("%02d", $seconds);

    return "$hours:$minutes:$seconds";
}

example:

input: PT2H34M25S
output: 02:34:25

input: P1DT3H12M8S
output: 27:12:08
Akkerman answered 5/2, 2022 at 17:12 Comment(0)
W
0

I have accomplished this by using use Carbon\CarbonInterval library.

public static function convert_youtube_duration_to_normal(string $youtube_time)
{
    return CarbonInterval::create($youtube_time)->forHumans();
}


Functions::convert_youtube_duration_to_normal('PT4M25S')

Which returns as output "4 minutes 25 seconds"

Whipstock answered 10/2, 2023 at 12:41 Comment(0)
S
-1

Try the following function:

function covtime($youtube_time) 
{
    $hours = '0';
    $minutes = '0';
    $seconds = '0';

    $hIndex = strpos($youtube_time, 'H');
    $mIndex = strpos($youtube_time, 'M');
    $sIndex = strpos($youtube_time, 'S');
    $length = strlen($youtube_time);
    if($hIndex > 0)
    {
        $hours = substr($youtube_time, 2, ($hIndex - 2));
    }
    if($mIndex > 0)
    {
        if($hIndex > 0)
        {
            $minutes = substr($youtube_time, ($hIndex + 1), ($mIndex - ($hIndex + 1)));
        }      
        else
        {
            $minutes = substr($youtube_time, 2, ($mIndex - 2));
        }
    }
    if($sIndex > 0)
    {
        if($mIndex > 0)
        {
            $seconds = substr($youtube_time, ($mIndex + 1), ($sIndex - ($mIndex + 1)));
        }
        else if($hIndex > 0)
        {
            $seconds = substr($youtube_time, ($hIndex + 1), ($sIndex - ($hIndex + 1)));
        }      
        else
        {
            $seconds = substr($youtube_time, 2, ($sIndex - 2));
        }
    }
    return $hours.":".$minutes.":".$seconds;        
}
Sentimentalize answered 17/9, 2014 at 12:35 Comment(0)
C
-1

You can try this -

function covtime($youtube_time){
    $start = new DateTime('@0'); // Unix epoch
    $start->add(new DateInterval($youtube_time));
    if (strlen($youtube_time)>8)
    {
    return $start->format('g:i:s');
}   else {
	return $start->format('i:s');
}
}
Cadmann answered 7/5, 2015 at 13:3 Comment(0)
B
-1

DateTime and DateInterval not working in Yii or some php version. So this is my solution. Its work with me.

function convertTime($time){        
    if ($time > 0){
        $time_result = '';
        $hours = intval($time / 3600);
        if ($hours > 0)
            $time_result = $time_result.$hours.':';
        $time = $time % 3600;
        $minutes = intval($time / 60);
        $seconds = $time % 60;
        $time_result = $time_result.(($minutes > 9)?$minutes:'0'.$minutes).':';
        $time_result = $time_result.(($seconds > 9)?$seconds:'0'.$seconds);
    }else 
        $time_result = '0:00';        

    return $time_result;
}
Bullheaded answered 24/11, 2016 at 4:21 Comment(0)
O
-1

Why the complication. Think outside the box.

    $seconds = substr(stristr($length, 'S', true), -2, 2);
    $seconds = preg_replace("/[^0-9]/", '', $seconds);
    $minutes =  substr(stristr($length, 'M', true), -2, 2);
    $minutes = preg_replace("/[^0-9]/", '', $minutes);
    $hours =  substr(stristr($length, 'H', true), -2, 2);
    $hours = preg_replace("/[^0-9]/", '', $hours);

OK. preg_replace is not really needed, but I put it to guarantee only numbers.

Then for my formatting (which you can do however you like without limits),

      if($hours == 0){ 
      $h= '';
      }else{ 
      $h= $hours.':';
      }

      if($minutes <10){ 
      $m= '0'.$minutes.':'; 
        if($h == ''){
        $m= $minutes.':'; 
        }
          if ($minutes == 0){
            $m= $minutes;                 
          }
      }else{ 
      $m= $minutes.':';
      }

      if($seconds <10){ 
      $s= '0'.$seconds; 
      }else{ 
      $s= $seconds;
      }
      $time= $h . $m . $s.'s';
Oder answered 26/2, 2017 at 14:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.