1 from Components.Task import Task, Job, job_manager, DiskspacePrecondition, Condition
3 class MakeFifoNode(Task):
4 def __init__(self, job, number):
5 Task.__init__(self, job, "Make FIFO Nodes")
6 self.setTool("/bin/mknod")
7 nodename = self.job.workspace + "/dvd_title_%d" % number + ".mpg"
8 self.args += [nodename, "p"]
11 def __init__(self, job, sourcefile, link_name):
12 Task.__init__(self, job, "Creating Symlink for source titles")
13 self.setTool("/bin/ln")
14 self.args += ["-s", sourcefile, link_name]
16 class DemuxTask(Task):
17 def __init__(self, job, inputfile, cutlist):
18 Task.__init__(self, job, "Demux video into ES")
20 self.global_preconditions.append(DiskspacePrecondition(4*1024*1024))
21 self.setTool("/usr/bin/projectx")
22 self.cutfile = self.job.workspace + "/cut.Xcl"
23 self.generated_files = [ ]
24 self.cutlist = cutlist
30 self.args += [inputfile, "-demux", "-out", self.job.workspace, "-cut", self.job.workspace + "/" + self.cutfile ]
35 def processOutputLine(self, line):
37 MSG_NEW_FILE = "---> new File: "
38 MSG_PROGRESS = "[PROGRESS] "
40 if line.startswith(MSG_NEW_FILE):
41 file = line[len(MSG_NEW_FILE):]
44 self.haveNewFile(file)
45 elif line.startswith(MSG_PROGRESS):
46 progress = line[len(MSG_PROGRESS):]
47 self.haveProgress(progress)
49 def haveNewFile(self, file):
50 print "PRODUCED FILE [%s]" % file
51 self.generated_files.append(file)
53 def haveProgress(self, progress):
54 print "PROGRESS [%s]" % progress
55 MSG_CHECK = "check & synchronize audio file"
57 if progress == "preparing collection(s)...":
59 elif progress[:len(MSG_CHECK)] == MSG_CHECK:
64 p = p - 1 + self.prog_state * 100
71 def writeCutfile(self):
72 f = open(self.cutfile, "w")
73 f.write("CollectionPanel.CutMode=4\n")
74 for p in self.cutlist:
82 f.write("%02d:%02d:%02d\n" % (h, m, s))
85 def cleanup(self, failed):
88 for f in self.generated_files:
91 class MplexTask(Task):
92 def __init__(self, job, outputfile, demux_task):
93 Task.__init__(self, job, "Mux ES into PS")
96 self.demux_task = demux_task
97 self.setTool("/usr/bin/mplex")
98 self.args += ["-f8", "-o", outputfile, "-v1"]
101 self.args += self.demux_task.generated_files
103 def processOutputLine(self, line):
104 print "[MplexTask] processOutputLine=", line
106 class RemoveESFiles(Task):
107 def __init__(self, job, demux_task):
108 Task.__init__(self, job, "Remove temp. files")
109 self.demux_task = demux_task
110 self.setTool("/bin/rm")
114 self.args += self.demux_task.generated_files
115 self.args += [self.demux_task.cutfile]
117 class DVDAuthorTask(Task):
118 def __init__(self, job):
119 Task.__init__(self, job, "Authoring DVD")
122 self.setTool("/usr/bin/dvdauthor")
123 self.CWD = self.job.workspace
124 self.args += ["-x", self.job.workspace+"/dvdauthor.xml"]
126 def processOutputLine(self, line):
127 print "[DVDAuthorTask] processOutputLine=", line
128 if line.startswith("STAT: Processing"):
129 self.callback(self, [], stay_resident=True)
131 class DVDAuthorFinalTask(Task):
132 def __init__(self, job):
133 Task.__init__(self, job, "dvdauthor finalize")
134 self.setTool("/usr/bin/dvdauthor")
135 self.args += ["-T", "-o", self.job.workspace + "/dvd"]
137 class WaitForResidentTasks(Task):
138 def __init__(self, job):
139 Task.__init__(self, job, "waiting for dvdauthor to finalize")
141 def run(self, callback, task_progress_changed):
142 print "waiting for %d resident task(s) %s to finish..." % (len(self.job.resident_tasks),str(self.job.resident_tasks))
143 if self.job.resident_tasks == 0:
146 class BurnTaskPostcondition(Condition):
147 def check(self, task):
148 return task.error is None
150 def getErrorMessage(self, task):
152 task.ERROR_MEDIA: ("Medium is not a writeable DVD!"),
153 task.ERROR_SIZE: ("Content does not fit on DVD!"),
154 task.ERROR_WRITE_FAILED: ("Write failed!"),
155 task.ERROR_DVDROM: ("No (supported) DVDROM found!"),
156 task.ERROR_UNKNOWN: ("An unknown error occured!")
159 class BurnTask(Task):
160 ERROR_MEDIA, ERROR_SIZE, ERROR_WRITE_FAILED, ERROR_DVDROM, ERROR_UNKNOWN = range(5)
161 def __init__(self, job):
162 Task.__init__(self, job, "burn")
165 self.end = 120 # 100 for writing, 10 for buffer flush, 10 for closing disc
166 self.postconditions.append(BurnTaskPostcondition())
167 self.setTool("/bin/growisofs")
168 self.args += ["-dvd-video", "-dvd-compat", "-Z", "/dev/cdroms/cdrom0", "-V", "Dreambox_DVD", "-use-the-force-luke=dummy", self.job.workspace + "/dvd"]
173 def processOutputLine(self, line):
175 print "[GROWISOFS] %s" % line
176 if line[8:14] == "done, ":
177 self.progress = float(line[:6])
178 print "progress:", self.progress
179 elif line.find("flushing cache") != -1:
181 elif line.find("closing disc") != -1:
183 elif line.startswith(":-["):
184 if line.find("ASC=30h") != -1:
185 self.error = self.ERROR_MEDIA
187 self.error = self.ERROR_UNKNOWN
188 print "BurnTask: unknown error %s" % line
189 elif line.startswith(":-("):
190 if line.find("No space left on device") != -1:
191 self.error = self.ERROR_SIZE
192 elif line.find("write failed") != -1:
193 self.error = self.ERROR_WRITE_FAILED
194 elif line.find("unable to open64(\"/dev/cdroms/cdrom0\",O_RDONLY): No such file or directory") != -1: # fixme
195 self.error = self.ERROR_DVDROM
196 elif line.find("media is not recognized as recordable DVD") != -1:
197 self.error = self.ERROR_MEDIA
199 self.error = self.ERROR_UNKNOWN
200 print "BurnTask: unknown error %s" % line
202 class RemoveDVDFolder(Task):
203 def __init__(self, job):
204 Task.__init__(self, job, "Remove temp. files")
205 self.setTool("/bin/rm")
206 self.args += ["-rf", self.job.workspace]
209 def __init__(self, cue):
210 Job.__init__(self, "DVD Burn")
212 from time import strftime
213 from Tools.Directories import SCOPE_HDD, resolveFilename, createDir
214 new_workspace = resolveFilename(SCOPE_HDD) + "tmp/" + strftime("%Y%m%d%H%M%S")
215 createDir(new_workspace)
216 self.workspace = new_workspace
217 self.fromDescription(self.createDescription())
219 def fromDescription(self, description):
220 nr_titles = int(description["nr_titles"])
222 <dvdauthor dest="%s">
226 <post> jump title 1; </post>
231 <titles>""" % (self.workspace+"/dvd")
232 for i in range(nr_titles):
233 chapterlist_entries = description["chapterlist%d_entries" % i]
235 print str(chapterlist_entries)
236 for j in range(chapterlist_entries):
237 chapterlist.append(int(description["chapterlist%d_%d" % (i, j)]))
238 print str(chapterlist)
239 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])
242 MakeFifoNode(self, title_no)
243 vob_tag = """file="%s/dvd_title_%d.mpg" chapters="%s" />""" % (self.workspace, title_no, chapters)
245 if title_no < nr_titles:
246 post_tag = "> jump title %d;</post>" % ( title_no+1 )
253 </pgc>""" % (vob_tag, post_tag)
259 f = open(self.workspace+"/dvdauthor.xml", "w")
265 for i in range(nr_titles):
266 inputfile = description["inputfile%d" % i]
267 cutlist_entries = description["cutlist%d_entries" % i]
269 for j in range(cutlist_entries):
270 cutlist.append(int(description["cutlist%d_%d" % (i, j)]))
272 link_name = self.workspace + "/source_title_%d.ts" % (i+1)
273 LinkTS(self, inputfile, link_name)
274 demux = DemuxTask(self, inputfile = link_name, cutlist = cutlist)
275 title_filename = self.workspace + "/dvd_title_%d.mpg" % (i+1)
276 MplexTask(self, title_filename, demux)
277 RemoveESFiles(self, demux)
279 #RemovePSFile(self, title_filename)
280 #DVDAuthorFinalTask(self)
281 WaitForResidentTasks(self)
283 #RemoveDVDFolder(self)
285 def createDescription(self):
286 # self.cue is a list of titles, with
287 # each title being a tuple of
289 # a list of cutpoints (in,out)
290 # a list of chaptermarks
291 # we turn this into a flat dict with
292 # nr_titles = the number of titles,
293 # cutlist%d_entries = the number of cutlist entries for title i,
294 # cutlist%d_%d = cutlist entry j for title i,
295 # chapterlist%d_entries = the number of chapters for title i,
296 # chapterlist%d_%d = chapter j for title i
297 res = { "nr_titles": len(self.cue) }
298 for i in range(len(self.cue)):
300 res["inputfile%d" % i] = c[0]
301 res["cutlist%d_entries" % i] = len(c[1])
302 for j in range(len(c[1])):
303 res["cutlist%d_%d" % (i,j)] = c[1][j]
305 res["chapterlist%d_entries" % i] = len(c[2])
306 for j in range(len(c[2])):
307 res["chapterlist%d_%d" % (i,j)] = c[2][j]
310 def Burn(session, cue):
311 print "burning cuesheet!"
313 job_manager.AddJob(j)