FIX translations. Thanks to Spaeleus for the report.
[enigma2-plugins.git] / mosaic / src / plugin.py
1 # -*- coding: UTF-8 -*-
2 # Mosaic by AliAbdul
3 # needs the aio-screengrabber v0.8 by seddi
4 from Components.ActionMap import NumberActionMap
5 from Components.config import config, ConfigSubsection, ConfigInteger
6 from Components.Console import Console
7 from Components.Label import Label
8 from Components.Language import language
9 from Components.Pixmap import Pixmap
10 from Components.VideoWindow import VideoWindow
11 from enigma import eServiceCenter, eServiceReference, eTimer, loadPNG
12 from os import environ
13 from Plugins.Plugin import PluginDescriptor
14 from Screens.ChannelSelection import BouquetSelector
15 from Screens.MessageBox import MessageBox
16 from Screens.Screen import Screen
17 from Tools.Directories import fileExists, resolveFilename, SCOPE_SKIN_IMAGE, SCOPE_LANGUAGE, SCOPE_PLUGINS
18 from Tools.LoadPixmap import LoadPixmap
19 import gettext
20
21 ################################################
22
23 grab_binary = "/usr/bin/grab"
24 grab_picture = "/tmp/mosaic.jpg"
25 grab_errorlog = "/tmp/mosaic.log"
26
27 config_limits = (3, 30)
28 config.plugins.Mosaic = ConfigSubsection()
29 config.plugins.Mosaic.countdown = ConfigInteger(default=5, limits=config_limits)
30
31 playingIcon = loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, 'skin_default/icons/ico_mp_play.png'))
32 pausedIcon = loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, 'skin_default/icons/ico_mp_pause.png'))
33
34 ################################################
35
36 lang = language.getLanguage()
37 environ["LANGUAGE"] = lang[:2]\r
38 gettext.bindtextdomain("enigma2", resolveFilename(SCOPE_LANGUAGE))\r
39 gettext.textdomain("enigma2")
40 gettext.bindtextdomain("Mosaic", "%s%s" % (resolveFilename(SCOPE_PLUGINS), "Extensions/Mosaic/locale/"))
41
42 def _(txt):\r
43         t = gettext.dgettext("Mosaic", txt)\r
44         if t == txt:\r
45                 t = gettext.gettext(txt)\r
46         return t
47
48 ################################################
49
50 class Mosaic(Screen):
51         PLAY = 0
52         PAUSE = 1
53
54         skin = """
55         <screen position="0,0" size="720,576" title="Mosaic" flags="wfNoBorder" backgroundColor="#ffffff" >
56                 <widget name="playState" position="55,55" size="16,16" alphatest="on" />
57                 <eLabel position="78,54" size="180,144" />
58                 <eLabel position="274,54" size="180,144" />
59                 <eLabel position="470,54" size="180,144" />
60                 <eLabel position="78,221" size="180,144" />
61                 <eLabel position="274,221" size="180,144" />
62                 <eLabel position="470,221" size="180,144" />
63                 <eLabel position="78,388" size="180,144" />
64                 <eLabel position="274,388" size="180,144" />
65                 <eLabel position="470,388" size="180,144" />
66                 <widget name="channel1" position="80,32" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
67                 <widget name="channel2" position="276,32" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
68                 <widget name="channel3" position="472,32" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
69                 <widget name="channel4" position="80,198" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
70                 <widget name="channel5" position="276,198" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
71                 <widget name="channel6" position="472,198" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
72                 <widget name="channel7" position="80,366" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
73                 <widget name="channel8" position="276,366" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
74                 <widget name="channel9" position="472,366" size="176,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
75                 <widget name="window1" position="78,54" zPosition="1" size="180,144" />
76                 <widget name="window2" position="274,54" zPosition="1" size="180,144" />
77                 <widget name="window3" position="470,54" zPosition="1" size="180,144" />
78                 <widget name="window4" position="78,221" zPosition="1" size="180,144" />
79                 <widget name="window5" position="274,221" zPosition="1" size="180,144" />
80                 <widget name="window6" position="470,221" zPosition="1" size="180,144" />
81                 <widget name="window7" position="78,388" zPosition="1" size="180,144" />
82                 <widget name="window8" position="274,388" zPosition="1" size="180,144" />
83                 <widget name="window9" position="470,388" zPosition="1" size="180,144" />
84                 <widget name="video1" position="78,54" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
85                 <widget name="video2" position="274,54" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
86                 <widget name="video3" position="470,54" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
87                 <widget name="video4" position="78,221" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
88                 <widget name="video5" position="274,221" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
89                 <widget name="video6" position="470,221" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
90                 <widget name="video7" position="78,388" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
91                 <widget name="video8" position="274,388" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
92                 <widget name="video9" position="470,388" zPosition="2" size="180,144" backgroundColor="#ffffffff" />
93                 <widget name="event1" position="78,54" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
94                 <widget name="event2" position="274,54" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
95                 <widget name="event3" position="470,54" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
96                 <widget name="event4" position="78,221" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
97                 <widget name="event5" position="274,221" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
98                 <widget name="event6" position="470,221" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
99                 <widget name="event7" position="78,388" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
100                 <widget name="event8" position="274,388" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
101                 <widget name="event9" position="470,388" size="180,20" zPosition="3" font="Regular;18" backgroundColor="#000000" foregroundColor="#ffffff" />
102                 <widget name="countdown" position="80,535" size="175,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" />
103                 <widget name="count" position="472,535" size="175,20" font="Regular;18" backgroundColor="#ffffff" foregroundColor="#000000" halign="right" />
104         </screen>"""
105
106         def __init__(self, session, services):
107                 Screen.__init__(self, session)
108                 
109                 self.session = session
110                 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
111                 self.consoleCmd = ""
112                 self.Console = Console()
113                 self.serviceHandler = eServiceCenter.getInstance()
114                 self.ref_list = services
115                 self.window_refs = [None, None, None, None, None, None, None, None, None]
116                 self.current_refidx = 0
117                 self.current_window = 1
118                 self.countdown = config.plugins.Mosaic.countdown.value
119                 self.working = False
120                 self.state = self.PLAY
121                 
122                 self["playState"] = Pixmap()
123                 for i in range(1, 10):
124                         self["window" + str(i)] = Pixmap()
125                         self["video" + str(i)] = VideoWindow(decoder = 0)
126                         self["video" + str(i)].hide()
127                         self["channel" + str(i)] = Label("")
128                         self["event" + str(i)] = Label("")
129                         self["event" + str(i)].hide()
130                 self["video1"].decoder = 0
131                 self["video1"].show()
132                 self["countdown"] = Label()
133                 self.updateCountdownLabel()
134                 self["count"] = Label()
135                 
136                 self["actions"] = NumberActionMap(["MosaicActions"],
137                         {
138                                 "ok": self.exit,
139                                 "cancel": self.closeWithOldService,
140                                 "green": self.play,
141                                 "yellow": self.pause,
142                                 "channelup": self.countdownPlus,
143                                 "channeldown": self.countdownMinus,
144                                 "1": self.numberPressed,
145                                 "2": self.numberPressed,
146                                 "3": self.numberPressed,
147                                 "4": self.numberPressed,
148                                 "5": self.numberPressed,
149                                 "6": self.numberPressed,
150                                 "7": self.numberPressed,
151                                 "8": self.numberPressed,
152                                 "9": self.numberPressed
153                         }, prio=-1)
154                 
155                 self.updateTimer = eTimer()
156                 self.updateTimer.timeout.get().append(self.updateCountdown)
157                 self.checkTimer = eTimer()
158                 self.checkTimer.timeout.get().append(self.checkGrab)
159                 self.checkTimer.start(500, 1)
160
161         def checkGrab(self):
162                 if fileExists(grab_binary):
163                         # Start the first service in the bouquet and show the service-name
164                         ref = self.ref_list[0]
165                         self.window_refs[0] = ref
166                         info = self.serviceHandler.info(ref)
167                         name = info.getName(ref).replace('\xc2\x86', '').replace('\xc2\x87', '')
168                         event_name = self.getEventName(info, ref)
169                         self["channel1"].setText(name)
170                         self["event1"].setText(event_name)
171                         self.session.nav.playService(ref)
172                         self["count"].setText(_("Channel: ") + "1 / " + str(len(self.ref_list)))
173                         self["playState"].instance.setPixmap(playingIcon)
174                         
175                         # Start updating the video-screenshots
176                         self.updateTimer.start(1, 1)
177                 else:
178                         self.session.openWithCallback(self.exit, MessageBox, _("%s does not exist!") % grab_binary, MessageBox.TYPE_ERROR, timeout=5)
179
180         def exit(self, callback=None):
181                 self.deleteConsoleCallbacks()
182                 self.close()
183
184         def deleteConsoleCallbacks(self):
185                 if self.Console.appContainers.has_key(self.consoleCmd):
186                         del self.Console.appContainers[self.consoleCmd].dataAvail[:]
187                         del self.Console.appContainers[self.consoleCmd].appClosed[:]
188                         del self.Console.appContainers[self.consoleCmd]
189                         del self.Console.extra_args[self.consoleCmd]
190                         del self.Console.callbacks[self.consoleCmd]
191
192         def closeWithOldService(self):
193                 self.session.nav.playService(self.oldService)
194                 self.deleteConsoleCallbacks()
195                 self.close()
196
197         def numberPressed(self, number):
198                 ref = self.window_refs[number-1]
199                 if ref is not None:
200                         self.session.nav.playService(ref)
201                         self.deleteConsoleCallbacks()
202                         self.close()
203
204         def play(self):
205                 if self.working == False and self.state == self.PAUSE:
206                         self.state = self.PLAY
207                         self.updateTimer.start(1000, 1)
208                         self["playState"].instance.setPixmap(playingIcon)
209
210         def pause(self):
211                 if self.working == False and self.state == self.PLAY:
212                         self.state = self.PAUSE
213                         self.updateTimer.stop()
214                         self["playState"].instance.setPixmap(pausedIcon)
215
216         def countdownPlus(self):
217                 self.changeCountdown(1)
218
219         def countdownMinus(self):
220                 self.changeCountdown(-1)
221
222         def changeCountdown(self, direction):
223                 if self.working == False:
224                         configNow = config.plugins.Mosaic.countdown.value
225                         configNow += direction
226                         
227                         if configNow < config_limits[0]:
228                                 configNow = config_limits[0]
229                         elif configNow > config_limits[1]:
230                                 configNow = config_limits[1]
231                         
232                         config.plugins.Mosaic.countdown.value = configNow
233                         config.plugins.Mosaic.countdown.save()
234                         
235                         self.updateCountdownLabel()
236
237         def makeNextScreenshot(self):
238                 # Grab video
239                 if not self.Console:
240                         self.Console = Console()
241                 self.consoleCmd = "%s -v -r 180 -l -j 100 %s" % (grab_binary, grab_picture)
242                 self.Console.ePopen(self.consoleCmd, self.showNextScreenshot)
243
244         def showNextScreenshot(self, result, retval, extra_args):
245                 if retval == 0:
246                         # Show screenshot in the current window
247                         pic = LoadPixmap(grab_picture)
248                         self["window" + str(self.current_window)].instance.setPixmap(pic)
249                 
250                         # Hide current video-window and show the running event-name
251                         self["video" + str(self.current_window)].hide()
252                         self["event" + str(self.current_window)].show()
253                 
254                         # Get next ref
255                         self.current_refidx += 1
256                         if self.current_refidx > (len(self.ref_list) -1):
257                                 self.current_refidx = 0
258                 
259                         # Play next ref
260                         ref = self.ref_list[self.current_refidx]
261                         info = self.serviceHandler.info(ref)
262                         name = info.getName(ref).replace('\xc2\x86', '').replace('\xc2\x87', '')
263                         event_name = self.getEventName(info, ref)
264                         self.session.nav.playService(ref)
265                 
266                         # Get next window index
267                         self.current_window += 1
268                         if self.current_window > 9:
269                                 self.current_window = 1
270                 
271                         # Save the ref
272                         self.window_refs[self.current_window-1] = ref
273                 
274                         # Save the event-name and hide the label
275                         self["event" + str(self.current_window)].hide()
276                         self["event" + str(self.current_window)].setText(event_name)
277                 
278                         # Show the new video-window
279                         self["video" + str(self.current_window)].show()
280                         self["video" + str(self.current_window)].decoder = 0
281                 
282                         # Show the servicename
283                         self["channel" + str(self.current_window)].setText(name)
284                         self["count"].setText(_("Channel: ") + str(self.current_refidx + 1) + " / " + str(len(self.ref_list)))
285                 
286                         # Restart timer
287                         self.working = False
288                         self.updateTimer.start(1, 1)
289                 else:
290                         print "[Mosaic] retval: %d result: %s" % (retval, result)
291                         
292                         try:
293                                 f = open(grab_errorlog, "w")
294                                 f.write("retval: %d\nresult: %s" % (retval, result))
295                                 f.close()
296                         except:
297                                 pass
298                         
299                         self.session.openWithCallback(self.exit, MessageBox, _("Error while creating screenshot. You need grab version 0.8 or higher!"), MessageBox.TYPE_ERROR, timeout=5)
300
301         def updateCountdown(self, callback=None):
302                 self.countdown -= 1
303                 self.updateCountdownLabel()
304                 if self.countdown == 0:
305                         self.countdown = config.plugins.Mosaic.countdown.value
306                         self.working = True
307                         self.makeNextScreenshot()
308                 else:
309                         self.updateTimer.start(1000, 1)
310
311         def updateCountdownLabel(self):
312                 self["countdown"].setText("%s %s / %s" % (_("Countdown:"), str(self.countdown), str(config.plugins.Mosaic.countdown.value)))
313
314         def getEventName(self, info, ref):
315                 event = info.getEvent(ref)
316                 if event is not None:
317                         eventName = event.getEventName()
318                         if eventName is None:
319                                 eventName = ""
320                 else:
321                         eventName = ""
322                 return eventName
323
324 ################################################
325 # Most stuff stolen from the GraphMultiEPG
326
327 Session = None
328 Servicelist = None
329 BouquetSelectorScreen = None
330
331 def getBouquetServices(bouquet):
332         services = []
333         Servicelist = eServiceCenter.getInstance().list(bouquet)
334         if Servicelist is not None:
335                 while True:
336                         service = Servicelist.getNext()
337                         if not service.valid():
338                                 break
339                         if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker):
340                                 continue
341                         services.append(service)
342         return services
343
344 def closeBouquetSelectorScreen(ret=None):
345         if BouquetSelectorScreen is not None:
346                 BouquetSelectorScreen.close()
347
348 def openMosaic(bouquet):
349         if bouquet is not None:
350                 services = getBouquetServices(bouquet)
351                 if len(services):
352                         Session.openWithCallback(closeBouquetSelectorScreen, Mosaic, services)
353
354 def main(session, servicelist, **kwargs):
355         global Session
356         Session = session
357         global Servicelist
358         Servicelist = servicelist
359         global BouquetSelectorScreen
360         
361         bouquets = Servicelist.getBouquetList()
362         if bouquets is not None:
363                 if len(bouquets) == 1:
364                         self.openMosaic(bouquets[0][1])
365                 elif len(bouquets) > 1:
366                         BouquetSelectorScreen = Session.open(BouquetSelector, bouquets, openMosaic, enableWrapAround=True)
367
368 def Plugins(**kwargs):
369         return PluginDescriptor(name=_("Mosaic"), where=PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main)