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