Source

mudo / legendas.py

Full commit
dani...@fastmail… 6f20859 




















































































































































































# coding: utf-8

import sys, os, subprocess, shutil

import re

re_tags = re.compile('<[^>]*>')
re_separador = re.compile("\r?\n\r?\n")
re_tempo = re.compile('(\d+):(\d+):(\d+),(\d+)')

MAX_ALTURA= 4 # numero maximo de linhas por ecran
MAX_LARGURA = 30 # tamanho maximo duma linha em caracteres

FONTE = "Times-Roman"
FONTSIZE = 40
SEPARACAO = 10 # pixeis

WIDTH=None
HEIGHT=None

# ito vou receber como argumentos do programa:
FRAMERATE=None # frames por segundo
DURACAO_LEGENDA_FIXO = 1 # segundos
DURACAO_LEGENDA_POR_LINHA = 0.5

def erro(mensagem):
    print mensagem
    sys.exit(1)

# converte uma string do gênero  "01:21:17,000" para um valor double de segundos
def parse_tempo(str):
    match = re_tempo.search(str.strip()) or erro("tempo errado "+str)
    (h,m,s,ms) = match.groups()
    return 3600 * int(h) + 60 * int(m) + int(s) + float(ms) / 1000

# faz o parse do ficheiro indicado e retorna uma lista de tuplos (tempo_inicial, tempo_final, texto_legenda)
# Conidera que é um ficheiro tipo "srt" que tem vários registos separados por uma linha vazia
# Cada registo é multilinha. A primeira tem um número sequencial, a segundo o intervalo de tempo, e as restantes o texto da legenda
# 1044
# 01:17:58,199 --> 01:18:03,204
# If you ever want a consult...
# Just, uh, give me a call.
def parse_legendas(file):
    f = open(file, "r")
    tudo = f.read()

    # os registos, que são separados por uma linha vazia
    partes = re_separador.split(tudo.strip())

    last_id = -1

    resultado = []

    for parte in partes:

        linhas = parte.split("\n")
        len(linhas) < 3 and erro("erro na parte "+parte)

        id = int(linhas[0])

        if last_id != -1 and id != last_id + 1:
            erro("erro na sequencia de ids "+str(id))

        last_id = id

        linha = linhas[1]
        separador = "-->"
        p = linha.find(separador)
        if p == -1: 
            erro("erro na linha do tempo "+linha[1])

        inicio = parse_tempo(linha[0:p])
        fim = parse_tempo(linha[p+ len(separador) : ])

        texto = "\n".join(linhas[2 :])

        resultado.append( (inicio, fim, texto) )
        #print id,inicio, fim, texto

    return resultado

# cria uma imagem com as linhas de legenda, e grava no ficheiro indicado
def cria_imagem_legenda(linhas, ficheiro):
    y= (HEIGHT - len(linhas) * FONTSIZE - (len(linhas)-1) * SEPARACAO) / 2

    # crio a partir do ficheiro que tem a moldura:
    cmd = ["convert", "moldura_base.png", "-font", FONTE, "-weight", "2", "-pointsize", str(FONTSIZE), "-fill", "white"]

    for linha in linhas:
        cmd = cmd + ["-draw", "gravity North text 0,"+str(y)+" '"+linha.replace("'","\\'")+"'"]

        y += FONTSIZE + SEPARACAO

    # o ficheiro criado deve ser grayscale, sem alpha channel... par ficar equivalente aos outros frames do filme

    # crio primeiro um jpg para ficar sem transparencia...
    ficheiro_aux = ficheiro + ".jpg"
    cmd = cmd + ["-blur", "5", "-blur", "5", ficheiro_aux]
    subprocess.Popen(cmd).wait()

    # e depois converto no ficheiro pedido (que deve ser um png...)
    subprocess.Popen(["convert", ficheiro_aux,"-type","Grayscale", ficheiro]).wait()
    os.remove(ficheiro_aux)

    
# Cria uma série de frames para mostrar uma legenda. O tempo que a legenda vai ficar depende
# do número de linhas que ela tem.
#
# Parametros:
# frame é o nr do frame do filme original onde quero inserir a legenda
#  texto é uma string com o texto completo da legenda. Esse texto pode ser dividido em varios se for grande de mais!
# legendas_dir é a pasta onde vou guardar os frames criados, que vão ter o formato "frame-numero.png" em que frame é o valor que recebi, e numero é um número sequencial
#
def cria_legenda(frame, texto, legendas_dir):
    linhas = []

    # remove tags, como "<i>"
    texto = re_tags.sub("", texto)

    # separa as linhas mais compridas que max_largura
    for linha in texto.split("\n"):
        linha = linha.strip()
        while len(linha) > MAX_LARGURA:
            p = linha.rfind(" ", 0, MAX_LARGURA)
            if p == -1: 
                p = MAX_LARGURA

            linhas.append(linha[0:p])
            linha = linha[p:].strip()
        linhas.append(linha)

    conta = 0
    while linhas:
        firstf = legendas_dir + "/%010i-%06i.png" % (frame, conta)
        conta +=1

        duracao = DURACAO_LEGENDA_FIXO + len(linhas[0: MAX_ALTURA]) * DURACAO_LEGENDA_POR_LINHA

        # o numero de frames que vai durar:
        frames_legenda = int(duracao* FRAMERATE)

        # cria o primeiro ficheiro
        cria_imagem_legenda(linhas[0: MAX_ALTURA], firstf)

        # se o numero de linhas for grande demais, divido em partes!
        linhas=linhas[MAX_ALTURA:]

        # os restantes frames são iguais ao primeiro portanto uso sym links
        for i in range(1, frames_legenda):
            f = legendas_dir + "/%010i-%06i.png" % (frame, conta)
            conta +=1

            subprocess.Popen(["ln", "-sf", os.path.basename(firstf), f]).wait()
            #shutil.copy(firstf, f)

if __name__ == '__main__':

    # teste:
    # printf "s'dkf jsdf dsjklf sdgla\nadeus"| python ../legendas.py 12 5 624 352
    #    frame = int(sys.argv[1])
    #    FRAMERATE = int(sys.argv[2])
    #    WIDTH = int(sys.argv[3])
    #    HEIGHT = int(sys.argv[4])
    #    texto = sys.stdin.read().strip()
    #    cria_legenda(frame, texto, "frames_legendas")

    FRAMERATE = int(sys.argv[1])
    WIDTH = int(sys.argv[2])
    HEIGHT = int(sys.argv[3])
    ficheiro_legendas = sys.argv[4]

    legendas = parse_legendas(ficheiro_legendas)
    for legenda in legendas:
        inicio, fim, texto = legenda

        meio = (inicio + fim )/2
        frame = int(meio * FRAMERATE)

        if frame > 71832:
            print "frame",frame,"texto","("+texto+")"

            cria_legenda(frame, texto, "frames_legendas")