Converting Youtube Data API V3 video duration form

2019-01-17 20:19发布

问题:

I'm trying to convert ISO 8601 string to seconds in JS/Node. The best I could come up with was:

function convert_time(duration) {
    var a = duration.match(/\d+/g)
    var duration = 0

    if(a.length == 3) {
        duration = duration + parseInt(a[0]) * 3600;
        duration = duration + parseInt(a[1]) * 60;
        duration = duration + parseInt(a[2]);
    }

    if(a.length == 2) {
        duration = duration + parseInt(a[0]) * 60;
        duration = duration + parseInt(a[1]);
    }

    if(a.length == 1) {
        duration = duration + parseInt(a[0]);
    }
    return duration
}

It works when I input strings such as "PT48S", "PT3M20S" or "PT3H2M31S", but fails miserably if the string is "PT1H11S". Does anyone have a better idea?

回答1:

I suggest this little hack to prevent your problematic case:

function convert_time(duration) {
    var a = duration.match(/\d+/g);

    if (duration.indexOf('M') >= 0 && duration.indexOf('H') == -1 && duration.indexOf('S') == -1) {
        a = [0, a[0], 0];
    }

    if (duration.indexOf('H') >= 0 && duration.indexOf('M') == -1) {
        a = [a[0], 0, a[1]];
    }
    if (duration.indexOf('H') >= 0 && duration.indexOf('M') == -1 && duration.indexOf('S') == -1) {
        a = [a[0], 0, 0];
    }

    duration = 0;

    if (a.length == 3) {
        duration = duration + parseInt(a[0]) * 3600;
        duration = duration + parseInt(a[1]) * 60;
        duration = duration + parseInt(a[2]);
    }

    if (a.length == 2) {
        duration = duration + parseInt(a[0]) * 60;
        duration = duration + parseInt(a[1]);
    }

    if (a.length == 1) {
        duration = duration + parseInt(a[0]);
    }
    return duration
}

Fiddle



回答2:

If you're using moment.js you can simply call...

moment.duration('PT15M33S').asMilliseconds();

= 933000 ms



回答3:

function YTDurationToSeconds(duration) {
  var match = duration.match(/PT(\d+H)?(\d+M)?(\d+S)?/);

  match = match.slice(1).map(function(x) {
    if (x != null) {
        return x.replace(/\D/, '');
    }
  });

  var hours = (parseInt(match[0]) || 0);
  var minutes = (parseInt(match[1]) || 0);
  var seconds = (parseInt(match[2]) || 0);

  return hours * 3600 + minutes * 60 + seconds;
}

works for these cases:

PT1H
PT23M
PT45S
PT1H23M
PT1H45S
PT23M45S
PT1H23M45S


回答4:

Here's my solution:

function parseDuration(duration) {
    var matches = duration.match(/[0-9]+[HMS]/g);

    var seconds = 0;

    matches.forEach(function (part) {
        var unit = part.charAt(part.length-1);
        var amount = parseInt(part.slice(0,-1));

        switch (unit) {
            case 'H':
                seconds += amount*60*60;
                break;
            case 'M':
                seconds += amount*60;
                break;
            case 'S':
                seconds += amount;
                break;
            default:
                // noop
        }
    });

    return seconds;
}


回答5:

My solution:

function convert_time(duration) {
  var total = 0;
  var hours = duration.match(/(\d+)H/);
  var minutes = duration.match(/(\d+)M/);
  var seconds = duration.match(/(\d+)S/);
  if (hours) total += parseInt(hours[1]) * 3600;
  if (minutes) total += parseInt(minutes[1]) * 60;
  if (seconds) total += parseInt(seconds[1]);
  return total;
}

Fiddle



回答6:

I've written a CoffeeScript variation (you can easily compile it at coffeescript.org when desired)

DIFFERENCE: the returning duration comes in a human readable format (e.g. 04:20, 01:05:48)

String.prototype.parseDuration = ->
    m = @.match /[0-9]+[HMS]/g
    res = ""
    fS = fM = !1
    for part in m
        unit = part.slice -1
        val = part.slice 0, part.length - 1
        switch unit
            when "H" then res += val.zeros( 2 ) + ":"
            when "M"
                fM = 1
                res += val.zeros( 2 ) + ":"
            when "S"
                fS = 1
                res += if fM then val.zeros 2 else "00:" + val.zeros 2

     if !fS then res += "00"
     res

I've also implemented this helper function to fill < 10 values with a leading zero:

String.prototype.zeros = ( x ) ->
    len = @length
    if !x or len >= x then return @
    zeros = ""
    zeros += "0" for [0..(x-len-1)]
    zeros + @

3nj0y!!!



回答7:

You can find a very simple PHP solution here - How To Convert Youtube API Time (ISO 8601 String Video Duration) to Seconds In PHP - Code

This function convert_time() takes one parameter as input - the Youtube API Time (Video Duration) which is in ISO 8601 string format and returns its duration in seconds.

function convert_time($str) 
{
    $n = strlen($str);
    $ans = 0;
    $curr = 0;
    for($i=0; $i<$n; $i++)
    {
        if($str[$i] == 'P' || $str[$i] == 'T')
        {

        }
        else if($str[$i] == 'H')
        {
            $ans = $ans + 3600*$curr;
            $curr = 0;
        }
        else if($str[$i] == 'M')
        {
            $ans = $ans + 60*$curr;
            $curr = 0;
        }
        else if($str[$i] == 'S')
        {
            $ans = $ans + $curr;
            $curr = 0;
        }
        else
        {
            $curr = 10*$curr + $str[$i];
        }
    }
    return($ans);
}

