Merge remote-tracking branch 'schwerkraft/master'
[enigma2-plugins.git] / moviecut / src_py / plugin.py
1 from Plugins.Plugin import PluginDescriptor
2 from Screens.Screen import Screen
3 from Screens.MessageBox import MessageBox
4 from Screens.ChoiceBox import ChoiceBox
5 from Screens.LocationBox import MovieLocationBox
6 import Screens.Standby
7 from Components.config import config, ConfigText, ConfigSelection, ConfigNothing, getConfigListEntry
8 from Components.ActionMap import ActionMap
9 from Components.ConfigList import ConfigList, ConfigListScreen
10 from Components.Sources.StaticText import StaticText
11 from enigma import eTimer, eServiceCenter, iServiceInformation, eConsoleAppContainer, eEnv
12 from os import access, chmod, X_OK
13 from __init__ import _
14
15 mcut_path = eEnv.resolve("${libdir}/enigma2/python/Plugins/Extensions/MovieCut/bin/mcut")
16
17 def main(session, service, **kwargs):
18         # Hack to make sure it is executable
19         if not access(mcut_path, X_OK):
20                 chmod(mcut_path, 493)
21         session.open(MovieCut, service, **kwargs)
22
23 def Plugins(**kwargs):
24         return PluginDescriptor(name="MovieCut", description=_("Execute cuts..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)
25
26
27 class MovieCut(ChoiceBox):
28         def __init__(self, session, service):
29                 self.service = service
30                 serviceHandler = eServiceCenter.getInstance()
31                 path = self.service.getPath()
32                 info = serviceHandler.info(self.service)
33                 if not info:
34                         self.name = path
35                 else:
36                         self.name = info.getName(self.service)
37                 tlist = [
38                         (_("Don't cut"), "CALLFUNC", self.confirmed0),
39                         (_("Replace the original movie with the cut movie"), "CALLFUNC", self.confirmed1),
40                         (_("Place the cut movie in a new file ending with \" cut\""), "CALLFUNC", self.confirmed2),
41                         (_("Advanced cut specification..."), "CALLFUNC", self.confirmed3),
42                 ]
43                 ChoiceBox.__init__(self, session, _("How would you like to cut \"%s\"?") % (self.name), list = tlist, selection = 0)
44                 self.skinName = "ChoiceBox"
45
46         def confirmed0(self, arg):
47                 self.close()
48
49         def confirmed1(self, arg):
50                 MovieCutSpawn(self.session, self, [mcut_path, "-r", self.service.getPath()], self.name)
51
52         def confirmed2(self, arg):
53                 MovieCutSpawn(self.session, self, [mcut_path, self.service.getPath()], self.name)
54
55         def confirmed3(self, arg):
56                 serviceHandler = eServiceCenter.getInstance()
57                 info = serviceHandler.info(self.service)
58                 path = self.service.getPath()
59                 self.name = info.getName(self.service)
60                 descr = info.getInfoString(self.service, iServiceInformation.sDescription)
61                 self.session.openWithCallback(self.advcutConfirmed, AdvancedCutInput, self.name, path, descr)
62
63         def advcutConfirmed(self, ret):
64                 if len(ret) <= 1 or not ret[0]:
65                         self.close()
66                         return
67                 clist = [mcut_path]
68                 if ret[1] == True:
69                         clist.append("-r")
70                 clist.append(self.service.getPath())
71                 if ret[2] != False:
72                         clist += ["-o", ret[2]]
73                 if ret[3] != False:
74                         clist += ["-n", ret[3]]
75                 if ret[4] != False:
76                         clist += ["-d", ret[4]]
77                 if ret[5] != False:
78                         clist.append("-c")
79                         clist += ret[5]
80                 MovieCutSpawn(self.session, self, clist, self.name)
81                 
82 class AdvancedCutInput(Screen, ConfigListScreen):
83         def __init__(self, session, name, path, descr):
84                 Screen.__init__(self, session)
85                 self.skinName = [ "AdvancedCutInput", "Setup" ]
86
87                 self["key_green"] = StaticText(_("OK"))
88                 self["key_red"] = StaticText(_("Cancel"))
89
90                 if self.baseName(path) == self.baseName(name):
91                         title = ""
92                 else:
93                         title = name
94                 dir = self.dirName(path)
95                 file = self.baseName(path) + " cut"
96                 self.input_replace = ConfigSelection(choices = [("no", _("No")), ("yes", _("Yes"))], default = "no")
97                 self.input_file = ConfigText(default = file, fixed_size = False, visible_width = 45)
98                 self.input_title = ConfigText(default = title, fixed_size = False, visible_width = 45)
99                 self.input_descr = ConfigText(default = descr, fixed_size = False, visible_width = 45)
100                 tmp = config.movielist.videodirs.value
101                 if not dir in tmp:
102                         tmp.append(dir)
103                 self.input_dir = ConfigSelection(choices = tmp, default = dir)
104                 self.input_manual = ConfigSelection(choices = [("no", _("Cutlist")), ("yes", _("Manual specification"))], default = "no")
105                 self.input_space = ConfigNothing()
106                 self.input_manualcuts = ConfigText(default = "", fixed_size = False)
107                 self.input_manualcuts.setUseableChars(" 0123456789:.")
108
109                 self["actions"] = ActionMap(["SetupActions"],
110                 {
111                         "ok": self.keySelectOrGo,
112                         "save": self.keyGo,
113                         "cancel": self.keyCancel,
114                 }, -2)
115
116                 self.list = []
117                 ConfigListScreen.__init__(self, self.list)
118                 self.entry_replace = getConfigListEntry(_("Replace original:"), self.input_replace)
119                 self.entry_file = getConfigListEntry(_("New filename:"), self.input_file)
120                 self.entry_title = getConfigListEntry(_("New title:"), self.input_title)
121                 self.entry_descr = getConfigListEntry(_("New description:"), self.input_descr)
122                 self.entry_dir = getConfigListEntry(_("New location:"), self.input_dir)
123                 self.entry_manual = getConfigListEntry(_("Cut source:"), self.input_manual)
124                 self.entry_space = getConfigListEntry(_("Cuts (an IN OUT IN OUT ... sequence of hour:min:sec)"), self.input_space)
125                 self.entry_manualcuts = getConfigListEntry(":", self.input_manualcuts)
126                 self.createSetup(self["config"])
127
128                 self.onLayoutFinish.append(self.layoutFinished)
129
130         def layoutFinished(self):
131                 self.setTitle(_("Cut Parameter Input"))
132
133         def createSetup(self, configlist):
134                 list = [
135                         self.entry_replace
136                 ]
137                 if self.input_replace.value == "no":
138                         list.extend((
139                                 self.entry_file,
140                                 self.entry_dir,
141                         ))
142                 list.extend((
143                         self.entry_title,
144                         self.entry_descr,
145                         self.entry_manual,
146                 ))
147                 if self.input_manual.value == "yes":
148                         list.extend((
149                                 self.entry_space,
150                                 self.entry_manualcuts,
151                         ))
152                 self.list = list
153                 configlist.list = list
154                 configlist.l.setList(list)
155
156         def keyLeft(self):
157                 ConfigListScreen.keyLeft(self)
158                 cc = self["config"].getCurrent()
159                 if cc is self.entry_replace or cc is self.entry_manual:
160                         self.createSetup(self["config"])
161
162         def keyRight(self):
163                 ConfigListScreen.keyRight(self)
164                 cc = self["config"].getCurrent()
165                 if cc is self.entry_replace or cc is self.entry_manual:
166                         self.createSetup(self["config"])
167
168         def pathSelected(self, res):
169                 if res is not None:
170                         if config.movielist.videodirs.value != self.input_dir.choices:
171                                 self.input_dir.setChoices(config.movielist.videodirs.value, default=res)
172                         self.input_dir.value = res
173
174         def keySelectOrGo(self):
175                 if self["config"].getCurrent() == self.entry_dir:
176                         self.session.openWithCallback(
177                                 self.pathSelected,
178                                 MovieLocationBox,
179                                 _("Choose target folder"),
180                                 self.input_dir.value,
181                         )
182                 else:
183                         self.keyGo()
184
185         def keyGo(self):
186                 if self.input_replace.value == "yes":
187                         path = False
188                 else:
189                         path = self.rejoinName(self.input_dir.value, self.input_file.value)
190                 if self.input_manual.value == "no":
191                         cuts = False
192                 else:
193                         cuts = self.input_manualcuts.value.split(' ')
194                         while "" in cuts:
195                                 cuts.remove("")
196                 self.close((True, self.input_replace.value == "yes", path, self.input_title.value, self.input_descr.value, cuts))
197
198         def keyCancel(self):
199                 self.close((False,))
200
201         def baseName(self, str):
202                 name = str.split('/')[-1]
203                 if name.endswith(".ts") is True:
204                         return name[:-3]
205                 else:
206                         return name
207
208         def dirName(self, str):
209                 return '/'.join(str.split('/')[:-1]) + '/'
210
211         def rejoinName(self, dir, name):
212                 name = name.strip()
213                 if name.endswith(".ts") is True:
214                         return dir + name[:-3]
215                 else:
216                         return dir + name
217
218 class MovieCutQueue:
219         def __init__(self):
220                 self.container = eConsoleAppContainer()
221                 self.appClosed_conn = self.container.appClosed.connect(self.runDone)
222                 self.queue = []
223                 self.running = False
224
225         def enqueue(self, cb, cmd):
226                 self.queue.append((cb, cmd))
227                 if not self.running:
228                         self.running = True
229                         self.runNext()
230                         return True
231                 else:
232                         return False
233
234         def runNext(self):
235                 if not self.queue:
236                         self.running = False
237                 else:
238                         self.container.execute(*self.queue[0][1])
239
240         def runDone(self, retval):
241                 cb = self.queue[0][0]
242                 self.queue = self.queue[1:]
243                 cb(retval)
244                 self.runNext()
245
246 global_mcut_errors = [_("The movie \"%s\" is successfully cut"),
247                       _("Cutting failed for movie \"%s\"")+":\n"+_("Bad arguments"),
248                       _("Cutting failed for movie \"%s\"")+":\n"+_("Couldn't open input .ts file"),
249                       _("Cutting failed for movie \"%s\"")+":\n"+_("Couldn't open input .cuts file"),
250                       _("Cutting failed for movie \"%s\"")+":\n"+_("Couldn't open input .ap file"),
251                       _("Cutting failed for movie \"%s\"")+":\n"+_("Couldn't open output .ts file"),
252                       _("Cutting failed for movie \"%s\"")+":\n"+_("Couldn't open output .cuts file"),
253                       _("Cutting failed for movie \"%s\"")+":\n"+_("Couldn't open output .ap file"),
254                       _("Cutting failed for movie \"%s\"")+":\n"+_("Empty .ap file"),
255                       _("Cutting failed for movie \"%s\"")+":\n"+_("No cuts specified"),
256                       _("Cutting failed for movie \"%s\"")+":\n"+_("Read/write error (disk full?)"),
257                       _("Cutting was aborted for movie \"%s\"")]
258
259 global_mcut_queue = MovieCutQueue()
260
261 global_mcut_block = False
262
263 class MovieCutSpawn:
264         def __init__(self, session, parent, clist, name):
265                 global global_mcut_queue
266                 global global_mcut_block
267                 self.session = session
268                 self.parent = parent
269                 self.name = name
270                 self.clist = [clist[0]] + clist
271                 self.mess = ""
272                 self.dialog = False
273                 self.waitTimer = eTimer()
274                 self.waitTimer_conn = self.waitTimer.timeout.connect(self.doWaitAck)
275                 if global_mcut_queue.enqueue(self.doAck, self.clist):
276                         mess = _("The movie \"%s\" is cut in the background.") % (self.name)
277                 else:
278                         mess = _("Another movie is currently cut.\nThe movie \"%s\" will be cut in the background after it.") % (self.name)
279                 global_mcut_block = True
280                 self.dialog = self.session.openWithCallback(self.endc, MessageBox, mess, MessageBox.TYPE_INFO)
281
282         def doAck(self, retval):
283                 global global_mcut_errors
284 #               if WIFEXITED(retval):
285 #                       self.mess = global_mcut_errors[WEXITSTATUS(retval)] % (self.name)
286 #               else:
287 #                       self.mess = global_mcut_errors[-1] % (self.name)
288                 if retval < 0 or retval > 10:
289                         self.mess = global_mcut_errors[11] % (self.name)
290                 else:
291                         self.mess = global_mcut_errors[retval] % (self.name)
292                 self.doWaitAck()
293
294         def doWaitAck(self):
295                 global global_mcut_block
296                 if Screens.Standby.inStandby or not self.session.in_exec or (global_mcut_block and not self.dialog):
297                         self.waitTimer.start(2000, True)
298                 else:
299                         global_mcut_block = True
300                         self.session.openWithCallback(self.endw, MessageBox, self.mess, MessageBox.TYPE_INFO)
301
302         def endw(self, arg = 0):
303                 global global_mcut_block
304                 global_mcut_block = False
305                 if self.session.current_dialog == self.dialog:
306                         self.session.current_dialog.close(True)
307                         self.endc(arg)
308
309         def endc(self, arg = 0):
310                 global global_mcut_block
311                 global_mcut_block = False
312                 self.dialog = False
313                 self.parent.close()
314 #               self.session.current_dialog.close()