EPGSearch: improvements on texts to make them better understandable
[enigma2-plugins.git] / epgsearch / src / EPGSearch.py
1 # for localized messages
2 from enigma import eEPGCache, eServiceCenter, eServiceReference, RT_HALIGN_LEFT, \
3                 RT_HALIGN_RIGHT, RT_VALIGN_CENTER, eListboxPythonMultiContent
4
5 from Tools.LoadPixmap import LoadPixmap
6 from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS
7 from ServiceReference import ServiceReference
8
9 from EPGSearchSetup import EPGSearchSetup
10 from Screens.ChannelSelection import SimpleChannelSelection
11 from Screens.ChoiceBox import ChoiceBox
12 from Screens.EpgSelection import EPGSelection
13 from Screens.ChannelSelection import ChannelSelectionBase
14 from Screens.EventView import EventViewEPGSelect, EventViewBase
15 from Screens.MessageBox import MessageBox
16 from Screens.InfoBar import InfoBar
17 from Screens.Screen import Screen
18 from Plugins.SystemPlugins.Toolkit.NTIVirtualKeyBoard import NTIVirtualKeyBoard
19
20 from Components.ActionMap import ActionMap, HelpableActionMap
21 from Components.Button import Button
22 from Components.config import config
23 from Components.EpgList import EPGList, EPG_TYPE_SINGLE, EPG_TYPE_MULTI
24 from Components.TimerList import TimerList
25 from Components.Sources.ServiceEvent import ServiceEvent
26 from Components.Sources.Event import Event
27 from Components.ServiceList import ServiceList, PiconLoader
28
29 from . import SearchType
30 from time import localtime, time
31 from operator import itemgetter
32 from Tools.BoundFunction import boundFunction
33 from skin import componentSizes
34
35 # Partnerbox installed and icons in epglist enabled?
36 try:
37         from Plugins.Extensions.Partnerbox.PartnerboxEPGList import \
38                         isInRemoteTimer, getRemoteClockPixmap
39         from Plugins.Extensions.Partnerbox.plugin import \
40                         showPartnerboxIconsinEPGList
41         PartnerBoxIconsEnabled = showPartnerboxIconsinEPGList()
42 except ImportError:
43         PartnerBoxIconsEnabled = False
44
45 # AutoTimer installed?
46 try:
47         from Plugins.Extensions.AutoTimer.AutoTimerEditor import \
48                         addAutotimerFromEvent, addAutotimerFromSearchString
49         autoTimerAvailable = True
50 except ImportError:
51         autoTimerAvailable = False
52
53 # Overwrite EPGSelection.__init__ with our modified one
54 baseEPGSelection__init__ = None
55 def EPGSelectionInit():
56         global baseEPGSelection__init__
57         if baseEPGSelection__init__ is None:
58                 baseEPGSelection__init__ = EPGSelection.__init__
59         EPGSelection.__init__ = EPGSelection__init__
60
61 # Modified EPGSelection __init__
62 def EPGSelection__init__(self, session, service, zapFunc=None, eventid=None, bouquetChangeCB=None, serviceChangeCB=None):
63         baseEPGSelection__init__(self, session, service, zapFunc, eventid, bouquetChangeCB, serviceChangeCB)
64
65         def bluePressed():
66                 cur = self["list"].getCurrent()
67                 if cur[0] is not None:
68                         name = cur[0].getEventName()
69                 else:
70                         name = ''
71                 self.session.open(EPGSearch, name)
72
73         self["epgsearch_epgselection"] = ActionMap(["InfobarAudioSelectionActions"],
74                         {
75                                 "audioSelection": bluePressed,
76                         })
77
78         if self.type != EPG_TYPE_MULTI and config.plugins.epgsearch.add_search_to_epg.value:
79                 self["epgsearch_actions"] = ActionMap(["EPGSelectActions"],
80                                 {
81                                         "blue": bluePressed,
82                                 })
83                 self["key_blue"].text = _("Search")
84
85
86 # Overwrite ChannelSelectionBase.__init__ with our modified one
87 baseChannelSelectionBase__init__ = None
88 def ChannelSelectionBaseInit():
89         global baseChannelSelectionBase__init__
90         if baseChannelSelectionBase__init__ is None:
91                 baseChannelSelectionBase__init__ = ChannelSelectionBase.__init__
92         ChannelSelectionBase.__init__ = ChannelSelectionBase__init__
93
94 def ChannelSelectionBase__init__(self, session):
95         baseChannelSelectionBase__init__(self, session)
96
97         def getEventName(cur):
98                 serviceref = cur
99                 refstr = serviceref.toString()
100                 event = None
101                 try:
102                         epg = eEPGCache.getInstance()
103                         event = epg.lookupEventTime(serviceref, -1, 0)
104                         if event is None:
105                                 info = eServiceCenter.getInstance().info(serviceref)
106                                 event = info.getEvent(0)
107                 except:
108                         pass
109                 if event is not None:
110                         return event.getEventName()
111                 else:
112                         return ""
113
114         def searchEPG():
115                 eventName = getEventName(cur = self.servicelist.getCurrent())
116                 from Plugins.Extensions.EPGSearch.EPGSearch import EPGSearch
117                 self.session.open(EPGSearch, eventName)
118
119         self["epgsearch_channelbase"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
120                         {
121                                 "audioSelection":  (searchEPG, _("Search EPG with Message")),
122                 })
123
124 # Overwrite EventViewBase.__init__ with our modified one
125 baseEventViewBase__init__ = None
126 def EventViewBaseInit():
127         global baseEventViewBase__init__
128         if baseEventViewBase__init__ is None:
129                 baseEventViewBase__init__ = EventViewBase.__init__
130         EventViewBase.__init__ = EventViewBase__init__
131
132 def EventViewBase__init__(self, Event, Ref, callback=None, similarEPGCB=None):
133         baseEventViewBase__init__(self, Event, Ref, callback, similarEPGCB)
134
135         def searchEPG():
136                 eventName = self.event.getEventName()
137                 self.session.open(EPGSearch, eventName)
138
139         self["epgsearch_eventview"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
140                         {
141                                 "audioSelection":  (searchEPG, _("Search EPG with event name")),
142                 })
143
144 # Overwrite pzyP4T.__init__ with our modified one
145 basePzyP4T__init__ = None
146 def pzyP4TInit():
147         global basePzyP4T__init__
148         try:
149                 from Plugins.Extensions.pzyP4T.plugin import PzyP4T
150                 if basePzyP4T__init__ is None:
151                         basePzyP4T__init__ = PzyP4T.__init__
152                 PzyP4T.__init__ = PzyP4T__init__
153         except:
154                 basepzyP4T__init__ = None
155
156 # Modified PzyP4T__init__ for audio-key
157 def PzyP4T__init__(self, session):
158         basePzyP4T__init__(self, session)
159
160         def openEPGSearch():
161                 event = event = self["Event"].getCurrentEvent()
162                 if event:
163                         eventName = event.getEventName()
164                         self.session.open(EPGSearch, eventName)
165
166         self["InfobarAudioSelectionActions"] = ActionMap(["InfobarAudioSelectionActions"],
167                         {
168                                 "audioSelection": openEPGSearch,
169                         })
170
171 # Modified EPGSearchList with support for PartnerBox
172 class EPGSearchList(EPGList):
173         def __init__(self, type=EPG_TYPE_SINGLE, selChangedCB=None, timer=None):
174                 EPGList.__init__(self, type, selChangedCB, timer)
175                 self.l.setBuildFunc(self.buildEPGSearchEntry)
176
177                 self.piconLoader = PiconLoader()
178
179                 sizes = componentSizes[EPGList.SKIN_COMPONENT_KEY]
180                 self._iconWidth = sizes.get(EPGList.SKIN_COMPONENT_ICON_WIDTH, 21)
181                 self._iconHeight = sizes.get(EPGList.SKIN_COMPONENT_ICON_HEIGHT, 21)
182                 self._iconHPos = sizes.get(EPGList.SKIN_COMPONENT_ICON_HPOS, 4)
183                 self._itemMargin = sizes.get(EPGList.SKIN_COMPONENT_ITEM_MARGIN, 10)
184
185                 servicelist_sizes = componentSizes["ServiceList"]
186                 self._picon_width = servicelist_sizes.get(ServiceList.KEY_PICON_WIDTH, 58)
187
188                 if PartnerBoxIconsEnabled:
189                         # Partnerbox Clock Icons
190                         self.remote_clock_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock.png')
191                         self.remote_clock_add_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_add.png')
192                         self.remote_clock_pre_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_pre.png')
193                         self.remote_clock_post_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_post.png')
194                         self.remote_clock_prepost_pixmap = LoadPixmap('/usr/lib/enigma2/python/Plugins/Extensions/Partnerbox/icons/remote_epgclock_prepost.png')
195
196         def buildEPGSearchEntry(self, service, eventId, beginTime, duration, EventName):
197                 rec1 = beginTime and self.timer.isInTimer(eventId, beginTime, duration, service)
198                 # Partnerbox
199                 if PartnerBoxIconsEnabled:
200                         rec2 = beginTime and isInRemoteTimer(self,beginTime, duration, service)
201                 else:
202                         rec2 = False
203                 r1 = self.weekday_rect
204                 r2 = self.datetime_rect
205                 r3 = self.descr_rect
206                 t = localtime(beginTime)
207                 serviceref = ServiceReference(service) # for Servicename
208                 serviceName = serviceref.getServiceName() + ": "
209
210                 #delete serviceName if set it in setup and it is most matched service
211                 if config.plugins.epgsearch.show_sname_in_title.value and service == self.mostSearchService:
212                         serviceName = ""
213
214                 res = [
215                         None, # no private data needed
216                         (eListboxPythonMultiContent.TYPE_TEXT, r1.left(), r1.top(), r1.width(), r1.height(), 0, RT_HALIGN_RIGHT|RT_VALIGN_CENTER, self.days[t[6]]),
217                         (eListboxPythonMultiContent.TYPE_TEXT, r2.left(), r2.top(), r2.width(), r1.height(), 0, RT_HALIGN_RIGHT|RT_VALIGN_CENTER, "%02d.%02d, %02d:%02d"%(t[2],t[1],t[3],t[4]))
218                 ]
219
220                 #add picon if set this option in setup
221                 picon = None
222                 if config.plugins.epgsearch.show_picon.value:
223                         picon = self.piconLoader.getPicon(service)
224                 left_pos = r3.left()
225                 if picon is not None:
226                         res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHABLEND, left_pos, 2, self._picon_width, r3.height()-3, picon))
227                         left_pos = r3.left() + self._picon_width + self._itemMargin
228                         serviceName = "" #if load picon delete servicename
229
230                 if rec1 or rec2:
231                         if rec1:
232                                 clock_pic = self.getClockPixmap(service, beginTime, duration, eventId)
233                                 # maybe Partnerbox too
234                                 if rec2:
235                                         clock_pic_partnerbox = getRemoteClockPixmap(self,service, beginTime, duration, eventId)
236                         else:
237                                 clock_pic = getRemoteClockPixmap(self,service, beginTime, duration, eventId)
238                         if rec1 and rec2:
239                                 # Partnerbox and local
240                                 res.extend((
241                                         (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, left_pos, self._iconHPos, self._iconWidth, self._iconHeight, clock_pic),
242                                         (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, left_pos + self._iconWidth + self._itemMargin, self._iconHPos, self._iconWidth, self._iconHeight, clock_pic_partnerbox),
243                                         (eListboxPythonMultiContent.TYPE_TEXT, left_pos + self._iconWidth*2 + self._itemMargin*2, r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, serviceName + EventName)))
244                         else:
245                                 res.extend((
246                                         (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, left_pos, self._iconHPos, self._iconWidth, self._iconHeight, clock_pic),
247                                         (eListboxPythonMultiContent.TYPE_TEXT, left_pos + self._iconWidth + self._itemMargin, r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, serviceName + EventName)))
248                 else:
249                         res.append((eListboxPythonMultiContent.TYPE_TEXT, left_pos, r3.top(), r3.width(), r3.height(), 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, serviceName + EventName))
250                 return res
251
252 # main class of plugin
253 class EPGSearch(EPGSelection):
254         def __init__(self, session, *args):
255                 Screen.__init__(self, session)
256                 self.skinName = ["EPGSearch", "EPGSelection"]
257
258                 self.searchargs = args
259                 self.currSearch = ""
260
261                 # XXX: we lose sort begin/end here
262                 self["key_yellow"] = Button(_("New Search"))
263                 self["key_blue"] = Button(_("History"))
264
265 # begin stripped copy of EPGSelection.__init__
266                 self.bouquetChangeCB = None
267                 self.serviceChangeCB = None
268                 self.ask_time = -1 #now
269                 self["key_red"] = Button("")
270                 self.closeRecursive = False
271                 self.saved_title = None
272                 self["Service"] = ServiceEvent()
273                 self["Event"] = Event()
274                 self.type = EPG_TYPE_SINGLE
275                 self.currentService=None
276                 self.zapFunc = None
277                 self.sort_type = 0
278                 self["key_green"] = Button(_("Add timer"))
279                 self.key_green_choice = self.ADD_TIMER
280                 self.key_red_choice = self.EMPTY
281                 self["list"] = EPGSearchList(type = self.type, selChangedCB = self.onSelectionChanged, timer = session.nav.RecordTimer)
282                 self["actions"] = ActionMap(["EPGSelectActions", "OkCancelActions", "MenuActions", "InputActions", "InfobarAudioSelectionActions"],
283                         {
284                                 "menu": self.menu,
285                                 "cancel": self.closeScreen,
286                                 "ok": self.eventSelected,
287                                 "timerAdd": self.timerAdd,
288                                 "yellow": self.yellowButtonPressed,
289                                 "blue": self.blueButtonPressed,
290                                 "info": self.infoKeyPressed,
291                                 "red": self.zapTo, # needed --> Partnerbox
292                                 "nextBouquet": self.nextBouquet, # just used in multi epg yet
293                                 "prevBouquet": self.prevBouquet, # just used in multi epg yet
294                                 "nextService": self.nextService, # just used in single epg yet
295                                 "prevService": self.prevService, # just used in single epg yet
296                                 "1": self.importFromTimer,
297                                 "2": self.importFromEPG,
298                                 "3": self.importFromAutoTimer,
299                                 "4": self.addAutoTimer,
300                                 "5": self.exportAutoTimer,
301                                 "6": self.openSPInfoScreen,
302                                 "7": self.openImdb,
303                                 "8": self.openTMDb,
304                                 "9": self.openTMDbSerie,
305                                 "0": self.setup,
306                                 "audioSelection": self.searchNoNumber,
307                         })
308
309                 self["actions"].csel = self
310                 self["list"].mostSearchService = ""
311                 self.onLayoutFinish.append(self.onCreate)
312 # end stripped copy of EPGSelection.__init__
313
314                 # Partnerbox
315                 if PartnerBoxIconsEnabled:
316                         EPGSelection.PartnerboxInit(self, False)
317
318                 # Hook up actions for yttrailer if installed
319                 try:
320                         from Plugins.Extensions.YTTrailer.plugin import baseEPGSelection__init__
321                 except ImportError as ie:
322                         pass
323                 else:
324                         if baseEPGSelection__init__ is not None:
325                                 self["trailerActions"] = ActionMap(["InfobarActions", "InfobarTeletextActions"],
326                                 {
327                                         "showTv": self.showTrailer,
328                                         "showRadio": self.showTrailerList,
329                                         "startTeletext": self.showConfig
330                                 })
331
332         def onCreate(self):
333                 self.setTitle(_("EPG Search"))
334
335                 if self.searchargs:
336                         self.doSearchEPG(*self.searchargs)
337                 else:
338                         l = self["list"]
339                         l.recalcEntrySize()
340                         l.list = []
341                         l.l.setList(l.list)
342                 del self.searchargs
343
344                 # Partnerbox
345                 if PartnerBoxIconsEnabled:
346                         EPGSelection.GetPartnerboxTimerlist(self)
347
348         def closeScreen(self):
349                 # Save our history
350                 config.plugins.epgsearch.save()
351                 EPGSelection.closeScreen(self)
352
353         def yellowButtonPressed(self):
354                 self.session.openWithCallback(
355                         self.searchEPG,
356                         NTIVirtualKeyBoard,
357                         title = _("Enter text to search for"),
358                         text = self.currSearch
359                 )
360
361         def infoKeyPressed(self):
362                 cur = self["list"].getCurrent()
363                 event = cur[0]
364                 service = cur[1]
365                 if event is not None:
366                         self.session.open(EventViewEPGSelect, event, service, self.eventViewCallback, self.openSingleServiceEPG, InfoBar.instance.openMultiServiceEPG, self.openSimilarList)
367
368         def openSimilarList(self, eventid, refstr):
369                 self.session.open(EPGSelection, refstr, None, eventid)
370
371         def openSingleServiceEPG(self):
372                 cur = self["list"].getCurrent()
373                 event = cur[0]
374                 service = cur[1]
375                 if event is not None:
376                         self.session.open(EPGSelection, cur[1].ref)
377
378         def menu(self):
379                 options = [
380                         (_("Import from Timer"), self.importFromTimer),
381                         (_("Import from EPG"), self.importFromEPG),
382                 ]
383                 keys = ["1","2"]
384
385                 if autoTimerAvailable:
386                         options.extend((
387                                 (_("Import from AutoTimer"), self.importFromAutoTimer),
388                                 (_("Save search as AutoTimer"), self.addAutoTimer),
389                                 (_("Export selected as AutoTimer"), self.exportAutoTimer),
390                         ))
391                         keys.extend(("3","4","5"))
392                 if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/SeriesPlugin/plugin.py")):
393                         options.append((_("Show series info (SP)"), self.openSPInfoScreen))
394                         keys.append("6")
395
396                 if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/IMDb/plugin.py")):
397                         options.append((_("Open selected in IMDb"), self.openImdb))
398                         keys.append("7")
399
400                 if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/AdvancedMovieSelection/plugin.py")):
401                         options.append((_("Open selected in TMDB Info (AMS)"), self.openTMDb))
402                         options.append((_("Open selected in TMDB Serie Info (AMS)"), self.openTMDbSerie))
403                         keys.extend(("8","9"))
404
405                 options.append((_("Setup"), self.setup))
406                 keys.append("0")
407
408                 self.session.openWithCallback(
409                         self.menuCallback,
410                         ChoiceBox,
411                         list = options,
412                         keys = keys,
413                         title = _("EPGSearch menu")
414                 )
415
416         def menuCallback(self, ret):
417                 ret and ret[1]()
418
419         def importFromTimer(self):
420                 self.session.openWithCallback(
421                         self.searchEPG,
422                         EPGSearchTimerImport
423                 )
424
425         def importFromEPG(self):
426                 self.session.openWithCallback(
427                         self.searchEPG,
428                         EPGSearchChannelSelection
429                 )
430
431         def importFromAutoTimer(self):
432                 removeInstance = False
433                 try:
434                         # Import Instance
435                         from Plugins.Extensions.AutoTimer.plugin import autotimer
436
437                         if autotimer is None:
438                                 removeInstance = True
439                                 # Create an instance
440                                 from Plugins.Extensions.AutoTimer.AutoTimer import AutoTimer
441                                 autotimer = AutoTimer()
442
443                         # Read in configuration
444                         autotimer.readXml()
445                 except Exception as e:
446                         self.session.open(
447                                 MessageBox,
448                                 _("Could not read AutoTimer timer list: %s") % e,
449                                 type = MessageBox.TYPE_ERROR
450                         )
451                 else:
452                         # Fetch match strings
453                         # XXX: we could use the timer title as description
454                         options = [(x.match, x.match) for x in autotimer.getTimerList()]
455
456                         self.session.openWithCallback(
457                                 self.searchEPGWrapper,
458                                 ChoiceBox,
459                                 title = _("Select text to search for"),
460                                 list = options
461                         )
462                 finally:
463                         # Remove instance if there wasn't one before
464                         if removeInstance:
465                                 autotimer = None
466
467         def addAutoTimer(self):
468                 if autoTimerAvailable:
469                         addAutotimerFromSearchString(self.session, self.currSearch)
470
471         def exportAutoTimer(self):
472                 cur = self['list'].getCurrent()
473                 if cur is None:
474                         return
475                 if autoTimerAvailable:
476                         addAutotimerFromEvent(self.session, cur[0], cur[1])
477
478         def openSPInfoScreen(self):
479                 cur = self['list'].getCurrent()
480                 if cur is None:
481                         return
482                 try:
483                         from Plugins.Extensions.SeriesPlugin.SeriesPluginInfoScreen import SeriesPluginInfoScreen
484                         service=cur[1]
485                         event = cur[0]
486                         self.session.open(SeriesPluginInfoScreen, service, event)
487                 except ImportError as ie:
488                         pass
489
490         def openImdb(self):
491                 cur = self['list'].getCurrent()
492                 if cur is None:
493                         return
494                 try:
495                         from Plugins.Extensions.IMDb.plugin import IMDB
496                         self.session.open(IMDB, cur[0].getEventName())
497                 except ImportError as ie:
498                         pass
499
500         def openTMDb(self):
501                 cur = self['list'].getCurrent()
502                 if cur is None:
503                         return
504                 try:
505                         from Plugins.Extensions.AdvancedMovieSelection.plugin import tmdbInfo
506                         tmdbInfo(self.session, cur[0].getEventName())
507                 except ImportError as ie:
508                         pass
509
510         def openTMDbSerie(self):
511                 cur = self['list'].getCurrent()
512                 if cur is None:
513                         return
514                 try:
515                         from Plugins.Extensions.AdvancedMovieSelection.plugin import tmdbSeriesInfo
516                         tmdbSeriesInfo(self.session, cur[0].getEventName())
517                 except ImportError as ie:
518                         pass
519
520         def setup(self):
521                 self.session.openWithCallback(self.closeSetupCallback, EPGSearchSetup)
522
523         def closeSetupCallback(self):
524                 self.doSearchEPG(self.currSearch, self.currSearchSave, self.currSearchDescription)
525
526         def searchNoNumber(self):
527                 search_txt = self.currSearch
528                 search_txt = search_txt.translate(None, "1234567890(/)").strip()
529
530                 self.doSearchEPG(search_txt, self.currSearchSave, self.currSearchDescription)
531
532         def blueButtonPressed(self):
533                 options = [(x, x) for x in config.plugins.epgsearch.history.value]
534
535                 if options:
536                         self.session.openWithCallback(
537                                 self.searchEPGWrapper,
538                                 ChoiceBox,
539                                 title = _("Select text to search for"),
540                                 list = options
541                         )
542                 else:
543                         self.session.open(
544                                 MessageBox,
545                                 _("No history"),
546                                 type = MessageBox.TYPE_INFO
547                         )
548
549         def searchEPGWrapper(self, ret):
550                 if ret:
551                         self.searchEPG(ret[1])
552
553         def searchEPG(self, searchString = None, searchSave = True):
554                 if not searchString:
555                         return
556                 searchType = config.plugins.epgsearch.search_type.value
557                 if  searchType == SearchType.ASK:
558                         boundCallback = boundFunction(self.onSearchEPGCallback, searchString=searchString, searchSave=searchSave)
559                         choices = [ (_("Title only"), False),
560                                                 (_("Title and Description"), True) ]
561                         self.session.openWithCallback(boundCallback, ChoiceBox, list=choices, title=_("Where to search for '%s'?") %(searchString), windowTitle=_("EPG Search"))
562                 else:
563                         searchDescription = searchType == SearchType.TITLE_DESCRIPTION
564                         self.doSearchEPG(searchString, searchSave, searchDescription)
565
566         def onSearchEPGCallback(self, answer, searchString=None, searchSave=True):
567                 searchDescription = answer and answer[1]
568                 self.doSearchEPG(searchString, searchSave, searchDescription)
569
570         def doSearchEPG(self, searchString = None, searchSave = True, searchDescription=False):
571                 self.currSearchSave = searchSave
572                 self.currSearchDescription = searchDescription
573                 if searchString:
574                         self.currSearch = searchString
575                         if searchSave:
576                                 # Maintain history
577                                 history = config.plugins.epgsearch.history.value
578                                 if searchString not in history:
579                                         history.insert(0, searchString)
580                                         maxLen = config.plugins.epgsearch.history_length.value
581                                         if len(history) > maxLen:
582                                                 del history[maxLen:]
583                                 else:
584                                         history.remove(searchString)
585                                         history.insert(0, searchString)
586
587                         # Search EPG, default to empty list
588                         searchType = eEPGCache.PARTIAL_TITLE_SEARCH
589                         if config.plugins.epgsearch.search_type.value == "exakt_title":
590                                 searchType = eEPGCache.EXAKT_TITLE_SEARCH
591                         epgcache = eEPGCache.getInstance() # XXX: the EPGList also keeps an instance of the cache but we better make sure that we get what we want :-)
592                         ret = epgcache.search(('RIBDT', 1000, searchType, searchString, eEPGCache.NO_CASE_CHECK)) or []
593                         if searchDescription:
594                                 ret += epgcache.search(('RIBDT', 1000, eEPGCache.PARTIAL_DESCRIPTION_SEARCH, searchString, eEPGCache.NO_CASE_CHECK)) or []
595                                 #condense by eventids
596                                 condensed = {}
597                                 for item in ret:
598                                         condensed[item[1]] = item
599                                 ret = condensed.values()
600                         ret.sort(key=itemgetter(2)) # sort by time
601
602                         #filter epg-matches for selected bouquet from settings
603                         if config.plugins.epgsearch.search_scope.value != "all" and len(ret):
604                                 ret = self.filterEPGmatches(ret)
605
606                         #add short description to search result
607                         if config.plugins.epgsearch.show_shortdesc.value and len(ret):
608                                 ret = self.addShortDescription(epgcache, ret)
609
610                         #get most searched service
611                         mostSearchService = ""
612                         if not config.plugins.epgsearch.show_picon.value and config.plugins.epgsearch.show_sname_in_title.value and len(ret):
613                                 mostSearchService = self.getMostSearchService(ret)
614
615                         # Update List
616                         l = self["list"]
617
618                         #set mostsearchservice to screen-title
619                         title = _("EPG Search")
620                         l.mostSearchService = mostSearchService #save the value also to EPGList-Class
621                         if not config.plugins.epgsearch.show_picon.value and mostSearchService != "":
622                                 serviceref = ServiceReference(mostSearchService) # for Servicename
623                                 serviceName = serviceref.getServiceName()
624                                 title += " - " + serviceName
625                         self.setTitle(title)
626                         l.recalcEntrySize()
627                         l.list = ret
628                         l.l.setList(ret)
629
630         def getMostSearchService(self, matches):
631                 services = [x[0]for x in matches]
632                 mostSearchService = max(services, key = services.count)
633                 return mostSearchService
634
635         def filterEPGmatches(self, matches):
636                 services = self.getBouquetServiceList(config.plugins.epgsearch.search_scope.value)
637                 if len(services): #only if return services to check
638                         ret1 = []
639                         for event in matches:
640                                 timecheck=True
641                                 if config.plugins.epgsearch.show_events.value == "current_future" and int(event[2])+int(event[3]) < time():
642                                         timecheck = False #if is an old event
643                                 elif config.plugins.epgsearch.show_events.value == "future" and int(event[2]) < time():
644                                         timecheck = False #if is a current or old event
645                                 if config.plugins.epgsearch.show_events.value == "current" and (int(event[2]) > time() or int(event[2])+int(event[3]) < time()):
646                                         timecheck = False #if is a future or an old event
647                                 if str(event[0]) in services and timecheck:
648                                         ret1.append(event)
649                         matches = ret1
650                 return matches
651
652         def addShortDescription(self, epgcache, events):
653                 new_events=[]
654                 for event in events:
655                         epgEvent = epgcache.lookupEventId(eServiceReference(event[0]), int(event[1]))
656                         if epgEvent:
657                                 shortdesc = str(epgEvent.getShortDescription())
658                                 if len(shortdesc) > 0:
659                                         event_lst = list(event)
660                                         event_lst[4] = event_lst[4] + "  |  " + shortdesc
661                                         event = tuple(event_lst)
662                         new_events.append(event)
663
664                 return new_events
665
666         def getBouquetServiceList(self, bouquet="current"):
667                 infoBarInstance = InfoBar.instance
668                 if infoBarInstance is not None:
669                         servicelist = infoBarInstance.servicelist
670                         if bouquet == "current":
671                                 bouquet = servicelist.getRoot().toString()
672                         services = infoBarInstance.getBouquetServices(eServiceReference(bouquet))
673
674                         service_list = []
675                         for service in services:
676                                 service_ref = service.ref.toString()
677
678                                 serviceref_split = service_ref.split(":")
679                                 if len(serviceref_split)>10: #perhaps on ip-tv or renamed service
680                                         serviceref_split[0]="1"
681                                         serviceref_split[1]="0"
682                                         #cut the service_ref
683                                         service_ref = ":".join(serviceref_split[:10]) + ":"
684
685                                 service_list.append(service_ref)
686
687                         return service_list
688
689 class EPGSearchTimerImport(Screen):
690         def __init__(self, session):
691                 Screen.__init__(self, session)
692                 self.skinName = ["EPGSearchTimerImport", "TimerEditList"]
693
694                 self.list = []
695                 self.fillTimerList()
696
697                 self["timerlist"] = TimerList(self.list)
698
699                 self["key_red"] = Button(_("Cancel"))
700                 self["key_green"] = Button(_("OK"))
701                 self["key_yellow"] = Button("")
702                 self["key_blue"] = Button("")
703
704                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions"],
705                 {
706                         "ok": self.search,
707                         "cancel": self.cancel,
708                         "green": self.search,
709                         "red": self.cancel
710                 }, -1)
711                 self.onLayoutFinish.append(self.setCustomTitle)
712
713         def setCustomTitle(self):
714                 self.setTitle(_("Select a timer to search"))
715
716         def fillTimerList(self):
717                 l = self.list
718                 del l[:]
719
720                 for timer in self.session.nav.RecordTimer.timer_list:
721                         l.append((timer, False))
722
723                 for timer in self.session.nav.RecordTimer.processed_timers:
724                         l.append((timer, True))
725                 l.sort(key = lambda x: x[0].begin)
726
727         def search(self):
728                 cur = self["timerlist"].getCurrent()
729                 if cur:
730                         self.close(cur.name)
731
732         def cancel(self):
733                 self.close(None)
734
735 class EPGSearchChannelSelection(SimpleChannelSelection):
736         def __init__(self, session):
737                 SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
738                 self.skinName = ["EPGSearchChannelSelection", "SimpleChannelSelection"]
739
740                 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
741                 {
742                                 "showEPGList": self.channelSelected
743                 })
744
745         def channelSelected(self):
746                 ref = self.getCurrentSelection()
747                 if (ref.flags & 7) == 7:
748                         self.enterPath(ref)
749                 elif not (ref.flags & eServiceReference.isMarker):
750                         self.session.openWithCallback(
751                                 self.epgClosed,
752                                 EPGSearchEPGSelection,
753                                 ref,
754                                 False
755                         )
756
757         def epgClosed(self, ret = None):
758                 if ret:
759                         self.close(ret)
760
761 class EPGSearchEPGSelection(EPGSelection):
762         def __init__(self, session, ref, openPlugin):
763                 EPGSelection.__init__(self, session, ref)
764                 self.skinName = ["EPGSearchEPGSelection", "EPGSelection"]
765                 self["key_green"].text = _("Search")
766                 self.openPlugin = openPlugin
767
768         def infoKeyPressed(self):
769                 self.timerAdd()
770
771         def timerAdd(self):
772                 cur = self["list"].getCurrent()
773                 evt = cur[0]
774                 sref = cur[1]
775                 if not evt:
776                         return
777
778                 if self.openPlugin:
779                         self.session.open(EPGSearch,evt.getEventName())
780                 else:
781                         self.close(evt.getEventName())