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