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