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