#!/usr/bin/env python2
"""
Lossless video cutting tool (using FFmpeg).
"""
import sys, os, subprocess, random, shutil, argparse

def timecode(s):
    parts = map(str.strip, s.split(':', 2))
    t = 0.0
    for s in parts:
        t = t * 60 + float(s)
    return t

def t2s(t):
    if abs(t - int(t)) < 0.01:
        return str(int(t))
    if abs(t * 10 - int(t * 10)) < 0.01:
        return "%.1f" % t
    return str(t)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("infile", metavar="<INFILE>",
                        help="input video file")
    parser.add_argument("outfile", metavar="<OUTFILE>", nargs='?',
                        help="output file name [default: <input>_cut.<ext>]")
    parser.add_argument("-A", "--start", metavar="TIME", type=timecode,
                        help="set cut in  point [H:MM:SS, M:SS, or seconds]")
    parser.add_argument("-B", "--end", metavar="TIME", type=timecode,
                        help="set cut out point [H:MM:SS, M:SS, or seconds]")
    parser.add_argument("-o", "--output", help=argparse.SUPPRESS)
    parser.add_argument("-m", "--no-metadata", action='store_true',
                        help="don't try to copy all metadata")
    parser.add_argument("-r", "--remove-rotate", action='store_true',
                        help="remove rotation metadata")
    parser.add_argument("-i", "--inplace", action='store_true',
                        help="replace input file")
    parser.add_argument("-a", "--ask", action='store_true',
                        help="ask for destination file name after cutting")
    parser.add_argument("-n", "--dry-run", action='store_true',
                        help="don't actually cut, only show FFmpeg command line")
    parser.add_argument("-q", "--quiet", action='store_true',
                        help="don't output FFmpeg's status")
    args = parser.parse_args()

    outfile = args.outfile
    if not outfile:
        if args.inplace or args.ask:
            c = list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
            random.shuffle(c)
            add = "_" + ''.join(c[:7])
        elif args.start or args.end:
            add = "_cut"
        elif args.remove_rotate:
            add = "_rot"
        else:
            add = "_remux"
        base, ext = os.path.splitext(args.infile)
        outfile = base + add + ext

    ffmpeg = ["ffmpeg", "-y"]
    if args.quiet:
        ffmpeg += ["-loglevel", "quiet"]
    if args.start:
        ffmpeg += ["-ss", t2s(args.start)]
    if args.end:
        ffmpeg += ["-to", t2s(args.end)]
    ffmpeg += ["-i", args.infile, "-avoid_negative_ts", "make_zero"]
    if not args.no_metadata:
        ffmpeg += ["-map_metadata", "0"]
    if args.remove_rotate:
        ffmpeg += ["-metadata:s:v:0", "rotate=0"]
    ffmpeg += ["-c", "copy", outfile]

    if args.dry_run or not(args.quiet):
        print (os.getenv("PS4") or "+ ") + ' '.join((s if not(' ' in s) else ('"%s"' % s)) for s in ffmpeg)
    if args.dry_run:
        sys.exit(0)

    try:
        res = subprocess.Popen(ffmpeg).wait()
    except EnvironmentError, e:
        print >>sys.stderr, "ERROR: failed to call FFmpeg:", e
        sys.exit(1)
    if res != 0:
        print >>sys.stderr, "ERROR: FFmpeg failed with exit code", res
        sys.exit(res)

    if args.ask:
        base, ext = os.path.splitext(os.path.basename(args.infile))
        target = base + "_cut"
        try:
            target = raw_input("output file name [default: %s] => " % target).strip() or target
        except (IOError, EOFError, EnvironmentError, KeyboardInterrupt):
            print "Aborted."
            try:
                os.unlink(outfile)
            except EnvironmentError:
                pass
            sys.exit(1)
        if not target.lower().endswith(ext.lower()):
            target += ext
        target = os.path.join(os.path.dirname(args.infile), target)
    elif args.inplace:
        target = args.infile
    else:
        target = None

    if target:
        if (os.name != "posix") and os.path.exists(target):
            try:
                os.unlink(target)
            except EnvironmentError:
                pass
        try:
            shutil.move(outfile, target)
        except EnvironmentError, e:
            print >>sys.stderr, "ERROR: could not move FFmpeg output file %r to target %r:" % (outfile, target), e
            sys.exit(1)
