allow burning DVDs with multiple titles where playback automatically jumps to the...
authorAndreas Frisch <andreas.frisch@multimedia-labs.de>
Mon, 25 Aug 2008 15:40:42 +0000 (15:40 +0000)
committerAndreas Frisch <andreas.frisch@multimedia-labs.de>
Mon, 25 Aug 2008 15:40:42 +0000 (15:40 +0000)
lib/python/Plugins/Extensions/DVDBurn/Process.py
lib/python/Plugins/Extensions/DVDBurn/TitleList.py
lib/python/Plugins/Extensions/DVDBurn/keymap.xml

index 5123ea5..217c724 100644 (file)
@@ -1,11 +1,24 @@
 from Components.Task import Task, Job, job_manager, DiskspacePrecondition, Condition
 
+class MakeFifoNode(Task):
+       def __init__(self, job, number):
+               Task.__init__(self, job, "Make FIFO Nodes")
+               self.setTool("/bin/mknod")
+               nodename = self.job.workspace + "/dvd_title_%d" % number + ".mpg"
+               self.args += [nodename, "p"]
+
+class LinkTS(Task):
+       def __init__(self, job, sourcefile, link_name):
+               Task.__init__(self, job, "Creating Symlink for source titles")
+               self.setTool("/bin/ln")
+               self.args += ["-s", sourcefile, link_name]
+
 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.setTool("/usr/bin/projectx")
                self.cutfile = self.job.workspace + "/cut.Xcl"
                self.generated_files = [ ]
                self.cutlist = cutlist
@@ -47,7 +60,6 @@ class DemuxTask(Task):
                        self.prog_state += 1
                else:
                        try:
-                               print "have progress:", progress
                                p = int(progress)
                                p = p - 1 + self.prog_state * 100
                                if p > self.progress:
@@ -83,11 +95,14 @@ class MplexTask(Task):
                self.weighting = 500
                self.demux_task = demux_task
                self.setTool("/usr/bin/mplex")
-               self.args += ["-f8", "-o", self.job.workspace + "/" + outputfile, "-v1"]
+               self.args += ["-f8", "-o", outputfile, "-v1"]
 
        def prepare(self):
                self.args += self.demux_task.generated_files
 
+       def processOutputLine(self, line):
+               print "[MplexTask] processOutputLine=", line
+
 class RemoveESFiles(Task):
        def __init__(self, job, demux_task):
                Task.__init__(self, job, "Remove temp. files")
@@ -100,19 +115,18 @@ class RemoveESFiles(Task):
                self.args += [self.demux_task.cutfile]
 
 class DVDAuthorTask(Task):
-       def __init__(self, job, inputfiles, chapterlist):
-               Task.__init__(self, job, "dvdauthor")
+       def __init__(self, job):
+               Task.__init__(self, job, "Authoring DVD")
 
                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
+               self.CWD = self.job.workspace
+               self.args += ["-x", self.job.workspace+"/dvdauthor.xml"]
 
-class RemovePSFile(Task):
-       def __init__(self, job, psfile):
-               Task.__init__(self, job, "Remove temp. files")
-               self.setTool("/bin/rm")
-               self.args += ["-f", psfile]
+       def processOutputLine(self, line):
+               print "[DVDAuthorTask] processOutputLine=", line
+               if line.startswith("STAT: Processing"):
+                       self.callback(self, [], stay_resident=True)
 
 class DVDAuthorFinalTask(Task):
        def __init__(self, job):
@@ -120,6 +134,15 @@ class DVDAuthorFinalTask(Task):
                self.setTool("/usr/bin/dvdauthor")
                self.args += ["-T", "-o", self.job.workspace + "/dvd"]
 
+class WaitForResidentTasks(Task):
+       def __init__(self, job):
+               Task.__init__(self, job, "waiting for dvdauthor to finalize")
+               
+       def run(self, callback, task_progress_changed):
+               print "waiting for %d resident task(s) %s to finish..." % (len(self.job.resident_tasks),str(self.job.resident_tasks))
+               if self.job.resident_tasks == 0:
+                       callback(self, [])
+
 class BurnTaskPostcondition(Condition):
        def check(self, task):
                return task.error is None
@@ -195,30 +218,69 @@ class DVDJob(Job):
 
        def fromDescription(self, description):
                nr_titles = int(description["nr_titles"])
-
+               authorxml = """
+<dvdauthor dest="%s">
+   <vmgm>
+      <menus>
+         <pgc>
+            <post> jump title 1; </post>
+         </pgc>
+      </menus>
+   </vmgm>
+   <titleset>
+      <titles>""" % (self.workspace+"/dvd")
                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 = [ ]
+                       print str(chapterlist_entries)
                        for j in range(chapterlist_entries):
                                chapterlist.append(int(description["chapterlist%d_%d" % (i, j)]))
