5123ea5d5904605eca7eb2bf316ecbf351f48fa1
[vuplus_dvbapp] / lib / python / Plugins / Extensions / DVDBurn / Process.py
1 from Components.Task import Task, Job, job_manager, DiskspacePrecondition, Condition
2
3 class DemuxTask(Task):
4         def __init__(self, job, inputfile, cutlist):
5                 Task.__init__(self, job, "Demux video into ES")
6
7                 self.global_preconditions.append(DiskspacePrecondition(4*1024*1024))
8                 self.setTool("/opt/bin/projectx")
9                 self.cutfile = self.job.workspace + "/cut.Xcl"
10                 self.generated_files = [ ]
11                 self.cutlist = cutlist
12
13                 self.end = 300
14                 self.prog_state = 0
15                 self.weighting = 1000
16
17                 self.args += [inputfile, "-demux", "-out", self.job.workspace, "-cut", self.job.workspace + "/" + self.cutfile ]
18
19         def prepare(self):
20                 self.writeCutfile()
21
22         def processOutputLine(self, line):
23                 line = line[:-1]
24                 MSG_NEW_FILE = "---> new File: "
25                 MSG_PROGRESS = "[PROGRESS] "
26
27                 if line.startswith(MSG_NEW_FILE):
28                         file = line[len(MSG_NEW_FILE):]
29                         if file[0] == "'":
30                                 file = file[1:-1]
31                         self.haveNewFile(file)
32                 elif line.startswith(MSG_PROGRESS):
33                         progress = line[len(MSG_PROGRESS):]
34                         self.haveProgress(progress)
35
36         def haveNewFile(self, file):
37                 print "PRODUCED FILE [%s]" % file
38                 self.generated_files.append(file)
39
40         def haveProgress(self, progress):
41                 print "PROGRESS [%s]" % progress
42                 MSG_CHECK = "check & synchronize audio file"
43                 MSG_DONE = "done..."
44                 if progress == "preparing collection(s)...":
45                         self.prog_state = 0
46                 elif progress[:len(MSG_CHECK)] == MSG_CHECK:
47                         self.prog_state += 1
48                 else:
49                         try:
50                                 print "have progress:", progress
51                                 p = int(progress)
52                                 p = p - 1 + self.prog_state * 100
53                                 if p > self.progress:
54                                         self.progress = p
55                         except ValueError:
56                                 print "val error"
57                                 pass
58
59         def writeCutfile(self):
60                 f = open(self.cutfile, "w")
61                 f.write("CollectionPanel.CutMode=4\n")
62                 for p in self.cutlist:
63                         s = p / 90000
64                         m = s / 60
65                         h = m / 60
66
67                         m %= 60
68                         s %= 60
69
70                         f.write("%02d:%02d:%02d\n" % (h, m, s))
71                 f.close()
72
73         def cleanup(self, failed):
74                 if failed:
75                         import os
76                         for f in self.generated_files:
77                                 os.remove(f)
78
79 class MplexTask(Task):
80         def __init__(self, job, outputfile, demux_task):
81                 Task.__init__(self, job, "Mux ES into PS")
82
83                 self.weighting = 500
84                 self.demux_task = demux_task
85                 self.setTool("/usr/bin/mplex")
86                 self.args += ["-f8", "-o", self.job.workspace + "/" + outputfile, "-v1"]
87
88         def prepare(self):
89                 self.args += self.demux_task.generated_files
90
91 class RemoveESFiles(Task):
92         def __init__(self, job, demux_task):
93                 Task.__init__(self, job, "Remove temp. files")
94                 self.demux_task = demux_task
95                 self.setTool("/bin/rm")
96
97         def prepare(self):
98                 self.args += ["-f"]
99                 self.args += self.demux_task.generated_files
100                 self.args += [self.demux_task.cutfile]
101
102 class DVDAuthorTask(Task):
103         def __init__(self, job, inputfiles, chapterlist):
104                 Task.__init__(self, job, "dvdauthor")
105
106                 self.weighting = 300
107                 self.setTool("/usr/bin/dvdauthor")
108                 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])
109                 self.args += ["-t", chapterargs, "-o", self.job.workspace + "/dvd", "-f"] + inputfiles
110
111 class RemovePSFile(Task):
112         def __init__(self, job, psfile):
113                 Task.__init__(self, job, "Remove temp. files")
114                 self.setTool("/bin/rm")
115                 self.args += ["-f", psfile]
116
117 class DVDAuthorFinalTask(Task):
118         def __init__(self, job):
119                 Task.__init__(self, job, "dvdauthor finalize")
120                 self.setTool("/usr/bin/dvdauthor")
121                 self.args += ["-T", "-o", self.job.workspace + "/dvd"]
122
123 class BurnTaskPostcondition(Condition):
124         def check(self, task):
125                 return task.error is None
126
127         def getErrorMessage(self, task):
128                 return {
129                         task.ERROR_MEDIA: ("Medium is not a writeable DVD!"),
130                         task.ERROR_SIZE: ("Content does not fit on DVD!"),
131                         task.ERROR_WRITE_FAILED: ("Write failed!"),
132                         task.ERROR_DVDROM: ("No (supported) DVDROM found!"),
133                         task.ERROR_UNKNOWN: ("An unknown error occured!")
134                 }[task.error]
135
136 class BurnTask(Task):
137         ERROR_MEDIA, ERROR_SIZE, ERROR_WRITE_FAILED, ERROR_DVDROM, ERROR_UNKNOWN = range(5)
138         def __init__(self, job):
139                 Task.__init__(self, job, "burn")
140
141                 self.weighting = 500
142                 self.end = 120 # 100 for writing, 10 for buffer flush, 10 for closing disc
143                 self.postconditions.append(BurnTaskPostcondition())
144                 self.setTool("/bin/growisofs")
145                 self.args += ["-dvd-video", "-dvd-compat", "-Z", "/dev/cdroms/cdrom0", "-V", "Dreambox_DVD", "-use-the-force-luke=dummy", self.job.workspace + "/dvd"]
146
147         def prepare(self):
148                 self.error = None
149
150         def processOutputLine(self, line):
151                 line = line[:-1]
152                 print "[GROWISOFS] %s" % line
153                 if line[8:14] == "done, ":
154                         self.progress = float(line[:6])
155                         print "progress:", self.progress
156                 elif line.find("flushing cache") != -1:
157                         self.progress = 100
158                 elif line.find("closing disc") != -1:
159                         self.progress = 110
160                 elif line.startswith(":-["):
161                         if line.find("ASC=30h") != -1:
162                                 self.error = self.ERROR_MEDIA
163                         else:
164                                 self.error = self.ERROR_UNKNOWN
165                                 print "BurnTask: unknown error %s" % line
166                 elif line.startswith(":-("):
167                         if line.find("No space left on device") != -1:
168                                 self.error = self.ERROR_SIZE
169                         elif line.find("write failed") != -1:
170                                 self.error = self.ERROR_WRITE_FAILED
171                         elif line.find("unable to open64(\"/dev/cdroms/cdrom0\",O_RDONLY): No such file or directory") != -1: # fixme
172                                 self.error = self.ERROR_DVDROM
173                         elif line.find("media is not recognized as recordable DVD") != -1:
174                                 self.error = self.ERROR_MEDIA
175                         else:
176                                 self.error = self.ERROR_UNKNOWN
177                                 print "BurnTask: unknown error %s" % line
178
179 class RemoveDVDFolder(Task):
180         def __init__(self, job):
181                 Task.__init__(self, job, "Remove temp. files")
182                 self.setTool("/bin/rm")
183                 self.args += ["-rf", self.job.workspace]
184
185 class DVDJob(Job):
186         def __init__(self, cue):
187                 Job.__init__(self, "DVD Burn")
188                 self.cue = cue
189                 from time import strftime
190                 from Tools.Directories import SCOPE_HDD, resolveFilename, createDir
191                 new_workspace = resolveFilename(SCOPE_HDD) + "tmp/" + strftime("%Y%m%d%H%M%S")
192                 createDir(new_workspace)
193                 self.workspace = new_workspace
194                 self.fromDescription(self.createDescription())
195
196         def fromDescription(self, description):
197                 nr_titles = int(description["nr_titles"])
198
199                 for i in range(nr_titles):
200                         inputfile = description["inputfile%d" % i]
201                         cutlist_entries = description["cutlist%d_entries" % i]
202                         cutlist = [ ]
203                         for j in range(cutlist_entries):
204                                 cutlist.append(int(description["cutlist%d_%d" % (i, j)]))
205
206                         chapterlist_entries = description["chapterlist%d_entries" % i]
207                         chapterlist = [ ]
208                         for j in range(chapterlist_entries):
209                                 chapterlist.append(int(description["chapterlist%d_%d" % (i, j)]))
210
211                         demux = DemuxTask(self, inputfile = inputfile, cutlist = cutlist)
212
213                         title_filename =  self.workspace + "/dvd_title_%d.mpg" % i
214
215                         MplexTask(self, "dvd_title_%d.mpg" % i, demux)
216                         RemoveESFiles(self, demux)
217                         DVDAuthorTask(self, [title_filename], chapterlist = chapterlist)
218                         RemovePSFile(self, title_filename)
219                 DVDAuthorFinalTask(self)
220                 BurnTask(self)
221                 RemoveDVDFolder(self)
222
223         def createDescription(self):
224                 # self.cue is a list of titles, with 
225                 #   each title being a tuple of 
226                 #     inputfile,
227                 #     a list of cutpoints (in,out)
228                 #     a list of chaptermarks
229                 # we turn this into a flat dict with
230                 # nr_titles = the number of titles,
231                 # cutlist%d_entries = the number of cutlist entries for title i,
232                 # cutlist%d_%d = cutlist entry j for title i,
233                 # chapterlist%d_entries = the number of chapters for title i,
234                 # chapterlist%d_%d = chapter j for title i
235                 res = { "nr_titles": len(self.cue) }
236                 for i in range(len(self.cue)):
237                         c = self.cue[i]
238                         res["inputfile%d" % i] = c[0]
239                         res["cutlist%d_entries" % i] = len(c[1])
240                         for j in range(len(c[1])):
241                                 res["cutlist%d_%d" % (i,j)] = c[1][j]
242
243                         res["chapterlist%d_entries" % i] = len(c[2])
244                         for j in range(len(c[2])):
245                                 res["chapterlist%d_%d" % (i,j)] = c[2][j]
246                 return res
247
248 def Burn(session, cue):
249         print "burning cuesheet!"
250         j = DVDJob(cue)
251         job_manager.AddJob(j)
252         return j