#!/usr/local/bin/python
# -*- coding: utf-8 -*-

import os
import sys
import gobject
import pygtk
pygtk.require("2.0")
import gtk
import gtk.glade
import commands
import glob
import tempfile
import subprocess
from subprocess import CalledProcessError
from threading import Thread
import StringIO
import time
import signal

def get_shared_path():
	testfile = 'linuxvideoconverter.glade'
	sharedirs = [".",os.path.join(os.path.dirname(sys.argv[0]),"../share/linuxvideoconverter")]
	sharepath = None
	for sharedir in sharedirs:
		fname = os.path.join(os.path.abspath(sharedir),testfile)
		if os.path.exists(fname):
			sharepath = os.path.abspath(sharedir)
			break
	
	if sharepath is None:
		raise Exception, "Linux video converter shared files " + testfile + " cannot be found in any of " + str(sharedirs) + " default paths"
	
	return sharepath


last_check_call_pid = None
def collect_check_call(*args,**kwargs):
	global last_check_call_pid

	io = tempfile.TemporaryFile()
	kwargs["stdin"] = None
	kwargs["stdout"] = io
	kwargs["stderr"] = io
	p = subprocess.Popen(*args,**kwargs)
	last_check_call_pid = p.pid
	ret = p.wait()
	io.seek(0)
	output = io.read()
	if ret != 0:
		e = subprocess.CalledProcessError(ret,args[0])
		e.output = output
		raise e
	return output

class MakeVideo(Thread):

	def __init__(self,reporter,source,target,format):
		"""reporter is a function that takes one argument, either:
			the name of a stage, or
			an exception
		"""
		Thread.__init__(self,name="MakeVideo")
		self.setDaemon(True)
		self.reporter = reporter
		self.source = source
		self.target = target
		self.format = format
		self.cancel = False
		self.error = False
		self.stage = None

	def abort(self):
		self.cancel = True
		if hasattr(self,"run"):
			def async_kill():
				try: os.kill(self.run.pid,15)
				except Exception: pass
				for discard in range(5): time.sleep(1)
				try: os.kill(self.run.pid,9)
				except Exception: pass
				try: self.run.wait()
				except Exception: pass
				try: self.run.stdout.close()
				except Exception: pass
			t = Thread(target=async_kill)
			t.start()


	def run(self):
		self.reporter(0.0)

		nice = []
		try:
			collect_check_call("which ionice".split())
			nice.extend(["ionice","-c","3"])
		except Exception: pass
		try:
			collect_check_call("which nice".split())
			nice.extend(["nice","-n","20"])
		except Exception: pass

		try:
			runs = []
			if self.format == "youtubeavi":

				output = collect_check_call(["file",self.source])
				if "8000 Hz" in output or "11025 Hz" in output:
					resample = ["-af","resample=44100:0:2"]
				else: resample = []

				runs.append(
					nice + [
					"mencoder",
					self.source,
					"-ovc","x264",
					"-vf","scale=320:-3",
					"-x264encopts","bitrate=800:pass=1",
					"-oac","copy",
					"-o","/dev/null"
					]
				)

				runs.append(
					nice + [
					"mencoder",
					self.source,
					"-ovc","x264",
					"-vf","scale=320:-3",
					"-x264encopts","bitrate=800:pass=2",
					"-oac","faac",
					"-faacopts","br=128",
					"-o",self.target
					] + resample
				)

			else:
				raise Exception,"The format code %s is not supported"%self.format


			for n,run in enumerate(runs):
				debug = []
				self.run = subprocess.Popen(run,stdin=None,stdout=subprocess.PIPE,
							stderr=subprocess.STDOUT,bufsize=512)
				while True:
					data = self.run.stdout.read(512)
					if not data: break
					lines = data.splitlines(True)
					poses = [ l for l in lines if l.startswith("Pos:") and len(l) > 25 ]
					nonposes = [ l for l in lines if not l.startswith("Pos:") ]
					debug.extend(nonposes)
					if not poses: continue
					line = poses[0]
					chunk = line[line.find("(")+1:line.find(")")-1].strip()
					thisstage_percentage = float(chunk) / 100.0
					paststages_percentage = float(n) / float(len(runs))
					progress = paststages_percentage + thisstage_percentage / len(runs)
					progress = progress * 0.999
					if not self.cancel: self.reporter(progress)
				ret = self.run.wait()
				if self.cancel: return
				if ret != 0:
					e = subprocess.CalledProcessError(ret,run)
					e.output = "".join(debug)
					raise e
		
		except Exception,e:
			self.reporter(e)
			return

		self.reporter(1.0)

class NotAVideoFile(Exception): pass
def validate_video(video):
	file(video,"r")
	bli = collect_check_call(["file","-ib",video])
	if not bli.startswith("video/"): raise NotAVideoFile,video


