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