#!/usr/bin/python
"""

	Simple multiple CD burning tool with a GTK interface.
	Allows you to burn a directory full of files > 650MB
	(or whatever CD/DVD size you set) to multiple CD/DVDs.

    	You will need twice as much hard disk space as the data
	you are burning.

"""

import os
import sys
import gtk
import gobject
import thread

WIDTH=600
HEIGHT=150
SCREEN_WIDTH=1024
SCREEN_HEIGHT=768

CDR_DEVICE = "/dev/cdrw"					# A wodim/cdrecord-compatible CD device name
CD_SIZE_MB = 650						# CD-R size
ISO_CMD = "mkisofs -R -o %s %s"					# Mkisofs command, 1=output, 2=input
#CDRECORD_CMD = "test=%s; sleep 10"				# Handy for testing without burning
CDRECORD_CMD = "cdrecord dev=" + CDR_DEVICE + " %s"		# Burn command
EJECT_CMD = "eject " + CDR_DEVICE				# Eject after burn
CD_SIZE_BYTES = (CD_SIZE_MB * 1024) * 1024

class Main(gtk.Window):

	def destroy(self, widget, data=None):
		gtk.main_quit()

	def delete_event(self, widget, event, data=None):
		return False

	def startburn(self, widget=None, data=None):
		self.burn(sys.argv[1], sys.argv[2])
		return False

	def __init__(self):
		gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
                self.connect("delete_event", self.delete_event)
                self.connect("destroy", self.destroy)
                self.set_title("Burn CDs")
                self.layout = gtk.Fixed()
		# Icon
		self.im = gtk.Image()
		self.im.set_from_file("info.png")
		self.layout.put(self.im, 10, 10)
		# Output text
		self.lbl = gtk.Label("Collating files...")
		self.layout.put(self.lbl, 60, 20)
		# Burn button
		self.btnburn = gtk.Button()
		self.btnburn.connect("clicked", self.doburn, None)
		box1 = gtk.HBox(False, 0)
	        box1.set_border_width(2)
		image = gtk.Image()
		image.set_from_file("burn.png")
		label = gtk.Label("Burn")
		box1.pack_start(image, False, False, 3)
		box1.pack_start(label, False, False, 3)
		image.show()
		label.show()
		self.btnburn.add(box1)
		self.layout.put(self.btnburn, 475, 10)
		# Progress meter
		self.pg = gtk.ProgressBar()
		self.pg.set_pulse_step(0.05)
		self.layout.put(self.pg, 400, 120)
		self.layout.show_all()
		self.btnburn.hide()
		self.add(self.layout)
		self.resize(WIDTH, HEIGHT)
		self.move(SCREEN_WIDTH / 2 - WIDTH / 2, SCREEN_HEIGHT / 2 - HEIGHT / 2)
		self.realize()
		self.show()
		# Pulse progress meter
		gobject.timeout_add(100, self.pulse)
		# Start burning
		gobject.idle_add(self.startburn)

	def main(self):
		gtk.threads_init()
		gtk.main()

	def pulse(self, widget=None, data=None):
		self.pg.pulse()
		return True

	def burn(self, dir, tmp):
		"""
			Does the burning - finds all files in 'dir' and 
			creates multiple directories in 'tmp'. It then copies
			the files from dir to the tmp subdirs.

			Finally, mkisofs and cdrecord are then used to burn
			the contents of the directories to CDs, prompting
			the user to change disks in between.
		"""

		# Add trailing slashes if necessary
		if not dir.endswith("/"): dir = dir + "/"
		if not tmp.endswith("/"): tmp = tmp + "/"
		self.sdir = dir
		self.stmp = tmp

		# Purge any old temp folders
		os.system("rm -rf %s" % self.stmp + "CD*")

		# Read all files into a list of tuples - filename and size in bytes
		self.files = []
		self.totalbytes = 0
		for f in os.listdir(self.sdir):
			# Only for files
			if os.stat(self.sdir + f)[3] == 1:
				size = os.stat(self.sdir + f)[6]
				self.files.append( (f, size) )
				self.totalbytes = self.totalbytes + size
		# Calculate total in MB
		self.totalmeg = (self.totalbytes / 1024) / 1024
		self.nofiles = len(self.files)
		self.nocds = (self.totalmeg / CD_SIZE_MB) + 1
		self.info = "%s files, totalling %sMB (%s bytes). You will need %s blank CD(s)." % ( self.nofiles, self.totalmeg, self.totalbytes, self.nocds )
		self.window.set_title(self.info)

		# Create subdirectories
		for i in range(1, self.nocds+1):
			os.mkdir(self.stmp + "CD" + str(i))

		# Start filecopying
		self.lbl.set_text("Making CD images, please wait...\n" + self.info)
		thread.start_new_thread(self.copyfiles, (self,))

	def copyfiles(self, widget=None, data=None):
		"""
		Performs file copying to the temporary directories.
		"""

		bytescopied = 0
		currentcd = 1
		for i in self.files:
			if bytescopied + i[1] > CD_SIZE_BYTES:
				currentcd = currentcd + 1
				bytescopied = 0
			bytescopied = bytescopied + i[1]
			cpcmd = "cp %s %s" % (self.sdir + i[0], "%sCD%s/%s" % (self.stmp, currentcd, i[0]))
			print cpcmd
			os.system(cpcmd)

		# Make the ISO files
		for i in range(1, self.nocds+1):
			cmd = ISO_CMD % (self.stmp + "CD" + str(i) + ".iso", self.stmp + "CD" + str(i))
			print "mkiso: " + cmd
			os.system(cmd)

		# CD prompt
		self.curcd = 1
		gobject.idle_add(self.cdprompt)
		return False

	def cdprompt(self):
		"""
		Changes the UI to prompt for a CD
		"""
		self.im.set_from_file("prompt.png")
		self.lbl.set_text("Please insert blank CD " + str(self.curcd) + " and press the burn button.")
		self.btnburn.show()
		self.pg.hide()
		return False

	def doburn(self, widget=None, data=None):
		"""
		Switches to the burning message and starts a new
		thread to execute the burn command.
		"""
		# Set burning message
		self.im.set_from_file("burning.png")
		self.lbl.set_text("Burning CD %s of %s, please wait..." % (self.curcd, self.nocds))
		self.btnburn.hide()
		self.pg.show()
	
		# Call cdrecord to do the burn on a separate thread.
		thread.start_new_thread(self.burncmd, (self,))
		return False

	def burncmd(self, widget=None, data=None):
		"""
		Executes the burn command and then prompts for the next CD
		or displays a finished message if we've done all CDs.
		"""
		burncmd = CDRECORD_CMD % (self.stmp + "CD" + str(self.curcd) + ".iso")
		print burncmd
		os.system(burncmd)

		# Next CD
		self.curcd = self.curcd + 1
		if (self.curcd > self.nocds):
			gobject.idle_add(self.burncomplete)
		else:
			# Prompt for next CD
			self.cdprompt()
	
	def burncomplete(self,widget=None,data=None):
		# Finished
		self.lbl.set_text("Burning complete.")
		self.im.set_from_file("finished.png")
		self.pg.hide()
		# Clear tmp folders
		os.system("rm -rf %s" % self.stmp + "CD*")
		# Eject CD
		os.system(EJECT_CMD)
		return False

if len(sys.argv) < 3:
	print "Usage: multicdburn <directory> <tmpdirectory>"
else:
	if __name__ == "__main__":
	        m = Main()
		m.main()
