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