Testing Some Inputs:

"PT2M23S" => 143
"PT2M" => 120
"PT28S" => 28
"PT5H22M31S" => 19351
"PT3H" => 10800
"PT1H6M" => 3660
"PT1H6S" => 3606


回答8:

I ran into issues with the above solution. I decided to write it as obtuse as possible. I also use my own "getIntValue" in place of parseInt for extra sanity.

Just thought other searching might appreciate the update.

Fiddle

    function convertYouTubeTimeFormatToSeconds(timeFormat) {

    if ( timeFormat === null || timeFormat.indexOf("PT") !== 0 ) {
        return 0;
    }

    // match the digits into an array
    // each set of digits into an item
    var digitArray      = timeFormat.match(/\d+/g);
    var totalSeconds    = 0;

    // only 1 value in array
    if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') == -1 && timeFormat.indexOf('S') == -1) {
        totalSeconds    += getIntValue(digitArray[0]) * 60 * 60;
    }

    else if (timeFormat.indexOf('H') == -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') == -1) {
        totalSeconds    += getIntValue(digitArray[0]) * 60;
    }

    else if (timeFormat.indexOf('H') == -1 && timeFormat.indexOf('M') == -1 && timeFormat.indexOf('S') > -1) {
        totalSeconds    += getIntValue(digitArray[0]);
    }


    // 2 values in array
    else if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') == -1) {
        totalSeconds    += getIntValue(digitArray[0]) * 60 * 60;
        totalSeconds    += getIntValue(digitArray[1]) * 60;
    }

    else if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') == -1 && timeFormat.indexOf('S') > -1) {
        totalSeconds    += getIntValue(digitArray[0]) * 60 * 60;
        totalSeconds    += getIntValue(digitArray[1]);
    }

    else if (timeFormat.indexOf('H') == -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') > -1) {
        totalSeconds    += getIntValue(digitArray[0]) * 60;
        totalSeconds    += getIntValue(digitArray[1]);
    }


    // all 3 values
    else if (timeFormat.indexOf('H') > -1 && timeFormat.indexOf('M') > -1 && timeFormat.indexOf('S') > -1) {
        totalSeconds    += getIntValue(digitArray[0]) * 60 * 60;
        totalSeconds    += getIntValue(digitArray[1]) * 60;
        totalSeconds    += getIntValue(digitArray[2]);
    }

//  console.log(timeFormat, totalSeconds);

    return totalSeconds;
}
function getIntValue(value) {
    if (value === null) {
        return 0;
    }

    else {

        var intValue = 0;
        try {
            intValue        = parseInt(value);
            if (isNaN(intValue)) {
                intValue    = 0;
            }
        } catch (ex) { }

        return Math.floor(intValue);
    }
}


回答9:

Python

It works by parsing the input string 1 character at a time, if the character is numerical it simply adds it (string add, not mathematical add) to the current value being parsed. If it is one of 'wdhms' the current value is assigned to the appropriate variable (week, day, hour, minute, second), and value is then reset ready to take the next value. Finally it sum the number of seconds from the 5 parsed values.

def ytDurationToSeconds(duration): #eg P1W2DT6H21M32S
    week = 0
    day  = 0
    hour = 0
    min  = 0
    sec  = 0

    duration = duration.lower()

    value = ''
    for c in duration:
        if c.isdigit():
            value += c
            continue

        elif c == 'p':
            pass
        elif c == 't':
            pass
        elif c == 'w':
            week = int(value) * 604800
        elif c == 'd':
            day = int(value)  * 86400
        elif c == 'h':
            hour = int(value) * 3600
        elif c == 'm':
            min = int(value)  * 60
        elif c == 's':
            sec = int(value)

        value = ''

    return week + day + hour + min + sec


回答10:

This is not java specific, but i would like to add JAVA snippet as that may helpful to other users

String duration = "PT1H23M45S";
Pattern pattern = Pattern.compile("PT(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?");
Matcher matcher = pattern.matcher(duration);
long sec = 0;
long min = 0;
long hour = 0;
if (matcher.find())
{
    if(matcher.group(1)!=null)
        hour = NumberUtils.toInt(matcher.group(1));
    if(matcher.group(2)!=null)
        min = NumberUtils.toInt(matcher.group(2));
    if(matcher.group(3)!=null)
        sec = NumberUtils.toInt(matcher.group(3));

}
long totalSec = (hour*3600)+(min*60)+sec;
System.out.println(totalSec);


回答11:

Assuming the input is valid, we can use the regex exec method to iterate on the string and extract the group sequentially:

const YOUTUBE_TIME_RE = /(\d+)([HMS])/g;
const YOUTUBE_TIME_UNITS = {
    'H': 3600,
    'M': 60,
    'S': 1
}

/**
 * Returns the # of seconds in a youtube time string
 */
function parseYoutubeDate(date: string): number {
    let ret = 0;
    let match: RegExpExecArray;
    while (match = YOUTUBE_TIME_RE.exec(date)) {
        ret += (YOUTUBE_TIME_UNITS[match[2]]) * Number(match[1]);
    }
    return ret;
}