Подтвердить что ты не робот

Преобразование Youtube Data API V3 в формат длительности видео в секундах в JavaScript/Node.js

Я пытаюсь преобразовать строку ISO 8601 в несколько секунд в JS/ Node. Лучшее, что я мог придумать, было:

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
}

Он работает, когда я ввожу строки, такие как "PT48S", "PT3M20S" или "PT3H2M31S", но терпит неудачу, если строка "PT1H11S". Кто-нибудь имеет лучшую идею?

4b9b3361

Ответ 1

Я предлагаю этот небольшой взлом, чтобы предотвратить ваш проблемный случай:

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

Если вы используете moment.js, вы можете просто позвонить...

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

= 933000 мс

Ответ 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;
}

работает для этих случаев:

PT1H
PT23M
PT45S
PT1H23M
PT1H45S
PT23M45S
PT1H23M45S

Ответ 4

Здесь мое решение:

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

Мое решение:

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

Я написал вариант CoffeeScript (вы можете легко скомпилировать его на coffeescript.org по желанию)

РАЗЛИЧИЯ: продолжительность возвращения приходит в формате, читаемом человеком (например, 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

Я также реализовал эту вспомогательную функцию для заполнения < 10 значений с начальным нулем:

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

3nj0y!!!

Ответ 7

Вы можете найти очень простое PHP-решение здесь - Как конвертировать Время API Youtube (длительность видео по ISO 8601) до секунд в PHP-коде

Эта функция convert_time() принимает один параметр в качестве входного сигнала - время API Youtube (продолжительность видео), которое находится в формате строки ISO 8601 и возвращает его продолжительность в секундах.

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);
}

Тестирование некоторых входов:

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

Ответ 8

Я столкнулся с проблемами с вышеупомянутым решением. Я решил написать это как можно более тупым. Я также использую свой собственный "getIntValue" вместо parseInt для дополнительного удобства.

Просто подумал, что другие поиски могут оценить обновление.

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

Он работает путем анализа входной строки 1 символа за раз, если символ является численным, он просто добавляет его (добавление строки, а не математическое добавление) в текущее значение, которое анализируется. Если это одно из "wdhms", текущее значение присваивается соответствующей переменной (неделя, день, час, минута, секунда), а затем значение reset готово к следующему значению. Наконец, он суммирует количество секунд из 5 проанализированных значений.

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

Это не спецификация java, но я хотел бы добавить фрагмент JAVA, который может помочь другим пользователям.

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

Предполагая, что ввод действителен, мы можем использовать метод regex exec для итерации по строке и последовательного извлечения группы:

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;
}

Ответ 12

Я понимаю, что Eval непопулярен, но здесь самый простой и быстрый подход, который я могу себе представить. Наслаждаться.

function formatDuration(x) {
   return eval(x.replace('PT','').replace('H','*3600+').replace('M','*60+').replace('S', '+').slice(0, -1));
}

Ответ 13

Здесь @redgetan решение в ES6.

Я также исправил это на годы, недели и дни.

https://www.digi.com/resources/documentation/digidocs/90001437-13/reference/r_iso_8601_duration_format.htm

// Copied from:
// https://stackoverflow.com/questions/22148885/converting-youtube-data-api-v3-video-duration-format-to-seconds-in-javascript-no
function parseISO8601Duration(duration) {
    const match = duration.match(/P(\d+Y)?(\d+W)?(\d+D)?T(\d+H)?(\d+M)?(\d+S)?/)
    // An invalid case won't crash the app.
    if (!match) {
        console.error('Invalid YouTube video duration: ${duration}')
        return 0
    }
    const [
        years,
        weeks,
        days,
        hours,
        minutes,
        seconds
    ] = match.slice(1).map(_ => _ ? parseInt(_.replace(/\D/, '')) : 0)
  return (((years * 365 + weeks * 7 + days) * 24 + hours) * 60 + minutes) * 60 + seconds
}

if (parseISO8601Duration('PT1H') !== 3600) {
    throw new Error()
}

if (parseISO8601Duration('PT23M') !== 1380) {
    throw new Error()
}

if (parseISO8601Duration('PT45S') !== 45) {
    throw new Error()
}

if (parseISO8601Duration('PT1H23M') !== 4980) {
    throw new Error()
}

if (parseISO8601Duration('PT1H45S') !== 3645) {
    throw new Error()
}

if (parseISO8601Duration('PT1H23M45S') !== 5025) {
    throw new Error()
}

if (parseISO8601Duration('P43W5DT5M54S') !== 26438754) {
    throw new Error()
}

if (parseISO8601Duration('P1Y43W5DT5M54S') !== 57974754) {
    throw new Error()
}

Ответ 14

Я думаю, что использование moment.js будет более простым решением. Но если кто-то ищет нестандартное решение, вот вам простое регулярное выражение:

var regex = /PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/;
var regex_result = regex.exec("PT1H11S"); //Can be anything like PT2M23S / PT2M / PT28S / PT5H22M31S / PT3H/ PT1H6M /PT1H6S
var hours = parseInt(regex_result[1] || 0);
var minutes = parseInt(regex_result[2] || 0);
var seconds = parseInt(regex_result[3] || 0);
var total_seconds = hours * 60 * 60 + minutes * 60 + seconds;