InfoBarTunerState 1.1.6: Added SecondInfobar Handling
[enigma2-plugins.git] / infobartunerstate / src / InfoBarTunerState.py
1 #######################################################################
2 #
3 #    InfoBar Tuner State for Enigma-2
4 #    Coded by betonme (c) 2011 <glaserfrank(at)gmail.com>
5 #    Support: http://www.i-have-a-dreambox.com/wbb2/thread.php?threadid=162629
6 #
7 #    This program is free software; you can redistribute it and/or
8 #    modify it under the terms of the GNU General Public License
9 #    as published by the Free Software Foundation; either version 2
10 #    of the License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU General Public License for more details.
16 #
17 #######################################################################
18
19 # for localized messages
20 from . import _
21
22 import math
23 import os
24 import NavigationInstance
25 import socket
26 import sys
27
28 from collections import defaultdict
29 from operator import attrgetter, itemgetter
30 try:
31         from itertools import izip_longest as zip_longest # py2x
32 except:
33         from itertools import zip_longest # py3k
34
35 # Plugin
36 from Plugins.Plugin import PluginDescriptor
37
38 # Config
39 from Components.config import *
40
41 # Screen
42 from Components.Label import Label
43 from Components.Language import *
44 from Components.Pixmap import Pixmap, MultiPixmap
45 from Components.ProgressBar import ProgressBar
46 from Components.ServiceEventTracker import ServiceEventTracker
47 from Screens.Screen import Screen
48 from Screens.InfoBar import InfoBar
49 from Screens.InfoBarGenerics import InfoBarShowHide
50 from Screens.Screen import Screen
51 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
52
53 from ServiceReference import ServiceReference
54 from time import strftime, time, localtime, mktime
55 from datetime import datetime, timedelta
56
57 from enigma import iServiceInformation, ePoint, eSize, getDesktop, iFrontendInformation
58 from enigma import eTimer
59 from enigma import iPlayableService, iRecordableService
60 from enigma import eDVBResourceManager, eActionMap, eListboxPythonMultiContent, eListboxPythonStringContent, eListbox, gFont, RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_HALIGN_CENTER, eEPGCache, eServiceCenter, eServiceReference
61
62 from skin import parseColor, parseFont
63
64 try:
65         from Plugins.Extensions.2IB import SecondInfoBar
66         SecondInfobarAvailable = True
67 except:
68         SecondInfobarAvailable = False
69
70 # Plugin internal
71 from netstat import netstat
72
73
74 # Extenal plugins: WebInterface
75 try:
76         from Plugins.Extensions.WebInterface.WebScreens import StreamingWebScreen 
77 except:
78         StreamingWebScreen = None
79
80
81 # Globals
82 InfoBarShow = None
83 InfoBarHide = None
84 InfoBarToggle = None
85
86
87 # Type Enum
88 INFO, RECORD, STREAM, FINISHED = range( 4 )
89
90
91 # Constants
92 INFINITY =  u"\u221E".encode("utf-8")
93
94
95 #######################################################
96 # InfoBarShowHide for MoviePlayer integration
97 def overwriteInfoBar():
98         global InfoBarShow, InfoBarHide, InfoBarToggle
99         if config.infobartunerstate.show_infobar.value:
100                 if InfoBarShow is None:
101                         # Backup original function
102                         InfoBarShow = InfoBarShowHide._InfoBarShowHide__onShow
103                         # Overwrite function
104                         InfoBarShowHide._InfoBarShowHide__onShow = InfoBarShowTunerState
105         if InfoBarHide is None:
106                 # Backup original function
107                 InfoBarHide = InfoBarShowHide._InfoBarShowHide__onHide
108                 # Overwrite function
109                 InfoBarShowHide._InfoBarShowHide__onHide = InfoBarHideTunerState
110         if config.infobartunerstate.show_ontoggle.value:
111                 if InfoBarToggle is None:
112                         # Backup original function
113                         InfoBarToggle = InfoBarShowHide.toggleShow
114                         # Overwrite function
115                         InfoBarShowHide.toggleShow = InfoBarToggleTunerState
116
117 # InfoBar Events
118 def recoverInfoBar():
119         global InfoBarShow, InfoBarHide, InfoBarToggle
120         if InfoBarShow:
121                 InfoBarShowHide._InfoBarShowHide__onShow = InfoBarShow
122                 InfoBarShow = None
123         if InfoBarHide:
124                 InfoBarShowHide._InfoBarShowHide__onHide = InfoBarHide
125                 InfoBarHide = None
126         if InfoBarToggle:
127                 InfoBarShowHide.toggleShow = InfoBarToggle
128                 InfoBarToggle = None
129
130
131 def InfoBarShowTunerState(self):
132         from Plugins.Extensions.InfoBarTunerState.plugin import gInfoBarTunerState
133         global gInfoBarTunerState
134         global InfoBarShow
135         if InfoBarShow:
136                 InfoBarShow(self)
137         if gInfoBarTunerState:
138                 gInfoBarTunerState.show()
139
140 def InfoBarHideTunerState(self):
141         from Plugins.Extensions.InfoBarTunerState.plugin import gInfoBarTunerState
142         global gInfoBarTunerState
143         global InfoBarHide
144         if InfoBarHide:
145                 InfoBarHide(self)
146         if gInfoBarTunerState:
147                 gInfoBarTunerState.hide()
148
149 def InfoBarToggleTunerState(self):
150         from Plugins.Extensions.InfoBarTunerState.plugin import gInfoBarTunerState
151         global gInfoBarTunerState
152         global InfoBarToggle
153         if InfoBarToggle:
154                 InfoBarToggle(self)
155         if gInfoBarTunerState:
156                 gInfoBarTunerState.toggle()
157
158
159 #######################################################
160 # Extension menu
161 def addExtension():
162         # Add to extension menu
163         from Components.PluginComponent import plugins
164         from Plugins.Extensions.InfoBarTunerState.plugin import IBTSSHOW, IBTSSETUP, show, setup
165         if plugins:
166                 if config.infobartunerstate.extensions_menu_show.value:
167                         for p in plugins.getPlugins( where = PluginDescriptor.WHERE_EXTENSIONSMENU ):
168                                 if p.name == IBTSSHOW:
169                                         # Plugin is already in menu
170                                         break
171                         else:
172                                 # Plugin not in menu - add it
173                                 plugin = PluginDescriptor(name = IBTSSHOW, description = IBTSSHOW, where = PluginDescriptor.WHERE_EXTENSIONSMENU, needsRestart = False, fnc = show)
174                                 plugins.plugins[PluginDescriptor.WHERE_EXTENSIONSMENU].append(plugin)
175                 if config.infobartunerstate.extensions_menu_setup.value:
176                         for p in plugins.getPlugins( where = PluginDescriptor.WHERE_EXTENSIONSMENU ):
177                                 if p.name == IBTSSETUP:
178                                         # Plugin is already in menu
179                                         break
180                         else:
181                                 # Plugin not in menu - add it
182                                 plugin = PluginDescriptor(name = IBTSSETUP, description = IBTSSETUP, where = PluginDescriptor.WHERE_EXTENSIONSMENU, needsRestart = False, fnc = setup)
183                                 plugins.plugins[PluginDescriptor.WHERE_EXTENSIONSMENU].append(plugin)
184
185 def removeExtension():
186         # Remove from extension menu
187         from Components.PluginComponent import plugins
188         from Plugins.Extensions.InfoBarTunerState.plugin import IBTSSHOW, IBTSSETUP
189         if config.infobartunerstate.extensions_menu_show.value:
190                 for p in plugins.getPlugins( where = PluginDescriptor.WHERE_EXTENSIONSMENU ):
191                         if p.name == IBTSSHOW:
192                                 plugins.plugins[PluginDescriptor.WHERE_EXTENSIONSMENU].remove(p)
193                                 break
194         if config.infobartunerstate.extensions_menu_setup.value:
195                 for p in plugins.getPlugins( where = PluginDescriptor.WHERE_EXTENSIONSMENU ):
196                         if p.name == IBTSSETUP:
197                                 plugins.plugins[PluginDescriptor.WHERE_EXTENSIONSMENU].remove(p)
198                                 break
199
200 #######################################################
201 # Logical background task
202 class InfoBarTunerState(object):
203         def __init__(self, session):
204                 self.session = session
205                 
206                 self._shown = False
207                 
208                 self.infobar = None
209                 self.info = None
210                 
211                 self.epg = eEPGCache.getInstance()
212                 
213                 #TODO showTimer is used to avoid several recalls
214                 #TODO find another solution, e.g.
215                 # if IBTS is already shown, skip
216                 self.showTimer = eTimer()
217                 self.showTimer_conn = self.showTimer.timeout.connect(self.tunerShow)
218                 
219                 self.hideTimer = eTimer()
220                 self.hideTimer_conn = self.hideTimer.timeout.connect(self.tunerHide)
221                 
222                 self.updateTimer = eTimer()
223                 self.updateTimer_conn = self.updateTimer.timeout.connect(self.update)
224                 
225                 self.forceBindInfoBarTimer = eTimer()
226                 self.forceBindInfoBarTimer_conn = self.forceBindInfoBarTimer.timeout.connect(self.bindInfoBar)
227                 
228                 self.entries = defaultdict(list)
229                 
230                 # Get Initial Skin parameters
231                 win = self.session.instantiateDialog(TunerStateBase)
232                 self.positionx = win.instance.position().x()
233                 self.positiony = win.instance.position().y()
234                 self.height = win.instance.size().height()
235                 self.spacing = win.spacing
236                 self.padding = win.padding
237                 
238                 desktopSize = getDesktop(0).size()
239                 self.desktopwidth = desktopSize.width()
240                 
241                 #TODO is it possible to create copies of a screen to avoid recreation
242                 win.close()
243                 
244                 # Bind recording and streaming events
245                 self.appendEvents()
246                 
247                 # Bind InfoBarEvents
248                 #self.bindInfoBar()
249                 #self.onLayoutFinish.append(self.bindInfoBar)
250                 # Workaround
251                 # The Plugin starts before the InfoBar is instantiated
252                 # Check every second if the InfoBar instance exists and try to bind our functions
253                 # Is there an alternative solution?
254                 if config.infobartunerstate.show_infobar.value:
255                         self.forceBindInfoBarTimer.start(1000, False)
256                 
257                 #if config.infobartunerstate.show_overwrite.value:
258                 overwriteInfoBar()
259                 
260                 #TODO PiP
261                 #self.session.
262                 #InfoBar.instance.session
263                 #pip.currentService = service
264                 #pip.pipservice = iPlayableService
265                 #Events:
266                 #eventNewProgramInfo
267                 #decoder state
268                                 
269                 # Add current running records / streams
270                 # We do it right here to ensure the InfoBar is intantiated
271                 self.updateRecordTimer()
272                 if config.infobartunerstate.show_streams.value:
273                         self.updateStreams()
274
275         def appendEvents(self):
276                 # Recording Events
277                 # If we append our function, we will never see the timer state StateEnded for repeating timer
278                 if self.__onRecordingEvent not in self.session.nav.RecordTimer.on_state_change:
279                         self.session.nav.RecordTimer.on_state_change.insert(0, self.__onRecordingEvent)
280                 # Streaming Events
281                 if config.infobartunerstate.show_streams.value:
282                         if StreamingWebScreen:
283                                 try:
284                                         from Plugins.Extensions.WebInterface.WebScreens import streamingEvents
285                                         if self.__onStreamingEvent not in streamingEvents:
286                                                 streamingEvents.append(self.__onStreamingEvent)
287                                 except:
288                                         pass
289
290         def removeEvents(self):
291                 # Recording Events
292                 # If we append our function, we will never see the timer state StateEnded for repeating timer
293                 if self.__onRecordingEvent in self.session.nav.RecordTimer.on_state_change:
294                         self.session.nav.RecordTimer.on_state_change.remove(self.__onRecordingEvent)
295                 # Streaming Events
296                 if StreamingWebScreen:
297                         try:
298                                 from Plugins.Extensions.WebInterface.WebScreens import streamingEvents
299                                 if self.__onStreamingEvent in streamingEvents:
300                                         streamingEvents.remove(self.__onStreamingEvent)
301                         except:
302                                 pass
303
304         def bindInfoBar(self):
305                 # Reimport InfoBar to force update of the class instance variable
306                 # Rebind only if it isn't done already 
307                 from Screens.InfoBar import InfoBar
308                 if InfoBar.instance:
309                         self.infobar = InfoBar.instance
310                         bindShow = False
311                         bindHide = False
312                         if hasattr(InfoBar.instance, "onShow"):
313                                 if self.__onInfoBarEventShow not in InfoBar.instance.onShow:
314                                         InfoBar.instance.onShow.append(self.__onInfoBarEventShow)
315                                 bindShow = True
316                         if hasattr(InfoBar.instance, "onHide"):
317                                 if self.__onInfoBarEventHide not in InfoBar.instance.onHide:
318                                         InfoBar.instance.onHide.append(self.__onInfoBarEventHide)
319                                 bindHide = True
320                         if bindShow and bindHide:
321                                 # Bind was successful
322                                 self.forceBindInfoBarTimer.stop()
323
324         def unbindInfoBar(self):
325                 if self.infobar:
326                         if hasattr(self.infobar, "onShow"):
327                                 if self.__onInfoBarEventShow in self.infobar.onShow:
328                                         self.infobar.onShow.remove(self.__onInfoBarEventShow)
329                         if hasattr(self.infobar, "onHide"):
330                                 if self.__onInfoBarEventHide in self.infobar.onHide:
331                                         self.infobar.onHide.remove(self.__onInfoBarEventHide)
332
333         def __onInfoBarEventShow(self):
334                 self.show()
335
336         def __onInfoBarEventHide(self):
337                 self.hide()
338
339         def __onRecordingEvent(self, timer):
340                 if not timer.justplay:
341                         print "IBTS Timer Event "+ str(timer.state) + ' ' + str(timer.repeated)
342 #TODO
343 # w.processRepeated()
344 # w.state = TimerEntry.StateWaiting
345                         if timer.state == timer.StatePrepared:
346                                 print "IBTS StatePrepared"
347                                 pass
348                         
349                         elif timer.state == timer.StateRunning:
350                                 id = getTimerID( timer )
351                                 print "IBTS Timer running ID", id, id in self.entries
352                                 if id not in self.entries:
353                                         #channel = timer.service_ref.getServiceName()
354                                         tuner, tunertype = getTuner(timer.record_service)
355                                                 
356                                         #TEST Bug Repeating timer blocking tuner and are not marked as finished
357                                         #timer.timeChanged = self.__OnTimeChanged
358                                         
359                                         name = timer.name
360                                         service_ref = timer.service_ref
361                                         
362                                         # Is this really necessary?
363                                         try: timer.Filename
364                                         except: timer.calculateFilename()
365                                         filename = timer.Filename
366                                         
367                                         # Delete references to avoid blocking tuners
368                                         del timer
369                                         
370                                         number = service_ref and getNumber(service_ref.ref)
371                                         channel = service_ref and service_ref.getServiceName()
372                                         
373                                         win = self.session.instantiateDialog(TunerState, RECORD, tuner, tunertype, name, number, channel, filename)
374                                         self.entries[id] = win
375                                         if config.infobartunerstate.show_events.value:
376                                                 self.show(True)
377                         
378                         # Finished repeating timer will report the state StateEnded+1 or StateWaiting
379                         else:
380                                 id = getTimerID( timer )
381                                 # The id of a finished repeated timer can be changed
382                                 #RecordTimerEntry(name=How I Met Your Mother, begin=Wed Jul 18 11:37:00 2012, serviceref=1:0:19:EF75:3F9:1:C00000:0:0:0:, justplay=False)
383                                 #RecordTimerEntry(name=How I Met Your Mother, begin=Thu Jul 19 11:37:00 2012, serviceref=1:0:19:EF75:3F9:1:C00000:0:0:0:, justplay=False)
384                                 #print "IBTS Timer finished ID", id, id in self.entries
385                                 if id in self.entries:
386                                         win = self.entries[id]
387                                         
388                                         begin = timer.begin
389                                         end = timer.end
390                                         endless = timer.autoincrease
391                                         del timer
392                                         
393                                         win.updateType( FINISHED )
394                                         win.updateTimes( begin, end, endless )
395                                 
396                                 # Show also if no matching id is found
397                                 if config.infobartunerstate.show_events.value:
398                                         self.show(True)
399
400         def __onStreamingEvent(self, event, stream):
401                 if StreamingWebScreen and stream:
402                         print "IBTS Stream Event"
403                         if event == StreamingWebScreen.EVENT_START:
404                                 
405                                 try:
406                                         from Plugins.Extensions.WebInterface.WebScreens import streamingScreens
407                                 except:
408                                         streamingScreens = []
409                                 
410                                 # Extract parameters
411                                 tuner, tunertype = getTuner( stream.getRecordService() ) 
412                                 ref = stream.getRecordServiceRef()
413                                 ip = stream.clientIP
414                                 id = getStreamID(stream)
415                                 
416                                 # Delete references to avoid blocking tuners
417                                 del stream
418                                 
419                                 port, host, client = "", "", ""
420                                 
421 #                               # Workaround to retrieve the client ip
422 #                               # Change later and use the WebScreens getActiveStreamingClients if implemented
423 #                               ipports = [ (win.ip, win.port) for win in self.entries.itervalues() ]
424 #                               for conn in netstat(getstate='ESTABLISHED', getuid=False, getpid=False, readable=False):
425 #                                       # Check if it is a streaming connection
426 #                                       if conn[3] == '8001':
427 #                                               ip = conn[4]
428 #                                               port = conn[5]
429 #                                               # Check if ip and port is already known
430 #                                               if (ip, port) not in ipports:
431 #                                                       break
432 #                               else:
433 #                                       # No new connection found, leave it empty
434 #                                       ip, port, = "", ""
435                                 
436                                 #TODO Port is actually not given
437                                 
438                                 event = ref and self.epg and self.epg.lookupEventTime(ref, -1, 0)
439                                 if event: 
440                                         name = event.getEventName()
441                                 else:
442                                         name = ""
443                                         #TODO check file streaming
444                                 
445                                 service_ref = ServiceReference(ref)
446                                 filename = "" #TODO file streaming - read meta eit
447                                 
448                                 try:
449                                         host = ip and socket.gethostbyaddr( ip )
450                                         client = host and host[0].split('.')[0]
451                                 except socket.herror, x:
452                                         pass
453                                 
454                                 number = service_ref and getNumber(service_ref.ref)
455                                 channel = service_ref and service_ref.getServiceName()
456                                 channel = channel.replace('\xc2\x86', '').replace('\xc2\x87', '')
457                                 
458                                 win = self.session.instantiateDialog(TunerState, STREAM, tuner, tunertype, name, number, channel, filename, client, ip, port)
459                                 self.entries[id] = win
460                                 if config.infobartunerstate.show_events.value:
461                                         self.show(True)
462                         
463                         elif event == StreamingWebScreen.EVENT_END:
464                                 
465                                 # Remove Finished Stream
466                                 id = getStreamID(stream)
467                                 
468                                 # Delete references to avoid blocking tuners
469                                 del stream
470                                 
471                                 if id in self.entries:
472                                         win = self.entries[id]
473                                         
474                                         begin = win.begin
475                                         end = time()
476                                         endless = False
477                                         
478                                         win.updateType( FINISHED )
479                                         win.updateTimes( begin, end, endless )
480                                         
481                                         if config.infobartunerstate.show_events.value:
482                                                 self.show(True)
483
484         def __OnTimeChanged(self):
485                 #TODO Config show on timer time changed
486                 self.show(True)
487
488         def updateRecordTimer(self):
489                 for timer in NavigationInstance.instance.RecordTimer.timer_list:
490                         if timer.isRunning() and not timer.justplay:
491                                 self.__onRecordingEvent(timer)
492
493         def updateStreams(self):
494                 #TODO updateStreams but retrieving IP is not possible
495                 try:
496                         from Plugins.Extensions.WebInterface.WebScreens import streamingScreens
497                 except:
498                         streamingScreens = []
499                 
500                 #TODO file streaming actually not supported
501                 for stream in streamingScreens:
502                         # Check if screen exists
503                         if stream and stream.request and 'file' not in stream.request.args:
504                                 self.__onStreamingEvent(StreamingWebScreen.EVENT_START, stream)
505
506         def updateNextTimer(self):
507                 number_pending_records = int( config.infobartunerstate.number_pending_records.value )
508                 print "IBTS updateNextTimer", number_pending_records
509                 
510                 nextwins = [ id for id in self.entries.keys() if id.startswith('next')]
511                 
512                 if number_pending_records:
513                         pending_seconds = int( config.infobartunerstate.pending_hours.value ) * 3600
514                         pending_limit = (time() + pending_seconds) if pending_seconds else 0
515                         
516                         timer_list = getNextPendingRecordTimers()[:number_pending_records]
517                         
518                         if timer_list:
519                                 timer_list.reverse()
520                                 
521                                 for i, (timer, begin, end) in enumerate(timer_list):
522                                         id = 'next'+str(i)
523                                         if timer:
524                                                 name = timer.name
525                                                 service_ref = timer.service_ref
526                                                 
527                                                 # Is this really necessary?
528                                                 try: timer.Filename
529                                                 except: timer.calculateFilename()
530                                                 
531                                                 try: filename = timer.Filename
532                                                 except: filename = timer.name
533                                                 
534                                                 # Delete references to avoid blocking tuners
535                                                 del timer
536                                                 
537                                                 if pending_limit and pending_limit < begin:
538                                                         # Skip timer
539                                                         continue
540                                                 
541                                                 number = service_ref and getNumber(service_ref.ref)
542                                                 channel = service_ref and service_ref.getServiceName()
543                                                 
544                                                 # Only add timer if not recording
545                                                 #if not self.entries.has_key(str( timer ):
546                                                 if self.entries.has_key(id):
547                                                         nextwins.remove(id)
548                                                         win = self.entries[id]
549                                                         win.updateName(name)
550                                                         win.updateNumberChannel(number, channel)
551                                                         win.updateFilename(filename)
552                                                 else:
553                                                         win = self.session.instantiateDialog(TunerState, INFO, '', '', name, number, channel, filename)
554                                                 win.updateTimes( begin, end, win.endless )
555                                                 self.entries[id] = win
556                                         else:
557                                                 if self.entries.has_key(id):
558                                                         del self.entries[id]
559                         
560                         # Close all not touched next windows
561                         if nextwins:
562                                 for id in nextwins:
563                                         if self.entries.has_key(id):
564                                                 del self.entries[id]
565
566         def show(self, autohide=False, forceshow=False):
567                 print "IBTS show"
568                 
569                 #TEST
570                 if SecondInfobarAvailable:
571                         try:
572                                 if self.infobar.SIBdialog.shown:
573                                         print "IBTS SecondInfobar is shown"
574                                         return
575                         except Exception, e:
576                                 print "InfoBarTunerState show SIB exception " + str(e)
577                 
578                 allowclosing = True
579                 if self.updateTimer.isActive() and autohide:
580                         # Avoid closing if the update timer is active
581                         allowclosing = False
582                 if self.showTimer.isActive():
583                         self.showTimer.stop()
584                 if forceshow:
585                         self.tunerShow(forceshow=forceshow)
586                 else:
587                         self.showTimer.start( 10, True )
588                         self.updateTimer.start( 60 * 1000 )
589                 if allowclosing:
590                         if autohide or self.session.current_dialog is None or not issubclass(self.session.current_dialog.__class__, InfoBarShowHide):
591                                 # Start timer to avoid permanent displaying
592                                 # Do not start timer if no timeout is configured
593                                 #timeout = int(config.infobartunerstate.infobar_timeout.value) or int(config.usage.infobar_timeout.index)
594                                 timeout = int(config.usage.infobar_timeout.index)
595                                 if timeout > 0:
596                                         if self.hideTimer.isActive():
597                                                 self.hideTimer.stop()
598                                         self.hideTimer.startLongTimer( timeout )
599                                 if self.updateTimer.isActive():
600                                         self.updateTimer.stop()
601                 else:
602                         if self.hideTimer.isActive():
603                                 self.hideTimer.stop()
604
605         def toggle(self):
606                 print "IBTS toggle"
607                 if self._shown is False:
608                         self.show()
609                 else:
610                         self.hide()
611
612         def tunerShow(self, forceshow=False):
613                 print "IBTS tunerShow"
614                 self._shown = True
615                 
616                 self.updateNextTimer()
617                 
618                 if self.entries:
619                         # There are active entries
620                         
621                         # Close info screen
622                         if self.info:
623                                 self.info.hide()
624                         
625                         # Rebind InfoBar Events
626                         #self.bindInfoBar()
627                         
628                         # Only show the Tuner information dialog,
629                         # if no screen is displayed or the InfoBar is visible
630                         #TODO Info can also be showed if info.rectangle is outside currentdialog.rectangle
631         #               if self.session.current_dialog is None \
632         #                       or isinstance(self.session.current_dialog, InfoBar):
633                         #MAYBE Tuner Informationen werden zusammen mit der EMCMediaCenter InfoBar angezeigt
634                         #or isinstance(self.session.current_dialog, EMCMediaCenter):
635                         
636                         # Delete entries:
637                         #  if entry reached timeout
638                         #  if number of entries is reached
639                         numberfinished = 0
640                         for id, win in sorted( self.entries.items(), key=lambda x: (x[1].end), reverse=True ):
641                                 if win.type == FINISHED:
642                                         numberfinished += 1
643                                 if win.toberemoved == True \
644                                         or win.type == FINISHED and numberfinished > int( config.infobartunerstate.number_finished_records.value ):
645                                         # Delete Stopped Timers
646                                         self.session.deleteDialog(win)
647                                         del self.entries[id]
648                         
649                         # Update windows
650                         # Dynamic column resizing and repositioning
651                         widths = []
652                         for id, win in self.entries.items():
653                                 if win.type == RECORD:
654                                         #TODO Avolid blocking - avoid using getTimer to update the timer times use timer.time_changed if possible
655                                         timer = getTimer( id )
656                                         #print id, timer
657                                         if timer:
658                                                 begin = timer.begin
659                                                 end = timer.end
660                                                 endless = timer.autoincrease
661                                                 
662                                                 if not win.tuner or not win.tunertype:
663                                                         win.tuner, win.tunertype = getTuner(timer.record_service)
664                                                 service_ref = None
665                                                 if not win.channel or not win.number:
666                                                         service_ref = timer.service_ref
667                                                         
668                                                 del timer
669                                                 
670                                                 if service_ref:
671                                                         win.number = win.number or service_ref and getNumber(service_ref.ref)
672                                                         win.channel = win.channel or service_ref and service_ref.getServiceName()
673                                                         win.channel = win.channel.replace('\xc2\x86', '').replace('\xc2\x87', '')
674                                                 
675                                                 win.updateTimes( begin, end, endless )
676                                                 win.update()
677                                         else:
678                                                 # This can happen, if the time has been changed or if the timer does not exist anymore
679                                                 begin = win.begin
680                                                 end = win.end
681                                                 if end < begin or end > time():
682                                                         end = time()
683                                                 endless = False
684                                                 win.updateType( FINISHED )
685                                                 win.updateTimes( begin, end, endless )
686                                                 win.update()
687                                                 #TEST
688                                                 del self.entries[id]
689                                                 self.updateRecordTimer()
690                                 elif win.type == STREAM:
691                                         if config.infobartunerstate.show_streams.value:
692                                                 #TODO Avolid blocking - avoid using getStream to update the current name
693                                                 stream = getStream( id )
694                                                 if stream:
695                                                         ref = stream.getRecordServiceRef()
696                                                         
697                                                         if not win.tuner or not win.tunertype:
698                                                                 win.tuner, win.tunertype = getTuner(stream.getRecordService())
699                                                         
700                                                         del stream
701                                                         
702                                                         event = ref and self.epg and self.epg.lookupEventTime(ref, -1, 0)
703                                                         if event: 
704                                                                 name = event.getEventName()
705                                                         else:
706                                                                 name = ""
707                                                         
708                                                         begin = win.begin
709                                                         end = None
710                                                         endless = True
711                                                         
712                                                         service_ref = None
713                                                         if not win.number:
714                                                                 service_ref = ServiceReference(ref)
715                                                                 win.number = service_ref and getNumber(service_ref.ref)
716                                                         if not win.channel:
717                                                                 service_ref = service_ref or ServiceReference(ref)
718                                                                 win.channel = win.channel or service_ref and service_ref.getServiceName()
719                                                         
720                                                         win.updateName( name )
721                                                         win.updateTimes( begin, end, endless )
722                                                         win.update()
723                                                 else:
724                                                         win.toberemoved = True
725                                         else:
726                                                 # Should never happen delete
727                                                 begin = win.begin
728                                                 end = time()
729                                                 endless = False
730                                                 win.updateType( FINISHED )
731                                                 win.updateTimes( begin, end, endless )
732                                                 win.update()
733                                 else:
734                                         # Type INFO / FINISHED
735                                         win.update()
736                                 
737                                 # Calculate field width
738                                 widths = map( lambda (w1, w2): max( w1, w2 ), zip_longest( widths, win.widths ) )
739                 
740                 #if self.entries:
741                         # Get initial padding / offset position and apply user offset
742                         padding = self.padding + int(config.infobartunerstate.offset_padding.value)
743                         #print "IBTS px, self.padding, config.padding", px, self.padding, int(config.infobartunerstate.offset_padding.value)
744                         
745                         # Calculate field spacing
746                         spacing = self.spacing + int(config.infobartunerstate.offset_spacing.value)
747                         #print "IBTS spacing, self.spaceing, config.spacing", spacing, self.spacing, int(config.infobartunerstate.offset_spacing.value)
748                         #widths = [ width+spacing if width>0 else 0 for width in widths ]
749                         
750                         # Apply user offsets
751                         posx = self.positionx + int(config.infobartunerstate.offset_horizontal.value)
752                         #print "IBTS posx, self.positionx, config.offset_horizontal", posx, self.positionx, int(config.infobartunerstate.offset_horizontal.value)
753                         posy = self.positiony + int(config.infobartunerstate.offset_vertical.value)
754                         height = self.height
755                         #print "IBTS widths", widths
756                         
757                         # Handle maximum width
758                         overwidth = posx + sum(widths) + len([w for w in widths if w]) * spacing + padding - self.desktopwidth + int(config.infobartunerstate.offset_rightside.value)
759                         #print "IBTS overwidth", overwidth
760                         
761                         # Order windows
762                         #wins = sorted( self.entries.itervalues(), key=lambda x: (x.type, x.endless, x.timeleft, x.begin), reverse=False )
763                         
764                         #TEST 1
765                         #wins = sorted( self.entries.itervalues(), key=lambda x: (x.type, x.endless, x.timeleft, x.begin), reverse=config.infobartunerstate.list_goesup.value )
766                         
767                         #TEST 2
768                         #wins = []
769                         #wins =       sorted( [ w for w in self.entries.values() if w.type == INFO ],     key=lambda x: (x.type, x.endless, x.begin), reverse=False )
770                         #wins.extend( sorted( [ w for w in self.entries.values() if w.type == RECORD ],   key=lambda x: (x.type, x.endless, x.timeleft, x.begin), reverse=False ) )
771                         #wins.extend( sorted( [ w for w in self.entries.values() if w.type == FINISHED ], key=lambda x: (x.type, x.endless, x.timeleft, x.begin), reverse=False ) )
772                         #wins.extend( sorted( [ w for w in self.entries.values() if w.type == STREAM ],   key=lambda x: (x.type, x.endless, x.timeleft, x.begin), reverse=False ) )
773                         #if config.infobartunerstate.list_goesup.value:
774                         #       wins.reverse()
775                         
776                         #TEST 3
777                         wins = sorted( self.entries.itervalues(), key=lambda x: (x.type, x.endless, x.begin), reverse=config.infobartunerstate.list_goesup.value )
778                         
779                         # Resize, move and show windows
780                         for win in wins:
781                                 win.move( posx, posy )
782                                 win.reorder( widths, overwidth )
783                                 posy += height
784                                 # Show windows
785                                 win.show()
786                         
787                 elif forceshow:
788                         # No entries available
789                         try:
790                                 if not self.info:
791                                         self.info = self.session.instantiateDialog( TunerStateInfo, _("Nothing running") )
792                                 self.info.show()
793                                 print "IBTS self.info.type", self.info.type
794                         except Exception, e:
795                                 print "InfoBarTunerState show exception " + str(e)
796
797         def update(self):
798                 print "IBTS updating"
799                 #for win in self.entries.itervalues():
800                 #       #TODO Update also names, width, order, type ...
801                 #       win.update()
802                 self.tunerShow()
803
804         def hide(self):
805                 print "IBTS hide"
806                 if self.updateTimer.isActive():
807                         self.updateTimer.stop()
808                 if self.hideTimer.isActive():
809                         self.hideTimer.stop()
810                 self.hideTimer.start( 10, True )
811
812         def tunerHide(self):
813                 print "IBTS tunerHide"
814                 for win in self.entries.itervalues():
815                         win.hide()
816                 if self.info:
817                         self.info.hide()
818                 self._shown = False
819
820         def close(self):
821                 print "IBTS close"
822                 recoverInfoBar()
823                 removeExtension()
824                 self.unbindInfoBar()
825                 self.removeEvents()
826                 self.hide()
827                 for id, win in self.entries.items():
828                         self.session.deleteDialog(win)
829                         del self.entries[id]
830                 from Plugins.Extensions.InfoBarTunerState.plugin import gInfoBarTunerState
831                 global gInfoBarTunerState
832                 gInfoBarTunerState = None
833
834
835 #######################################################
836 # Base screen class, contains all skin relevant parts
837 class TunerStateBase(Screen):
838         # Skin will only be read once
839         skinfile = os.path.join( resolveFilename(SCOPE_PLUGINS), "Extensions/InfoBarTunerState/skin.xml" )
840         skin = open(skinfile).read()
841
842         def __init__(self, session):
843                 Screen.__init__(self, session)
844                 self.skinName = "TunerState"
845                 
846                 self["Background"] = Pixmap()
847                 self["Type"] = MultiPixmap()
848                 self["Progress"] = ProgressBar()
849                 
850                 for i in xrange( len( config.infobartunerstate.fields.dict() ) ):
851                 #for i, c in enumerate( config.infobartunerstate.fields.dict().itervalues() ):
852                         label = Label()
853                         #fieldid = "Field"+str(i)
854                         self[ "Field"+str(i) ] = label
855                 
856                 self.padding = 0
857                 self.spacing = 0
858                 
859                 self.widths = []
860                 
861                 self.typewidth = 0
862                 self.progresswidth = 0
863                 
864                 self.onLayoutFinish.append(self.layoutFinished)
865
866         def applySkin(self):
867                 attribs = [ ] 
868                 if self.skinAttributes is not None:
869                         for (attrib, value) in self.skinAttributes:
870                                 if attrib == "padding":
871                                         self.padding = int(value)
872                                 elif attrib == "spacing":
873                                         self.spacing = int(value)
874                                 else:
875                                         attribs.append((attrib, value))
876                 self.skinAttributes = attribs
877                 return Screen.applySkin(self)
878
879         def layoutFinished(self):
880                 #TODO Possible to read in applySkin
881                 self.typewidth = self["Type"].instance.size().width()
882                 self.progresswidth = self["Progress"].instance.size().width()
883
884         def reorder(self, widths, overwidth=0):
885                 # Get initial padding / offset position and apply user offset
886                 padding = self.padding + int(config.infobartunerstate.offset_padding.value)
887                 #print "IBTS px, self.padding, config.padding", px, self.padding, int(config.infobartunerstate.offset_padding.value)
888                 
889                 # Calculate field spacing
890                 spacing = self.spacing + int(config.infobartunerstate.offset_spacing.value)
891                 #print "IBTS spacing, self.spaceing, config.spacing", spacing, self.spacing, int(config.infobartunerstate.offset_spacing.value)
892                 
893                 px = padding
894                 py = 0
895                 sh = self.instance.size().height()
896                 #print self.widths
897                 
898                 fieldwidths = config.infobartunerstate.fieldswidth.dict().values()
899                 
900                 for i, (c, width) in enumerate( zip( config.infobartunerstate.fields.dict().values(), widths ) ):
901                         fieldid = "Field"+str(i)
902                         field = c.value
903                         if field == "TypeIcon":
904                                 self["Type"].instance.move( ePoint(px, py) )
905                         
906                         elif field == "TimerProgressGraphical":
907                                 #self[field].instance.resize( eSize(width, sh) )
908                                 # Center the progress field vertically
909                                 y = int( ( sh - self["Progress"].instance.size().height() ) / 2 )
910                                 self["Progress"].instance.move( ePoint(px, y) )
911                         
912                         elif field == "Name":
913                                 if config.infobartunerstate.variable_field_width.value:
914                                         width -= max(0, overwidth)
915                                 else:
916                                         width -= overwidth
917                                 self[fieldid].instance.resize( eSize(width, sh) )
918                                 self[fieldid].instance.move( ePoint(px, py) )
919                         
920                         #elif field == "None":
921                         #       pass
922                         
923                         else:
924                                 self[fieldid].instance.resize( eSize(width, sh) )
925                                 self[fieldid].instance.move( ePoint(px, py) )
926                         
927                         #TODO I think we could simplify this
928                         # Avoid unnecesarry resize and move operations
929                         #for j, fieldwidth in enumerate( config.infobartunerstate.fieldswidth.dict().values() ):
930                         #       if i == j and int(fieldwidth.value) > 0 and not (field == "TimerProgressGraphical" or field == "TypeIcon" or field == "None"):
931                         if fieldwidths:
932                                 fieldwidth = int( fieldwidths[i].value )
933                                 if fieldwidth > 0 and not (field == "TimerProgressGraphical" or field == "TypeIcon" or field == "None"):
934                                         # Handle new maximum width
935                                         if width > 0:
936                                                 overwidth +=  fieldwidth - width
937                                         else:           
938                                                 overwidth +=  fieldwidth - width + spacing
939                                         width = fieldwidth
940                                         self[fieldid].instance.resize( eSize(width, sh) )
941                                         self[fieldid].instance.move( ePoint(px, py) )
942                                         
943                         if width:
944                                 px += width + spacing
945                         
946                 # Set background
947                 bw = self["Background"].instance.size().width()
948                 # Avoid background start position is within our window
949                 bw = px-bw if px-bw<0 else 0
950                 self["Background"].instance.move( ePoint(bw, py) )
951                 self.instance.resize( eSize(px, sh) )
952
953         def move(self, posx, posy):
954                 self.instance.move(ePoint(posx, posy))
955
956
957 #######################################################
958 # Displaying screen class, show nothing running
959 class TunerStateInfo(TunerStateBase):
960         #TODO reuse TunerState and avoid a clone class
961         def __init__(self, session, name):
962                 TunerStateBase.__init__(self, session)
963                 
964                 self.type = INFO
965                 self.name = name
966                 
967                 if not config.infobartunerstate.background_transparency.value:
968                         self["Background"].show()
969                 else:
970                         self["Background"].hide()
971                 
972                 self["Progress"].hide()
973                 
974                 #for i, c in enumerate( config.infobartunerstate.fields.dict().itervalues() ):
975                 for i in xrange( len( config.infobartunerstate.fields.dict() ) ):
976                         fieldid = "Field"+str(i)
977                         
978                         if fieldid == "Field0":
979                                 #self[field].setText( str(self.name).encode("utf-8") )
980                                 self[fieldid].setText( str(self.name) )
981                 
982                 self.onLayoutFinish.append(self.popup)
983
984         def popup(self):
985                 print "IBTS popup"
986                 
987                 self["Type"].setPixmapNum(3)
988                 
989                 widths = []
990                 widths.append( self.typewidth )
991                 
992                 height = self.instance.size().height()
993                 
994                 #for i, c in enumerate( config.infobartunerstate.fields.dict().itervalues() ):
995                 for i in xrange( len( config.infobartunerstate.fields.dict() ) ):
996                         fieldid = "Field"+str(i)
997                         
998                         #Workaround#1 Set default size
999                         self[fieldid].instance.resize( eSize(1000, height) )
1000                         
1001                         width = max(self[fieldid].instance.calculateSize().width(), 0)
1002                         #print width
1003                         
1004                         #Workaround#2 Expand the calculate size
1005                         width = int( width * 1.10 )
1006                         
1007                         #self[field].instance.resize( eSize(width, height) )
1008                         
1009                         widths.append( width )
1010                 
1011                 self.widths = widths
1012                 
1013                 #spacing = self.spacing + int(config.infobartunerstate.offset_spacing.value)
1014                 #widths = [ width+spacing if width>0 else 0 for width in widths ]
1015                 
1016                 posx = self.instance.position().x() + int(config.infobartunerstate.offset_horizontal.value) 
1017                 posy = self.instance.position().y() + int(config.infobartunerstate.offset_vertical.value)
1018                 
1019                 self.move( posx, posy )
1020                 self.reorder(widths)
1021
1022
1023 #######################################################
1024 # Displaying screen class, every entry is an instance of this class
1025 class TunerState(TunerStateBase):
1026         def __init__(self, session, type, tuner, tunertype, name="", number="", channel="", filename="", client="", ip="", port=""):
1027                 #TODO use parameter ref instead of number and channel
1028                 TunerStateBase.__init__(self, session)
1029                 
1030                 self.toberemoved = False
1031                 self.removeTimer = eTimer()
1032                 self.removeTimer_conn = self.removeTimer.timeout.connect(self.remove)
1033                 
1034                 self.type = type
1035                 self.tuner = tuner
1036                 self.tunertype = tunertype
1037                 
1038                 self.name = name
1039                 
1040                 self.number = number
1041                 self.channel = channel
1042                 
1043                 self.filename = filename + ".ts"
1044                 self.destination = filename and os.path.dirname( filename )
1045                 
1046                 self.filesize = None
1047                 self.freespace = None
1048                 
1049                 self.client = client
1050                 self.ip = ip
1051                 self.port = port
1052                 
1053                 self.begin = time()
1054                 self.end = 0
1055                 self.timeleft = None
1056                 self.timeelapsed = None
1057                 self.duration = None
1058                 self.progress = None
1059                 self.endless = False
1060         
1061         def updateName(self, name):
1062                 self.name = name
1063
1064         def updateNumberChannel(self, number, channel):
1065                 self.number = number
1066                 self.channel = channel
1067
1068         def updateFilename(self, filename):
1069                 self.filename = filename + ".ts"
1070
1071         def updateType(self, type):
1072                 if self.type != type:
1073                         self.type = type
1074                 if self.type == FINISHED:
1075                         print "IBTS updateType FINISHED"
1076                         self.tuner = _("-")
1077                         self.tunertype = _("-")
1078                         # Check if timer is already started
1079                         if not self.removeTimer.isActive():
1080                                 # Check if timeout is configured
1081                                 timeout = int(config.infobartunerstate.timeout_finished_records.value)
1082                                 if timeout > 0:
1083                                         self.removeTimer.startLongTimer( timeout )
1084
1085         def updateTimes(self, begin, end, endless):
1086                 self.begin = begin
1087                 self.end = end
1088                 self.endless = endless
1089
1090         def updateDynamicContent(self):
1091                 #TODO cleanup this function
1092                 
1093                 # Time and progress
1094                 now = time()
1095                 begin = self.begin
1096                 end = self.end
1097                 
1098                 duration = None
1099                 timeleft = None
1100                 timeelapsed = None
1101                 progress = None
1102                 
1103                 duration = begin and end and end - begin
1104                 if duration and duration < 0:
1105                         duration = None
1106                 
1107                 if self.type == FINISHED:
1108                         # Finished events
1109                         timeelapsed = None #duration
1110                 elif begin and end and begin < now:
1111                         timeelapsed = min(now - begin, duration)
1112                 else:
1113                         # Future event
1114                         timeelapsed = None
1115                 
1116                 if not self.endless and self.end:
1117                         
1118                         if self.type == FINISHED:
1119                                 # Finished events
1120                                 timeleft = None #0
1121                         elif begin and end and begin < now:
1122                                 timeleft = max(end - now, 0)
1123                         else:
1124                                 # Future event
1125                                 timeleft = None
1126                         
1127                         if timeelapsed and duration:
1128                                 # Adjust the watched movie length (98% of movie length) 
1129                                 # else we will never see the 100%
1130                                 # Alternative using math.ceil but then we won't see 0
1131                                 length = duration / 100.0 * 98.0
1132                                 # Calculate progress and round up
1133                                 progress = timeelapsed / length * 100.0
1134                                 # Normalize progress
1135                                 if progress < 0: progress = 0
1136                                 elif progress > 100: progress = 100
1137                         else:
1138                                 progress = None
1139                         
1140                 self.duration = duration and duration is not None and math.ceil( ( duration ) / 60.0 )
1141                 self.timeleft = timeleft and timeleft is not None and math.ceil( ( timeleft ) / 60.0 )
1142                 self.timeelapsed = timeelapsed and timeelapsed is not None and math.ceil( ( timeelapsed ) / 60.0 )
1143                 self.progress = progress and progress is not None and int( progress )
1144                 #print "IBTS duration, timeleft, timeelapsed, progress", self.duration, self.timeleft, self.timeelapsed, self.progress
1145                 
1146                 # File site and free disk space
1147                 filename = self.filename
1148                 if filename and os.path.exists( filename ):
1149                         filesize = os.path.getsize( filename ) 
1150                         self.filesize = filesize / (1024*1024)
1151                         
1152                         try:
1153                                 stat = os.statvfs( filename )
1154                                 self.freespace = ( stat.f_bfree / 1000 * stat.f_bsize / 1000 ) / 1024
1155                                 #free = os.stat(path).st_size/1048576)
1156                         except OSError:
1157                                 pass
1158
1159         def update(self):
1160                 #TODO Handle Live / Stream Entries - Update several Labels
1161                 self.updateDynamicContent()
1162                 height = self.instance.size().height()
1163                 widths = []
1164                 
1165                 # Set background transparency
1166                 if not config.infobartunerstate.background_transparency.value:
1167                         self["Background"].show()
1168                 else:
1169                         self["Background"].hide()
1170                 
1171                 self["Type"].hide()
1172                 self["Progress"].hide()
1173                 
1174                 for i, c in enumerate( config.infobartunerstate.fields.dict().itervalues() ):
1175                         fieldid = "Field"+str(i)
1176                         field = c.value
1177                         text = ""
1178                         
1179                         if field == "TypeIcon":
1180                                 self["Type"].show()
1181                                 if self.type == RECORD:
1182                                         self["Type"].setPixmapNum(0)
1183                                 elif self.type == STREAM:
1184                                         self["Type"].setPixmapNum(1)
1185                                 elif self.type == FINISHED:
1186                                         self["Type"].setPixmapNum(2)
1187                                 elif self.type == INFO:
1188                                         self["Type"].setPixmapNum(3)
1189                                 else:
1190                                         widths.append( 0 )
1191                                         continue
1192                                 # No resize necessary
1193                                 widths.append( self.typewidth )
1194                                 continue
1195                         
1196                         elif field == "TypeText":
1197                                 if self.type == RECORD:
1198                                         text = _("Record")
1199                                 elif self.type == STREAM:
1200                                         text = _("Stream")
1201                                 elif self.type == FINISHED:
1202                                         text = _("Finished")
1203                         
1204                         elif field == "Tuner":
1205                                 if self.tuner:
1206                                         text = self.tuner
1207                         
1208                         elif field == "TunerType":
1209                                 if self.tunertype:
1210                                         text = self.tunertype
1211                         
1212                         elif field == "Number":
1213                                 if self.number is not None:
1214                                         text = _("%d") % ( self.number )
1215                         
1216                         elif field == "Channel":
1217                                 text = self.channel
1218                         
1219                         elif field == "Name":
1220                                 text = self.name
1221                                 #TODO update name for streams
1222                         
1223                         elif field == "TimeLeft":
1224                                 if not self.endless:
1225                                         if self.timeleft is not None:
1226                                                 # Show timeleft recording time
1227                                                 text = _("%d Min") % ( self.timeleft )
1228                                 else: 
1229                                         # Add infinity symbol for indefinitely recordings
1230                                         text = INFINITY
1231                         
1232                         elif field == "TimeElapsed":
1233                                 if self.timeelapsed is not None:
1234                                         text = _("%d Min") % ( self.timeelapsed )
1235                         
1236                         elif field == "TimeLeftDuration":
1237                                 # Calculate timeleft minutes
1238                                 if not self.endless:
1239                                         if self.type is not FINISHED and self.timeleft is not None:
1240                                         #if self.timeleft is not None:
1241                                                 # Show timeleft recording time
1242                                                 text = _("%d Min") % ( self.timeleft )
1243                                         elif self.duration is not None:
1244                                                 # Fallback show recording length
1245                                                 text = _("%d Min") % ( self.duration )
1246                                 else: 
1247                                         # Add infinity symbol for indefinitely recordings
1248                                         text = INFINITY
1249                         
1250                         elif field == "Begin":
1251                                 lbegin = self.begin and localtime( self.begin )
1252                                 text = lbegin and strftime( config.infobartunerstate.time_format_begin.value, lbegin )
1253                         
1254                         elif field == "End":
1255                                 lend = self.end and localtime( self.end )
1256                                 text = lend and strftime( config.infobartunerstate.time_format_end.value, lend )
1257                         
1258                         elif field == "Duration":
1259                                 if self.duration is not None:
1260                                         text = _("%d Min") % ( self.duration )
1261                         
1262                         elif field == "TimerProgressText":
1263                                 if self.progress is not None:
1264                                         text = _("%d %%") % ( self.progress )
1265                         
1266                         elif field == "TimerProgressGraphical":
1267                                 if self.progress is not None:
1268                                         self["Progress"].setValue( self.progress )
1269                                         self["Progress"].show()
1270                                         # No resize necessary
1271                                         widths.append( self.progresswidth )
1272                                 else:
1273                                         if not config.infobartunerstate.placeholder_pogressbar.value:
1274                                                 widths.append( 0 )
1275                                         else:   
1276                                                 widths.append( self.progresswidth )
1277                                 continue
1278                         
1279                         elif field == "TimerDestination":
1280                                 text = self.destination
1281                         
1282                         elif field == "StreamClient":
1283                                 text = self.client or self.ip
1284                         
1285                         elif field == "StreamClientPort":
1286                                 if self.port:
1287                                         text = self.client or self.ip
1288                                         text += ":" + str(self.port)
1289                         
1290                         elif field == "DestinationStreamClient":
1291                                 text = self.destination or self.client or self.ip
1292                         
1293                         elif field == "FileSize":
1294                                 if self.filesize  is not None:
1295                                         text = _("%d MB") % ( self.filesize )
1296                         
1297                         elif field == "FreeSpace":
1298                                 if self.freespace is not None:
1299                                         text = _("%d GB") % ( self.freespace )
1300                         
1301                         elif field == "None":
1302                                 # text is already initialized with ""
1303                                 pass
1304                         
1305                         # Set text, append field, resize field and append width
1306                         self[fieldid].setText( text )
1307                         
1308                         # Set horizontal alignment
1309                         if field == 'Number' or field == 'TimeLeftDuration' or field == 'TimeLeft' or field == 'TimeElapsed' or field == 'Duration' or field == 'TimerProgressText' or field == 'FileSize' or field == 'FreeSpace':
1310                                 self[fieldid].instance.setHAlign(2) # import _enigma # alignRight = _enigma.eLabel_alignRight
1311                         
1312                         #Workaround#1
1313                         self[fieldid].instance.resize( eSize(1000, height) )
1314                         
1315                         width = max(self[fieldid].instance.calculateSize().width(), 0)
1316                         #print width
1317                         
1318                         #Workaround#2
1319                         width = int( width * 1.10 )
1320                         
1321                         #self[fieldid].instance.resize( eSize(width, height) )
1322                         
1323                         widths.append( width )
1324                 
1325                 self.widths = widths
1326
1327         def remove(self):
1328                 self.toberemoved = True 
1329
1330
1331 #######################################################
1332 # Global helper functions
1333 def getTimerID(timer):
1334         #return str( timer.name ) + str( timer.repeatedbegindate ) + str( timer.service_ref ) + str( timer.justplay )
1335         return str( timer )
1336
1337 def getTimer(id):
1338         #for timer in self.session.nav.RecordTimer.timer_list + self.session.nav.RecordTimer.processed_timers:
1339         for timer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers:
1340                 #print "timerlist:", getTimerID( timer )
1341                 if getTimerID( timer ) == id:
1342                         return timer
1343         return None
1344
1345 def getStreamID(stream):
1346         #TEST_MULTIPLESTREAMS
1347         #if id == str(stream.getRecordServiceRef()) + str(stream.clientIP):
1348         ##if(id == str(stream.getRecordServiceRef().toString()) + str(stream.clientIP)):
1349         return str(stream.screenIndex) + str(stream.clientIP)
1350
1351 def getStream(id):
1352         try:
1353                 from Plugins.Extensions.WebInterface.WebScreens import streamingScreens 
1354         except:
1355                 streamingScreens = []
1356         
1357         for stream in streamingScreens:
1358                 if stream:
1359                         if getStreamID(stream) == id:
1360                                 return stream
1361         return None
1362
1363 def getTuner(service):
1364         # service must be an instance of iPlayableService or iRecordableService
1365         #TODO detect stream of HDD
1366         feinfo = service and service.frontendInfo()
1367         data = feinfo and feinfo.getAll(False)
1368         if data:
1369                 number = data.get("tuner_number", -1)
1370                 type = data.get("tuner_type", "")
1371                 if number is not None and number > -1:
1372                         return ( chr( int(number) + ord('A') ), type)
1373         return "", ""
1374
1375 def readBouquetList(self):
1376         serviceHandler = eServiceCenter.getInstance()
1377         refstr = '1:134:1:0:0:0:0:0:0:0:FROM BOUQUET \"bouquets.tv\" ORDER BY bouquet'
1378         bouquetroot = eServiceReference(refstr)
1379         self.bouquetlist = {}
1380         list = serviceHandler.list(bouquetroot)
1381         if list is not None:
1382                 self.bouquetlist = list.getContent("CN", True)
1383
1384 def getNumber(actservice):
1385         # actservice must be an instance of eServiceReference
1386         from Screens.InfoBar import InfoBar
1387         Servicelist = None
1388         if InfoBar and InfoBar.instance:
1389                 Servicelist = InfoBar.instance.servicelist
1390         mask = (eServiceReference.isMarker | eServiceReference.isDirectory)
1391         number = 0
1392         bouquets = Servicelist and Servicelist.getBouquetList()
1393         if bouquets:
1394                 #TODO get alternative for actbouquet
1395                 actbouquet = Servicelist.getRoot()
1396                 serviceHandler = eServiceCenter.getInstance()
1397                 for name, bouquet in bouquets:
1398                         if not bouquet.valid(): #check end of list
1399                                 break
1400                         if bouquet.flags & eServiceReference.isDirectory:
1401                                 servicelist = serviceHandler.list(bouquet)
1402                                 if not servicelist is None:
1403                                         while True:
1404                                                 service = servicelist.getNext()
1405                                                 if not service.valid(): #check end of list
1406                                                         break
1407                                                 playable = not (service.flags & mask)
1408                                                 if playable:
1409                                                         number += 1
1410                                                 if actbouquet:
1411                                                         if actbouquet == bouquet and actservice == service:
1412                                                                 return number
1413                                                 else:
1414                                                         if actservice == service:
1415                                                                 return number
1416         return None
1417
1418 def getNextPendingRecordTimers():
1419         timer_list = []
1420         now = time()
1421         for timer in NavigationInstance.instance.RecordTimer.timer_list:
1422                 next_act = timer.getNextActivation()
1423                 if timer.justplay or (timer.isRunning() and not timer.repeated) or next_act < now:
1424                         continue
1425                 if timer.begin:
1426                         if not timer.isRunning():
1427                                 begin = timer.begin
1428                                 end = timer.end
1429                         else:
1430                                 begin, end = processRepeated(timer)
1431                         timer_list.append( (timer, begin, end) )
1432         return sorted( timer_list, key=lambda x: (x[1]) )
1433
1434
1435 # Adapted from TimerEntry
1436 def processRepeated(timer, findRunningEvent = False):
1437         print "ProcessRepeated"
1438         
1439         def addOneDay(timedatestruct):
1440                 oldHour = timedatestruct.tm_hour
1441                 newdate =  (datetime(timedatestruct.tm_year, timedatestruct.tm_mon, timedatestruct.tm_mday, timedatestruct.tm_hour, timedatestruct.tm_min, timedatestruct.tm_sec) + timedelta(days=1)).timetuple()
1442                 if localtime(mktime(newdate)).tm_hour != oldHour:
1443                         return (datetime(timedatestruct.tm_year, timedatestruct.tm_mon, timedatestruct.tm_mday, timedatestruct.tm_hour, timedatestruct.tm_min, timedatestruct.tm_sec) + timedelta(days=2)).timetuple()
1444                 return newdate
1445         
1446         begin = timer.begin
1447         end = timer.end
1448                 
1449         if (timer.repeated != 0):
1450                 now = int(time()) + 1
1451
1452                 #to avoid problems with daylight saving, we need to calculate with localtime, in struct_time representation
1453                 localrepeatedbegindate = localtime(timer.repeatedbegindate)
1454                 localbegin = localtime(begin)
1455                 localend = localtime(end)
1456                 localnow = localtime(now)
1457
1458                 print "localrepeatedbegindate:", strftime("%c", localrepeatedbegindate)
1459                 print "localbegin:", strftime("%c", localbegin)
1460                 print "localend:", strftime("%c", localend)
1461                 print "localnow:", strftime("%c", localnow)
1462
1463                 day = []
1464                 flags = timer.repeated
1465                 for x in (0, 1, 2, 3, 4, 5, 6):
1466                         if (flags & 1 == 1):
1467                                 day.append(0)
1468                                 print "Day: " + str(x)
1469                         else:
1470                                 day.append(1)
1471                         flags = flags >> 1
1472
1473                 # if day is NOT in the list of repeated days
1474                 # OR if the day IS in the list of the repeated days, check, if event is currently running... then if findRunningEvent is false, go to the next event
1475                 while ((day[localbegin.tm_wday] != 0) or (mktime(localrepeatedbegindate) > mktime(localbegin))  or
1476                         ((day[localbegin.tm_wday] == 0) and ((findRunningEvent and localend < localnow) or ((not findRunningEvent) and localbegin < localnow)))):
1477                         localbegin = addOneDay(localbegin)
1478                         localend = addOneDay(localend)
1479                         print "localbegin after addOneDay:", strftime("%c", localbegin)
1480                         print "localend after addOneDay:", strftime("%c", localend)
1481                         
1482                 #we now have a struct_time representation of begin and end in localtime, but we have to calculate back to (gmt) seconds since epoch
1483                 begin = int(mktime(localbegin))
1484                 end = int(mktime(localend))
1485                 if begin == end:
1486                         end += 1
1487                 
1488                 print "ProcessRepeated result"
1489                 print strftime("%c", localtime(begin))
1490                 print strftime("%c", localtime(end))
1491         
1492         return begin, end