treewide: remove references to /media/cf and /media/usb
[enigma2-plugins.git] / merlinepgcenter / src / HelperFunctions.py
1 #
2 #  MerlinEPGCenter E2 Plugin
3 #
4 #  $Id: HelperFunctions.py,v 1.0 2011-02-14 21:53:00 shaderman Exp $
5 #
6 #  Coded by Shaderman (c) 2011
7 #  Support: www.dreambox-tools.info
8 #
9 #  This plugin is licensed under the Creative Commons 
10 #  Attribution-NonCommercial-ShareAlike 3.0 Unported 
11 #  License. To view a copy of this license, visit
12 #  http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative
13 #  Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
14 #
15 #  Alternatively, this plugin may be distributed and executed on hardware which
16 #  is licensed by Dream Multimedia GmbH.
17
18 #  This plugin is NOT free software. It is open source, you are allowed to
19 #  modify it (if you keep the license), but it may not be commercially 
20 #  distributed other than under the conditions noted above.
21 #
22
23
24 # for localized messages
25 from . import _
26
27 #PYTHON IMPORTS
28 from datetime import timedelta as dt_timedelta, date as dt_date
29 from time import localtime, time
30
31 # ENIGMA IMPORTS
32 from Components.config import config
33 from Components.ScrollLabel import ScrollLabel
34 from enigma import eEnv, eSize, fontRenderClass, ePoint, eSlider, eTimer, iRecordableService, eDVBVolumecontrol
35 import NavigationInstance
36 from RecordTimer import RecordTimerEntry, AFTEREVENT
37 from Screens.MessageBox import MessageBox
38 import Screens.Standby
39 from Tools.Directories import fileExists, resolveFilename, SCOPE_SKIN_IMAGE, SCOPE_CURRENT_SKIN, SCOPE_CURRENT_PLUGIN
40 from Tools.LoadPixmap import LoadPixmap
41
42
43 LIST_TYPE_EPG = 0
44 LIST_TYPE_UPCOMING = 1
45
46 WEEKSECONDS = 7*86400
47 WEEKDAYS = (_("Monday"), _("Tuesday"), _("Wednesday"), _("Thursday"), _("Friday"), _("Saturday"), _("Sunday"))
48
49
50 # most functions were taken and modified from Components.VolumeControl
51 class EmbeddedVolumeControl():
52         def __init__(self):
53                 self.volctrl = eDVBVolumecontrol.getInstance()
54                 self.hideVolTimer = eTimer()
55                 self.hideVolTimer_conn = self.hideVolTimer.timeout.connect(self.volHide)
56                 
57         def volSave(self):
58                 if self.volctrl.isMuted():
59                         config.audio.volume.value = 0
60                 else:
61                         config.audio.volume.value = self.volctrl.getVolume()
62                 config.audio.volume.save()
63                 
64         def volUp(self):
65                 self.setVolume(+1)
66                 
67         def volDown(self):
68                 self.setVolume(-1)
69                 
70         def setVolume(self, direction):
71                 oldvol = self.volctrl.getVolume()
72                 if direction > 0:
73                         self.volctrl.volumeUp()
74                 else:
75                         self.volctrl.volumeDown()
76                 is_muted = self.volctrl.isMuted()
77                 vol = self.volctrl.getVolume()
78                 self["volume"].show()
79                 if is_muted:
80                         self.volMute() # unmute
81                 elif not vol:
82                         self.volMute(False, True) # mute but dont show mute symbol
83                 if self.volctrl.isMuted():
84                         self["volume"].setValue(0)
85                 else:
86                         self["volume"].setValue(self.volctrl.getVolume())
87                 self.volSave()
88                 self.hideVolTimer.start(3000, True)
89                 
90         def volHide(self):
91                 self["volume"].hide()
92                 
93         def volMute(self, showMuteSymbol=True, force=False):
94                 vol = self.volctrl.getVolume()
95                 if vol or force:
96                         self.volctrl.volumeToggleMute()
97                         if self.volctrl.isMuted():
98                                 if showMuteSymbol:
99                                         self["mute"].show()
100                                 self["volume"].setValue(0)
101                         else:
102                                 self["mute"].hide()
103                                 self["volume"].setValue(vol)
104                                 
105         def getIsMuted(self):
106                 return self.volctrl.isMuted()
107                 
108         def setMutePixmap(self):
109                 if self.volctrl.isMuted():
110                         self["mute"].show()
111                 else:
112                         self["mute"].hide()
113                         
114 class ResizeScrollLabel(ScrollLabel):
115         def __init__(self, text = ""):
116                 ScrollLabel.__init__(self, text)
117                 
118         def resize(self, s):
119                 lineheight=fontRenderClass.getInstance().getLineHeight( self.long_text.getFont() )
120                 if not lineheight:
121                         lineheight = 30 # assume a random lineheight if nothing is visible
122                 lines = (int)(s.height() / lineheight)
123                 self.pageHeight = (int)(lines * lineheight)
124                 self.instance.resize(eSize(s.width(), self.pageHeight+(int)(lineheight/6)))
125                 self.scrollbar.move(ePoint(s.width()-20,0))
126                 self.scrollbar.resize(eSize(20,self.pageHeight+(int)(lineheight/6)))
127                 self.long_text.resize(eSize(s.width()-30, self.pageHeight*16))
128                 self.setText(self.message)
129
130 class PiconLoader():
131         def __init__(self):
132                 self.nameCache = { }
133                 config.plugins.merlinEpgCenter.epgPaths.addNotifier(self.piconPathChanged, initial_call = False)
134                 
135         def getPiconFilename(self, sRef):
136                 pngname = ""
137                 # strip all after last :
138                 pos = sRef.rfind(':')
139                 if pos != -1:
140                         sRef = sRef[:pos].rstrip(':').replace(':','_')
141                 pngname = self.nameCache.get(sRef, "")
142                 if pngname == "":
143                         pngname = self.findPicon(sRef)
144                         if pngname != "":
145                                 self.nameCache[sRef] = pngname
146                         if pngname == "": # no picon for service found
147                                 pngname = self.nameCache.get("default", "")
148                                 if pngname == "": # no default yet in cache..
149                                         pngname = self.findPicon("picon_default")
150                                         if pngname == "":
151                                                 pngname = resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/PiconMissing_small.png")
152                                         self.nameCache["default"] = pngname
153                 return pngname
154                 
155         def getPicon(self, pngname):
156                 return LoadPixmap(cached = True, path = self.getPiconFilename(pngname))
157                 
158         def findPicon(self, sRef):
159                 pngname = config.plugins.merlinEpgCenter.epgPaths.value + sRef + ".png"
160                 if not fileExists(pngname):
161                         pngname = resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/PiconMissing_small.png")
162                 return pngname
163                 
164         def piconPathChanged(self, configElement = None):
165                 self.nameCache.clear()
166                 
167 def findDefaultPicon(serviceName):
168         searchPaths = (eEnv.resolve('${datadir}/enigma2/%s/'),)
169         
170         pos = serviceName.rfind(':')
171         if pos != -1:
172                 serviceName = serviceName[:pos].rstrip(':').replace(':','_')
173         
174         for path in searchPaths:
175                 pngname = (path % "picon") + serviceName + ".png"
176                 if fileExists(pngname):
177                         return pngname
178         return resolveFilename(SCOPE_CURRENT_PLUGIN, "Extensions/MerlinEPGCenter/images/PiconMissing.png")
179         
180 # derived from Tools.FuzzyDate
181 def getFuzzyDay(t):
182         d = localtime(t)
183         nt = time()
184         n = localtime()
185         
186         if d[:3] == n[:3]:
187                 # same day
188                 date = _("Today")
189         elif dt_date.fromtimestamp(t) == dt_date.today() + dt_timedelta(days = 1):
190                 # next day
191                 date = _("Tomorrow")
192         elif nt < t and (t - nt) < WEEKSECONDS:
193                 # same week
194                 date = WEEKDAYS[d.tm_wday]
195         else:
196                 date = "%d.%d.%d" % (d.tm_mday, d.tm_mon, d.tm_year)
197                 
198         return date
199         
200 # used to let timer pixmaps blink in our lists
201 class BlinkTimer():
202         def __init__(self, session):
203                 self.session = session
204                 self.state = False # blinking state
205                 self.lists = [] # epg list, upcoming list
206                 self.listSets = [set(), set()] # 1st set for epg list, 2nd for upcoming list
207                 self.delay = 0
208                 self.stopping = False
209                 self.timerRunning = False
210                 self.timer = eTimer()
211                 self.callbacks = []
212                 self.resume()
213                 
214         def gotRecordEvent(self, service, event):
215                 if event in (iRecordableService.evEnd, iRecordableService.evStart, None):
216                         numRecs = len(self.session.nav.getRecordings())
217                         if numRecs and not self.timerRunning:
218                                 self.timer.start(1000)
219                                 self.timerRunning = True
220                                 # notify the EpgCenterList instances, they need to invalidate to start blinking
221                                 for x in self.callbacks:
222                                         x()
223                         elif not numRecs and self.timerRunning and not self.stopping:
224                                 self.stopping = True
225                                 
226         def suspend(self):
227                 if self.gotRecordEvent in self.session.nav.record_event:
228                         self.session.nav.record_event.remove(self.gotRecordEvent)
229                 self.changeBlinkState_conn = None
230                 if self.getIsRunning():
231                         self.timer.stop()
232                         self.delay = 0
233                         self.stopping = False
234                         self.state = False
235                         self.timerRunning = False
236                         
237         def resume(self):
238                 self.changeBlinkState_conn = self.timer.timeout.connect(self.changeBlinkState)
239                 self.session.nav.record_event.append(self.gotRecordEvent)
240                 if self.session.nav.RecordTimer.isRecording():
241                         self.gotRecordEvent(None, None)
242                         
243         def appendList(self, l):
244                 self.lists.append(l)
245                 
246         def changeBlinkState(self):
247                 self.state = not self.state
248                 
249                 i = 0
250                 while i < 2:
251                         for idx in self.listSets[i]:
252                                 self.lists[i].l.invalidateEntry(idx)
253                         i += 1
254                         
255                 if self.stopping:
256                         self.delayStop()
257                         
258         def getBlinkState(self):
259                 return self.state
260                 
261         def getIsRunning(self):
262                 return self.timerRunning
263                 
264         def getIsStopping(self):
265                 return self.stopping
266                 
267         def getIsInList(self, idx):
268                 return idx in self.listSets[LIST_TYPE_EPG]
269                 
270         def gotListElements(self):
271                 if len(self.listSets[LIST_TYPE_EPG]) or len(self.listSets[LIST_TYPE_UPCOMING]):
272                         return True
273                 else:
274                         return False
275                         
276         # make one more tick befor stopping the timer to show the picon again
277         def delayStop(self):
278                 self.stopping = True
279                 self.delay += 1
280                 
281                 if self.delay > 1:
282                         self.timer.stop()
283                         self.delay = 0
284                         self.stopping = False
285                         self.state = False
286                         self.timerRunning = False
287                         
288         def updateEntry(self, listType, idx, isRunning):
289                 if idx in self.listSets[listType]:
290                         if not isRunning:
291                                 self.listSets[listType].discard(idx)
292                 elif isRunning:
293                         self.listSets[listType].add(idx)
294                         if not self.timerRunning and self.gotListElements():
295                                 self.delay = 0
296                                 self.stopping = False
297                                 
298         def reset(self):
299                 if not self.timerRunning:
300                         return
301                         
302                 self.listSets[LIST_TYPE_EPG].clear()
303                 self.listSets[LIST_TYPE_UPCOMING].clear()
304                 
305 # interface between AutoTimer and our timer list
306 class TimerListObject(object):
307         def __init__(self, begin, end, service_ref, name, justplay, disabled, autoTimerId, match, searchType, counter, counterLeft, destination, services, bouquets, includedDays, excludedDays):
308                 self.begin              = begin
309                 self.end                = end
310                 self.service_ref        = service_ref
311                 self.name               = name
312                 self.justplay           = justplay
313                 self.disabled           = disabled
314                 self.autoTimerId        = autoTimerId
315                 self.state              = 0 # TimerEntry.StateWaiting
316                 
317                 # additional information
318                 self.match              = match
319                 self.searchType         = searchType
320                 self.counter            = counter
321                 self.counterLeft        = counterLeft
322                 self.destination        = destination
323                 self.services           = services
324                 self.bouquets           = bouquets
325                 self.includedDays       = includedDays
326                 self.excludedDays       = excludedDays
327                 
328         def isRunning(self):
329                 return False
330