[EPGSearch] Append AT-search items to history
[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, 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(_("History"))
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
254         def onCreate(self):
255                 self.setTitle(_("EPG Search"))
256                 self.currSearchATList = None
257                 
258                 if self.searchkwargs and self.searchkwargs.has_key("AT"):
259                         #show matches from SearchFilter
260                         l = self["list"]
261                         l.recalcEntrySize()
262                         resultlist = self.searchkwargs["AT"]
263                         self.currSearchATList = resultlist[:]
264                         if config.plugins.epgsearch.show_shortdesc.value and len(resultlist):
265                                 epgcache = eEPGCache.getInstance()
266                                 resultlist = self.addShortDescription(epgcache, resultlist)
267                         l.list = resultlist
268                         l.l.setList(resultlist)
269                 elif self.searchargs:
270                         self.doSearchEPG(*self.searchargs)
271                 else:
272                         l = self["list"]
273                         l.recalcEntrySize()
274                         l.list = []
275                         l.l.setList(l.list)
276                 del self.searchargs
277                 del self.searchkwargs
278                 
279                 # Partnerbox
280                 if PartnerBoxIconsEnabled:
281                         EPGSelection.GetPartnerboxTimerlist(self)
282
283         def timerAdd(self):
284                 proceed = False
285                 if self.key_green_choice == self.REMOVE_TIMER or self.currSearchATList is None:
286                         EPGSelection.timerAdd(self)
287                 else:
288                         cur = self["list"].getCurrent()
289                         evt = cur[0]
290                         serviceref = cur[1]
291                         event = parseEvent(evt)
292                         for item in self.currSearchATList:
293                                 if item[1] == evt.getEventId():
294                                         if item[4] != evt.getEventName():
295                                                 #add org searchTitle from search filter to event (perhaps changed by SeriesPlugin)
296                                                 event_lst = list(event)
297                                                 event_lst[2] = item[4]
298                                                 event = tuple(event_lst)
299                                                 proceed = True
300                                         break
301                         if proceed:
302                                 newEntry = RecordTimerEntry(serviceref, checkOldTimers = True, dirname = preferredTimerPath(), *event)
303                                 self.session.openWithCallback(self.finishedAdd, TimerEntry, newEntry)
304                         else:
305                                 EPGSelection.timerAdd(self)
306
307         def closeScreen(self):
308                 # Save our history
309                 config.plugins.epgsearch.save()
310                 EPGSelection.closeScreen(self)
311
312         def yellowButtonPressed(self):
313                 self.session.openWithCallback(
314                         self.searchEPG,
315                         NTIVirtualKeyBoard,
316                         title = _("Enter text to search for"),
317                         text = self.currSearch
318                 )
319
320         def infoKeyPressed(self):
321                 cur = self["list"].getCurrent()
322                 event = cur[0]
323                 service = cur[1]
324                 if event is not None:
325                         self.session.open(EventViewEPGSelect, event, service, self.eventViewCallback, self.openSingleServiceEPG, InfoBar.instance.openMultiServiceEPG, self.openSimilarList)
326
327         def openSearchFilterList(self):
328                 import EPGSearchFilter as EPGSearchFilterFile
329                 reload (EPGSearchFilterFile)
330                 from EPGSearchFilter import openSearchFilterList as EPGSearchFilter_openSearchFilterList
331                 EPGSearchFilter_openSearchFilterList(self.session, None, None)
332
333         def openSimilarList(self, eventid, refstr):
334                 self.session.open(EPGSelection, refstr, None, eventid)
335
336         def openSingleServiceEPG(self):
337                 cur = self["list"].getCurrent()
338                 event = cur[0]
339                 service = cur[1]
340                 if event is not None:
341                         self.session.open(EPGSelection, cur[1].ref)
342
343         def menu(self):
344                 options = [
345                         (_("Import from Timer"), self.importFromTimer),
346                         (_("Import from EPG"), self.importFromEPG),
347                 ]
348                 keys = ["1","2"]
349
350                 if autoTimerAvailable:
351                         options.extend((
352                                 (_("Import from AutoTimer"), self.importFromAutoTimer),
353                                 (_("Save search as AutoTimer"), self.addAutoTimer),
354                                 (_("Export selected as AutoTimer"), self.exportAutoTimer),
355                         ))
356                         keys.extend(("3","4","5"))
357                 if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/SeriesPlugin/plugin.py")):
358                         options.append((_("Show series info (SP)"), self.openSPInfoScreen))
359                         keys.append("6")
360
361                 if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/IMDb/plugin.py")):
362                         options.append((_("Open selected in IMDb"), self.openImdb))
363                         keys.append("7")
364
365                 if fileExists(resolveFilename(SCOPE_PLUGINS, "Extensions/AdvancedMovieSelection/plugin.py")):
366                         options.append((_("Open selected in TMDB Info (AMS)"), self.openTMDb))
367                         options.append((_("Open selected in TMDB Serie Info (AMS)"), self.openTMDbSerie))
368                         keys.extend(("8","9"))
369
370                 options.append((_("Setup"), self.setup))
371                 keys.append("0")
372
373                 for p in self.pluginList:
374                         if not p in options and p[0] != _("Search EPG"):
375                                 options.append(p)
376
377                 self.session.openWithCallback(
378                         self.menuCallback,
379                         ChoiceBox,
380                         list = options,
381                         keys = keys,
382                         title = _("EPGSearch menu")
383                 )
384
385         def menuCallback(self, ret):
386                 if ret in self.pluginList:
387                         cur = self['list'].getCurrent()
388                         event = cur and cur[0]
389                         service = cur and cur[1]
390                         if event:
391                                 ret[1](self.session, event, service)
392                 else:
393                         ret and ret[1]()
394
395         def importFromTimer(self):
396                 self.session.openWithCallback(
397                         self.searchEPG,
398                         EPGSearchTimerImport
399                 )
400
401         def importFromEPG(self):
402                 self.session.openWithCallback(
403                         self.searchEPG,
404                         EPGSearchChannelSelection
405                 )
406
407         def importFromAutoTimer(self):
408                 removeInstance = False
409                 try:
410                         # Import Instance
411                         from Plugins.Extensions.AutoTimer.plugin import autotimer
412
413                         if autotimer is None:
414                                 removeInstance = True
415                                 # Create an instance
416                                 from Plugins.Extensions.AutoTimer.AutoTimer import AutoTimer
417                                 autotimer = AutoTimer()
418
419                         # Read in configuration
420                         autotimer.readXml()
421                 except Exception as e:
422                         self.session.open(
423                                 MessageBox,
424                                 _("Could not read AutoTimer timer list: %s") % e,
425                                 type = MessageBox.TYPE_ERROR
426                         )
427                 else:
428                         # Fetch match strings
429                         # XXX: we could use the timer title as description
430                         options = [(x.match, x.match) for x in autotimer.getTimerList()]
431
432                         self.session.openWithCallback(
433                                 self.searchEPGWrapper,
434                                 ChoiceBox,
435                                 title = _("Select text to search for"),
436                                 list = options
437                         )
438                 finally:
439                         # Remove instance if there wasn't one before
440                         if removeInstance:
441                                 autotimer = None
442
443         def addAutoTimer(self):
444                 if autoTimerAvailable:
445                         addAutotimerFromSearchString(self.session, self.currSearch)
446
447         def exportAutoTimer(self):
448                 cur = self['list'].getCurrent()
449                 if cur is None:
450                         return
451                 if autoTimerAvailable:
452                         addAutotimerFromEvent(self.session, cur[0], cur[1])
453
454         def openSPInfoScreen(self):
455                 cur = self['list'].getCurrent()
456                 if cur is None:
457                         return
458                 try:
459                         from Plugins.Extensions.SeriesPlugin.SeriesPluginInfoScreen import SeriesPluginInfoScreen
460                         service=cur[1]
461                         event = cur[0]
462                         self.session.open(SeriesPluginInfoScreen, service, event)
463                 except ImportError as ie:
464                         pass
465
466         def openImdb(self):
467                 cur = self['list'].getCurrent()
468                 if cur is None:
469                         return
470                 try:
471                         from Plugins.Extensions.IMDb.plugin import IMDB
472                         self.session.open(IMDB, cur[0].getEventName())
473                 except ImportError as ie:
474                         pass
475
476         def openTMDb(self):
477                 cur = self['list'].getCurrent()
478                 if cur is None:
479                         return
480                 try:
481                         from Plugins.Extensions.AdvancedMovieSelection.plugin import tmdbInfo
482                         tmdbInfo(self.session, cur[0].getEventName())
483                 except ImportError as ie:
484                         pass
485
486         def openTMDbSerie(self):
487                 cur = self['list'].getCurrent()
488                 if cur is None:
489                         return
490                 try:
491                         from Plugins.Extensions.AdvancedMovieSelection.plugin import tmdbSeriesInfo
492                         tmdbSeriesInfo(self.session, cur[0].getEventName())
493                 except ImportError as ie:
494                         pass
495
496         def setup(self):
497                 self.session.openWithCallback(self.closeSetupCallback, EPGSearchSetup)
498
499         def closeSetupCallback(self):
500                 if self.currSearchATList:
501                         self.searchargs = None
502                         self.searchkwargs = {"AT": self.currSearchATList}
503                         self.onCreate()
504                 else:
505                         self.doSearchEPG(self.currSearch, self.currSearchSave, self.currSearchDescription)
506
507         def searchNoNumber(self):
508                 search_txt = self.currSearch
509                 search_txt = search_txt.translate(None, "1234567890(/)").strip()
510                 self.doSearchEPG(search_txt, self.currSearchSave, self.currSearchDescription)
511
512         def blueButtonPressed(self):
513                 options = [(x, x) for x in config.plugins.epgsearch.history.value]
514                 if autoTimerAvailable:
515                         epgsearchAT = EPGSearchAT()
516                         epgsearchAT.load()
517                         for timer in epgsearchAT.getTimerList():
518                                 options.append((timer.name + _(" with search filter"),timer))
519
520                 if options:
521                         self.session.openWithCallback(
522                                 self.searchEPGWrapper,
523                                 ChoiceBox,
524                                 title = _("Select text to search for"),
525                                 list = options
526                         )
527                 else:
528                         self.session.open(
529                                 MessageBox,
530                                 _("No history"),
531                                 type = MessageBox.TYPE_INFO
532                         )
533
534         def searchEPGWrapper(self, ret):
535                 if ret:
536                         if autoTimerAvailable:
537                                 from Plugins.Extensions.AutoTimer.AutoTimerComponent import AutoTimerComponent
538                                 searchWithAT = isinstance(ret[1],AutoTimerComponent)
539                         else:
540                                 searchWithAT = False
541                         if searchWithAT:
542                                 timer = ret[1]
543                                 epgsearchAT = EPGSearchAT()
544                                 epgsearchAT.add(timer)
545                                 total, new, modified, timers, conflicts, similars = epgsearchAT.parseEPG(simulateOnly = True)
546                                 results = []
547                                 if timers:
548                                         epgcache = eEPGCache.getInstance()
549                                         for t in timers:
550                                                 if timer.hasOffset():
551                                                         rbegin = t[1] + timer.offset[0]
552                                                         rend = t[2] - timer.offset[1]
553                                                 else:
554                                                         rbegin = t[1] + config.recording.margin_before.value * 60
555                                                         rend = t[2] - config.recording.margin_after.value * 60
556                                                 evt = epgcache.lookupEventTime(eServiceReference(t[3]), rbegin)
557                                                 if evt:
558                                                         results.append((t[3], evt.getEventId(), rbegin, rend - rbegin, t[0]))
559
560                                 self.searchargs = None
561                                 self.searchkwargs = {"AT": results}
562                                 epgsearchAT = None
563                                 self.onCreate()
564                                 
565                         else:
566                                 self.searchEPG(ret[1])
567
568         def searchEPG(self, searchString = None, searchSave = True):
569                 if not searchString:
570                         return
571                 searchType = config.plugins.epgsearch.search_type.value
572                 if  searchType == SearchType.ASK:
573                         boundCallback = boundFunction(self.onSearchEPGCallback, searchString=searchString, searchSave=searchSave)
574                         choices = [ (_("Title only"), False),
575                                                 (_("Title and Description"), True) ]
576                         self.session.openWithCallback(boundCallback, ChoiceBox, list=choices, title=_("Where to search for '%s'?") %(searchString), windowTitle=_("EPG Search"))
577                 else:
578                         searchDescription = searchType == SearchType.TITLE_DESCRIPTION
579                         self.doSearchEPG(searchString, searchSave, searchDescription)
580
581         def onSearchEPGCallback(self, answer, searchString=None, searchSave=True):
582                 searchDescription = answer and answer[1]
583                 self.doSearchEPG(searchString, searchSave, searchDescription)
584
585         def doSearchEPG(self, searchString = None, searchSave = True, searchDescription=False):
586                 self.currSearchSave = searchSave
587                 self.currSearchDescription = searchDescription
588                 if searchString:
589                         self.currSearch = searchString
590                         if searchSave:
591                                 # Maintain history
592                                 history = config.plugins.epgsearch.history.value
593                                 if searchString not in history:
594                                         history.insert(0, searchString)
595                                         maxLen = config.plugins.epgsearch.history_length.value
596                                         if len(history) > maxLen:
597                                                 del history[maxLen:]
598                                 else:
599                                         history.remove(searchString)
600                                         history.insert(0, searchString)
601
602                         # Search EPG, default to empty list
603                         searchType = eEPGCache.PARTIAL_TITLE_SEARCH
604                         if config.plugins.epgsearch.search_type.value == "exact_title":
605                                 searchType = eEPGCache.EXACT_TITLE_SEARCH
606                         epgcache = eEPGCache.getInstance() # XXX: the EPGList also keeps an instance of the cache but we better make sure that we get what we want :-)
607                         ret = epgcache.search(('RIBDT', 1000, searchType, searchString, eEPGCache.NO_CASE_CHECK)) or []
608                         if searchDescription:
609                                 ret += epgcache.search(('RIBDT', 1000, eEPGCache.PARTIAL_DESCRIPTION_SEARCH, searchString, eEPGCache.NO_CASE_CHECK)) or []
610                                 #condense by eventids
611                                 condensed = {}
612                                 for item in ret:
613                                         condensed[item[1]] = item
614                                 ret = condensed.values()
615                         ret.sort(key=itemgetter(2)) # sort by time
616
617                         #filter epg-matches for selected bouquet from settings
618                         if config.plugins.epgsearch.search_scope.value != "all" and len(ret):
619                                 ret = self.filterEPGmatches(ret)
620
621                         #add short description to search result
622                         if config.plugins.epgsearch.show_shortdesc.value and len(ret):
623                                 ret = self.addShortDescription(epgcache, ret)
624
625                         #get most searched service
626                         mostSearchService = ""
627                         if not config.plugins.epgsearch.show_picon.value and config.plugins.epgsearch.show_sname_in_title.value and len(ret):
628                                 mostSearchService = self.getMostSearchService(ret)
629
630                         # Update List
631                         l = self["list"]
632
633                         #set mostsearchservice to screen-title
634                         title = _("EPG Search")
635                         l.mostSearchService = mostSearchService #save the value also to EPGList-Class
636                         if not config.plugins.epgsearch.show_picon.value and mostSearchService != "":
637                                 serviceref = ServiceReference(mostSearchService) # for Servicename
638                                 serviceName = serviceref.getServiceName()
639                                 title += " - " + serviceName
640                         self.setTitle(title)
641                         l.recalcEntrySize()
642                         l.list = ret
643                         l.l.setList(ret)
644
645         def getMostSearchService(self, matches):
646                 services = [x[0]for x in matches]
647                 mostSearchService = max(services, key = services.count)
648                 return mostSearchService
649
650         def filterEPGmatches(self, matches):
651                 services = self.getBouquetServiceList(config.plugins.epgsearch.search_scope.value)
652                 if len(services): #only if return services to check
653                         ret1 = []
654                         for event in matches:
655                                 timecheck=True
656                                 if config.plugins.epgsearch.show_events.value == "current_future" and int(event[2])+int(event[3]) < time():
657                                         timecheck = False #if is an old event
658                                 elif config.plugins.epgsearch.show_events.value == "future" and int(event[2]) < time():
659                                         timecheck = False #if is a current or old event
660                                 if config.plugins.epgsearch.show_events.value == "current" and (int(event[2]) > time() or int(event[2])+int(event[3]) < time()):
661                                         timecheck = False #if is a future or an old event
662                                 if str(event[0]) in services and timecheck:
663                                         ret1.append(event)
664                         matches = ret1
665                 return matches
666
667         def addShortDescription(self, epgcache, events):
668                 new_events=[]
669                 for event in events:
670                         epgEvent = epgcache.lookupEventId(eServiceReference(event[0]), int(event[1]))
671                         if epgEvent:
672                                 shortdesc = str(epgEvent.getShortDescription())
673                                 if len(shortdesc) > 0:
674                                         event_lst = list(event)
675                                         event_lst[4] = event_lst[4] + "  |  " + shortdesc
676                                         event = tuple(event_lst)
677                         new_events.append(event)
678
679                 return new_events
680
681         def getBouquetServiceList(self, bouquet="current"):
682                 infoBarInstance = InfoBar.instance
683                 if infoBarInstance is not None:
684                         servicelist = infoBarInstance.servicelist
685                         if bouquet == "current":
686                                 bouquet = servicelist.getRoot().toString()
687                         services = infoBarInstance.getBouquetServices(eServiceReference(bouquet))
688
689                         service_list = []
690                         for service in services:
691                                 service_ref = service.ref.toString()
692
693                                 serviceref_split = service_ref.split(":")
694                                 if len(serviceref_split)>10: #perhaps on ip-tv or renamed service
695                                         serviceref_split[0]="1"
696                                         serviceref_split[1]="0"
697                                         #cut the service_ref
698                                         service_ref = ":".join(serviceref_split[:10]) + ":"
699
700                                 service_list.append(service_ref)
701
702                         return service_list
703
704 class EPGSearchTimerImport(Screen):
705         def __init__(self, session):
706                 Screen.__init__(self, session)
707                 self.skinName = ["EPGSearchTimerImport", "TimerEditList"]
708
709                 self.list = []
710                 self.fillTimerList()
711
712                 self["timerlist"] = TimerList(self.list)
713
714                 self["key_red"] = Button(_("Cancel"))
715                 self["key_green"] = Button(_("OK"))
716                 self["key_yellow"] = Button("")
717                 self["key_blue"] = Button("")
718
719                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions"],
720                 {
721                         "ok": self.search,
722                         "cancel": self.cancel,
723                         "green": self.search,
724                         "red": self.cancel
725                 }, -1)
726                 self.onLayoutFinish.append(self.setCustomTitle)
727
728         def setCustomTitle(self):
729                 self.setTitle(_("Select a timer to search"))
730
731         def fillTimerList(self):
732                 l = self.list
733                 del l[:]
734
735                 for timer in self.session.nav.RecordTimer.timer_list:
736                         l.append((timer, False))
737
738                 for timer in self.session.nav.RecordTimer.processed_timers:
739                         l.append((timer, True))
740                 l.sort(key = lambda x: x[0].begin)
741
742         def search(self):
743                 cur = self["timerlist"].getCurrent()
744                 if cur:
745                         self.close(cur.name)
746
747         def cancel(self):
748                 self.close(None)
749
750 class EPGSearchChannelSelection(SimpleChannelSelection):
751         def __init__(self, session):
752                 SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
753                 self.skinName = ["EPGSearchChannelSelection", "SimpleChannelSelection"]
754
755                 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
756                 {
757                                 "showEPGList": self.channelSelected
758                 })
759
760         def channelSelected(self):
761                 ref = self.getCurrentSelection()
762                 if (ref.flags & 7) == 7:
763                         self.enterPath(ref)
764                 elif not (ref.flags & eServiceReference.isMarker):
765                         self.session.openWithCallback(
766                                 self.epgClosed,
767                                 EPGSearchEPGSelection,
768                                 ref,
769                                 False
770                         )
771
772         def epgClosed(self, ret = None):
773                 if ret:
774                         self.close(ret)
775
776 class EPGSearchEPGSelection(EPGSelection):
777         def __init__(self, session, ref, openPlugin):
778                 EPGSelection.__init__(self, session, ref)
779                 self.skinName = ["EPGSearchEPGSelection", "EPGSelection"]
780                 self["key_green"].text = _("Search")
781                 self.openPlugin = openPlugin
782
783         def infoKeyPressed(self):
784                 self.timerAdd()
785
786         def timerAdd(self):
787                 cur = self["list"].getCurrent()
788                 evt = cur[0]
789                 sref = cur[1]
790                 if not evt:
791                         return
792
793                 if self.openPlugin:
794                         self.session.open(EPGSearch,evt.getEventName())
795                 else:
796                         self.close(evt.getEventName())