allow to restrict autotimer to alternatives,
[vuplus_dvbapp-plugin] / autotimer / src / AutoTimerEditor.py
1 # -*- coding: UTF-8 -*-
2 # for localized messages
3 from . import _
4
5 # GUI (Screens)
6 from Screens.Screen import Screen
7 from Components.ConfigList import ConfigListScreen
8 from Screens.ChannelSelection import SimpleChannelSelection
9 from Screens.EpgSelection import EPGSelection
10 from Screens.MessageBox import MessageBox
11 from Screens.ChoiceBox import ChoiceBox
12
13 # GUI (Summary)
14 from Screens.Setup import SetupSummary
15
16 # GUI (Components)
17 from Components.ActionMap import ActionMap
18 from Components.Button import Button
19
20 # Configuration
21 from Components.config import getConfigListEntry, ConfigEnableDisable, \
22         ConfigYesNo, ConfigText, ConfigClock, ConfigNumber, ConfigSelection, \
23         config, NoSave
24
25 # Timer
26 from RecordTimer import AFTEREVENT
27
28 # Needed to convert our timestamp back and forth
29 from time import localtime, mktime
30
31 # Show ServiceName instead of ServiceReference
32 from ServiceReference import ServiceReference
33
34 # addAutotimerFromService, AutoTimerChannelSelection
35 from enigma import eServiceCenter, eServiceReference, iServiceInformation
36
37 # Default Record Directory
38 from Tools import Directories
39
40 # Tags
41 from Screens.MovieSelection import getPreferredTagEditor
42
43 weekdays = [
44         ("0", _("Monday")),
45         ("1", _("Tuesday")),
46         ("2", _("Wednesday")),
47         ("3", _("Thursday")),
48         ("4", _("Friday")),
49         ("5", _("Saturday")),
50         ("6", _("Sunday")),
51         ("weekend", _("Weekend")),
52         ("weekday", _("Weekday"))
53 ]
54
55 class ExtendedConfigText(ConfigText):
56         def __init__(self, default = "", fixed_size = True, visible_width = False):
57                 ConfigText.__init__(self, default = default, fixed_size = fixed_size, visible_width = visible_width)
58
59                 # Workaround some characters currently not "typeable" using NumericalTextInput
60                 mapping = self.mapping
61                 if mapping:
62                         if "&" not in mapping[0]:
63                                 mapping[0] += "&"
64                         if ";" not in mapping[0]:
65                                 mapping[0] += ";"
66                         if "%" not in mapping[0]:
67                                 mapping[0] += "%"
68
69 class SimpleBouquetSelection(SimpleChannelSelection):
70         def __init__(self, session, title):
71                 SimpleChannelSelection.__init__(self, session, title)
72                 self.skinName = "SimpleChannelSelection"
73
74         def channelSelected(self):
75                 ref = self.getCurrentSelection()
76                 if (ref.flags & 7) == 7:
77                         self.close(ref)
78                 else:
79                         # We return the currently active path here
80                         # Asking the user if this is what he wants might be better though
81                         self.close(self.servicePath[-1])
82
83 class AutoTimerChannelSelection(SimpleChannelSelection):
84         def __init__(self, session, autotimer):
85                 SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
86                 self.skinName = "SimpleChannelSelection"
87                 self.autotimer = autotimer
88
89                 self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
90                         {
91                                 "showEPGList": self.channelSelected
92                         }
93                 )
94
95         def channelSelected(self):
96                 ref = self.getCurrentSelection()
97                 if (ref.flags & 7) == 7:
98                         self.enterPath(ref)
99                 elif not (ref.flags & eServiceReference.isMarker):
100                         self.session.open(
101                                 AutoTimerEPGSelection,
102                                 ref
103                         )
104
105 class AutoTimerEPGSelection(EPGSelection):
106         def __init__(self, *args):
107                 EPGSelection.__init__(self, *args)
108                 self.skinName = "EPGSelection"
109
110         def infoKeyPressed(self):
111                 self.timerAdd()
112
113         def timerAdd(self):
114                 cur = self["list"].getCurrent()
115                 evt = cur[0]
116                 sref = cur[1]
117                 if not evt:
118                         return
119
120                 addAutotimerFromEvent(self.session, evt = evt, service = sref)
121
122         def onSelectionChanged(self):
123                 pass
124
125 class AutoTimerEditorBase:
126         """ Base Class for all Editors """
127         def __init__(self, timer, editingDefaults = False):
128                 # Keep Timer
129                 self.timer = timer
130                 self.editingDefaults = editingDefaults
131
132                 # See if we are filtering some strings
133                 excludes = (
134                         timer.getExcludedTitle(),
135                         timer.getExcludedShort(),
136                         timer.getExcludedDescription(),
137                         timer.getExcludedDays()
138                 )
139                 includes = (
140                         timer.getIncludedTitle(),
141                         timer.getIncludedShort(),
142                         timer.getIncludedDescription(),
143                         timer.getIncludedDays()
144                 )
145                 if excludes[0] or excludes[1] \
146                                 or excludes[2] or excludes[3] \
147                                 or includes[0] or includes[1] \
148                                 or includes[2] or includes[3]:
149                         self.filterSet = True
150                 else:
151                         self.filterSet = False
152                 self.excludes = excludes
153                 self.includes = includes
154
155                 # See if services are restricted
156                 self.services = timer.services
157                 self.bouquets = timer.bouquets
158                 if self.services or self.bouquets:
159                         self.serviceRestriction = True
160                 else:
161                         self.serviceRestriction = False
162
163                 self.createSetup(timer)
164
165         def createSetup(self, timer):
166                 # Name
167                 self.name = NoSave(ExtendedConfigText(default = timer.name, fixed_size = False))
168
169                 # Match
170                 self.match = NoSave(ExtendedConfigText(default = timer.match, fixed_size = False))
171
172                 # Encoding
173                 default = timer.encoding
174                 selection = ['UTF-8', 'ISO8859-15']
175                 if default not in selection:
176                         selection.append(default)
177                 self.encoding = NoSave(ConfigSelection(choices = selection, default = default))
178
179                 # ...
180                 self.searchType = NoSave(ConfigSelection(choices = [("partial", _("partial match")), ("exact", _("exact match"))], default = timer.searchType))
181                 self.searchCase = NoSave(ConfigSelection(choices = [("sensitive", _("case-sensitive search")), ("insensitive", _("case-insensitive search"))], default = timer.searchCase))
182
183                 # Alternatives override
184                 self.overrideAlternatives = NoSave(ConfigYesNo(default = timer.overrideAlternatives))
185
186                 # Justplay
187                 self.justplay = NoSave(ConfigSelection(choices = [("zap", _("zap")), ("record", _("record"))], default = {0: "record", 1: "zap"}[int(timer.justplay)]))
188
189                 # Timespan
190                 now = [x for x in localtime()]
191                 if timer.hasTimespan():
192                         default = True
193                         now[3] = timer.timespan[0][0]
194                         now[4] = timer.timespan[0][1]
195                         begin = mktime(now)
196                         now[3] = timer.timespan[1][0]
197                         now[4] = timer.timespan[1][1]
198                         end = mktime(now)
199                 else:
200                         default = False
201                         now[3] = 20
202                         now[4] = 15
203                         begin = mktime(now)
204                         now[3] = 23
205                         now[4] = 15
206                         end = mktime(now)
207                 self.timespan = NoSave(ConfigEnableDisable(default = default))
208                 self.timespanbegin = NoSave(ConfigClock(default = begin))
209                 self.timespanend = NoSave(ConfigClock(default = end))
210
211                 # Services have their own Screen
212
213                 # Offset
214                 if timer.hasOffset():
215                         default = True
216                         begin = timer.getOffsetBegin()
217                         end = timer.getOffsetEnd()
218                 else:
219                         default = False
220                         begin = 5
221                         end = 5
222                 self.offset = NoSave(ConfigEnableDisable(default = default))
223                 self.offsetbegin = NoSave(ConfigNumber(default = begin))
224                 self.offsetend = NoSave(ConfigNumber(default = end))
225
226                 # AfterEvent
227                 if timer.hasAfterEvent():
228                         default = {
229                                 None: "default",
230                                 AFTEREVENT.NONE: "nothing",
231                                 AFTEREVENT.DEEPSTANDBY: "deepstandby",
232                                 AFTEREVENT.STANDBY: "standby",
233                                 AFTEREVENT.AUTO: "auto"
234                         }[timer.afterevent[0][0]]
235                 else:
236                         default = "default"
237                 self.afterevent = NoSave(ConfigSelection(choices = [
238                         ("default", _("standard")), ("nothing", _("do nothing")),
239                         ("standby", _("go to standby")),
240                         ("deepstandby", _("go to deep standby")),
241                         ("auto", _("auto"))], default = default))
242
243                 # AfterEvent (Timespan)
244                 if timer.hasAfterEvent() and timer.afterevent[0][1][0] is not None:
245                         default = True
246                         now[3] = timer.afterevent[0][1][0][0]
247                         now[4] = timer.afterevent[0][1][0][1]
248                         begin = mktime(now)
249                         now[3] = timer.afterevent[0][1][1][0]
250                         now[4] = timer.afterevent[0][1][1][1]
251                         end = mktime(now)
252                 else:
253                         default = False
254                         now[3] = 23
255                         now[4] = 15
256                         begin = mktime(now)
257                         now[3] = 7
258                         now[4] = 0
259                         end = mktime(now)
260                 self.afterevent_timespan = NoSave(ConfigEnableDisable(default = default))
261                 self.afterevent_timespanbegin = NoSave(ConfigClock(default = begin))
262                 self.afterevent_timespanend = NoSave(ConfigClock(default = end))
263
264                 # Enabled
265                 self.enabled = NoSave(ConfigYesNo(default = timer.enabled))
266
267                 # Maxduration
268                 if timer.hasDuration():
269                         default = True
270                         duration = timer.getDuration()
271                 else:
272                         default = False
273                         duration =70
274                 self.duration = NoSave(ConfigEnableDisable(default = default))
275                 self.durationlength = NoSave(ConfigNumber(default = duration))
276
277                 # Counter
278                 if timer.hasCounter():
279                         default = timer.matchCount
280                 else:
281                         default = 0
282                 self.counter = NoSave(ConfigNumber(default = default))
283                 self.counterLeft = NoSave(ConfigNumber(default = timer.matchLeft))
284                 default = timer.getCounterFormatString()
285                 selection = [("", _("Never")), ("%m", _("Monthly")), ("%U", _("Weekly (Sunday)")), ("%W", _("Weekly (Monday)"))]
286                 if default not in ('', '%m', '%U', '%W'):
287                         selection.append((default, _("Custom (%s)") % (default)))
288                 self.counterFormatString = NoSave(ConfigSelection(selection, default = default))
289
290                 # Avoid Duplicate Description
291                 self.avoidDuplicateDescription = NoSave(ConfigSelection([
292                                 ("0", _("No")),
293                                 ("1", _("On same service")),
294                                 ("2", _("On any service")),
295                         ],
296                         default = str(timer.getAvoidDuplicateDescription())
297                 ))
298
299                 # Custom Location
300                 if timer.hasDestination():
301                         default = True
302                 else:
303                         default = False
304
305                 self.useDestination = NoSave(ConfigYesNo(default = default))
306
307                 default = timer.destination or Directories.resolveFilename(Directories.SCOPE_HDD)
308                 choices = config.movielist.videodirs.value
309
310                 if default not in choices:
311                         choices.append(default)
312                 self.destination = NoSave(ConfigSelection(default = default, choices = choices))
313
314                 # Tags
315                 self.timerentry_tags = timer.tags
316                 self.tags = NoSave(ConfigSelection(choices = [len(self.timerentry_tags) == 0 and _("None") or ' '.join(self.timerentry_tags)]))
317
318         def pathSelected(self, res):
319                 if res is not None:
320                         # I'm pretty sure this will always fail
321                         if config.movielist.videodirs.value != self.destination.choices:
322                                         self.destination.setChoices(config.movielist.videodirs.value, default = res)
323                         self.destination.value = res
324
325         def chooseDestination(self):
326                 from Screens.LocationBox import MovieLocationBox
327
328                 self.session.openWithCallback(
329                         self.pathSelected,
330                         MovieLocationBox,
331                         _("Choose target folder"),
332                         self.destination.value,
333                         minFree = 100 # Same requirement as in Screens.TimerEntry
334                 )
335
336         def tagEditFinished(self, ret):
337                 if ret is not None:
338                         self.timerentry_tags = ret
339                         self.tags.setChoices([len(ret) == 0 and _("None") or ' '.join(ret)])
340
341         def chooseTags(self):
342                 preferredTagEditor = getPreferredTagEditor()
343                 if preferredTagEditor:
344                         self.session.openWithCallback(
345                                 self.tagEditFinished,
346                                 preferredTagEditor,
347                                 self.timerentry_tags
348                         )
349
350 class AutoTimerEditor(Screen, ConfigListScreen, AutoTimerEditorBase):
351         """Edit AutoTimer"""
352
353         skin = """<screen name="AutoTimerEdit" title="Edit AutoTimer" position="75,155" size="565,280">
354                 <widget name="config" position="5,5" size="555,225" scrollbarMode="showOnDemand" />
355                 <ePixmap position="0,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
356                 <ePixmap position="140,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
357                 <ePixmap position="280,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
358                 <ePixmap position="420,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
359                 <widget name="key_red" position="0,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
360                 <widget name="key_green" position="140,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
361                 <widget name="key_yellow" position="280,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
362                 <widget name="key_blue" position="420,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
363         </screen>"""
364
365         def __init__(self, session, timer, editingDefaults = False):
366                 Screen.__init__(self, session)
367
368                 AutoTimerEditorBase.__init__(self, timer, editingDefaults)
369
370                 # Summary
371                 self.setup_title = _("AutoTimer Editor")
372                 self.onChangedEntry = []
373
374                 # We might need to change shown items, so add some notifiers
375                 self.timespan.addNotifier(self.reloadList, initial_call = False)
376                 self.offset.addNotifier(self.reloadList, initial_call = False)
377                 self.duration.addNotifier(self.reloadList, initial_call = False)
378                 self.afterevent.addNotifier(self.reloadList, initial_call = False)
379                 self.afterevent_timespan.addNotifier(self.reloadList, initial_call = False)
380                 self.counter.addNotifier(self.reloadList, initial_call = False)
381                 self.useDestination.addNotifier(self.reloadList, initial_call = False)
382
383                 self.refresh()
384
385                 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changed)
386
387                 # Initialize Buttons
388                 self["key_red"] = Button(_("Cancel"))
389                 self["key_green"] = Button(_("OK"))
390                 self["key_yellow"] = Button()
391                 self["key_blue"] = Button()
392
393                 # Set Button texts
394                 self.renameServiceButton()
395                 self.renameFilterButton()
396
397                 # Define Actions
398                 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
399                         {
400                                 "cancel": self.cancel,
401                                 "save": self.maybeSave,
402                                 "ok": self.ok,
403                                 "yellow": self.editFilter,
404                                 "blue": self.editServices
405                         }, -2
406                 )
407
408                 # Trigger change
409                 self.changed()
410
411                 self.onLayoutFinish.append(self.setCustomTitle)
412
413         def setCustomTitle(self):
414                 self.setTitle(_("Edit AutoTimer"))
415
416         def renameFilterButton(self):
417                 if self.filterSet:
418                         self["key_yellow"].setText(_("Edit Filters"))
419                 else:
420                         self["key_yellow"].setText(_("Add Filters"))
421
422         def renameServiceButton(self):
423                 if self.serviceRestriction:
424                         self["key_blue"].setText(_("Edit Services"))
425                 else:
426                         self["key_blue"].setText(_("Add Services"))
427
428         def changed(self):
429                 for x in self.onChangedEntry:
430                         try:
431                                 x()
432                         except Exception:
433                                 pass
434
435         def getCurrentEntry(self):
436                 return self["config"].getCurrent()[0]
437
438         def getCurrentValue(self):
439                 return str(self["config"].getCurrent()[1].getText())
440
441         def createSummary(self):
442                 return SetupSummary
443
444         def refresh(self):
445                 # First three entries are only showed when not editing defaults
446                 list = []
447                 if not self.editingDefaults:
448                         list.extend((
449                                 getConfigListEntry(_("Enabled"), self.enabled),
450                                 getConfigListEntry(_("Description"), self.name),
451                                 getConfigListEntry(_("Match Title"), self.match),
452                         ))
453
454                 list.extend((
455                         getConfigListEntry(_("EPG Encoding"), self.encoding),
456                         getConfigListEntry(_("Search Type"), self.searchType),
457                         getConfigListEntry(_("Search strictness"), self.searchCase),
458                         getConfigListEntry(_("Timer Type"), self.justplay),
459                         getConfigListEntry(_("Override found with alternative Service"), self.overrideAlternatives),
460                         getConfigListEntry(_("Only match during Timespan"), self.timespan)
461                 ))
462
463                 # Only allow editing timespan when it's enabled
464                 if self.timespan.value:
465                         list.extend((
466                                 getConfigListEntry(_("Begin of Timespan"), self.timespanbegin),
467                                 getConfigListEntry(_("End of Timespan"), self.timespanend)
468                         ))
469
470                 list.append(getConfigListEntry(_("Custom offset"), self.offset))
471
472                 # Only allow editing offsets when it's enabled
473                 if self.offset.value:
474                         list.extend((
475                                 getConfigListEntry(_("Offset before recording (in m)"), self.offsetbegin),
476                                 getConfigListEntry(_("Offset after recording (in m)"), self.offsetend)
477                         ))
478
479                 list.append(getConfigListEntry(_("Set maximum Duration"), self.duration))
480
481                 # Only allow editing maxduration when it's enabled
482                 if self.duration.value:
483                         list.append(getConfigListEntry(_("Maximum Duration (in m)"), self.durationlength))
484
485                 list.append(getConfigListEntry(_("After event"), self.afterevent))
486
487                 # Only allow setting afterevent timespan when afterevent is active
488                 if self.afterevent.value != "default":
489                         list.append(getConfigListEntry(_("Execute after Event during Timespan"), self.afterevent_timespan))
490
491                         # Only allow editing timespan when it's enabled
492                         if self.afterevent_timespan.value:
493                                 list.extend((
494                                         getConfigListEntry(_("Begin of after Event Timespan"), self.afterevent_timespanbegin),
495                                         getConfigListEntry(_("End of after Event Timespan"), self.afterevent_timespanend)
496                                 ))
497
498                 list.append(getConfigListEntry(_("Record a maximum of x times"), self.counter))
499
500                 # Only allow setting matchLeft when counting hits
501                 if self.counter.value:
502                         if not self.editingDefaults:
503                                 list.append(getConfigListEntry(_("Ammount of recordings left"), self.counterLeft))
504                         list.append(getConfigListEntry(_("Reset Count"), self.counterFormatString))
505
506                 list.append(getConfigListEntry(_("Require Description to be unique"), self.avoidDuplicateDescription))
507
508                 # We always add this option though its expert only in enigma2
509                 list.append(getConfigListEntry(_("Use a custom location"), self.useDestination))
510                 if self.useDestination.value:
511                         list.append(getConfigListEntry(_("Custom Location"), self.destination))
512
513                 list.append(getConfigListEntry(_("Tags"), self.tags))
514
515                 self.list = list
516
517         def reloadList(self, value):
518                 self.refresh()
519                 self["config"].setList(self.list)
520
521         def editFilter(self):
522                 self.session.openWithCallback(
523                         self.editFilterCallback,
524                         AutoTimerFilterEditor,
525                         self.filterSet,
526                         self.excludes,
527                         self.includes
528                 )
529
530         def editFilterCallback(self, ret):
531                 if ret:
532                         self.filterSet = ret[0]
533                         self.excludes = ret[1]
534                         self.includes = ret[2]
535                         self.renameFilterButton()
536
537         def editServices(self):
538                 self.session.openWithCallback(
539                         self.editServicesCallback,
540                         AutoTimerServiceEditor,
541                         self.serviceRestriction,
542                         self.services,
543                         self.bouquets
544                 )
545
546         def editServicesCallback(self, ret):
547                 if ret:
548                         self.serviceRestriction = ret[0]
549                         self.services = ret[1][0]
550                         self.bouquets = ret[1][1]
551                         self.renameServiceButton()
552
553         def ok(self):
554                 cur = self["config"].getCurrent()
555                 cur = cur and cur[1]
556                 if cur == self.destination:
557                         self.chooseDestination()
558                 elif cur == self.tags:
559                         self.chooseTags()
560                 else:
561                         ConfigListScreen.keyOK(self)
562
563         def cancel(self):
564                 if self["config"].isChanged():
565                         self.session.openWithCallback(
566                                 self.cancelConfirm,
567                                 MessageBox,
568                                 _("Really close without saving settings?")
569                         )
570                 else:
571                         self.close(None)
572
573         def cancelConfirm(self, ret):
574                 if ret:
575                         self.close(None)
576
577         def maybeSave(self):
578                 if self.editingDefaults:
579                         self.save()
580                         return
581                 # Check if any match is set
582                 if not self.match.value.strip():
583                         self.session.open(
584                                         MessageBox,
585                                         _("The match attribute is mandatory."),
586                                         type = MessageBox.TYPE_ERROR,
587                                         timeout = 5
588                         )
589                 # Check if we have a trailing whitespace
590                 elif self.match.value[-1:] == " ":
591                         self.session.openWithCallback(
592                                 self.saveCallback,
593                                 MessageBox,
594                                 _('You entered "%s" as Text to match.\nDo you want to remove trailing whitespaces?') % (self.match.value)
595                         )
596                 # Just save else
597                 else:
598                         self.save()
599
600         def saveCallback(self, ret):
601                 if ret is not None:
602                         if ret:
603                                 self.match.value = self.match.value.rstrip()
604                         self.save()
605                 # Don't to anything if MessageBox was canceled!
606
607         def save(self):
608                 # Match
609                 self.timer.match = self.match.value
610
611                 # Name
612                 self.timer.name = self.name.value.strip() or self.timer.match
613
614                 # Encoding
615                 self.timer.encoding = self.encoding.value
616
617                 # ...
618                 self.timer.searchType = self.searchType.value
619                 self.timer.searchCase = self.searchCase.value
620
621                 # Alternatives
622                 self.timer.overrideAlternatives = self.overrideAlternatives.value
623
624                 # Enabled
625                 self.timer.enabled = self.enabled.value
626
627                 # Justplay
628                 self.timer.justplay = self.justplay.value == "zap"
629
630                 # Timespan
631                 if self.timespan.value:
632                         start = self.timespanbegin.value
633                         end = self.timespanend.value
634                         self.timer.timespan = (start, end)
635                 else:
636                         self.timer.timespan = None
637
638                 # Services
639                 if self.serviceRestriction:
640                         self.timer.services = self.services
641                         self.timer.bouquets = self.bouquets
642                 else:
643                         self.timer.services = None
644                         self.timer.bouquets = None
645
646                 # Offset
647                 if self.offset.value:
648                         self.timer.offset = (self.offsetbegin.value*60, self.offsetend.value*60)
649                 else:
650                         self.timer.offset = None
651
652                 # AfterEvent
653                 if self.afterevent.value == "default":
654                         self.timer.afterevent = []
655                 else:
656                         afterevent = {
657                                 "nothing": AFTEREVENT.NONE,
658                                 "deepstandby": AFTEREVENT.DEEPSTANDBY,
659                                 "standby": AFTEREVENT.STANDBY,
660                                 "auto": AFTEREVENT.AUTO
661                         }[self.afterevent.value]
662                         # AfterEvent Timespan
663                         if self.afterevent_timespan.value:
664                                 start = self.afterevent_timespanbegin.value
665                                 end = self.afterevent_timespanend.value
666                                 self.timer.afterevent = [(afterevent, (start, end))]
667                         else:
668                                 self.timer.afterevent = [(afterevent, None)]
669
670                 # Maxduration
671                 if self.duration.value:
672                         self.timer.maxduration = self.durationlength.value*60
673                 else:
674                         self.timer.maxduration = None
675
676                 # Ex-&Includes
677                 if self.filterSet:
678                         self.timer.exclude = self.excludes
679                         self.timer.include = self.includes
680                 else:
681                         self.timer.exclude = None
682                         self.timer.include = None
683
684                 # Counter
685                 if self.counter.value:
686                         self.timer.matchCount = self.counter.value
687                         if self.counterLeft.value <= self.counter.value:
688                                 self.timer.matchLeft = self.counterLeft.value
689                         else:
690                                 self.timer.matchLeft = self.counter.value
691                         if self.counterFormatString.value:
692                                 self.timer.matchFormatString = self.counterFormatString.value
693                         else:
694                                 self.timer.matchFormatString = ''
695                 else:
696                         self.timer.matchCount = 0
697                         self.timer.matchLeft = 0
698                         self.timer.matchFormatString = ''
699
700                 self.timer.avoidDuplicateDescription = int(self.avoidDuplicateDescription.value)
701
702                 if self.useDestination.value:
703                         self.timer.destination = self.destination.value
704                 else:
705                         self.timer.destination = None
706
707                 self.timer.tags = self.timerentry_tags
708
709                 # Close
710                 self.close(self.timer)
711
712 class AutoTimerFilterEditor(Screen, ConfigListScreen):
713         """Edit AutoTimer Filter"""
714
715         skin = """<screen name="AutoFilterEditor" title="Edit AutoTimer Filters" position="75,150" size="565,245">
716                 <widget name="config" position="5,5" size="555,200" scrollbarMode="showOnDemand" />
717                 <ePixmap position="5,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
718                 <ePixmap position="145,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
719                 <ePixmap position="285,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
720                 <ePixmap position="425,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
721                 <widget name="key_red" position="5,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
722                 <widget name="key_green" position="145,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
723                 <widget name="key_yellow" position="285,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
724                 <widget name="key_blue" position="425,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
725         </screen>"""
726
727         def __init__(self, session, filterset, excludes, includes):
728                 Screen.__init__(self, session)
729
730                 # Summary
731                 self.setup_title = _("AutoTimer Filters")
732                 self.onChangedEntry = []
733
734                 self.typeSelection = NoSave(ConfigSelection(choices = [
735                         ("title", _("in Title")),
736                         ("short", _("in Shortdescription")),
737                         ("desc", _("in Description")),
738                         ("day", _("on Weekday"))]
739                 ))
740                 self.typeSelection.addNotifier(self.refresh, initial_call = False)
741
742                 self.enabled = NoSave(ConfigEnableDisable(default = filterset))
743
744                 self.excludes = excludes
745                 self.includes = includes
746
747                 self.reloadList()
748
749                 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changed)
750
751                 # Initialize Buttons
752                 self["key_red"] = Button(_("Cancel"))
753                 self["key_green"] = Button(_("Save"))
754                 self["key_yellow"] = Button(_("delete"))
755                 self["key_blue"] = Button(_("New"))
756
757                 # Define Actions
758                 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
759                         {
760                                 "cancel": self.cancel,
761                                 "save": self.save,
762                                 "yellow": self.remove,
763                                 "blue": self.new
764                         }
765                 )
766
767                 # Trigger change
768                 self.changed()
769
770                 self.onLayoutFinish.append(self.setCustomTitle)
771
772         def setCustomTitle(self):
773                 self.setTitle(_("Edit AutoTimer Filters"))
774
775
776         def changed(self):
777                 for x in self.onChangedEntry:
778                         try:
779                                 x()
780                         except Exception:
781                                 pass
782
783         def getCurrentEntry(self):
784                 return self["config"].getCurrent()[0]
785
786         def getCurrentValue(self):
787                 return str(self["config"].getCurrent()[1].getText())
788
789         def createSummary(self):
790                 return SetupSummary
791
792         def saveCurrent(self):
793                 del self.excludes[self.idx][:]
794                 del self.includes[self.idx][:]
795
796                 # Warning, accessing a ConfigListEntry directly might be considered evil!
797
798                 idx = -1
799                 for item in self["config"].getList()[:]:
800                         idx += 1
801                         # Skip empty entries (and those which are no filters)
802                         if item[1].value == "" or idx < 2:
803                                 continue
804                         elif idx < self.lenExcludes:
805                                 self.excludes[self.idx].append(item[1].value.encode("UTF-8"))
806                         else:
807                                 self.includes[self.idx].append(item[1].value.encode("UTF-8"))
808
809         def refresh(self, *args, **kwargs):
810                 self.saveCurrent()
811
812                 self.reloadList()
813                 self["config"].setList(self.list)
814
815         def reloadList(self):
816                 self.list = [
817                         getConfigListEntry(_("Enable Filtering"), self.enabled),
818                         getConfigListEntry(_("Filter"), self.typeSelection)
819                 ]
820
821                 if self.typeSelection.value == "day":
822                         self.idx = 3
823
824                         # Weekdays are presented as ConfigSelection
825                         self.list.extend([
826                                 getConfigListEntry(_("Exclude"), NoSave(ConfigSelection(choices = weekdays, default = x)))
827                                         for x in self.excludes[3]
828                         ])
829                         self.lenExcludes = len(self.list)
830                         self.list.extend([
831                                 getConfigListEntry(_("Include"), NoSave(ConfigSelection(choices = weekdays, default = x)))
832                                         for x in self.includes[3]
833                         ])
834                         return
835                 elif self.typeSelection.value == "title":
836                         self.idx = 0
837                 elif self.typeSelection.value == "short":
838                         self.idx = 1
839                 else: # self.typeSelection.value == "desc":
840                         self.idx = 2
841
842                 self.list.extend([
843                         getConfigListEntry(_("Exclude"), NoSave(ExtendedConfigText(default = x, fixed_size = False)))
844                                 for x in self.excludes[self.idx]
845                 ])
846                 self.lenExcludes = len(self.list)
847                 self.list.extend([
848                         getConfigListEntry(_("Include"), NoSave(ExtendedConfigText(default = x, fixed_size = False)))
849                                 for x in self.includes[self.idx]
850                 ])
851
852         def remove(self):
853                 idx = self["config"].getCurrentIndex()
854                 if idx and idx > 1:
855                         if idx < self.lenExcludes:
856                                 self.lenExcludes -= 1
857
858                         list = self["config"].getList()
859                         list.remove(self["config"].getCurrent())
860                         self["config"].setList(list)
861
862         def new(self):
863                 self.session.openWithCallback(
864                         self.typeSelected,
865                         ChoiceBox,
866                         _("Select type of Filter"),
867                         [
868                                 (_("Exclude"), 0),
869                                 (_("Include"), 1),
870                         ]
871                 )
872
873         def typeSelected(self, ret):
874                 if ret is not None:
875                         list = self["config"].getList()
876
877                         if ret[1] == 0:
878                                 pos = self.lenExcludes
879                                 self.lenExcludes += 1
880                                 text = ret[0]
881                         else:
882                                 pos = len(self.list)
883                                 text = ret[0]
884
885                         if self.typeSelection.value == "day":
886                                 entry = getConfigListEntry(text, NoSave(ConfigSelection(choices = weekdays)))
887                         else:
888                                 entry = getConfigListEntry(text, NoSave(ExtendedConfigText(fixed_size = False)))
889
890                         list.insert(pos, entry)
891                         self["config"].setList(list)
892
893         def cancel(self):
894                 if self["config"].isChanged():
895                         self.session.openWithCallback(
896                                 self.cancelConfirm,
897                                 MessageBox,
898                                 _("Really close without saving settings?")
899                         )
900                 else:
901                         self.close(None)
902
903         def cancelConfirm(self, ret):
904                 if ret:
905                         self.close(None)
906
907         def save(self):
908                 self.refresh()
909
910                 self.close((
911                         self.enabled.value,
912                         self.excludes,
913                         self.includes
914                 ))
915
916 class AutoTimerServiceEditor(Screen, ConfigListScreen):
917         """Edit allowed Services of a AutoTimer"""
918
919         skin = """<screen name="AutoTimerServiceEditor" title="Edit AutoTimer Services" position="75,150" size="565,245">
920                 <widget name="config" position="5,5" size="555,200" scrollbarMode="showOnDemand" />
921                 <ePixmap position="5,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
922                 <ePixmap position="145,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
923                 <ePixmap position="285,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
924                 <ePixmap position="425,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
925                 <widget name="key_red" position="5,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
926                 <widget name="key_green" position="145,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
927                 <widget name="key_yellow" position="285,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
928                 <widget name="key_blue" position="425,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
929         </screen>"""
930
931         def __init__(self, session, servicerestriction, servicelist, bouquetlist):
932                 Screen.__init__(self, session)
933
934                 # Summary
935                 self.setup_title = _("AutoTimer Services")
936                 self.onChangedEntry = []
937
938                 self.services = (
939                         servicelist[:],
940                         bouquetlist[:]
941                 )
942
943                 self.enabled = NoSave(ConfigEnableDisable(default = servicerestriction))
944                 self.typeSelection = NoSave(ConfigSelection(choices = [
945                         ("channels", _("Channels")),
946                         ("bouquets", _("Bouquets"))]
947                 ))
948                 self.typeSelection.addNotifier(self.refresh, initial_call = False)
949
950                 self.reloadList()
951
952                 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changed)
953
954                 # Initialize Buttons
955                 self["key_red"] = Button(_("Cancel"))
956                 self["key_green"] = Button(_("OK"))
957                 self["key_yellow"] = Button(_("delete"))
958                 self["key_blue"] = Button(_("New"))
959
960                 # Define Actions
961                 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
962                         {
963                                 "cancel": self.cancel,
964                                 "save": self.save,
965                                 "yellow": self.remove,
966                                 "blue": self.new
967                         }
968                 )
969
970                 # Trigger change
971                 self.changed()
972
973                 self.onLayoutFinish.append(self.setCustomTitle)
974
975         def setCustomTitle(self):
976                 self.setTitle(_("Edit AutoTimer Services"))
977
978         def saveCurrent(self):
979                 del self.services[self.idx][:]
980
981                 # Warning, accessing a ConfigListEntry directly might be considered evil!
982
983                 myl = self["config"].getList()[:]
984                 myl.pop(0) # Enabled
985                 myl.pop(0) # Type
986                 for item in myl:
987                         self.services[self.idx].append(item[1].value)
988
989         def refresh(self, *args, **kwargs):
990                 self.saveCurrent()
991
992                 self.reloadList()
993                 self["config"].setList(self.list)
994
995         def reloadList(self):
996                 self.list = [
997                         getConfigListEntry(_("Enable Service Restriction"), self.enabled),
998                         getConfigListEntry(_("Editing"), self.typeSelection)
999                 ]
1000
1001                 if self.typeSelection.value == "channels":
1002                         self.idx = 0
1003                 else: # self.typeSelection.value == "bouquets":
1004                         self.idx = 1
1005
1006                 self.list.extend([
1007                         getConfigListEntry(_("Record on"), NoSave(ConfigSelection(choices = [(str(x), ServiceReference(str(x)).getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', ''))])))
1008                                 for x in self.services[self.idx]
1009                 ])
1010
1011         def changed(self):
1012                 for x in self.onChangedEntry:
1013                         try:
1014                                 x()
1015                         except Exception:
1016                                 pass
1017
1018         def getCurrentEntry(self):
1019                 return self["config"].getCurrent()[0]
1020
1021         def getCurrentValue(self):
1022                 return str(self["config"].getCurrent()[1].getText())
1023
1024         def createSummary(self):
1025                 return SetupSummary
1026
1027         def remove(self):
1028                 if self["config"].getCurrentIndex() != 0:
1029                         list = self["config"].getList()
1030                         list.remove(self["config"].getCurrent())
1031                         self["config"].setList(list)
1032
1033         def new(self):
1034                 if self.typeSelection.value == "channels":
1035                         self.session.openWithCallback(
1036                                 self.finishedServiceSelection,
1037                                 SimpleChannelSelection,
1038                                 _("Select channel to record on")
1039                         )
1040                 else: # self.typeSelection.value == "bouquets":
1041                         self.session.openWithCallback(
1042                                 self.finishedServiceSelection,
1043                                 SimpleBouquetSelection,
1044                                 _("Select bouquet to record on")
1045                         )
1046
1047         def finishedServiceSelection(self, *args):
1048                 if args:
1049                         list = self["config"].getList()
1050                         sname = args[0].toString()
1051
1052                         if self.typeSelection.value == "channels" and not (args[0].flags & eServiceReference.isGroup):
1053                                 # strip all after last : when adding a (non alternative) channel
1054                                 pos = sname.rfind(':')
1055                                 if pos != -1:
1056                                         sname = sname[:pos+1]
1057
1058                         list.append(getConfigListEntry(_("Record on"), NoSave(ConfigSelection(choices = [(sname, ServiceReference(args[0]).getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', ''))]))))
1059                         self["config"].setList(list)
1060
1061         def cancel(self):
1062                 if self["config"].isChanged():
1063                         self.session.openWithCallback(
1064                                 self.cancelConfirm,
1065                                 MessageBox,
1066                                 _("Really close without saving settings?")
1067                         )
1068                 else:
1069                         self.close(None)
1070
1071         def cancelConfirm(self, ret):
1072                 if ret:
1073                         self.close(None)
1074
1075         def save(self):
1076                 self.refresh()
1077
1078                 self.close((
1079                         self.enabled.value,
1080                         self.services
1081                 ))
1082
1083 def addAutotimerFromSearchString(session, match):
1084         from AutoTimerComponent import AutoTimerComponent
1085         from AutoTimerImporter import AutoTimerImporter
1086         from plugin import autotimer
1087
1088         # Create instance if needed
1089         if autotimer is None:
1090                 from AutoTimer import AutoTimer
1091                 autotimer = AutoTimer()
1092                 autotimer.readXml()
1093
1094         session.openWithCallback(
1095                 importerCallback,
1096                 AutoTimerImporter,
1097                 AutoTimerComponent(
1098                         autotimer.getUniqueId(),
1099                         match,
1100                         '',             # Match
1101                         True    # Enabled
1102                 ),
1103                 match,          # Proposed Match
1104                 None,           # Proposed Begin
1105                 None,           # Proposed End
1106                 None,           # Proposed Disabled
1107                 None,           # Proposed ServiceReference
1108                 None,           # Proposed afterEvent
1109                 None,           # Proposed justplay
1110                 None,           # Proposed dirname, can we get anything useful here?
1111                 []                      # Proposed tags
1112         )
1113
1114 def addAutotimerFromEvent(session, evt = None, service = None):
1115         from AutoTimerComponent import AutoTimerComponent
1116         from AutoTimerImporter import AutoTimerImporter
1117         from plugin import autotimer
1118
1119         # Create instance if needed
1120         if autotimer is None:
1121                 from AutoTimer import AutoTimer
1122                 autotimer = AutoTimer()
1123                 autotimer.readXml()
1124
1125         match = evt and evt.getEventName() or ""
1126         name = match or "New AutoTimer"
1127         sref = None
1128         if service is not None:
1129                 service = str(service)
1130                 myref = eServiceReference(service)
1131                 if not (myref.flags & eServiceReference.isGroup):
1132                         # strip all after last :
1133                         pos = service.rfind(':')
1134                         if pos != -1:
1135                                 service = service[:pos+1]
1136
1137                 sref = ServiceReference(myref)
1138         if evt:
1139                 # timespan defaults to +- 1h
1140                 begin = evt.getBeginTime()-3600
1141                 end = begin + evt.getDuration()+7200
1142         else:
1143                 begin = end = 0
1144
1145         # XXX: we might want to make sure that we actually collected any data because the importer does not do so :-)
1146
1147         session.openWithCallback(
1148                 importerCallback,
1149                 AutoTimerImporter,
1150                 AutoTimerComponent(
1151                         autotimer.getUniqueId(),
1152                         name,
1153                         '',             # Match
1154                         True    # Enabled
1155                 ),
1156                 match,          # Proposed Match
1157                 begin,          # Proposed Begin
1158                 end,            # Proposed End
1159                 None,           # Proposed Disabled
1160                 sref,           # Proposed ServiceReference
1161                 None,           # Proposed afterEvent
1162                 None,           # Proposed justplay
1163                 None,           # Proposed dirname, can we get anything useful here?
1164                 []                      # Proposed tags
1165         )
1166
1167 def addAutotimerFromService(session, service = None):
1168         from AutoTimerComponent import AutoTimerComponent
1169         from AutoTimerImporter import AutoTimerImporter
1170         from plugin import autotimer
1171
1172         # Create instance if needed
1173         if autotimer is None:
1174                 from AutoTimer import AutoTimer
1175                 autotimer = AutoTimer()
1176                 autotimer.readXml()
1177
1178         serviceHandler = eServiceCenter.getInstance()
1179         info = serviceHandler.info(service)
1180
1181         match = info and info.getName(service) or ""
1182         name = match or "New AutoTimer"
1183         sref = info and info.getInfoString(service, iServiceInformation.sServiceref)
1184         if sref:
1185                 # strip all after last :
1186                 pos = sref.rfind(':')
1187                 if pos != -1:
1188                         sref = sref[:pos+1]
1189
1190                 sref = ServiceReference(sref)
1191         if info:
1192                 begin = info.getInfo(service, iServiceInformation.sTimeCreate)
1193                 end = begin + info.getLength(service)
1194         else:
1195                 begin = end = 0
1196
1197         from os.path import dirname
1198         path = dirname(service.getPath())
1199         if not path == '/':
1200                 path += '/'
1201
1202         tags = info.getInfoString(service, iServiceInformation.sTags)
1203         tags = tags and tags.split(' ') or []
1204
1205         # XXX: we might want to make sure that we actually collected any data because the importer does not do so :-)
1206
1207         session.openWithCallback(
1208                 importerCallback,
1209                 AutoTimerImporter,
1210                 AutoTimerComponent(
1211                         autotimer.getUniqueId(),
1212                         name,
1213                         '',             # Match
1214                         True    # Enabled
1215                 ),
1216                 match,          # Proposed Match
1217                 begin,          # Proposed Begin
1218                 end,            # Proposed End
1219                 None,           # Proposed Disabled
1220                 sref,           # Proposed ServiceReference
1221                 None,           # Proposed afterEvent
1222                 None,           # Proposed justplay
1223                 path,           # Proposed dirname
1224                 tags            # Proposed tags
1225         )
1226
1227 def importerCallback(ret):
1228         if ret:
1229                 ret, session = ret
1230
1231                 session.openWithCallback(
1232                         editorCallback,
1233                         AutoTimerEditor,
1234                         ret
1235                 )
1236         else:
1237                 # Remove instance if not running in background
1238                 if not config.plugins.autotimer.autopoll.value:
1239                         from plugin import autotimer
1240                         autotimer = None
1241
1242 def editorCallback(ret):
1243         if ret:
1244                 from plugin import autotimer
1245
1246                 # Create instance if needed (should have been created by addAutotimerFrom* above though)
1247                 if autotimer is None:
1248                         from AutoTimer import AutoTimer
1249                         autotimer = AutoTimer()
1250                         autotimer.readXml()
1251
1252                 autotimer.add(ret)
1253
1254                 # Save modified xml
1255                 autotimer.writeXml()
1256
1257         # Remove instance if not running in background
1258         if not config.plugins.autotimer.autopoll.value:
1259                 autotimer = None
1260