+                       print str(chapterlist)
+                       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])
+
+                       title_no = i+1
+                       MakeFifoNode(self, title_no)
+                       vob_tag = """file="%s/dvd_title_%d.mpg" chapters="%s" />""" % (self.workspace, title_no, chapters)
+                                               
+                       if title_no < nr_titles:
+                               post_tag = "> jump title %d;</post>" % ( title_no+1 )
+                       else:
+                               post_tag = " />"
+                       authorxml += """
+         <pgc>
+            <vob %s
+            <post%s
+         </pgc>""" % (vob_tag, post_tag)
+               authorxml += """
+      </titles>
+   </titleset>
+</dvdauthor>
+"""
+               f = open(self.workspace+"/dvdauthor.xml", "w")
+               f.write(authorxml)
+               f.close()
 
-                       demux = DemuxTask(self, inputfile = inputfile, cutlist = cutlist)
+               DVDAuthorTask(self)
 
-                       title_filename =  self.workspace + "/dvd_title_%d.mpg" % i
+               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)]))
 
-                       MplexTask(self, "dvd_title_%d.mpg" % i, demux)
+                       link_name =  self.workspace + "/source_title_%d.ts" % (i+1)
+                       LinkTS(self, inputfile, link_name)
+                       demux = DemuxTask(self, inputfile = link_name, cutlist = cutlist)
+                       title_filename =  self.workspace + "/dvd_title_%d.mpg" % (i+1)
+                       MplexTask(self, title_filename, demux)
                        RemoveESFiles(self, demux)
-                       DVDAuthorTask(self, [title_filename], chapterlist = chapterlist)
-                       RemovePSFile(self, title_filename)
-               DVDAuthorFinalTask(self)
+                       
+                       #RemovePSFile(self, title_filename)
+               #DVDAuthorFinalTask(self)
+               WaitForResidentTasks(self)
                BurnTask(self)
-               RemoveDVDFolder(self)
+               #RemoveDVDFolder(self)
 
        def createDescription(self):
                # self.cue is a list of titles, with 
index 99344e4..1b28932 100644 (file)
@@ -19,10 +19,10 @@ class TitleList(Screen):
                    <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" />
                    <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" />
                    <widget source="title_label" render="Label" position="6,48" size="436,24" font="Regular;18" />
-                   <widget source="titles" render="Listbox" scrollbarMode="showOnDemand" position="6,72" size="400,350">
+                   <widget source="titles" render="Listbox" scrollbarMode="showOnDemand" position="6,72" size="540,350">
                        <convert type="StaticMultiList" />
                    </widget>
-                   <widget source="statusbar" render="Label" position="6,422" size="436,24" font="Regular;18" halign="left" />
+                   <widget source="statusbar" render="Label" position="6,422" size="548,24" font="Regular;18" halign="left" />
                </screen>"""
 
        def __init__(self, session, project = None):
@@ -48,7 +48,7 @@ class TitleList(Screen):
                self["key_blue"] = StaticText(_("Save"))
 
                self["title_label"] = StaticText(_("Table of content to be burned to DVD:"))
-               self["statusbar"] = StaticText(_("When complete, press Key 0 to burn the collection!"))
+               self["statusbar"] = StaticText(_("When complete, press record key to burn the collection!"))
 
                self["actions"] = ActionMap(["OkCancelActions"],
                        {
@@ -79,8 +79,7 @@ class TitleList(Screen):
                        return None
                t = self.project.addService(source)
                self.updateTitleList()
-
-               self.editTitle(t)
+               #self.editTitle(t)
 
        def removeCurrentTitle(self):
                title = self.getCurrentTitle()
@@ -94,6 +93,7 @@ class TitleList(Screen):
        def burnProject(self):
                print "producing final cue sheet:"
                cue = self.produceFinalCuesheet()
+               print str(cue)
                import Process
                job = Process.Burn(self.session, cue)
                print cue
index 6c57ba4..56ff0c9 100644 (file)
@@ -2,9 +2,10 @@
        <map context="DVDTitleList">
                <key id="KEY_RED" mapto="addTitle" flags="m" />
                <key id="KEY_GREEN" mapto="editTitle" flags="m" />
-               <key id="KEY_BLUE" mapto="removeCurrentTitle" flags="m" />
-               <key id="KEY_YELLOW" mapto="saveProject" flags="m" />
+               <key id="KEY_BLUE" mapto="saveProject" flags="m" />
+               <key id="KEY_YELLOW" mapto="removeCurrentTitle" flags="m" />
                <key id="KEY_RECORD" mapto="burnProject" flags="m" />
                <key id="KEY_0" mapto="burnProject" flags="m" />
+               <key id="KEY_VIDEO" mapto="addTitle" flags="m" />
        </map>
 </keymap>