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