DVDBurn: switch TiteList to templatedmulticontentlist, allow skinning and display...
[vuplus_dvbapp] / lib / python / Plugins / Extensions / DVDBurn / TitleList.py
1 import DVDProject, TitleList, TitleCutter, TitleProperties, ProjectSettings, DVDToolbox, Process
2 from Screens.Screen import Screen
3 from Screens.ChoiceBox import ChoiceBox
4 from Screens.InputBox import InputBox
5 from Screens.MessageBox import MessageBox
6 from Screens.HelpMenu import HelpableScreen
7 from Screens.TaskView import JobView
8 from Components.Task import job_manager
9 from Components.ActionMap import HelpableActionMap, ActionMap
10 from Components.Sources.List import List
11 from Components.Sources.StaticText import StaticText
12 from Components.Sources.Progress import Progress
13 from Components.MultiContent import MultiContentEntryText
14 from enigma import gFont, RT_HALIGN_LEFT, RT_HALIGN_RIGHT
15 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
16
17 class TitleList(Screen, HelpableScreen):
18         skin = """
19                 <screen name="TitleList" position="center,center" size="560,445" title="DVD Tool" >
20                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
21                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
22                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" />
23                         <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on" />
24                         <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" />
25                         <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" />
26                         <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" />
27                         <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" />
28                         <widget source="title_label" render="Label" position="10,48" size="540,38" font="Regular;18" transparent="1" />
29                         <widget source="error_label" render="Label" position="10,48" size="540,395" zPosition="3" font="Regular;20" transparent="1" />
30                         <widget source="titles" render="Listbox" scrollbarMode="showOnDemand" position="10,86" size="540,312" zPosition="3" transparent="1" >
31                                 <convert type="TemplatedMultiContent">
32                                         {"template": [
33                                                         MultiContentEntryText(pos = (0, 0), size = (420, 20), font = 0, flags = RT_HALIGN_LEFT, text = 1), # index 1 Title,
34                                                         MultiContentEntryText(pos = (0, 20), size = (328, 17), font = 1, flags = RT_HALIGN_LEFT, text = 2), # index 2 description,
35                                                         MultiContentEntryText(pos = (420, 6), size = (120, 20), font = 1, flags = RT_HALIGN_RIGHT, text = 3), # index 3 begin time,
36                                                         MultiContentEntryText(pos = (328, 20), size = (154, 17), font = 1, flags = RT_HALIGN_RIGHT, text = 4), # index 4 channel,
37                                                 ],
38                                         "fonts": [gFont("Regular", 20), gFont("Regular", 14)],
39                                         "itemHeight": 37
40                                         }
41                                 </convert>
42                         </widget>
43                         <widget source="space_bar" render="Progress" position="10,410" size="540,26" borderWidth="1" backgroundColor="#254f7497" />
44                         <widget source="space_label" render="Label" position="40,414" size="480,22" zPosition="2" font="Regular;18" halign="center" transparent="1" foregroundColor="#000000" />
45                 </screen>"""
46
47         def __init__(self, session, project = None):
48                 Screen.__init__(self, session)
49                 HelpableScreen.__init__(self)
50                 
51                 self["titleactions"] = HelpableActionMap(self, "DVDTitleList",
52                         {
53                                 "addTitle": (self.addTitle, _("Add a new title"), _("Add title")),
54                                 "titleProperties": (self.titleProperties, _("Properties of current title"), _("Title properties")),
55                                 "removeCurrentTitle": (self.removeCurrentTitle, _("Remove currently selected title"), _("Remove title")),
56                                 "settings": (self.settings, _("Collection settings"), _("Settings")),
57                                 "burnProject": (self.askBurnProject, _("Burn DVD"), _("Burn DVD")),
58                         })
59
60                 self["MovieSelectionActions"] = HelpableActionMap(self, "MovieSelectionActions",
61                         {
62                                 "contextMenu": (self.showMenu, _("menu")),
63                         })
64
65                 self["actions"] = ActionMap(["OkCancelActions"],
66                         {
67                                 "cancel": self.leave
68                         })
69
70                 self["key_red"] = StaticText()
71                 self["key_green"] = StaticText(_("Add title"))
72                 self["key_yellow"] = StaticText()
73                 self["key_blue"] = StaticText(_("Settings"))
74
75                 self["title_label"] = StaticText()
76                 self["error_label"] = StaticText()
77                 self["space_label"] = StaticText()
78                 self["space_bar"] = Progress()
79
80                 if project is not None:
81                         self.project = project
82                 else:
83                         self.newProject()
84
85                 self["titles"] = List([])
86                 self.updateTitleList()
87                 self.previous_size = 0
88                 self.onLayoutFinish.append(self.layoutFinished)
89
90         def layoutFinished(self):
91                 self.setTitle(_("DVD Titlelist"))
92
93         def checkBackgroundJobs(self):
94                 for job in job_manager.getPendingJobs():
95                         print "type(job):", type(job)
96                         print "Process.DVDJob:", Process.DVDJob
97                         if type(job) == Process.DVDJob:
98                                 self.backgroundJob = job
99                                 return
100                 self.backgroundJob = None
101
102         def showMenu(self):
103                 menu = []
104                 self.checkBackgroundJobs()
105                 if self.backgroundJob:
106                         j = self.backgroundJob
107                         menu.append(("%s: %s (%d%%)" % (j.getStatustext(), j.name, int(100*j.progress/float(j.end))), self.showBackgroundJob))
108                 menu.append((_("DVD media toolbox"), self.toolbox))
109                 menu.append((_("Preview menu"), self.previewMenu))
110                 if self.project.settings.output.getValue() == "dvd":
111                         if len(self["titles"].list):
112                                 menu.append((_("Burn DVD"), self.burnProject))
113                 elif self.project.settings.output.getValue() == "iso":
114                         menu.append((_("Create DVD-ISO"), self.burnProject))
115                 menu.append((_("Burn existing image to DVD"), self.selectImage))
116                 menu.append((_("Edit chapters of current title"), self.editTitle))
117                 menu.append((_("Reset and renumerate title names"), self.resetTitles))
118                 menu.append((_("Exit"), self.leave))
119                 self.session.openWithCallback(self.menuCallback, ChoiceBox, title="", list=menu)
120
121         def menuCallback(self, choice):
122                 if choice:
123                         choice[1]()
124
125         def showBackgroundJob(self):
126                 job_manager.in_background = False
127                 self.session.openWithCallback(self.JobViewCB, JobView, self.backgroundJob)
128                 self.backgroundJob = None
129         
130         def titleProperties(self):
131                 if self.getCurrentTitle():
132                         self.session.openWithCallback(self.updateTitleList, TitleProperties.TitleProperties, self, self.project, self["titles"].getIndex())
133
134         def selectImage(self):
135                 self.session.openWithCallback(self.burnISO, ProjectSettings.FileBrowser, "image", self.project.settings)
136
137         def newProject(self):
138                 self.project = DVDProject.DVDProject()
139                 if self.loadTemplate():
140                         self.project.session = self.session
141                         self.settingsCB()
142
143         def addTitle(self):
144                 from Screens.MovieSelection import MovieSelection
145                 from Components.ActionMap import HelpableActionMap
146                 class DVDMovieSelection(MovieSelection):
147                         skin = """<screen name="DVDMovieSelection" position="center,center" size="560,445" title="Select a movie">
148                                 <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
149                                 <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
150                                 <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" />
151                                 <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" />
152                                 <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" />
153                                 <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" />
154                                 <widget name="waitingtext" position="0,45" size="560,395" zPosition="4" font="Regular;22" halign="center" valign="center" />
155                                 <widget name="list" position="5,40" size="550,375" zPosition="2" scrollbarMode="showOnDemand" />
156                                 <widget name="DescriptionBorder" pixmap="skin_default/border_eventinfo.png" position="0,316" zPosition="1" size="560,103" transparent="1" alphatest="on" />
157                                 <widget source="Service" render="Label" position="5,318" zPosition="1" size="480,35" font="Regular;17" foregroundColor="#cccccc">
158                                         <convert type="MovieInfo">ShortDescription</convert>
159                                 </widget>
160                                 <widget source="Service" render="Label" position="495,318" zPosition="1" size="60,22" font="Regular;17" halign="right">
161                                         <convert type="ServiceTime">Duration</convert>
162                                         <convert type="ClockToText">AsLength</convert>
163                                 </widget>
164                                 <widget source="Service" render="Label" position="380,337" zPosition="2" size="175,22" font="Regular;17" halign="right">
165                                         <convert type="MovieInfo">RecordServiceName</convert>
166                                 </widget>
167                                 <widget source="Service" render="Label" position="5,357" zPosition="1" size="550,58" font="Regular;19">
168                                         <convert type="EventName">ExtendedDescription</convert>
169                                 </widget>
170                                 <widget name="freeDiskSpace" position="10,425" size="540,20" font="Regular;19" valign="center" halign="right" />
171                         </screen>"""
172                         def __init__(self, session):
173                                 MovieSelection.__init__(self, session)
174                                 self["key_red"] = StaticText(_("Close"))
175                                 self["key_green"] = StaticText(_("Add"))
176                                 self["key_yellow"] = StaticText(_("Edit title"))
177                                 self["ColorActions"] = HelpableActionMap(self, "ColorActions",
178                                 {
179                                         "red": (self.close, _("Close title selection")),
180                                         "green": (self.insertWithoutEdit, ("insert without cutlist editor")),
181                                         "yellow": (self.movieSelected, _("Add a new title"))
182                                 })
183                         def updateTags(self):
184                                 pass
185                         def doContext(self):
186                                 print "context menu forbidden inside DVDBurn to prevent calling multiple instances"
187                         def insertWithoutEdit(self):
188                                 current = self.getCurrent()
189                                 if current is not None:
190                                         current.edit = False
191                                         self.close(current)
192                         def movieSelected(self):
193                                 current = self.getCurrent()
194                                 if current is not None:
195                                         current.edit = True
196                                         self.close(current)
197                 self.session.openWithCallback(self.selectedSource, DVDMovieSelection)
198
199         def selectedSource(self, source):
200                 if source is None:
201                         return None
202                 t = self.project.addService(source)
203                 try:
204                         editor = source.edit
205                 except AttributeError:
206                         editor = True
207                 self.editTitle(t, editor)
208
209         def removeCurrentTitle(self):
210                 title = self.getCurrentTitle()
211                 self.removeTitle(title)
212         
213         def removeTitle(self, title):
214                 if title is not None:
215                         self.project.titles.remove(title)
216                         self.updateTitleList()
217
218         def toolbox(self):
219                 self.session.open(DVDToolbox.DVDToolbox)
220
221         def settings(self):
222                 self.session.openWithCallback(self.settingsCB, ProjectSettings.ProjectSettings, self.project)
223
224         def settingsCB(self, update=True):
225                 if not update:
226                         return
227                 self["title_label"].text = _("Table of content for collection") + " \"" + self.project.settings.name.getValue() + "\":"
228
229         def loadTemplate(self):
230                 filename = resolveFilename(SCOPE_PLUGINS)+"Extensions/DVDBurn/DreamboxDVD.ddvdp.xml"
231                 if self.project.load(filename):
232                         self["error_label"].setText("")
233                         return True
234                 else:
235                         self["error_label"].setText(self.project.error)
236                         return False
237
238         def askBurnProject(self):
239                 if len(self["titles"].list):
240                         self.session.openWithCallback(self.burnProject,MessageBox,text = _("Do you want to burn this collection to DVD medium?"), type = MessageBox.TYPE_YESNO)
241
242         def burnProject(self, answer=True):
243                 if not answer:
244                         return
245                 if self.project.settings.authormode.getValue() == "data_ts":
246                         job = Process.DVDdataJob(self.project)
247                         job_manager.AddJob(job)
248                         job_manager.in_background = False
249                         self.session.openWithCallback(self.JobViewCB, JobView, job)
250                 else:
251                         job = Process.DVDJob(self.project)
252                         job_manager.AddJob(job)
253                         job_manager.in_background = False
254                         self.session.openWithCallback(self.JobViewCB, JobView, job)
255
256         def burnISO(self, path, scope, configRef):
257                 if path:
258                         job = Process.DVDisoJob(self.project, path)
259                         job_manager.AddJob(job)
260                         job_manager.in_background = False
261                         self.session.openWithCallback(self.JobViewCB, JobView, job)
262
263         def JobViewCB(self, in_background):
264                 job_manager.in_background = in_background
265
266         def previewMenu(self):
267                 job = Process.DVDJob(self.project, menupreview=True)
268                 job_manager.in_background = False
269                 job_manager.AddJob(job)
270
271         def updateTitleList(self):
272                 list = [ ]
273                 for title in self.project.titles:
274                         list.append((title, title.properties.menutitle.getValue(), title.properties.menusubtitle.getValue(), title.DVBchannel, title.formatDVDmenuText("$D.$M.$Y, $T", 0)))
275                 self["titles"].list = list
276                 self.updateSize()
277                 if len(list):
278                         self["key_red"].text = _("Remove title")
279                         self["key_yellow"].text = _("Title properties")
280                 else:
281                         self["key_red"].text = ""
282                         self["key_yellow"].text = ""
283
284         def updateSize(self):
285                 size = self.project.size/(1024*1024)
286                 MAX_DL = self.project.MAX_DL-100
287                 MAX_SL = self.project.MAX_SL-100
288                 print "updateSize:", size, "MAX_DL:", MAX_DL, "MAX_SL:", MAX_SL
289                 if size > MAX_DL:
290                         percent = 100 * size / float(MAX_DL)
291                         self["space_label"].text = "%d MB - " % size + _("exceeds dual layer medium!") + " (%.2f%% " % (100-percent) + _("free") + ")"
292                         self["space_bar"].value = int(percent)
293                         if self.previous_size < MAX_DL:
294                                 self.session.open(MessageBox,text = _("exceeds dual layer medium!"), type = MessageBox.TYPE_ERROR)
295                 elif size > MAX_SL:
296                         percent = 100 * size / float(MAX_DL)
297                         self["space_label"].text = "%d MB  " % size + _("of a DUAL layer medium used.") + " (%.2f%% " % (100-percent) + _("free") + ")"
298                         self["space_bar"].value = int(percent)
299                         if self.previous_size < MAX_SL:
300                                 self.session.open(MessageBox,text = _("Your collection exceeds the size of a single layer medium, you will need a blank dual layer DVD!"), type = MessageBox.TYPE_INFO)
301                 elif size < MAX_SL:
302                         percent = 100 * size / float(MAX_SL)
303                         self["space_label"].text = "%d MB " % size + _("of a SINGLE layer medium used.") + " (%.2f%% " % (100-percent) + _("free") + ")"
304                         self["space_bar"].value = int(percent)
305                 self.previous_size = size
306
307         def getCurrentTitle(self):
308                 t = self["titles"].getCurrent()
309                 return t and t[0]
310
311         def editTitle(self, title = None, editor = True):
312                 t = title or self.getCurrentTitle()
313                 if t is not None:
314                         self.current_edit_title = t
315                         if editor:
316                                 self.session.openWithCallback(self.titleEditDone, TitleCutter.TitleCutter, t)
317                         else:
318                                 self.session.openWithCallback(self.titleEditDone, TitleCutter.CutlistReader, t)
319
320         def titleEditDone(self, cutlist):
321                 t = self.current_edit_title
322                 t.initDVDmenuText(self.project,len(self.project.titles))
323                 t.cuesheet = cutlist
324                 t.produceFinalCuesheet()
325                 if t.VideoType != 0:
326                         self.session.openWithCallback(self.DVDformatCB,MessageBox,text = _("The DVD standard doesn't support H.264 (HDTV) video streams. Do you want to create a Dreambox format data DVD (which will not play in stand-alone DVD players) instead?"), type = MessageBox.TYPE_YESNO)
327                 else:
328                         self.updateTitleList()
329
330         def resetTitles(self):
331                 count = 0
332                 for title in self.project.titles:
333                         count += 1
334                         title.initDVDmenuText(self.project,count)
335                 self.updateTitleList()
336
337         def DVDformatCB(self, answer):
338                 t = self.current_edit_title
339                 if answer == True:
340                         self.project.settings.authormode.setValue("data_ts")
341                         self.updateTitleList()
342                 else:
343                         self.removeTitle(t)
344
345         def leave(self):
346                 self.close()