2013-05-24 12 views
6

Per motivi di interesse, desidero convertire le durate video da YouTubes ISO 8601 in secondi. A prova di futuro la mia soluzione, ho scelto a really long video per testarlo contro.Come convertire la durata dell'API di YouTube in secondi?

L'API fornisce questo per la sua durata - "duration": "P1W2DT6H21M32S"

ho provato l'analisi di questo periodo con dateutil come suggerito in stackoverflow.com/questions/969285.

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

Questo genera un'eccezione

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

Che cosa mi manca?

risposta

13

Il modulo dateutil incorporato in Python supporta solo l'analisi delle date ISO 8601, non delle durate ISO 8601. A tale scopo, è possibile utilizzare la libreria "isodate" (in pypi allo https://pypi.python.org/pypi/isodate - installare tramite pip o easy_install). Questa libreria ha il pieno supporto per le durate ISO 8601, convertendole in oggetti datetime.timedelta. Quindi, una volta importata la libreria, è semplice come:

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

wow, considerando che ho scritto il mio parser, questo rende tutto così semplice :) grazie! –

+0

@MorganWilde Una delle grandi cose di Python è che di solito è possibile trovare una soluzione esistente, se non nella libreria standard, quindi su pypi. È buona norma cercare di evitare l'implementazione di qualsiasi cosa se esiste già una soluzione (in qualsiasi lingua, non solo in python). – zstewart

1

Il video non è 1 settimana, 2 giorni, 6 ore e 21 minuti e 32 secondi?

Youtube lo mostra come 222 ore 21 minuti e 17 secondi; 1 * 7 * 24 + 2 * 24 + 6 = 222. Non so da dove arriva la discrepanza di 17 secondi contro 32 secondi; può anche essere un errore di arrotondamento.

A mio parere, scrivere un parser non è difficile. Sfortunatamente, dateutil non sembra analizzare gli intervalli, ma solo i punti datetime.

Aggiornamento:

vedo che c'è un pacchetto per questo, ma solo come un esempio di potere regexp, brevità, e la sintassi incomprensibile, ecco un parser per voi:

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 deliberatamente non sto calcolando il numero esatto di secondi da questi dati qui. Sembra banale (vedi sopra), ma in realtà non lo è. Ad esempio, la distanza di 2 mesi dal 1 ° gennaio è di 58 giorni (30 + 28) o 59 (30 + 29), a seconda dell'anno, mentre dal 1 ° marzo è sempre 61 giorni. Una corretta implementazione del calendario dovrebbe tenere conto di tutto ciò; per un calcolo della lunghezza della clip di YouTube, deve essere eccessivo.

+0

sembra che sia la soluzione migliore finora :) –

+0

Scopri quanto orribile la mia soluzione è ...: D La brevità sfugge certamente il mio ingegno ... –

+0

@MorganWilde: bene, controlla miniera nel risposta aggiornata Non scrivo sempre gli automi finiti, ma quando lo faccio, cerco di usare un linguaggio specifico per il dominio :) – 9000

0

Quindi questo è ciò che mi si avvicinò con - un parser personalizzato per interpretare il tempo:

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 

Ora probabilmente è super non fresco e così via, ma funziona, quindi sto condividendo perché Mi importa di te gente.

4

Opere su python 2.7+. Adottato da 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 

Maniglie formato di durata iso8061 a misura Youtube Utilizza fino a ore

1

Questo funziona analizzando l'ingresso stringa 1 carattere per volta, se il carattere è numerico semplicemente aggiunge (stringa aggiunge, non matematico aggiungi) al valore corrente che viene analizzato. Se è uno dei 'wdhms', il valore corrente viene assegnato alla variabile appropriata (settimana, giorno, ora, minuto, secondo), e il valore viene quindi resettato pronto a prendere il valore successivo. Infine somma il numero di secondi tra i 5 valori analizzati.

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 
Problemi correlati