#!/usr/bin/env python
# encoding: utf-8
'''
autoencode.__init__ -- Automatically encode files using handbrake

autoencode.__init__ is a automatic Handbrake encoder with the option to watch a folder, copy files to a temp folder, encode, and move to a destination folder.

It defines autoencode

@author:     McSwindler

@copyright:  2015 James Swindle. All rights reserved.

@license:    MIT

@contact:    wilingua@gmail.com
@deffield    updated: 2015-05-02
'''

import sys
import os
import time
import string
import argparse
import traceback
import os
import re
import sys
import time
import hashlib
import shutil
from subprocess import PIPE, Popen
import traceback
import psutil
import enzyme
from datetime import datetime
import sqlite3 as lite


from argparse import ArgumentParser
from os.path import expanduser

__all__ = []
__version__ = 0.1
__date__ = '2015-05-02'
__updated__ = '2015-05-02'

FILE_TYPES = ['.mkv', '.iso', '.vob', '.ts', 'VIDEO_TS']
DEBUG = 1
TESTRUN = 0
PROFILE = 0

def main(argv=None):
    '''Command line options.'''

    program_name = os.path.basename(sys.argv[0])
    program_version = "v0.1"
    program_build_date = "%s" % __updated__

    #program_usage = '''usage: spam two eggs''' # optional - will be autogenerated by optparse
    program_longdesc = '''''' # optional - give further explanation about what the program does
    program_license = "Copyright 2015 McSwindler (James Swindle)                                            \
                Licensed under the MIT License\nhttp://opensource.org/licenses/MIT"

    if argv is None:
        argv = sys.argv[1:]
    try:
        # setup option parser
        parser = ArgumentParser(epilog=program_longdesc, 
                                description=program_license, formatter_class=argparse.ArgumentDefaultsHelpFormatter)
        parser.add_argument("-i", "--in", dest="indir", help="set input directory", nargs='+', metavar="PATH")
        parser.add_argument("-o", "--out", dest="outdir", help="set output directory", metavar="PATH")
        parser.add_argument("-t", "--temp", dest="tempdir", help="set temporary directory", metavar="PATH")
        parser.add_argument("-f", "--filetype", dest="filetype", help="set output filetype")
        parser.add_argument("-H", "--handbrake", dest="handbrake", help="path to handbrake executable", metavar="FILE")

        # set defaults
        parser.set_defaults(outdir=expanduser("~") + os.sep + "Videos" + os.sep + "Encoded", 
                            indir=expanduser("~") + os.sep + "Videos",
                            filetype="mkv",
                            handbrake="C:\Program Files\Handbrake\HandBrakeCLI.exe")

        # process options
        args = parser.parse_args(argv)

        if args.indir:
            #if args.indir is not list:
            #    args.indir = [args.indir]
            print("indir = %s" % args.indir)
        if args.outdir:
            print("outdir = %s" % args.outdir)
        if args.tempdir:
            print("tempdir = %s" % args.tempdir)
        if args.filetype not in ["mkv", "mp4", "m4v"]:
            raise Exception("Invalid FileType, only mkv, mp4, and m4v are valid")

        # MAIN BODY #
        while True:
            AutoEncode(args)

    except Exception:
        indent = len(program_name) * " "
        sys.stderr.write(program_name + ": " + str(sys.exc_info()[0]) + "\n")
        sys.stderr.write(indent + "  for help use --help\n")
        print(traceback.format_exc())
        return 2


