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