2013-05-24 15 views
6

Ze względu na zainteresowanie chcę przekształcić czas trwania wideo z YouTubes ISO 8601 na sekundy. Aby w przyszłości sprawdzić moje rozwiązanie, wybrałem a really long video, aby przetestować.Jak przekonwertować czas trwania interfejsu API YouTube na sekundy?

API zapewnia to dla jego trwania - "duration": "P1W2DT6H21M32S"

próbowałem parsowania ten czas z dateutil jak zasugerowano w stackoverflow.com/questions/969285.

import dateutil.parser 
duration = = dateutil.parser.parse('P1W2DT6H21M32S') 

ta zgłasza wyjątek

TypeError: unsupported operand type(s) for +=: 'NoneType' and 'int' 

Czego mi brakuje?

Odpowiedz

13

Wbudowany w Pythonie moduł dateutil obsługuje tylko daty ISO 8601, a nie czasy ISO 8601. Do tego celu możesz użyć biblioteki "isodate" (w pypi pod https://pypi.python.org/pypi/isodate - zainstaluj przez pip lub easy_install). Ta biblioteka ma pełne wsparcie dla czasu trwania ISO 8601, konwertując je na obiekty datetime.timedelta. Tak więc po zaimportowaniu biblioteki jest to tak proste, jak:

dur=isodate.parse_duration('P1W2DT6H21M32S') 
print dur.total_seconds() 
+0

wow, biorąc pod uwagę, że napisałem własny parser, to po prostu sprawia, że ​​wszystko wydaje się takie łatwe :) dzięki! –

+0

@MorganWilde Jedną z największych zalet Pythona jest to, że zazwyczaj można znaleźć istniejące rozwiązanie, jeśli nie w standardowej bibliotece, a następnie na pypi. Dobrą praktyką jest unikanie implementacji czegokolwiek, jeśli rozwiązanie już istnieje (w dowolnym języku, a nie tylko pytonie). – zstewart

1

Czy wideo nie trwa 1 tydzień, 2 dni, 6 godzin i 21 minut i 32 sekundy?

Youtube pokazuje to jako 222 godziny 21 minut 17 sekund; 1 * 7 * 24 + 2 * 24 + 6 = 222. Nie wiem, skąd pochodzi różnica 17 sekund do 32 sekund; może również być błędem zaokrąglania.

Moim zdaniem, pisanie do tego parsera nie jest takie trudne. Niestety, dateutil nie wydaje się analizować interwałów, tylko punkty datetime.

Aktualizacja:

widzę, że jest to pakiet do tego, ale tylko jako przykład regexp władzy, zwięzłości i składni niezrozumiały, oto parser dla Ciebie:

import re 

# see http://en.wikipedia.org/wiki/ISO_8601#Durations 
ISO_8601_period_rx = re.compile(
    'P' # designates a period 
    '(?:(?P<years>\d+)Y)?' # years 
    '(?:(?P<months>\d+)M)?' # months 
    '(?:(?P<weeks>\d+)W)?' # weeks 
    '(?:(?P<days>\d+)D)?' # days 
    '(?:T' # time part must begin with a T 
    '(?:(?P<hours>\d+)H)?' # hourss 
    '(?:(?P<minutes>\d+)M)?' # minutes 
    '(?:(?P<seconds>\d+)S)?' # seconds 
    ')?' # end of time part 
) 


from pprint import pprint 
pprint(ISO_8601_period_rx.match('P1W2DT6H21M32S').groupdict()) 

# {'days': '2', 
# 'hours': '6', 
# 'minutes': '21', 
# 'months': None, 
# 'seconds': '32', 
# 'weeks': '1', 
# 'years': None} 

I celowo nie liczę dokładnej liczby sekund z tych danych tutaj. Wygląda to banalnie (patrz wyżej), ale tak naprawdę nie jest. Na przykład odległość 2 miesięcy od 1 stycznia wynosi 58 dni (30 + 28) lub 59 (30 + 29), w zależności od roku, natomiast od 1 marca zawsze wynosi 61 dni. Właściwa implementacja kalendarza powinna wziąć to wszystko pod uwagę; do obliczenia długości klipu YouTube, musi być nadmierny.

+0

wydaje się, że jest to najlepsze rozwiązanie tak daleko :) –

+0

Sprawdź, jak straszne moje rozwiązanie jest ...: D Zwięzłość pewno ucieka moje zmysły ... –

+0

@MorganWilde: dobrze, sprawdź w kopalni zaktualizowana odpowiedź. Nie zawsze piszę skończone automaty, ale kiedy to robię, staram się używać dobrze znanego języka specyficznego dla domeny :) – 9000

0

Więc to co wymyśliłem - niestandardowego parsera interpretować czas:

def durationToSeconds(duration): 
    """ 
    duration - ISO 8601 time format 
    examples : 
     'P1W2DT6H21M32S' - 1 week, 2 days, 6 hours, 21 mins, 32 secs, 
     'PT7M15S' - 7 mins, 15 secs 
    """ 
    split = duration.split('T') 
    period = split[0] 
    time = split[1] 
    timeD = {} 

    # days & weeks 
    if len(period) > 1: 
     timeD['days'] = int(period[-2:-1]) 
    if len(period) > 3: 
     timeD['weeks'] = int(period[:-3].replace('P', '')) 

    # hours, minutes & seconds 
    if len(time.split('H')) > 1: 
     timeD['hours'] = int(time.split('H')[0]) 
     time = time.split('H')[1] 
    if len(time.split('M')) > 1: 
     timeD['minutes'] = int(time.split('M')[0]) 
     time = time.split('M')[1]  
    if len(time.split('S')) > 1: 
     timeD['seconds'] = int(time.split('S')[0]) 

    # convert to seconds 
    timeS = timeD.get('weeks', 0) * (7*24*60*60) + \ 
      timeD.get('days', 0) * (24*60*60) + \ 
      timeD.get('hours', 0) * (60*60) + \ 
      timeD.get('minutes', 0) * (60) + \ 
      timeD.get('seconds', 0) 

    return timeS 

Teraz to chyba nie jest super-cool i tak dalej, ale to działa, więc dzielę ponieważ Dbam o twoich ludzi.

4

Działa na pytonie 2.7+. Przyjęte z JavaScript one-liner for Youtube v3 question here.

import re 

def YTDurationToSeconds(duration): 
    match = re.match('PT(\d+H)?(\d+M)?(\d+S)?', duration).groups() 
    hours = _js_parseInt(match[0]) if match[0] else 0 
    minutes = _js_parseInt(match[1]) if match[1] else 0 
    seconds = _js_parseInt(match[2]) if match[2] else 0 
    return hours * 3600 + minutes * 60 + seconds 

# js-like parseInt 
# https://gist.github.com/douglasmiranda/2174255 
def _js_parseInt(string): 
    return int(''.join([x for x in string if x.isdigit()])) 

# example output 
YTDurationToSeconds(u'PT15M33S') 
# 933 

Uchwyty iso8061 formatu czas trwania do stopnia Youtube Wykorzystuje do godziny

1

Działa to przez parsowania wejściowy ciąg 1 znak w czasie, gdy postać jest numeryczna to po prostu dodaje go (string dodać, nie matematyczny add) do aktualnie analizowanej wartości. Jeśli jest to jeden z "wdhms", aktualna wartość jest przypisana do odpowiedniej zmiennej (tydzień, dzień, godzina, minuta, sekunda), a następnie wartość jest resetowana gotowa do podjęcia następnej wartości. W końcu to suma liczba sekund z 5 parsowanych wartości.

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 
Powiązane problemy