class LinuxVideoConverter(gtk.glade.XML):

	def get(self,n): return self.get_widget(n)

	def __init__ (self):
		gtk.glade.XML.__init__(self,os.path.join(get_shared_path(),'linuxvideoconverter.glade'))
		self.signal_autoconnect(self)
		self.run_setup()

	def gtk_main_quit(*args):
		gtk.main_quit()

	def run_setup(self):
		self.valid_source = False
		self.get("ok").set_sensitive(False)

		def redo_model():
			model = gtk.ListStore(str,str,str,str,str)
			device_list = self.get("format")
			device_list.set_model(model)
			return device_list,model

		widget,model = redo_model()
		#for a in range(1,2):
			#cell = gtk.CellRendererText()
			#widget.pack_start(cell, True)
			#widget.add_attribute(cell, 'text', a)
		model.append(["AVI for YouTube","youtubeavi","320x240","H.264 at 800 kbps","AAC at 128 kbps"])

		self.get("format").set_active(0)
		self.get("setup_options").show()

	def update_format_details(self,*args):
		a = self.get("format").get_active()
		b = self.get("format").get_model()[a]
		res,vc,ac = b[2],b[3],b[4]
		self.get("details").set_markup("<i>%s, %s, %s</i>"%(res,vc,ac))


	def on_source_selected(self,*args):

		filename = self.get("source").get_filename()
		self.valid_source = False
		if filename:
			try:
				valid = validate_video(filename)
				self.valid_source = True
			except (IOError,OSError),e:
				self.warning_dialog("The file you chose cannot be opened","The file you selected cannot be opened by by Linux video converter.",str(e))
			except Exception,e:
				self.warning_dialog("The file you chose is not a supported video file","The file you selected is not a valid video file and cannot be converted by Linux video converter.",str(e))

		self.update_readiness()

	def update_readiness(self):
		if self.valid_source: self.get("progress").set_text("Ready when you are")
		else: self.get("progress").set_text("Please select a valid video")
		self.get("ok").set_sensitive(self.valid_source)

	def convert_video(self,*args):
		filename = self.get("source").get_filename()
		
		format = self.get("format").get_model()[self.get("format").get_active()][1]

		f,ext = os.path.splitext(filename)
		if format in ["youtubeavi"]: ext = ".avi"
		target = f + " (converted)" + ext

		def idle_reporter(arg): gobject.idle_add(self.handle_progress_report,arg)

		self.get("ok").set_sensitive(False)
		self.process = MakeVideo(idle_reporter,filename,target,format)
		self.process.start()

	def handle_cancellation(self,arg):
		if hasattr(self,"process"):
			self.process.abort()
			delattr(self,"process")
			self.update_readiness()
			self.get("progress").set_fraction(0.0)
			self.get("progress").set_text("Cancelled.  Ready to start again.")
		else:
			gtk.main_quit()

	def handle_progress_report(self,arg):
		if type(arg) in [ int,float ]:
			self.get("progress").set_fraction(arg)
			if arg == 1 or arg == 1.0:
				self.get("progress").set_text("Your video has been successfully converted")
				self.get("ok").set_sensitive(True)
				delattr(self,"process")
			else: self.get("progress").set_text("Converting your video...")
			return

		ternary = str(arg)
		if hasattr(arg,"output") and arg.output: ternary = "%s\nDetails from the operating system:\n\n%s"%(arg,arg.output)
		try: raise arg
		except CalledProcessError,e:
			if e.returncode == 127: self.error_dialog("Please install Mencoder","Linux video converter requires Mencoder to transcode this video file.  Please install it using your operating system's software manager.")
			else: self.error_dialog("An unexpected error took place","An unrecoverable error has stopped the creation of your new video.  Please report the details of this error to the Linux video converter developers, so we can fix it right away.",ternary)
		except Exception,e:
			self.error_dialog("An unexpected error took place","An unrecoverable error has stopped the creation of your new video.  Please report the details of this error to the Linux video converter developers, so we can fix it right away.",ternary)

		self.get("progress").set_text("Your video could not be converted")
		self.get("ok").set_sensitive(True)
		delattr(self,"process")

	# info dialogs

	def info_dialog(self,p,s,t = None): self.dialog(p,s,t)
	def warning_dialog(self,p,s,t = None): self.dialog(p,s,t,type=gtk.MESSAGE_WARNING)
	def error_dialog(self,p,s,t = None): self.dialog(p,s,t,type=gtk.MESSAGE_ERROR)
	def dialog(self,primary,secondary,ternary = None,type=None):
		kwargs = {
			"buttons":gtk.BUTTONS_CLOSE,
			"flags":gtk.DIALOG_MODAL
		}
		if type: kwargs["type"]=type
		dialog = gtk.MessageDialog(**kwargs)
		dialog.set_transient_for(self.get("setup_options"))
		dialog.set_markup("<b><big><big>%s</big></big></b>"%primary)
		dialog.format_secondary_markup("<big>%s</big>"%secondary)
		if ternary:
			dialog.format_secondary_markup(
				"<big>%s</big>\n\n%s"%(secondary,ternary))
		
		def destroy(*args): dialog.destroy()
		dialog.connect("response",destroy)
		dialog.show()

	def show_about(self,*args):
		def nothing(*args): 
			self.get("about").hide()
			return True
		self.get("about").connect("delete-event",nothing)
		self.get("about").connect("response",nothing)
		self.get("about").show()


def main():
	gtk.gdk.threads_init()
	app = LinuxVideoConverter()
	gtk.main()

if __name__ == "__main__":
	main()

