EDIT:
I took a closer look at the feed, and it seems the "time" entries are just pointers for the thumbnails. The actual duration for a video is set in seconds <yt:duration seconds='667'/>
so you could just add them together as integers and then use the DateTime class to convert to whatever your format is. Example here.
END EDIT
First of all, to get all the times, you could need an atom feed reader in PHP. There are plenty out there. Do not try to parse the XML, ATOM is a well known standard that should be easily used (if you really only want the times, you could go with an xpath query).
Now that you have all the times at your disposal, you need a way to add them up easily, preferably without messing with nested loops and if-statements.
Here's a class that represents a single time entry for a single video:
final class Duration
{
private $hours;
private $minutes;
private $seconds;
private $centis;
/* we don't want any Durations not created with a create function */
private function __construct() {}
public static function fromString($input = '00:00:00.000') {
$values = self::valuesFromString($input);
return self::fromValues($values['hours'], $values['minutes'], $values['seconds'], $values['centis']);
}
public function addString($string) {
$duration = self::fromString($string);
return $this->addDuration($duration);
}
public function addDuration(Duration $duration) {
// add the durations, and return a new duration;
$values = self::valuesFromString((string) $duration);
// adding logic here
$centis = $values['centis'] + $this->centis;
$this->fixValue($centis, 1000, $values['seconds']);
$seconds = $values['seconds'] + $this->seconds;
$this->fixValue($seconds, 60, $values['minutes']);
$minutes = $values['minutes'] + $this->minutes;
$this->fixValue($minutes, 60, $values['hours']);
$hours = $values['hours'] + $this->hours;
return self::fromValues($hours, $minutes, $seconds, $centis);
}
public function __toString() {
return str_pad($this->hours,2,'0',STR_PAD_LEFT) . ':'
. str_pad($this->minutes,2,'0',STR_PAD_LEFT) . ':'
. str_pad($this->seconds,2,'0',STR_PAD_LEFT) . '.'
. str_pad($this->centis,3,'0',STR_PAD_LEFT);
}
public function toValues() {
return self::valuesFromString($this);
}
private static function valuesFromString($input) {
if (1 !== preg_match('/(?<hours>[0-9]{2}):(?<minutes>([0-5]{1}[0-9]{1})):(?<seconds>[0-5]{1}[0-9]{1}).(?<centis>[0-9]{3})/', $input, $matches)) {
throw new InvalidArgumentException('Invalid input string (should be 01:00:00.000): ' . $input);
}
return array(
'hours' => (int) $matches['hours'],
'minutes' => (int) $matches['minutes'],
'seconds' => (int) $matches['seconds'],
'centis' => (int) $matches['centis']
);
}
private static function fromValues($hours = 0, $minutes = 0, $seconds = 0, $centis = 0) {
$duration = new Duration();
$duration->hours = $hours;
$duration->minutes = $minutes;
$duration->seconds = $seconds;
$duration->centis = $centis;
return $duration;
}
private function fixValue(&$input, $max, &$nextUp) {
if ($input >= $max) {
$input -= $max;
$nextUp += 1;
}
}
}
You can create a new Duration only by calling the static factory fromString(), and that accepts only strings in the form "00:00:00.000" (hours:minutes:seconds.milliseconds):
$duration = Duration::fromString('00:04:16.250');
Next, you can add another string or an actual duration object, to create a new Duration:
$newDuration = $duration->addString('00:04:16.250');
$newDuration = $duration->addDuration($duration);
The Duration object will output it's own duration string in the format '00:00:00.000':
echo $duration;
// Gives
00:04:16.250
Or, if you're interested in the separate values, you can get them like so:
print_r($duration->toValues());
// Gives
Array
(
[hours] => 0
[minutes] => 4
[seconds] => 16
[milliseconds] => 250
)
Final example for using this in a loop to get the total video time:
$allTimes = array(
'00:30:05:250',
'01:24:38:250',
'00:07:01:750'
);
$d = Duration::fromString();
foreach ($allTimes as $time) {
$d = $d->addString($time);
}
echo $d . "\n";
print_r($d->toValues());
// Gives
02:01:45.250
Array
(
[hours] => 2
[minutes] => 1
[seconds] => 45
[milliseconds] => 250
)
For questions on why I used a final class with private constructor:
I wrote this as an exercise for myself, following Mathias Veraes's blog post on "named constructors".
Also, I couldn't resist adding his "TestFrameworkInATweet" as well:
function it($m,$p){echo ($p?'✔︎':'✘')." It $m\n"; if(!$p){$GLOBALS['f']=1;}}function done(){if(@$GLOBALS['f'])die(1);}
function throws($exp,Closure $cb){try{$cb();}catch(Exception $e){return $e instanceof $exp;}return false;}
it('should be an empty duration from string', Duration::fromString() == '00:00:00.000');
it('should throw an exception with invalid input string', throws("InvalidArgumentException", function () { Duration::fromString('invalid'); }));
it('should throw an exception with invalid seconds input string', throws("InvalidArgumentException", function () { Duration::fromString('00:00:61:000'); }));
it('should throw an exception with invalid minutes input string', throws("InvalidArgumentException", function () { Duration::fromString('00:61:00:000'); }));
it('should add milliseconds to seconds', Duration::fromString('00:00:00.999')->addString('00:00:00.002') == Duration::fromString('00:00:01.001'));
it('should add seconds to minutes', Duration::fromString('00:00:59.000')->addString('00:00:02.000') == Duration::fromString('00:01:01.000'));
it('should add minutes to hours', Duration::fromString('00:59:00.000')->addString('00:02:00.000') == Duration::fromString('01:01:00.000'));
it('should add all levels up', Duration::fromString('00:59:59.999')->addString('00:01:01.002') == Duration::fromString('01:01:01.001'));
$duration = Duration::fromString('00:00:01.500');
it('should add a Duration', $duration->addDuration($duration) == '00:00:03.000');