+from Components.Task import Task, Job, job_manager, DiskspacePrecondition, Condition
+
+class DemuxTask(Task):
+ def __init__(self, job, inputfile, cutlist):
+ Task.__init__(self, job, "Demux video into ES")
+
+ self.global_preconditions.append(DiskspacePrecondition(4*1024*1024))
+ self.setTool("/opt/bin/projectx")
+ self.cutfile = self.job.workspace + "/cut.Xcl"
+ self.generated_files = [ ]
+ self.cutlist = cutlist
+
+ self.end = 300
+ self.prog_state = 0
+ self.weighting = 1000
+
+ self.args += [inputfile, "-demux", "-out", self.job.workspace, "-cut", self.job.workspace + "/" + self.cutfile ]
+
+ def prepare(self):
+ self.writeCutfile()
+
+ def processOutputLine(self, line):
+ line = line[:-1]
+ MSG_NEW_FILE = "---> new File: "
+ MSG_PROGRESS = "[PROGRESS] "
+
+ if line.startswith(MSG_NEW_FILE):
+ file = line[len(MSG_NEW_FILE):]
+ if file[0] == "'":
+ file = file[1:-1]
+ self.haveNewFile(file)
+ elif line.startswith(MSG_PROGRESS):
+ progress = line[len(MSG_PROGRESS):]
+ self.haveProgress(progress)
+
+ def haveNewFile(self, file):
+ print "PRODUCED FILE [%s]" % file
+ self.generated_files.append(file)
+
+ def haveProgress(self, progress):
+ print "PROGRESS [%s]" % progress
+ MSG_CHECK = "check & synchronize audio file"
+ MSG_DONE = "done..."
+ if progress == "preparing collection(s)...":
+ self.prog_state = 0
+ elif progress[:len(MSG_CHECK)] == MSG_CHECK:
+ self.prog_state += 1
+ else:
+ try:
+ print "have progress:", progress
+ p = int(progress)
+ p = p - 1 + self.prog_state * 100
+ if p > self.progress:
+ self.progress = p
+ except ValueError:
+ print "val error"
+ pass
+
+ def writeCutfile(self):
+ f = open(self.cutfile, "w")
+ f.write("CollectionPanel.CutMode=4\n")
+ for p in self.cutlist:
+ s = p / 90000
+ m = s / 60
+ h = m / 60
+
+ m %= 60
+ s %= 60
+
+ f.write("%02d:%02d:%02d\n" % (h, m, s))
+ f.close()
+
+ def cleanup(self, failed):
+ if failed:
+ import os
+ for f in self.generated_files:
+ os.remove(f)
+
+class MplexTask(Task):
+ def __init__(self, job, outputfile, demux_task):
+ Task.__init__(self, job, "Mux ES into PS")
+
+ self.weighting = 500
+ self.demux_task = demux_task
+ self.setTool("/usr/bin/mplex")
+ self.args += ["-f8", "-o", self.job.workspace + "/" + outputfile, "-v1"]
+
+ def prepare(self):
+ self.args += self.demux_task.generated_files
+
+class RemoveESFiles(Task):
+ def __init__(self, job, demux_task):
+ Task.__init__(self, job, "Remove temp. files")
+ self.demux_task = demux_task
+ self.setTool("/bin/rm")
+
+ def prepare(self):
+ self.args += ["-f"]
+ self.args += self.demux_task.generated_files
+ self.args += [self.demux_task.cutfile]
+
+class DVDAuthorTask(Task):
+ def __init__(self, job, inputfiles, chapterlist):
+ Task.__init__(self, job, "dvdauthor")
+
+ self.weighting = 300
+ self.setTool("/usr/bin/dvdauthor")
+ chapterargs = "--chapters=" + ','.join(["%d:%02d:%02d.%03d" % (p / (90000 * 3600), p % (90000 * 3600) / (90000 * 60), p % (90000 * 60) / 90000, (p % 90000) / 90) for p in chapterlist])
+ self.args += ["-t", chapterargs, "-o", self.job.workspace + "/dvd", "-f"] + inputfiles
+
+class RemovePSFile(Task):
+ def __init__(self, job, psfile):
+ Task.__init__(self, job, "Remove temp. files")
+ self.setTool("/bin/rm")
+ self.args += ["-f", psfile]
+
+class DVDAuthorFinalTask(Task):
+ def __init__(self, job):
+ Task.__init__(self, job, "dvdauthor finalize")
+ self.setTool("/usr/bin/dvdauthor")
+ self.args += ["-T", "-o", self.job.workspace + "/dvd"]
+
+class BurnTaskPostcondition(Condition):
+ def check(self, task):
+ return task.error is None
+
+ def getErrorMessage(self, task):
+ return {
+ task.ERROR_MEDIA: ("Medium is not a writeable DVD!"),
+ task.ERROR_SIZE: ("Content does not fit on DVD!"),
+ task.ERROR_WRITE_FAILED: ("Write failed!"),
+ task.ERROR_DVDROM: ("No (supported) DVDROM found!"),
+ task.ERROR_UNKNOWN: ("An unknown error occured!")
+ }[task.error]
+
+class BurnTask(Task):
+ ERROR_MEDIA, ERROR_SIZE, ERROR_WRITE_FAILED, ERROR_DVDROM, ERROR_UNKNOWN = range(5)
+ def __init__(self, job):
+ Task.__init__(self, job, "burn")
+
+ self.weighting = 500
+ self.end = 120 # 100 for writing, 10 for buffer flush, 10 for closing disc
+ self.postconditions.append(BurnTaskPostcondition())
+ self.setTool("/bin/growisofs")
+ self.args += ["-dvd-video", "-dvd-compat", "-Z", "/dev/cdroms/cdrom0", "-V", "Dreambox_DVD", "-use-the-force-luke=dummy", self.job.workspace + "/dvd"]
+
+ def prepare(self):
+ self.error = None
+
+ def processOutputLine(self, line):
+ line = line[:-1]
+ print "[GROWISOFS] %s" % line
+ if line[8:14] == "done, ":
+ self.progress = float(line[:6])
+ print "progress:", self.progress
+ elif line.find("flushing cache") != -1:
+ self.progress = 100
+ elif line.find("closing disc") != -1:
+ self.progress = 110
+ elif line.startswith(":-["):
+ if line.find("ASC=30h") != -1:
+ self.error = self.ERROR_MEDIA
+ else:
+ self.error = self.ERROR_UNKNOWN
+ print "BurnTask: unknown error %s" % line
+ elif line.startswith(":-("):
+ if line.find("No space left on device") != -1:
+ self.error = self.ERROR_SIZE
+ elif line.find("write failed") != -1:
+ self.error = self.ERROR_WRITE_FAILED
+ elif line.find("unable to open64(\"/dev/cdroms/cdrom0\",O_RDONLY): No such file or directory") != -1: # fixme
+ self.error = self.ERROR_DVDROM
+ elif line.find("media is not recognized as recordable DVD") != -1:
+ self.error = self.ERROR_MEDIA
+ else:
+ self.error = self.ERROR_UNKNOWN
+ print "BurnTask: unknown error %s" % line
+
+class RemoveDVDFolder(Task):
+ def __init__(self, job):
+ Task.__init__(self, job, "Remove temp. files")
+ self.setTool("/bin/rm")
+ self.args += ["-rf", self.job.workspace + "/dvd"]
+
+class DVDJob(Job):
+ def __init__(self, cue):
+ Job.__init__(self, "DVD Burn")
+ self.cue = cue
+ self.workspace = "/media/hdd/tmp"
+ self.fromDescription(self.createDescription())
+
+ def fromDescription(self, description):
+ nr_titles = int(description["nr_titles"])
+
+ for i in range(nr_titles):
+ inputfile = description["inputfile%d" % i]
+ cutlist_entries = description["cutlist%d_entries" % i]
+ cutlist = [ ]
+ for j in range(cutlist_entries):
+ cutlist.append(int(description["cutlist%d_%d" % (i, j)]))
+
+ chapterlist_entries = description["chapterlist%d_entries" % i]
+ chapterlist = [ ]
+ for j in range(chapterlist_entries):
+ chapterlist.append(int(description["chapterlist%d_%d" % (i, j)]))
+
+ demux = DemuxTask(self, inputfile = inputfile, cutlist = cutlist)
+
+ title_filename = self.workspace + "/dvd_title_%d.mpg" % i
+
+ MplexTask(self, "dvd_title_%d.mpg" % i, demux)
+ RemoveESFiles(self, demux)
+ DVDAuthorTask(self, [title_filename], chapterlist = chapterlist)
+ RemovePSFile(self, title_filename)
+ DVDAuthorFinalTask(self)
+ BurnTask(self)
+ RemoveDVDFolder(self)
+
+ def createDescription(self):
+ # self.cue is a list of titles, with
+ # each title being a tuple of
+ # inputfile,
+ # a list of cutpoints (in,out)
+ # a list of chaptermarks
+ # we turn this into a flat dict with
+ # nr_titles = the number of titles,
+ # cutlist%d_entries = the number of cutlist entries for title i,
+ # cutlist%d_%d = cutlist entry j for title i,
+ # chapterlist%d_entries = the number of chapters for title i,
+ # chapterlist%d_%d = chapter j for title i
+ res = { "nr_titles": len(self.cue) }
+ for i in range(len(self.cue)):
+ c = self.cue[i]
+ res["inputfile%d" % i] = c[0]
+ res["cutlist%d_entries" % i] = len(c[1])
+ for j in range(len(c[1])):
+ res["cutlist%d_%d" % (i,j)] = c[1][j]
+
+ res["chapterlist%d_entries" % i] = len(c[2])
+ for j in range(len(c[2])):
+ res["chapterlist%d_%d" % (i,j)] = c[2][j]
+ return res
+
+def Burn(session, cue):
+ print "burning cuesheet!"
+ j = DVDJob(cue)
+ job_manager.AddJob(j)
+ return j