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