ts2mkv.py

Fri, 24 Nov 2017 16:16:50 +0100

author
mdd
date
Fri, 24 Nov 2017 16:16:50 +0100
changeset 8
92409c985e0d
parent 7
0d021d47eca5
child 9
1bf778001041
permissions
-rw-r--r--

added autocrop and 720p rescaler

#!/usr/bin/env python

import subprocess
from eit import readeit
import os, shlex

def filter_lines(data, search):
    ret = []
    for line in data.split("\n"):
        if line.find(search) == -1:
            continue
        ret.append(line)
    return "\n".join(ret)

def run_command(command):
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
    while True:
        output = process.stdout.readline()
        if output == '' and process.poll() is not None:
            break
        if output:
            print output.strip()
    rc = process.poll()
    return rc

class ts2mkv(object):
    def __init__(self):
        self.msg_prepare = ""
        self.msg_eit = ""
        self.msg_ffmpeg = ""
        self.command = None

        self.scaleto_720p = True

        self.video_options = [
            "-c:v libx264",
            "-preset faster", # slow
            "-tune film", # film / animation
            "-crf 19" # 21, better 19
        ]
        self.audio_options = [
            "-c:a copy",
        ]

    def get_stream_index(self, data):
        idx = data.find("Stream #")
        if idx == -1:
            return ""
        idx += 8
        self.msg_prepare += "Selecting: %s\n" % data
        return data[idx:idx+3]

    def get_movie_description(self, filename):
        # read the EIT file
        # TODO: fallback to meta file if no EIT
        # TODO: is there a way to get the imdb for the movie automagically?
        # http://www.omdbapi.com/apikey.aspx
        self.msg_eit = readeit(os.path.splitext(filename)[0] + ".eit")

    def get_crop_option(self):
        lines = filter_lines(self.msg_ffmpeg, "[Parsed_cropdetect").split("\n")
        option = None
        for line in lines:
            tmp = line[line.find(" crop="):].strip()
            #print "DEBUG: " + tmp
            if not option:
                option = tmp
            else:
                if option != tmp:
                    self.msg_prepare += "WARNING: cropdetect inconsistent over scan time, disabling autocrop\n"
                    return None
        self.msg_prepare += "Crop detected: %s\n" % option
        return option

    def get_ffmpeg_command(self, filename):
        commands = []
        fn = "\\'".join(p for p in filename.split("'"))

        #  ffmpeg -ss 00:05:00 -t 2 -i testfiles/chappie.ts -vf "cropdetect=24:16:0" -f null -

        cmd = ["ffmpeg",
                "-ss 00:05:00", "-t 1", # search to 5 minutes, analyze 1 second
                "-i %s" % fn,
                "-vf \"cropdetect=24:2:0\"", # detect black bar crop on top and bottom
                "-f null", "-" # no output file
                ]
        print " ".join(cmd)
        p = subprocess.Popen(shlex.split(" ".join(cmd)), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out, err = p.communicate()
        print "Command return code: ", p.poll()
        self.msg_ffmpeg = out + "\n" + err
        self.msg_ffmpeg = self.msg_ffmpeg[self.msg_ffmpeg.find("Input #0"):]

        # find "Stream #0:" lines
        info = filter_lines(self.msg_ffmpeg, "Stream #0:")

        fn = fn.replace(" ", "\\ ")

        # CRAP! ffmpeg cannot decode dvb_teletext streams to srt :(
        # are there any subtitle streams?!
        #s = filter_lines(info, "Subtitle:")
        #s = filter_lines(s, "(deu):")
        #if s != "":
        #    s = self.get_stream_index(s.split("\n")[0])
        #    commands.append([
        #        "ffmpeg",
        #        "-txt_format text",
        #        "-i %s" % fn,
        #        "-map %s" % s,
        #        "%s.srt" % os.path.splitext(fn)[0]
        #        ])

        v = self.get_stream_index(
            filter_lines(info, "Video:"))
        if v == "":
            print "No video stream found"
            return None

        audioall = filter_lines(info, "Audio:")
        audio = filter_lines(audioall, "(deu):")
        a = self.get_stream_index(
            filter_lines(audio, "ac3"))
        # TODO: wenn kein ac3 stream dann dts oder mpeg fallback
        audiomap = [a]
        if a == "":
            print "No suitable german audio stream found"
            return None

        audio = filter_lines(audioall, "(eng):")
        a = self.get_stream_index(
            filter_lines(audio, "ac3"))
        if a != "":
            # append english audio too!
            audiomap.append(a)



        self.msg_prepare += "Video Stream selected: Stream #%s\n" % v
        cmd = [
            "ffmpeg",
            "-i %s" % fn,
            "-map %s" % v,
            ]
        flt = []
        crop = self.get_crop_option()
        if crop:
            flt.append(crop)
        if self.scaleto_720p:
            flt.append("scale='min(1280,iw)':-2'") # -2 ensures division by two for codec
            self.msg_prepare += "Scaling cropped output stream to 720p\n"
        if len(flt) > 0:
            # append video filters
            cmd.append('-filter:v "%s"' % ",".join(flt))
        for a in audiomap:
            self.msg_prepare += "Audio Stream selected: Stream #%s\n" % a
            cmd.append("-map %s" % a)
        cmd.extend(self.video_options)
        cmd.extend(self.audio_options)
        cmd.append(os.path.splitext(fn)[0] + ".mkv")

        commands.append(" ".join(cmd))
        return commands

    def load(self, filename):
        self.filename = filename
        self.get_movie_description(filename)
        self.command = self.get_ffmpeg_command(filename)

if __name__ == "__main__":
    import sys
    os.system('cls' if os.name == 'nt' else 'clear')

    # TODO: get file from commandline
    #filename = "testfiles/chappie.ts"
    filename = "/srv/storage0/DREAMBOX/Science Fiction/THE_ISLAND.ts"

    mkv = ts2mkv()
    mkv.load(filename)
    if mkv.command:
        fd = open(os.path.splitext(filename)[0] + ".txt", "wb")
        fd.write(mkv.msg_eit)
        fd.write("\n\n# ---DEBUG---\n\n")
        fd.write(mkv.msg_prepare)
        fd.write(mkv.msg_ffmpeg)
        fd.close()
        print mkv.msg_ffmpeg

        for cmd in mkv.command:
            print "Executing ffmpeg:\n%s\n" % cmd
            run_command(cmd)

mercurial