Преобразование 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". Кто-нибудь имеет лучшую идею?
Ответы
Ответ 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;