class AutoEncode:
    file = None
    def __init__(self, args):
        for directory in args.indir:
            if not os.path.exists(directory):
                raise IOError("No such file or directory", directory)
        if not os.path.exists(args.outdir):
            os.makedirs(args.outdir)
        if args.tempdir and not os.path.exists(args.tempdir):
            os.makedirs(args.tempdir)
            os.makedirs(args.tempdir + os.sep + 'encoding')
        
        self._args = args
        self.file = self.getNewFile()
        if self.file:
            print("New File: %s" %self.file['file'])
            #if self._args.tempdir:
            #    print("Copying File...")
            #    shutil.copy(self.file, self._args.tempdir)
            #    self.file = self._args.tempdir + os.sep + os.path.basename(self.file)
                
            print("Starting HandBrake")
            outfile = self.handbrake(self.file['file'])
            if outfile:
                self._complete(outfile)
            else:
                print("HandBrake Failed, please check file: %s" % self.file['file'])
                
            self.file = None
                
        else:
            print("No Files to Process")
            time.sleep(900) # 15 minutes
            
    def __del__(self):
        if not self.file:
            return
        
        con = self._connect() 
        with con:
            cur = con.cursor()
            cur.execute('DELETE FROM videos WHERE name = ?', [self.file.name])
            con.commit()
        
    def getNewFile(self, index=None):
        if index is None:
            for i in range(len(self._args.indir)):
                f = self.getNewFile(i)
                if f:
                    return f
        elif index in range(len(self._args.indir)):
            indir = self._args.indir[index]
            os.chdir(indir)
            con = self._connect()
            with con:
                files = self._getFiles(con, indir)
                if len(files) > 0:
                    f = files[0]
                    cur = con.cursor()
                    cur.execute('INSERT INTO videos (name, date_started) VALUES (?, ?)', (f['name'], str(datetime.now())))
                    con.commit()
                    return f
        return None
    
    def _complete(self, outfile):
        if self._args.tempdir:
            print("Copying File...")
            shutil.copy(outfile, self._args.outdir)
        con = self._connect() 
        with con:
            cur = con.cursor()
            cur.execute('UPDATE videos SET date_completed = ? WHERE name = ?', (str(datetime.now()), self.file['name']))
            con.commit()
    def getHBArgs(self, infile):
        cmd = [self._args.handbrake, '-i', infile, '--scan']
        process = Popen(cmd, stdout=PIPE,stderr=PIPE)
        (output, err) = process.communicate()
        #exit_code = process.wait()
        sub = None
        audio = (None,0)
        for line in err.splitlines():
            m = re.search('([0-9]+), English(?: \([\w]+\) \(([0-9\.]+) ch\))?', line.decode("utf-8"))
            if m is not None:
                rid = int(m.group(1))
                if m.group(2) is not None: #audio detected
                    quality = float(m.group(2))
                    if quality > audio[1]:
                        audio = (rid, quality)
                elif sub is None:
                    sub = rid
        
        parms = [self._args.handbrake, '-i', infile, '-o', self._outputFile(infile), '-f', 'mkv', '--decomb', '--strict-anamorphic', '-e', 'x264', '-q', '20', '-m', '-x', 'ref=2:bframes=2:subq=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0', '--verbose', '1']
        if audio[0] is not None:
            parms += ['--audio', str(audio[0]), '--aencoder', 'copy', '--mixdown', 'auto', '--arate', 'auto', '--ab', 'auto', '--drc', '0.0']
        if sub is not None:
            parms += ['-s', str(sub)]
                        
        return parms
                
    def handbrake(self, infile):
        cmd = self.getHBArgs(infile)
        process = Popen(cmd, shell=True)
        p = psutil.Process(process.pid)
        p.nice(psutil.HIGH_PRIORITY_CLASS)
        process.wait()
        if process.returncode > 0:
            return None
        else:
            return cmd[4]
        
    def _outputFile(self, infile):
        filename, ext = os.path.splitext(os.path.basename(infile));
        if ext == '.mkv':
            with open(infile, 'rb') as f:
                mkv = enzyme.MKV(f)
                if mkv.info.title:
                    filename = re.sub('[^\w\-_\. ]', '-', mkv.info.title)
        
        count = 1
        sub = ''            
        while os.path.exists(self._args.outdir + os.sep + filename + sub + '.' + self._args.filetype):
            sub = '.' + str(count)
            count += 1
        filename += sub
            
        filename = filename + '.' + self._args.filetype
        if self._args.tempdir:
            return self._args.tempdir + os.sep + 'encoding' + os.sep + filename
        else:
            return self._args.outdir + os.sep + filename
    
    def _getFiles(self, con, indir):
        flist = list()
        dbFiles = self._getFilesFromDb(con)
        for subdir, dirs, files in os.walk(indir):
            if subdir == self._args.outdir or subdir == self._args.tempdir:
                continue
                   
            isTSGood = True 
            for f in files:
                f = os.path.join(subdir, f)
                name = f.replace(indir, '')
                ext = os.path.splitext(name)[1]
                try:
                    open(f)
                except IOError:
                    print(sys.exc_info()[0])
                    isTSGood = False
                    continue
                
                if ext not in FILE_TYPES or name in dbFiles:
                    continue
                fObj = {"file":f, "indir": indir, "name": name, "ext": ext}
                flist.append(fObj)
                
                
            if subdir.endswith('VIDEO_TS') and isTSGood:
                name = subdir.replace(indir, '')
                if name not in dbFiles:
                    fObj = {"file":subdir, "indir": indir, "name": name, "ext": "VIDEO_TS"}
                    flist.append(fObj)
                
        return flist
    
    def _getFilesFromDb(self, con):
        files = list()
        cur = con.cursor()
        cur.execute('SELECT * FROM videos WHERE date_started OR date_completed')
        for row in cur.fetchall():
            files.append(row[0])
        return files           
                    
    def _connect(self):
        try:
            con = lite.connect('autoencode.db')
            cur = con.cursor()
            cur.execute("CREATE TABLE IF NOT EXISTS videos(name TEXT, date_started TEXT, date_completed TEXT)")
        except lite.Error:
            print(traceback.format_exc())
            if con:
                con.close()
        return con       


if __name__ == "__main__":
    sys.exit(main())