add basic webif hook to force scan -> http(s)://<ip or hostname>/autotimer?parse
[enigma2-plugins.git] / epgsearch / src / EPGSearch.py
1 # for localized messages         
2 from . import _
3
4 from enigma import eEPGCache, eServiceReference, RT_HALIGN_LEFT, \
5                 RT_HALIGN_RIGHT, eListboxPythonMultiContent
6
7 from Tools.LoadPixmap import LoadPixmap
8 from ServiceReference import ServiceReference
9
10 from Screens.ChannelSelection import SimpleChannelSelection
11 from Screens.ChoiceBox import ChoiceBox
12 from Screens.EpgSelection import EPGSelection
13 from Screens.MessageBox import MessageBox
14 from Screens.Screen import Screen
15 from Screens.VirtualKeyBoard import VirtualKeyBoard
16
17 from Components.ActionMap import ActionMap
18 from Components.Button import Button
19 from Components.config import config
20 from Components.EpgList import EPGList, EPG_TYPE_SINGLE, EPG_TYPE_MULTI
21 from Components.TimerList import TimerList
22 from Components.Sources.ServiceEvent import ServiceEvent
23 from Components.Sources.Event import Event
24
25 from time import localtime
26
27 # Partnerbox installed?
28 try:
29         from Plugins.Extensions.Partnerbox.PartnerboxEPGList import \
30                         isInRemoteTimer, getRemoteClockPixmap
31         from Plugins.Extensions.Partnerbox.PartnerboxFunctions import \
32                         SetPartnerboxTimerlist, isInTimerList, sendPartnerBoxWebCommand, \
33                         FillE1TimerList, FillE2TimerList
34         import Plugins.Extensions.Partnerbox.PartnerboxFunctions \
35                         as partnerboxfunctions
36         from Plugins.Extensions.Partnerbox.PartnerboxSetup import \
37                         PartnerboxEntriesListConfigScreen
38         PartnerBoxInstalled = True
39 except:
40         PartnerBoxInstalled = False
41
42 #AutoTimer installed?
43 try:
44         from Plugins.Extensions.AutoTimer.AutoTimerEditor import \
45                         addAutotimerFromEvent, addAutotimerFromSearchString
46         autoTimerAvailable = True
47 except ImportError:
48         autoTimerAvailable = False
49
50 baseEPGSelection__init__ = None
51 def EPGSelectionInit():
52         global baseEPGSelection__init__
53         if baseEPGSelection__init__ is None:
54                 baseEPGSelection__init__ = EPGSelection.__init__
55         EPGSelection.__init__ = EPGSelection__init__
56         EPGSelection.bluePressed = bluePressed
57
58 def EPGSelection__init__(self, session, service, zapFunc=None, eventid=None, bouquetChangeCB=None, serviceChangeCB=None):
59         baseEPGSelection__init__(self, session, service, zapFunc, eventid, bouquetChangeCB, serviceChangeCB)
60         if self.type != EPG_TYPE_MULTI:
61                 self["epgsearch_actions"] = ActionMap(["EPGSelectActions"],
62                                 {
63                                         "blue": self.bluePressed,
64                                 })
65                 self["key_blue"].setText(_("EPG Search"))
66
67 def bluePressed(self):
68         try:
69                 # EPGList could be empty
70                 cur = self["list"].getCurrent()
71         except: # XXX: is this an IndexError? always be as specific as possible ;-)
72                 name = ''
73         else:
74                 name = cur[0].getEventName()
75         self.session.open(EPGSearch,name , False)
76
77 class EPGSearchList(EPGList):
78         def __init__(self, type=EPG_TYPE_SINGLE, selChangedCB=None, timer=None):
79                 EPGList.__init__(self, type, selChangedCB, timer)
80                 self.l.setBuildFunc(self.buildEPGSearchEntry)
81
82                 if PartnerBoxInstalled:
83                         # Partnerbox Clock Icons
84                         self.remote_clock_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock.png')
85                         self.remote_clock_add_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_add.png')
86                         self.remote_clock_pre_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_pre.png')
87                         self.remote_clock_post_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_post.png')
88                         self.remote_clock_prepost_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_prepost.png')
89
90         def buildEPGSearchEntry(self, service, eventId, beginTime, duration, EventName):
91                 rec1 = beginTime and self.timer.isInTimer(eventId, beginTime, duration, service)
92                 # Partnerbox 
93                 if PartnerBoxInstalled:
94                         rec2 = beginTime and isInRemoteTimer(self,beginTime, duration, service)
95                 else:
96                         rec2 = False
97                 r1 = self.weekday_rect
98                 r2 = self.datetime_rect
99                 r3 = self.descr_rect
100                 t = localtime(beginTime)
101                 serviceref = ServiceReference(service) # for Servicename
102                 res = [
103                         None, # no private data needed
104                         (eListboxPythonMultiContent.TYPE_TEXT, r1.left(), r1.top(), r1.width(), r1.height(), 0, RT_HALIGN_RIGHT, self.days[t[6]]),
105                         (eListboxPythonMultiContent.TYPE_TEXT, r2.left(), r2.top(), r2.width(), r1.height(), 0, RT_HALIGN_RIGHT, "%02d.%02d, %02d:%02d"%(t[2],t[1],t[3],t[4]))
106                 ]
107                 if rec1 or rec2:
108                         if rec1:                        
109                                 clock_pic = self.getClockPixmap(service, beginTime, duration, eventId)
110                                 # maybe Partnerbox too
111                                 if rec2:
112                                         clock_pic_partnerbox = getRemoteClockPixmap(self,service, beginTime, duration, eventId)
113                         else:
114                                 clock_pic = getRemoteClockPixmap(self,service, beginTime, duration, eventId)
115                         if rec1 and rec2:
116                                 # Partnerbox and local
117                                 res.extend((
118                                         (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, r3.left(), r3.top(), 21, 21, clock_pic),
119                                         (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, r3.left() + 25, r3.top(), 21, 21, clock_pic_partnerbox),
120                                         (eListboxPythonMultiContent.TYPE_TEXT, r3.left() + 50, r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, serviceref.getServiceName() + ": " + EventName)))
121                         else:
122                                 res.extend((
123                                         (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, r3.left(), r3.top(), 21, 21, clock_pic),
124                                         (eListboxPythonMultiContent.TYPE_TEXT, r3.left() + 25, r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, serviceref.getServiceName() + ": " + EventName)))
125                 else:
126                         res.append((eListboxPythonMultiContent.TYPE_TEXT, r3.left(), r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT, serviceref.getServiceName() + ": " + EventName))
127                 return res
128
129 class EPGSearch(EPGSelection):
130         def __init__(self, session, *args):
131                 Screen.__init__(self, session)
132                 self.skinName = "EPGSelection"
133
134                 self.searchargs = args
135                 self.currSearch = ""
136
137                 # XXX: we lose sort begin/end here
138                 self["key_yellow"] = Button(_("New Search"))
139                 self["key_blue"] = Button(_("History"))
140
141 # begin stripped copy of EPGSelection.__init__
142                 self.bouquetChangeCB = None
143                 self.serviceChangeCB = None
144                 self.ask_time = -1 #now
145                 self["key_red"] = Button("")
146                 self.closeRecursive = False
147                 self.saved_title = None
148                 self["Service"] = ServiceEvent()
149                 self["Event"] = Event()
150                 self.type = EPG_TYPE_SINGLE
151                 self.currentService=None
152                 self.zapFunc = None
153                 self.sort_type = 0
154                 self["key_green"] = Button(_("Add timer"))
155                 self.key_green_choice = self.ADD_TIMER
156                 self.key_red_choice = self.EMPTY
157                 self["list"] = EPGSearchList(type = self.type, selChangedCB = self.onSelectionChanged, timer = session.nav.RecordTimer)
158                 self["actions"] = ActionMap(["EPGSelectActions", "OkCancelActions", "MenuActions"],
159                         {
160                                 "menu": self.menu,
161                                 "cancel": self.closeScreen,
162                                 "ok": self.eventSelected,
163                                 "timerAdd": self.timerAdd,
164                                 "yellow": self.yellowButtonPressed,
165                                 "blue": self.blueButtonPressed,
166                                 "info": self.infoKeyPressed,
167                                 "red": self.zapTo, # needed --> Partnerbox
168                                 "nextBouquet": self.nextBouquet, # just used in multi epg yet
169                                 "prevBouquet": self.prevBouquet, # just used in multi epg yet
170                                 "nextService": self.nextService, # just used in single epg yet
171                                 "prevService": self.prevService, # just used in single epg yet
172                         })
173
174                 self["actions"].csel = self
175                 self.onLayoutFinish.append(self.onCreate)
176 # end stripped copy of EPGSelection.__init__
177
178                 # Partnerbox
179                 if PartnerBoxInstalled:
180                         EPGSelection.PartnerboxInit(self, False)
181
182         def onCreate(self):
183                 if self.searchargs:
184                         self.searchEPG(*self.searchargs)
185                 else:
186                         l = self["list"]
187                         l.recalcEntrySize()
188                         l.list = []
189                         l.l.setList(l.list)
190                 del self.searchargs
191
192                 # Partnerbox
193                 if PartnerBoxInstalled:
194                         EPGSelection.GetPartnerboxTimerlist(self)
195
196         def closeScreen(self):
197                 # Save our history
198                 config.plugins.epgsearch.save()
199                 EPGSelection.closeScreen(self)
200
201         def yellowButtonPressed(self):
202                 self.session.openWithCallback(
203                         self.searchEPG,
204                         VirtualKeyBoard,
205                         title = _("Enter text to search for")
206                 )
207
208         def menu(self):
209                 options = [
210                         (_("Import from Timer"), self.importFromTimer),
211                         (_("Import from EPG"), self.importFromEPG),
212                 ]
213
214                 if autoTimerAvailable:
215                         options.extend((
216                                 (_("Import from AutoTimer"), self.importFromAutoTimer),
217                                 (_("Save search as AutoTimer"), self.addAutoTimer),
218                                 (_("Export selected as AutoTimer"), self.exportAutoTimer),
219                         ))
220
221                 self.session.openWithCallback(
222                         self.menuCallback,
223                         ChoiceBox,
224                         list = options
225                 )
226
227         def menuCallback(self, ret):
228                 ret and ret[1]()
229
230         def importFromTimer(self):
231                 self.session.openWithCallback(
232                         self.searchEPG,
233                         EPGSearchTimerImport
234                 )
235
236         def importFromEPG(self):
237                 self.session.openWithCallback(
238                         self.searchEPG,
239                         EPGSearchChannelSelection
240                 )
241
242         def importFromAutoTimer(self):
243                 removeInstance = False
244                 try:
245                         # Import Instance
246                         from Plugins.Extensions.AutoTimer.plugin import autotimer
247
248                         if autotimer is None:
249                                 removeInstance = True
250                                 # Create an instance
251                                 from Plugins.Extensions.AutoTimer.AutoTimer import AutoTimer
252                                 autotimer = AutoTimer()
253
254                         # Read in configuration
255                         autotimer.readXml()
256                 except Exception, e:
257                         self.session.open(
258                                 MessageBox,
259                                 _("Could not read AutoTimer timer list: %s") % e,
260                                 type = MessageBox.TYPE_ERROR
261                         )
262                 else:
263                         # Fetch match strings
264                         # XXX: we could use the timer title as description
265                         options = [(x.match, x.match) for x in autotimer.getTimerList()]
266
267                         self.session.openWithCallback(
268                                 self.searchEPGWrapper,
269                                 ChoiceBox,
270                                 title = _("Select text to search for"),
271                                 list = options
272                         )
273                 finally:
274                         # Remove instance if there wasn't one before
275                         if removeInstance:
276                                 autotimer = None
277
278         def addAutoTimer(self):
279                 addAutotimerFromSearchString(self.session, self.currSearch)
280
281         def exportAutoTimer(self):
282                 cur = self['list'].getCurrent()
283                 if cur is None:
284                         return
285                 addAutotimerFromEvent(self.session, cur[0], cur[1])
286
287         def blueButtonPressed(self):
288                 options = [(x, x) for x in config.plugins.epgsearch.history.value]
289
290                 if options:
291                         self.session.openWithCallback(
292                                 self.searchEPGWrapper,
293                                 ChoiceBox,
294                                 title = _("Select text to search for"),
295                                 list = options
296                         )
297                 else:
298                         self.session.open(
299                                 MessageBox,
300                                 _("No history"),
301                                 type = MessageBox.TYPE_INFO
302                         )
303
304         def searchEPGWrapper(self, ret):
305                 if ret:
306                         self.searchEPG(ret[1])
307
308         def searchEPG(self, searchString = None, searchSave = True):
309                 if searchString:
310                         self.currSearch = searchString
311                         if searchSave:
312                                 # Maintain history
313                                 history = config.plugins.epgsearch.history.value
314                                 if searchString not in history:
315                                         history.insert(0, searchString)
316                                         if len(history) > 10:
317                                                 history.pop(10)
318                                 else:
319                                         history.remove(searchString)
320                                         history.insert(0, searchString)
321
322                         # Workaround to allow search for umlauts if we know the encoding (pretty bad, I know...)
323                         encoding = config.plugins.epgsearch.encoding.value
324                         if encoding != 'UTF-8':
325                                 try:
326                                         searchString = searchString.decode('UTF-8', 'replace').encode(encoding, 'replace')
327                                 except (UnicodeDecodeError, UnicodeEncodeError):
328                                         pass
329
330                         # Search EPG, default to empty list
331                         epgcache = eEPGCache.getInstance() # XXX: the EPGList also keeps an instance of the cache but we better make sure that we get what we want :-)
332                         ret = epgcache.search(('RIBDT', 200, eEPGCache.PARTIAL_TITLE_SEARCH, searchString, eEPGCache.NO_CASE_CHECK)) or []
333                         ret.sort(key = lambda x: x[2]) # sort by time
334
335                         # Update List
336                         l = self["list"]
337                         l.recalcEntrySize()
338                         l.list = ret
339                         l.l.setList(ret)
340
341 class EPGSearchTimerImport(Screen):
342         def __init__(self, session):
343                 Screen.__init__(self, session)
344                 self.skinName = "TimerEditList"
345
346                 self.list = []
347                 self.fillTimerList()
348
349                 self["timerlist"] = TimerList(self.list)
350
351                 self["key_red"] = Button(_("Cancel"))
352                 self["key_green"] = Button(_("OK"))
353                 self["key_yellow"] = Button("")
354                 self["key_blue"] = Button("")
355
356                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions"],
357                 {
358                         "ok": self.search,
359                         "cancel": self.cancel,
360                         "green": self.search,
361                         "red": self.cancel
362                 }, -1)
363                 self.onLayoutFinish.append(self.setCustomTitle)
364
365         def setCustomTitle(self):
366                 self.setTitle(_("Select a timer to search"))
367
368         def fillTimerList(self):
369                 l = self.list
370                 del l[:]
371
372                 for timer in self.session.nav.RecordTimer.timer_list:
373                         l.append((timer, False))
374
375                 for timer in self.session.nav.RecordTimer.processed_timers:
376                         l.append((timer, True))
377                 l.sort(key = lambda x: x[0].begin)
378
379         def search(self):
380                 cur = self["timerlist"].getCurrent()
381                 if cur:
382                         self.close(cur.name)
383
384         def cancel(self):
385                 self.close(None)
386
387 class EPGSearchChannelSelection(SimpleChannelSelection):
388         def __init__(self, session):
389                 SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
390                 self.skinName = "SimpleChannelSelection"
391
392                 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
393                 {
394                                 "showEPGList": self.channelSelected
395                 })
396
397         def channelSelected(self):
398                 ref = self.getCurrentSelection()
399                 if (ref.flags & 7) == 7:
400                         self.enterPath(ref)
401                 elif not (ref.flags & eServiceReference.isMarker):
402                         self.session.openWithCallback(
403                                 self.epgClosed,
404                                 EPGSearchEPGSelection,
405                                 ref,
406                                 False
407                         )
408
409         def epgClosed(self, ret = None):
410                 if ret:
411                         self.close(ret)
412
413 class EPGSearchEPGSelection(EPGSelection):
414         def __init__(self, session, ref, openPlugin):
415                 EPGSelection.__init__(self, session, ref)
416                 self.skinName = "EPGSelection"
417                 self["key_green"].setText(_("Search"))
418                 self.openPlugin = openPlugin
419
420         def infoKeyPressed(self):
421                 self.timerAdd()
422
423         def timerAdd(self):
424                 cur = self["list"].getCurrent()
425                 evt = cur[0]
426                 sref = cur[1]
427                 if not evt:
428                         return
429
430                 if self.openPlugin:
431                         self.session.open(
432                                 EPGSearch,
433                                 evt.getEventName()
434                         )
435                 else:
436                         self.close(evt.getEventName())
437