Saturday, May 30, 2009

edl2srt.py

tried make subtitle using mplayer. thankfully, mplayer has -edlfile option. so, using that, I can cue when subtitle should change.

given the following:

subtitle.txt

hello!
how are you?
bye

I can play mplayer -edlfile subtitle.edl movie.avi . and as I play the movie, I press i to cue for new subtitle text from subtitle.txt. Then,

python edl2srt.py subtitle.edl -t subtitle.txt > movie.srt
mplayer movie.avi -sub movie.srt

will play movie.avi with subtitle.

You can get comma separated cue seconds (seconds when i was pressed):

python edl2srt.py subtitle.edl

EDL to srt script is here: It needs optfunc module.

#!/usr/bin/python

# LICENSE: public domain.

"""converts edl file to srt.
EDL (Edit Decision List) file: http://www.mplayerhq.hu/DOCS/HTML/en/edl.html
SRT file: http://forum.doom9.org/archive/index.php/t-73953.html

requires optfunc found at: http://github.com/simonw/optfunc/tree/master"""

def split_float(f):
    i = int(f)
    return i, f - i
def split_float_str(fstr):
    l = fstr.split('.')
    return l[0], l[1]

def sec_to_hour_min_sec(seconds):
    m,sec = divmod(seconds, 60)
    hour,min = divmod(m, 60)
    return (hour,min,sec)

def timestamp(seconds):
    f = float(seconds)
    i,frac = split_float(f)
    hour,min,sec = sec_to_hour_min_sec(i)
    return (hour, min, sec, int(frac*100))

def to_timestamp(seconds, format="%(hour)s:%(min)s:%(sec)s,%(millisec)s"):
    hour,min,sec,millisec = timestamp(seconds)
    return format % {"hour":hour, "min":min, "sec":sec, "millisec":millisec}

class ED(object):
    "edit decision"
    def __init__(self, sec_from=0.0, sec_to=0.0, flag=0):
        self.sec_from = float(sec_from)
        self.sec_to = float(sec_to)
        self.flag = int(flag)

    def to_tuple(self):
        return (self.sec_from, self.sec_to, self.flag)

    def to_pair(self):
        return (self.sec_from, self.sec_to)

class EDL(object):
    def __init__(self, filename=None):
        self.edl = self.read_edl(filename) if filename else []

    def read_edl(self, filename):
        f = open(filename, "r")
        l = []
        for line in f:
            x = line.split()
            l.append(ED(x[0], x[1], x[2]))
        return l


    def seconds(self):
        l = []
        for ed in self.edl:
            l.extend([ed.sec_from, ed.sec_to])
        return l

    def second_pairs(self):
        l = []
        for x in self.edl:
            l.append(x.to_pair())
        return l

class Text(object):
    def __init__(self, filename=None):
        self.text = self.read_text(filename) if filename else []

    def __len__(self):
        return len(self.text)

    def __getitem__(self, key):
        return self.text[key]

    def __str__(self):
        return '\n'.join(self.text)

    def read_text(self, filename):
        f = open(filename, "r")
        l = []
        for line in f:
            x = line.strip()
            if x:
                l.append(x)
        f.close()
        return l

class Subtitle(object):
    def __init__(self, index=0, time_from=0, time_to=0, text=""):
        self.index = index
        self.time_from = to_timestamp(time_from)
        self.time_to = to_timestamp(time_to)
        self.text = text

    def __str__(self):
        return """%u
%s --> %s
%s""" % (self.index, self.time_from, self.time_to, self.text)


def insert_end(seconds, limit=3.0, gap=0.001):
    def delta(x,y):
        d = y - x
        return limit if d > limit else d
    return [(x, x + delta(x,y) - gap) for x,y in zip(seconds, seconds[1:])]

def make_srt(edl, text):
    index = 1
    sec_pairs = insert_end(edl.seconds())

    srt = []
    for i,(sec_from,sec_to) in enumerate(sec_pairs):
        t = text[i] if len(text) > i else None
        srt.append(Subtitle(i+1, sec_from, sec_to, t))
    return srt

def edl2srt(edlname, txtname=None):
    """Usage: %prog edlfile [-t textfile]
    edlfile specification is at:
        http://www.mplayerhq.hu/DOCS/HTML/en/edl.html
    textfile should contain subtitles delimited by new line.

Example
    mplayer a.avi -edlfile a.edl
        during movie play, press i to insert a cue to change subtitle
        defined in a.txt
    %prog a.edl -t a.txt > a.srt
    mplayer a.avi -sub a.srt"""
    edl = EDL(edlname)
    if txtname is None:
        print(','.join([str(x) for x in edl.seconds()]))
    else:
        txt = Text(txtname)
        print '\n\n'.join([str(x) for x in make_srt(edl, txt)])

if __name__ == "__main__":
    import optfunc
    optfunc.run(edl2srt)

Wednesday, May 6, 2009

fundep

FunctionalDependencies example:

> {-# LANGUAGE GeneralizedNewtypeDeriving
>     , FunctionalDependencies
>     , MultiParamTypeClasses #-}

> module Test where

> import Control.Monad.State
> import Control.Monad.Identity

> class (Monad m) => MonadFoo a m | m -> a where
> -- class (Monad m) => MonadFoo a m  where
>     -- save :: m [a]
>     save :: m ()

save has type (MonadFoo a m) => m () . And, although Foo (see below) is an instance of MonadFoo Char, GHC complains that there is No instance for (MonadFoo a Foo) arising from a use of 'save', if functional dependencies isn't used (see commented out code above).

One way to get around is to make save :: m [a] and explicitly annotate each usage of save with :: Foo String (see commented out code of action).

But, using functional dependencies, one can say that m uniquely identifies a. So, GHC will instantiate a with Char on Foo's save, since Foo is an instance of MonadFoo Char.

>     restore :: m [a]
>     push :: a -> m ()

> newtype FooT m a = FooT {
>     runFooT :: (StateT (String, Int) m) a
>     } deriving (Monad, Functor, MonadState (String, Int))

> instance (Monad m) => MonadFoo Char (FooT m) where
>     save = do
>         (current, _) <- get
>         put (current, 0)
>         -- return current
>     restore = do
>         (current, n) <- get
>         put (drop n current, 0)
>         return $ take n current
>     push x = do
>         (current, n) <- get
>         put (x : current, n + 1)

> newtype Foo a = Foo {
>     runFoo :: FooT Identity a
>     } deriving (Monad, Functor
>         , MonadState (String, Int), MonadFoo Char)

> action :: Foo String
> action = do
>     push 'a'
>     save
>     -- save :: Foo String
>     push 'b'
>     push 'c'
>     restore
> test = runIdentity $ runStateT (runFooT (runFoo action)) ("", 0)