1 #####################################################
2 # Permanent Timeshift Plugin for Enigma2 Dreamboxes
3 # Coded by Homey (c) 2011
6 # Support: www.dreambox-plugins.de
7 #####################################################
8 from Components.ActionMap import ActionMap
9 from Components.ConfigList import ConfigList, ConfigListScreen
10 from Components.config import config, configfile, getConfigListEntry, ConfigSubsection, ConfigYesNo, ConfigInteger, ConfigSelection, NoSave
11 from Components.Label import Label
12 from Components.Language import language
13 from Components.Pixmap import Pixmap
14 from Components.ServiceEventTracker import ServiceEventTracker
15 from Components.Sources.StaticText import StaticText
16 from Components.SystemInfo import SystemInfo
17 from Components.Task import Task, Job, job_manager as JobManager
18 from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath
19 from Screens.ChoiceBox import ChoiceBox
20 from Screens.ChannelSelection import ChannelSelection
21 from Screens.InfoBar import InfoBar as InfoBarOrg
22 from Screens.InfoBarGenerics import NumberZap, InfoBarSeek, InfoBarNumberZap, InfoBarTimeshiftState, InfoBarInstantRecord, InfoBarChannelSelection
23 from Screens.MessageBox import MessageBox
24 from Screens.Screen import Screen
25 from Screens.Setup import SetupSummary
26 from Screens.Standby import Standby, TryQuitMainloop
27 from Screens.PVRState import TimeshiftState
28 from ServiceReference import ServiceReference
29 from Tools import Directories, ASCIItranslit, Notifications
30 from Tools.Directories import fileExists, copyfile, resolveFilename, SCOPE_LANGUAGE, SCOPE_PLUGINS
31 from Plugins.Plugin import PluginDescriptor
32 from RecordTimer import RecordTimer, RecordTimerEntry, parseEvent
34 from random import randint
35 from enigma import eTimer, eServiceCenter, eBackgroundFileEraser, iPlayableService, iRecordableService, iServiceInformation
36 from os import environ, stat as os_stat, listdir as os_listdir, link as os_link, path as os_path, system as os_system, statvfs
37 from time import localtime, time, gmtime, strftime
38 from timer import TimerEntry
41 import Screens.InfoBar
42 import Screens.Standby
44 ##############################
45 ### Multilanguage Init ###
46 ##############################
49 lang = language.getLanguage()
50 environ["LANGUAGE"] = lang[:2]
51 gettext.bindtextdomain("enigma2", resolveFilename(SCOPE_LANGUAGE))
52 gettext.textdomain("enigma2")
53 gettext.bindtextdomain("PTSPlugin", "%s%s" % (resolveFilename(SCOPE_PLUGINS), "Extensions/PermanentTimeshift/locale/"))
56 t = gettext.dgettext("PTSPlugin", txt)
58 t = gettext.gettext(txt)
62 language.addCallback(localeInit)
64 ##############################
65 ##### CONFIG SETTINGS #####
66 ##############################
68 config.plugins.pts = ConfigSubsection()
69 config.plugins.pts.enabled = ConfigYesNo(default = True)
70 config.plugins.pts.maxevents = ConfigInteger(default=5, limits=(1, 99))
71 config.plugins.pts.maxlength = ConfigInteger(default=180, limits=(5, 999))
72 config.plugins.pts.startdelay = ConfigInteger(default=5, limits=(5, 999))
73 config.plugins.pts.showinfobar = ConfigYesNo(default = False)
74 config.plugins.pts.stopwhilerecording = ConfigYesNo(default = False)
75 config.plugins.pts.favoriteSaveAction = ConfigSelection([("askuser", _("Ask user")),("savetimeshift", _("Save and stop")),("savetimeshiftandrecord", _("Save and record")),("noSave", _("Don't save"))], "askuser")
76 config.plugins.pts.permanentrecording = ConfigYesNo(default = False)
77 config.plugins.pts.isRecording = NoSave(ConfigYesNo(default = False))
79 ###################################
80 ### PTS TimeshiftState Screen ###
81 ###################################
83 class PTSTimeshiftState(Screen):
85 <screen position="center,40" zPosition="2" size="420,70" backgroundColor="transpBlack" flags="wfNoBorder">
86 <widget name="state" position="10,3" size="80,27" font="Regular;20" halign="center" backgroundColor="transpBlack" />
87 <widget source="session.CurrentService" render="Label" position="95,5" size="120,27" font="Regular;20" halign="left" foregroundColor="white" backgroundColor="transpBlack">
88 <convert type="ServicePosition">Position</convert>
90 <widget source="session.CurrentService" render="Label" position="340,5" size="65,27" font="Regular;20" halign="left" foregroundColor="white" backgroundColor="transpBlack">
91 <convert type="ServicePosition">Length</convert>
93 <widget name="PTSSeekPointer" position="8,30" zPosition="3" size="19,50" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/PermanentTimeshift/images/timeline-now.png" alphatest="on" />
94 <ePixmap position="10,33" size="840,15" zPosition="1" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/PermanentTimeshift/images/slider_back.png" alphatest="on"/>
95 <widget source="session.CurrentService" render="Progress" position="10,33" size="390,15" zPosition="2" pixmap="/usr/lib/enigma2/python/Plugins/Extensions/PermanentTimeshift/images/slider.png" transparent="1">
96 <convert type="ServicePosition">Position</convert>
98 <widget name="eventname" position="10,49" zPosition="4" size="420,20" font="Regular;18" halign="center" backgroundColor="transpBlack" />
101 def __init__(self, session):
102 Screen.__init__(self, session)
103 self["state"] = Label(text="")
104 self["PTSSeekPointer"] = Pixmap()
105 self["eventname"] = Label(text="")
107 ###################################
108 ### PTS CopyTimeshift Task ###
109 ###################################
111 class CopyTimeshiftJob(Job):
112 def __init__(self, toolbox, cmdline, srcfile, destfile, eventname):
113 Job.__init__(self, _("Saving Timeshift files"))
114 self.toolbox = toolbox
115 AddCopyTimeshiftTask(self, cmdline, srcfile, destfile, eventname)
117 class AddCopyTimeshiftTask(Task):
118 def __init__(self, job, cmdline, srcfile, destfile, eventname):
119 Task.__init__(self, job, eventname)
120 self.toolbox = job.toolbox
121 self.setCmdline(cmdline)
122 self.srcfile = config.usage.timeshift_path.value + "/" + srcfile + ".copy"
123 self.destfile = destfile + ".ts"
125 self.ProgressTimer = eTimer()
126 self.ProgressTimer.callback.append(self.ProgressUpdate)
128 def ProgressUpdate(self):
129 if self.srcsize <= 0 or not fileExists(self.destfile, 'r'):
132 self.setProgress(int((os_path.getsize(self.destfile)/float(self.srcsize))*100))
133 self.ProgressTimer.start(15000, True)
136 if fileExists(self.srcfile, 'r'):
137 self.srcsize = os_path.getsize(self.srcfile)
138 self.ProgressTimer.start(15000, True)
140 self.toolbox.ptsFrontpanelActions("start")
141 config.plugins.pts.isRecording.value = True
144 self.setProgress(100)
145 self.ProgressTimer.stop()
146 self.toolbox.ptsCopyFilefinished(self.srcfile, self.destfile)
148 ###################################
149 ### PTS MergeTimeshift Task ###
150 ###################################
152 class MergeTimeshiftJob(Job):
153 def __init__(self, toolbox, cmdline, srcfile, destfile, eventname):
154 Job.__init__(self, _("Merging Timeshift files"))
155 self.toolbox = toolbox
156 AddMergeTimeshiftTask(self, cmdline, srcfile, destfile, eventname)
158 class AddMergeTimeshiftTask(Task):
159 def __init__(self, job, cmdline, srcfile, destfile, eventname):
160 Task.__init__(self, job, eventname)
161 self.toolbox = job.toolbox
162 self.setCmdline(cmdline)
163 self.srcfile = config.usage.default_path.value + "/" + srcfile
164 self.destfile = config.usage.default_path.value + "/" + destfile
166 self.ProgressTimer = eTimer()
167 self.ProgressTimer.callback.append(self.ProgressUpdate)
169 def ProgressUpdate(self):
170 if self.srcsize <= 0 or not fileExists(self.destfile, 'r'):
173 self.setProgress(int((os_path.getsize(self.destfile)/float(self.srcsize))*100))
174 self.ProgressTimer.start(7500, True)
177 if fileExists(self.srcfile, 'r') and fileExists(self.destfile, 'r'):
178 fsize1 = os_path.getsize(self.srcfile)
179 fsize2 = os_path.getsize(self.destfile)
180 self.srcsize = fsize1 + fsize2
181 self.ProgressTimer.start(7500, True)
183 self.toolbox.ptsFrontpanelActions("start")
184 config.plugins.pts.isRecording.value = True
187 self.setProgress(100)
188 self.ProgressTimer.stop()
189 self.toolbox.ptsMergeFilefinished(self.srcfile, self.destfile)
191 ##################################
192 ### Create APSC Files Task ###
193 ##################################
195 class CreateAPSCFilesJob(Job):
196 def __init__(self, toolbox, cmdline, eventname):
197 Job.__init__(self, _("Creating AP and SC Files"))
198 self.toolbox = toolbox
199 CreateAPSCFilesTask(self, cmdline, eventname)
201 class CreateAPSCFilesTask(Task):
202 def __init__(self, job, cmdline, eventname):
203 Task.__init__(self, job, eventname)
204 self.toolbox = job.toolbox
205 self.setCmdline(cmdline)
208 self.toolbox.ptsFrontpanelActions("start")
209 config.plugins.pts.isRecording.value = True
212 self.setProgress(100)
213 self.toolbox.ptsSaveTimeshiftFinished()
215 ###########################
216 ##### Class InfoBar #####
217 ###########################
218 class InfoBar(InfoBarOrg):
219 def __init__(self, session):
220 InfoBarOrg.__init__(self, session)
221 InfoBarOrg.instance = self
\r
223 self.__event_tracker = ServiceEventTracker(screen = self, eventmap =
225 iPlayableService.evStart: self.__evStart,
226 iPlayableService.evEnd: self.__evEnd,
227 iPlayableService.evSOF: self.__evSOF,
228 iPlayableService.evUpdatedInfo: self.__evInfoChanged,
229 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
230 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
231 iPlayableService.evUser+1: self.ptsTimeshiftFileChanged
234 self["PTSactions"] = ActionMap(["PTS_GlobalActions"],{"instantRecord": self.instantRecord, "restartTimeshift": self.restartTimeshift},-2)
235 self["PTSSeekPointerActions"] = ActionMap(["PTS_SeekPointerActions"],{"SeekPointerOK": self.ptsSeekPointerOK, "SeekPointerLeft": self.ptsSeekPointerLeft, "SeekPointerRight": self.ptsSeekPointerRight},-2)
236 self["PTSSeekPointerActions"].setEnabled(False)
238 self.pts_begintime = 0
239 self.pts_pathchecked = False
240 self.pts_pvrStateDialog = "TimeshiftState"
241 self.pts_seektoprevfile = False
242 self.pts_switchtolive = False
243 self.pts_currplaying = 1
244 self.pts_lastseekspeed = 0
245 self.pts_service_changed = False
246 self.pts_record_running = self.session.nav.RecordTimer.isRecording()
247 self.save_current_timeshift = False
248 self.save_timeshift_postaction = None
249 self.save_timeshift_filename = None
250 self.service_changed = 0
252 # Init Global Variables
253 self.session.ptsmainloopvalue = 0
254 config.plugins.pts.isRecording.value = False
256 # Init eBackgroundFileEraser
257 self.BgFileEraser = eBackgroundFileEraser.getInstance()
259 # Init PTS Delay-Timer
260 self.pts_delay_timer = eTimer()
261 self.pts_delay_timer.callback.append(self.ActivatePermanentTimeshift)
263 # Init PTS LengthCheck-Timer
264 self.pts_LengthCheck_timer = eTimer()
265 self.pts_LengthCheck_timer.callback.append(self.ptsLengthCheck)
267 # Init PTS MergeRecords-Timer
268 self.pts_mergeRecords_timer = eTimer()
269 self.pts_mergeRecords_timer.callback.append(self.ptsMergeRecords)
271 # Init PTS Merge Cleanup-Timer
272 self.pts_mergeCleanUp_timer = eTimer()
273 self.pts_mergeCleanUp_timer.callback.append(self.ptsMergePostCleanUp)
275 # Init PTS QuitMainloop-Timer
276 self.pts_QuitMainloop_timer = eTimer()
277 self.pts_QuitMainloop_timer.callback.append(self.ptsTryQuitMainloop)
279 # Init PTS CleanUp-Timer
280 self.pts_cleanUp_timer = eTimer()
281 self.pts_cleanUp_timer.callback.append(self.ptsCleanTimeshiftFolder)
282 self.pts_cleanUp_timer.start(30000, True)
284 # Init PTS SeekBack-Timer
285 self.pts_SeekBack_timer = eTimer()
286 self.pts_SeekBack_timer.callback.append(self.ptsSeekBackTimer)
288 # Init Block-Zap Timer
289 self.pts_blockZap_timer = eTimer()
291 # Record Event Tracker
292 self.session.nav.RecordTimer.on_state_change.append(self.ptsTimerEntryStateChange)
294 # Keep Current Event Info for recordings
295 self.pts_eventcount = 1
296 self.pts_curevent_begin = int(time())
297 self.pts_curevent_end = 0
298 self.pts_curevent_name = _("Timeshift")
299 self.pts_curevent_description = ""
300 self.pts_curevent_servicerefname = ""
301 self.pts_curevent_station = ""
302 self.pts_curevent_eventid = None
305 self.pts_seekpointer_MinX = 8
306 self.pts_seekpointer_MaxX = 396 # make sure you can divide this through 2
309 self.service_changed = 1
310 self.pts_delay_timer.stop()
311 self.pts_service_changed = True
314 self.service_changed = 0
317 if not config.plugins.pts.enabled.value or not self.timeshift_enabled:
320 if self.pts_currplaying == 1:
321 preptsfile = config.plugins.pts.maxevents.value
323 preptsfile = self.pts_currplaying-1
325 # Switch to previous TS file by seeking forward to next one
326 if fileExists("%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value, preptsfile), 'r') and preptsfile != self.pts_eventcount:
327 self.pts_seektoprevfile = True
328 self.ptsSetNextPlaybackFile("pts_livebuffer.%s" % (preptsfile))
330 self.setSeekState(self.SEEK_STATE_PAUSE)
331 if self.seekstate != self.SEEK_STATE_PLAY:
332 self.setSeekState(self.SEEK_STATE_PLAY)
336 def __evInfoChanged(self):
337 if self.service_changed:
338 self.service_changed = 0
340 # We zapped away before saving the file, save it now!
341 if self.save_current_timeshift:
342 self.SaveTimeshift("pts_livebuffer.%s" % (self.pts_eventcount))
344 # Delete Timeshift Records on zap
345 self.pts_eventcount = 0
346 self.pts_cleanUp_timer.start(3000, True)
348 def __evEventInfoChanged(self):
349 if not config.plugins.pts.enabled.value:
352 # Get Current Event Info
353 service = self.session.nav.getCurrentService()
354 old_begin_time = self.pts_begintime
355 info = service and service.info()
356 ptr = info and info.getEvent(0)
357 self.pts_begintime = ptr and ptr.getBeginTime() or 0
359 # Save current TimeShift permanently now ...
360 if info.getInfo(iServiceInformation.sVideoPID) != -1:
362 # Take care of Record Margin Time ...
363 if self.save_current_timeshift and self.timeshift_enabled:
364 if config.recording.margin_after.value > 0 and len(self.recording) == 0:
365 self.SaveTimeshift(mergelater=True)
366 recording = RecordTimerEntry(ServiceReference(self.session.nav.getCurrentlyPlayingServiceReference()), time(), time()+(config.recording.margin_after.value*60), self.pts_curevent_name, self.pts_curevent_description, self.pts_curevent_eventid, dirname = config.usage.default_path.value)
367 recording.dontSave = True
368 self.session.nav.RecordTimer.record(recording)
369 self.recording.append(recording)
373 # Restarting active timers after zap ...
374 if self.pts_delay_timer.isActive() and not self.timeshift_enabled:
375 self.pts_delay_timer.start(config.plugins.pts.startdelay.value*1000, True)
376 if self.pts_cleanUp_timer.isActive() and not self.timeshift_enabled:
377 self.pts_cleanUp_timer.start(3000, True)
379 # (Re)Start TimeShift
380 if not self.pts_delay_timer.isActive():
381 if not self.timeshift_enabled or old_begin_time != self.pts_begintime or old_begin_time == 0:
382 if self.pts_service_changed:
383 self.pts_service_changed = False
384 self.pts_delay_timer.start(config.plugins.pts.startdelay.value*1000, True)
386 self.pts_delay_timer.start(1000, True)
388 def __seekableStatusChanged(self):
390 if not self.isSeekable() and self.timeshift_enabled:
392 self["TimeshiftActivateActions"].setEnabled(enabled)
395 if config.plugins.pts.enabled.value and config.plugins.pts.showinfobar.value and self.timeshift_enabled and self.isSeekable():
398 self["PTSSeekPointerActions"].setEnabled(enabled)
400 # Reset Seek Pointer And Eventname in InfoBar
401 if config.plugins.pts.enabled.value and config.plugins.pts.showinfobar.value and self.timeshift_enabled and not self.isSeekable():
402 if self.pts_pvrStateDialog == "PTSTimeshiftState":
403 self.pvrStateDialog["eventname"].setText("")
404 self.ptsSeekPointerReset()
406 # setNextPlaybackFile() when switching back to live tv
407 if config.plugins.pts.enabled.value and self.timeshift_enabled and not self.isSeekable():
408 if self.pts_starttime <= (time()-5):
409 self.pts_blockZap_timer.start(3000, True)
410 self.pts_currplaying = self.pts_eventcount
411 self.ptsSetNextPlaybackFile("pts_livebuffer.%s" % (self.pts_eventcount))
413 def ActivatePermanentTimeshift(self):
414 if self.ptsCheckTimeshiftPath() is False or self.session.screen["Standby"].boolean is True or self.ptsLiveTVStatus() is False or (config.plugins.pts.stopwhilerecording.value and self.pts_record_running):
417 # Replace PVR Timeshift State Icon
418 if config.plugins.pts.showinfobar.value:
419 if self.pts_pvrStateDialog != "PTSTimeshiftState":
420 self.pts_pvrStateDialog = "PTSTimeshiftState"
421 self.pvrStateDialog = self.session.instantiateDialog(PTSTimeshiftState)
422 elif not config.plugins.pts.showinfobar.value and self.pts_pvrStateDialog != "TimeshiftState":
423 self.pts_pvrStateDialog = "TimeshiftState"
424 self.pvrStateDialog = self.session.instantiateDialog(TimeshiftState)
426 # Set next-file on event change only when watching latest timeshift ...
427 if self.isSeekable() and self.pts_eventcount == self.pts_currplaying:
428 pts_setnextfile = True
430 pts_setnextfile = False
432 # Update internal Event Counter
433 if self.pts_eventcount >= config.plugins.pts.maxevents.value:
434 self.pts_eventcount = 0
436 self.pts_eventcount += 1
438 # Do not switch back to LiveTV while timeshifting
439 if self.isSeekable():
444 # setNextPlaybackFile() on event change while timeshifting
445 if self.pts_eventcount > 1 and self.isSeekable() and pts_setnextfile:
446 self.ptsSetNextPlaybackFile("pts_livebuffer.%s" % (self.pts_eventcount))
448 # (Re)start Timeshift now
449 self.stopTimeshiftConfirmed(True, switchToLive)
450 ts = self.getTimeshift()
451 if ts and not ts.startTimeshift():
452 self.pts_starttime = time()
453 self.pts_LengthCheck_timer.start(120000)
454 self.timeshift_enabled = 1
455 self.save_timeshift_postaction = None
456 self.ptsGetEventInfo()
457 self.ptsCreateHardlink()
458 self.__seekableStatusChanged()
460 self.pts_eventcount = 0
462 def startTimeshift(self):
463 if config.plugins.pts.enabled.value:
464 self.pts_delay_timer.stop()
465 self.ActivatePermanentTimeshift()
466 self.activateTimeshiftEndAndPause()
468 InfoBarOrg.startTimeshift(self)
470 def stopTimeshift(self):
471 if not self.timeshift_enabled:
474 # Jump Back to Live TV
475 if config.plugins.pts.enabled.value and self.timeshift_enabled:
476 if self.isSeekable():
477 self.pts_switchtolive = True
478 self.ptsSetNextPlaybackFile("")
479 self.setSeekState(self.SEEK_STATE_PAUSE)
480 if self.seekstate != self.SEEK_STATE_PLAY:
481 self.setSeekState(self.SEEK_STATE_PLAY)
482 self.doSeek(-1) # seek 1 gop before end
483 self.seekFwd() # seekFwd to switch to live TV
486 InfoBarOrg.stopTimeshift(self)
488 def stopTimeshiftConfirmed(self, confirmed, switchToLive=True):
489 was_enabled = self.timeshift_enabled
493 ts = self.getTimeshift()
498 ts.stopTimeshift(switchToLive)
502 self.timeshift_enabled = 0
503 self.__seekableStatusChanged()
505 if was_enabled and not self.timeshift_enabled:
506 self.timeshift_enabled = 0
507 self.pts_LengthCheck_timer.stop()
509 def restartTimeshift(self):
510 self.ActivatePermanentTimeshift()
511 Notifications.AddNotification(MessageBox, _("PTS-Plugin: Restarting Timeshift!"), MessageBox.TYPE_INFO, timeout=5)
513 def saveTimeshiftPopup(self):
514 self.session.openWithCallback(self.saveTimeshiftPopupCallback, ChoiceBox, \
515 title=_("The Timeshift record was not saved yet!\nWhat do you want to do now with the timeshift file?"), \
516 list=((_("Save Timeshift as Movie and stop recording"), "savetimeshift"), \
517 (_("Save Timeshift as Movie and continue recording"), "savetimeshiftandrecord"), \
518 (_("Don't save Timeshift as Movie"), "noSave")))
520 def saveTimeshiftPopupCallback(self, answer):
524 if answer[1] == "savetimeshift":
525 self.saveTimeshiftActions("savetimeshift", self.save_timeshift_postaction)
526 elif answer[1] == "savetimeshiftandrecord":
527 self.saveTimeshiftActions("savetimeshiftandrecord", self.save_timeshift_postaction)
528 elif answer[1] == "noSave":
529 self.save_current_timeshift = False
530 self.saveTimeshiftActions("noSave", self.save_timeshift_postaction)
532 def saveTimeshiftEventPopup(self):
535 entrylist.append((_("Current Event:")+" %s" % (self.pts_curevent_name), "savetimeshift"))
537 filelist = os_listdir(config.usage.timeshift_path.value)
539 if filelist is not None:
542 for filename in filelist:
543 if (filename.startswith("pts_livebuffer.") is True) and (filename.endswith(".del") is False and filename.endswith(".meta") is False and filename.endswith(".eit") is False and filename.endswith(".copy") is False):
544 statinfo = os_stat("%s/%s" % (config.usage.timeshift_path.value,filename))
545 if statinfo.st_mtime < (time()-5.0):
546 # Get Event Info from meta file
547 readmetafile = open("%s/%s.meta" % (config.usage.timeshift_path.value,filename), "r")
548 servicerefname = readmetafile.readline()[0:-1]
549 eventname = readmetafile.readline()[0:-1]
550 description = readmetafile.readline()[0:-1]
551 begintime = readmetafile.readline()[0:-1]
556 entrylist.append((_("Record") + " #%s (%s): %s" % (filecount,strftime("%H:%M",localtime(int(begintime))),eventname), "%s" % filename))
558 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("Which event do you want to save permanently?"), list=entrylist)
560 def saveTimeshiftActions(self, action=None, postaction=None):
561 self.save_timeshift_postaction = postaction
564 if config.plugins.pts.favoriteSaveAction.value == "askuser":
565 self.saveTimeshiftPopup()
567 elif config.plugins.pts.favoriteSaveAction.value == "savetimeshift":
569 elif config.plugins.pts.favoriteSaveAction.value == "savetimeshiftandrecord":
570 if self.pts_curevent_end > time():
571 self.SaveTimeshift(mergelater=True)
572 self.ptsRecordCurrentEvent()
575 elif config.plugins.pts.favoriteSaveAction.value == "noSave":
576 config.plugins.pts.isRecording.value = False
577 self.save_current_timeshift = False
578 elif action == "savetimeshift":
580 elif action == "savetimeshiftandrecord":
581 if self.pts_curevent_end > time():
582 self.SaveTimeshift(mergelater=True)
583 self.ptsRecordCurrentEvent()
586 elif action == "noSave":
587 config.plugins.pts.isRecording.value = False
588 self.save_current_timeshift = False
590 # Post PTS Actions like ZAP or whatever the user requested
591 if self.save_timeshift_postaction == "zapUp":
592 InfoBarChannelSelection.zapUp(self)
593 elif self.save_timeshift_postaction == "zapDown":
594 InfoBarChannelSelection.zapDown(self)
595 elif self.save_timeshift_postaction == "historyBack":
596 InfoBarChannelSelection.historyBack(self)
597 elif self.save_timeshift_postaction == "historyNext":
598 InfoBarChannelSelection.historyNext(self)
599 elif self.save_timeshift_postaction == "switchChannelUp":
600 InfoBarChannelSelection.switchChannelUp(self)
601 elif self.save_timeshift_postaction == "switchChannelDown":
602 InfoBarChannelSelection.switchChannelDown(self)
603 elif self.save_timeshift_postaction == "openServiceList":
604 InfoBarChannelSelection.openServiceList(self)
605 elif self.save_timeshift_postaction == "showRadioChannelList":
606 InfoBarChannelSelection.showRadioChannelList(self, zap=True)
607 elif self.save_timeshift_postaction == "standby":
608 Notifications.AddNotification(Screens_Standby_Standby)
610 def SaveTimeshift(self, timeshiftfile=None, mergelater=False):
611 self.save_current_timeshift = False
614 if timeshiftfile is not None:
615 savefilename = timeshiftfile
617 if savefilename is None:
618 for filename in os_listdir(config.usage.timeshift_path.value):
619 if filename.startswith("timeshift.") and not filename.endswith(".del") and not filename.endswith(".copy"):
621 statinfo = os_stat("%s/%s" % (config.usage.timeshift_path.value,filename))
622 if statinfo.st_mtime > (time()-5.0):
623 savefilename=filename
624 except Exception, errormsg:
625 Notifications.AddNotification(MessageBox, _("PTS Plugin Error: %s" % (errormsg)), MessageBox.TYPE_ERROR)
627 if savefilename is None:
628 Notifications.AddNotification(MessageBox, _("No Timeshift found to save as recording!"), MessageBox.TYPE_ERROR)
630 timeshift_saved = True
631 timeshift_saveerror1 = ""
632 timeshift_saveerror2 = ""
635 config.plugins.pts.isRecording.value = True
638 self.pts_mergeRecords_timer.start(120000, True)
639 metamergestring = "pts_merge\n"
642 if timeshiftfile is None:
643 # Save Current Event by creating hardlink to ts file
644 if self.pts_starttime >= (time()-60):
645 self.pts_starttime -= 60
647 ptsfilename = "%s - %s - %s" % (strftime("%Y%m%d %H%M",localtime(self.pts_starttime)),self.pts_curevent_station,self.pts_curevent_name)
649 if config.usage.setup_level.index >= 2:
650 if config.recording.filename_composition.value == "long" and self.pts_curevent_name != pts_curevent_description:
651 ptsfilename = "%s - %s - %s - %s" % (strftime("%Y%m%d %H%M",localtime(self.pts_starttime)),self.pts_curevent_station,self.pts_curevent_name,self.pts_curevent_description)
652 elif config.recording.filename_composition.value == "short":
653 ptsfilename = "%s - %s" % (strftime("%Y%m%d",localtime(self.pts_starttime)),self.pts_curevent_name)
654 except Exception, errormsg:
655 print "PTS-Plugin: Using default filename"
657 if config.recording.ascii_filenames.value:
658 ptsfilename = ASCIItranslit.legacyEncode(ptsfilename)
660 fullname = Directories.getRecordingFilename(ptsfilename,config.usage.default_path.value)
661 os_link("%s/%s" % (config.usage.timeshift_path.value,savefilename), "%s.ts" % (fullname))
662 metafile = open("%s.ts.meta" % (fullname), "w")
663 metafile.write("%s\n%s\n%s\n%i\n%s" % (self.pts_curevent_servicerefname,self.pts_curevent_name.replace("\n", ""),self.pts_curevent_description.replace("\n", ""),int(self.pts_starttime),metamergestring))
665 self.ptsCreateEITFile(fullname)
666 elif timeshiftfile.startswith("pts_livebuffer"):
667 # Save stored timeshift by creating hardlink to ts file
668 readmetafile = open("%s/%s.meta" % (config.usage.timeshift_path.value,timeshiftfile), "r")
669 servicerefname = readmetafile.readline()[0:-1]
670 eventname = readmetafile.readline()[0:-1]
671 description = readmetafile.readline()[0:-1]
672 begintime = readmetafile.readline()[0:-1]
675 ptsfilename = "%s - %s - %s" % (strftime("%Y%m%d %H%M",localtime(int(begintime))),self.pts_curevent_station,eventname)
677 if config.usage.setup_level.index >= 2:
678 if config.recording.filename_composition.value == "long" and eventname != description:
679 ptsfilename = "%s - %s - %s - %s" % (strftime("%Y%m%d %H%M",localtime(int(begintime))),self.pts_curevent_station,eventname,description)
680 elif config.recording.filename_composition.value == "short":
681 ptsfilename = "%s - %s" % (strftime("%Y%m%d",localtime(int(begintime))),eventname)
682 except Exception, errormsg:
683 print "PTS-Plugin: Using default filename"
685 if config.recording.ascii_filenames.value:
686 ptsfilename = ASCIItranslit.legacyEncode(ptsfilename)
688 fullname=Directories.getRecordingFilename(ptsfilename,config.usage.default_path.value)
689 os_link("%s/%s" % (config.usage.timeshift_path.value,timeshiftfile),"%s.ts" % (fullname))
690 os_link("%s/%s.meta" % (config.usage.timeshift_path.value,timeshiftfile),"%s.ts.meta" % (fullname))
691 if fileExists("%s/%s.eit" % (config.usage.timeshift_path.value,timeshiftfile)):
692 os_link("%s/%s.eit" % (config.usage.timeshift_path.value,timeshiftfile),"%s.eit" % (fullname))
694 # Add merge-tag to metafile
696 metafile = open("%s.ts.meta" % (fullname), "a")
697 metafile.write("%s\n" % (metamergestring))
700 # Create AP and SC Files when not merging
702 self.ptsCreateAPSCFiles(fullname+".ts")
704 except Exception, errormsg:
705 timeshift_saved = False
706 timeshift_saveerror1 = errormsg
708 # Hmpppf! Saving Timeshift via Hardlink-Method failed. Probably other device?
709 # Let's try to copy the file in background now! This might take a while ...
710 if not timeshift_saved:
712 stat = statvfs(config.usage.default_path.value)
713 freespace = stat.f_bfree / 1000 * stat.f_bsize / 1000
714 randomint = randint(1, 999)
716 if timeshiftfile is None:
717 # Get Filesize for Free Space Check
718 filesize = int(os_path.getsize("%s/%s" % (config.usage.timeshift_path.value,savefilename)) / (1024*1024))
720 # Save Current Event by copying it to the other device
721 if filesize <= freespace:
722 os_link("%s/%s" % (config.usage.timeshift_path.value,savefilename), "%s/%s.%s.copy" % (config.usage.timeshift_path.value,savefilename,randomint))
723 copy_file = savefilename
724 metafile = open("%s.ts.meta" % (fullname), "w")
725 metafile.write("%s\n%s\n%s\n%i\n%s" % (self.pts_curevent_servicerefname,self.pts_curevent_name.replace("\n", ""),self.pts_curevent_description.replace("\n", ""),int(self.pts_starttime),metamergestring))
727 self.ptsCreateEITFile(fullname)
728 elif timeshiftfile.startswith("pts_livebuffer"):
729 # Get Filesize for Free Space Check
730 filesize = int(os_path.getsize("%s/%s" % (config.usage.timeshift_path.value, timeshiftfile)) / (1024*1024))
732 # Save stored timeshift by copying it to the other device
733 if filesize <= freespace:
734 os_link("%s/%s" % (config.usage.timeshift_path.value,timeshiftfile), "%s/%s.%s.copy" % (config.usage.timeshift_path.value,timeshiftfile,randomint))
735 copyfile("%s/%s.meta" % (config.usage.timeshift_path.value,timeshiftfile),"%s.ts.meta" % (fullname))
736 if fileExists("%s/%s.eit" % (config.usage.timeshift_path.value,timeshiftfile)):
737 copyfile("%s/%s.eit" % (config.usage.timeshift_path.value,timeshiftfile),"%s.eit" % (fullname))
738 copy_file = timeshiftfile
740 # Add merge-tag to metafile
742 metafile = open("%s.ts.meta" % (fullname), "a")
743 metafile.write("%s\n" % (metamergestring))
746 # Only copy file when enough disk-space available!
747 if filesize <= freespace:
748 timeshift_saved = True
749 copy_file = copy_file+"."+str(randomint)
751 # Get Event Info from meta file
752 if fileExists("%s.ts.meta" % (fullname)):
753 readmetafile = open("%s.ts.meta" % (fullname), "r")
754 servicerefname = readmetafile.readline()[0:-1]
755 eventname = readmetafile.readline()[0:-1]
759 JobManager.AddJob(CopyTimeshiftJob(self, "cp \"%s/%s.copy\" \"%s.ts\"" % (config.usage.timeshift_path.value,copy_file,fullname), copy_file, fullname, eventname))
760 if not Screens.Standby.inTryQuitMainloop and not Screens.Standby.inStandby and not mergelater and self.save_timeshift_postaction != "standby":
761 Notifications.AddNotification(MessageBox, _("Saving timeshift as movie now. This might take a while!"), MessageBox.TYPE_INFO, timeout=5)
763 timeshift_saved = False
764 timeshift_saveerror1 = ""
765 timeshift_saveerror2 = _("Not enough free Diskspace!\n\nFilesize: %sMB\nFree Space: %sMB\nPath: %s" % (filesize,freespace,config.usage.default_path.value))
767 except Exception, errormsg:
768 timeshift_saved = False
769 timeshift_saveerror2 = errormsg
771 if not timeshift_saved:
772 config.plugins.pts.isRecording.value = False
773 self.save_timeshift_postaction = None
774 errormessage = str(timeshift_saveerror1) + "\n" + str(timeshift_saveerror2)
775 Notifications.AddNotification(MessageBox, _("Timeshift save failed!")+"\n\n%s" % errormessage, MessageBox.TYPE_ERROR)
777 def ptsCleanTimeshiftFolder(self):
778 if not config.plugins.pts.enabled.value or self.ptsCheckTimeshiftPath() is False or self.session.screen["Standby"].boolean is True:
782 for filename in os_listdir(config.usage.timeshift_path.value):
783 if (filename.startswith("timeshift.") or filename.startswith("pts_livebuffer.")) and (filename.endswith(".del") is False and filename.endswith(".copy") is False and filename.endswith(".meta") is False and filename.endswith(".eit") is False):
785 statinfo = os_stat("%s/%s" % (config.usage.timeshift_path.value,filename))
786 # if no write for 5 sec = stranded timeshift
787 if statinfo.st_mtime < (time()-5.0):
788 print "PTS-Plugin: Erasing stranded timeshift %s" % filename
789 self.BgFileEraser.erase("%s/%s" % (config.usage.timeshift_path.value,filename))
791 # Delete Meta and EIT File too
792 if filename.startswith("pts_livebuffer.") is True:
793 self.BgFileEraser.erase("%s/%s.meta" % (config.usage.timeshift_path.value,filename))
794 self.BgFileEraser.erase("%s/%s.eit" % (config.usage.timeshift_path.value,filename))
796 print "PTS: IO-Error while cleaning Timeshift Folder ..."
798 def ptsGetEventInfo(self):
801 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
802 serviceHandler = eServiceCenter.getInstance()
803 info = serviceHandler.info(serviceref)
805 self.pts_curevent_servicerefname = serviceref.toString()
806 self.pts_curevent_station = info.getName(serviceref)
808 service = self.session.nav.getCurrentService()
809 info = service and service.info()
810 event = info and info.getEvent(0)
811 except Exception, errormsg:
812 Notifications.AddNotification(MessageBox, _("Getting Event Info failed!")+"\n\n%s" % errormsg, MessageBox.TYPE_ERROR, timeout=10)
814 if event is not None:
815 curEvent = parseEvent(event)
816 self.pts_curevent_begin = int(curEvent[0])
817 self.pts_curevent_end = int(curEvent[1])
818 self.pts_curevent_name = curEvent[2]
819 self.pts_curevent_description = curEvent[3]
820 self.pts_curevent_eventid = curEvent[4]
822 def ptsFrontpanelActions(self, action=None):
823 if self.session.nav.RecordTimer.isRecording() or SystemInfo.get("NumFrontpanelLEDs", 0) == 0:
827 if action == "start":
828 if fileExists("/proc/stb/fp/led_set_pattern"):
829 open("/proc/stb/fp/led_set_pattern", "w").write("0xa7fccf7a")
830 elif fileExists("/proc/stb/fp/led0_pattern"):
831 open("/proc/stb/fp/led0_pattern", "w").write("0x55555555")
832 if fileExists("/proc/stb/fp/led_pattern_speed"):
833 open("/proc/stb/fp/led_pattern_speed", "w").write("20")
834 elif fileExists("/proc/stb/fp/led_set_speed"):
835 open("/proc/stb/fp/led_set_speed", "w").write("20")
836 elif action == "stop":
837 if fileExists("/proc/stb/fp/led_set_pattern"):
838 open("/proc/stb/fp/led_set_pattern", "w").write("0")
839 elif fileExists("/proc/stb/fp/led0_pattern"):
840 open("/proc/stb/fp/led0_pattern", "w").write("0")
841 except Exception, errormsg:
842 print "PTS Plugin: %s" % (errormsg)
844 def ptsCreateHardlink(self):
845 for filename in os_listdir(config.usage.timeshift_path.value):
846 if filename.startswith("timeshift.") and not filename.endswith(".del") and not filename.endswith(".copy"):
848 statinfo = os_stat("%s/%s" % (config.usage.timeshift_path.value,filename))
849 if statinfo.st_mtime > (time()-5.0):
851 self.BgFileEraser.erase("%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,self.pts_eventcount))
852 self.BgFileEraser.erase("%s/pts_livebuffer.%s.meta" % (config.usage.timeshift_path.value,self.pts_eventcount))
853 except Exception, errormsg:
854 print "PTS Plugin: %s" % (errormsg)
857 # Create link to pts_livebuffer file
858 os_link("%s/%s" % (config.usage.timeshift_path.value,filename), "%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,self.pts_eventcount))
861 metafile = open("%s/pts_livebuffer.%s.meta" % (config.usage.timeshift_path.value,self.pts_eventcount), "w")
862 metafile.write("%s\n%s\n%s\n%i\n" % (self.pts_curevent_servicerefname,self.pts_curevent_name.replace("\n", ""),self.pts_curevent_description.replace("\n", ""),int(self.pts_starttime)))
864 except Exception, errormsg:
865 Notifications.AddNotification(MessageBox, _("Creating Hardlink to Timeshift file failed!")+"\n"+_("The Filesystem on your Timeshift-Device does not support hardlinks.\nMake sure it is formated in EXT2 or EXT3!")+"\n\n%s" % errormsg, MessageBox.TYPE_ERROR)
868 self.ptsCreateEITFile("%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,self.pts_eventcount))
870 # Permanent Recording Hack
871 if config.plugins.pts.permanentrecording.value:
873 fullname = Directories.getRecordingFilename("%s - %s - %s" % (strftime("%Y%m%d %H%M",localtime(self.pts_starttime)),self.pts_curevent_station,self.pts_curevent_name),config.usage.default_path.value)
874 os_link("%s/%s" % (config.usage.timeshift_path.value,filename), "%s.ts" % (fullname))
876 metafile = open("%s.ts.meta" % (fullname), "w")
877 metafile.write("%s\n%s\n%s\n%i\nautosaved\n" % (self.pts_curevent_servicerefname,self.pts_curevent_name.replace("\n", ""),self.pts_curevent_description.replace("\n", ""),int(self.pts_starttime)))
879 except Exception, errormsg:
880 print "PTS Plugin: %s" % (errormsg)
881 except Exception, errormsg:
882 errormsg = str(errormsg)
883 if errormsg.find('Input/output error') != -1:
884 errormsg += _("\nAn Input/output error usually indicates a corrupted filesystem! Please check the filesystem of your timeshift-device!")
885 Notifications.AddNotification(MessageBox, _("Creating Hardlink to Timeshift file failed!")+"\n%s" % (errormsg), MessageBox.TYPE_ERROR)
887 def ptsRecordCurrentEvent(self):
888 recording = RecordTimerEntry(ServiceReference(self.session.nav.getCurrentlyPlayingServiceReference()), time(), self.pts_curevent_end, self.pts_curevent_name, self.pts_curevent_description, self.pts_curevent_eventid, dirname = config.usage.default_path.value)
889 recording.dontSave = True
890 self.session.nav.RecordTimer.record(recording)
891 self.recording.append(recording)
893 def ptsMergeRecords(self):
894 if self.session.nav.RecordTimer.isRecording():
895 self.pts_mergeRecords_timer.start(120000, True)
900 ptsmergeeventname = ""
901 ptsgetnextfile = False
902 ptsfilemerged = False
904 filelist = os_listdir(config.usage.default_path.value)
906 if filelist is not None:
909 for filename in filelist:
910 if filename.endswith(".meta"):
911 # Get Event Info from meta file
912 readmetafile = open("%s/%s" % (config.usage.default_path.value,filename), "r")
913 servicerefname = readmetafile.readline()[0:-1]
914 eventname = readmetafile.readline()[0:-1]
915 eventtitle = readmetafile.readline()[0:-1]
916 eventtime = readmetafile.readline()[0:-1]
917 eventtag = readmetafile.readline()[0:-1]
921 ptsgetnextfile = False
922 ptsmergeSRC = filename[0:-5]
924 if ASCIItranslit.legacyEncode(eventname) == ASCIItranslit.legacyEncode(ptsmergeeventname):
926 if fileExists("%s/%s.eit" % (config.usage.default_path.value, ptsmergeSRC[0:-3])):
927 copyfile("%s/%s.eit" % (config.usage.default_path.value, ptsmergeSRC[0:-3]),"%s/%s.eit" % (config.usage.default_path.value, ptsmergeDEST[0:-3]))
929 # Delete AP and SC Files
930 self.BgFileEraser.erase("%s/%s.ap" % (config.usage.default_path.value, ptsmergeDEST))
931 self.BgFileEraser.erase("%s/%s.sc" % (config.usage.default_path.value, ptsmergeDEST))
933 # Add Merge Job to JobManager
934 JobManager.AddJob(MergeTimeshiftJob(self, "cat \"%s/%s\" >> \"%s/%s\"" % (config.usage.default_path.value,ptsmergeSRC,config.usage.default_path.value,ptsmergeDEST), ptsmergeSRC, ptsmergeDEST, eventname))
935 config.plugins.pts.isRecording.value = True
938 ptsgetnextfile = True
940 if eventtag == "pts_merge" and not ptsgetnextfile:
941 ptsgetnextfile = True
942 ptsmergeDEST = filename[0:-5]
943 ptsmergeeventname = eventname
944 ptsfilemerged = False
946 # If still recording or transfering, try again later ...
947 if fileExists("%s/%s" % (config.usage.default_path.value,ptsmergeDEST)):
948 statinfo = os_stat("%s/%s" % (config.usage.default_path.value,ptsmergeDEST))
949 if statinfo.st_mtime > (time()-10.0):
950 self.pts_mergeRecords_timer.start(120000, True)
953 # Rewrite Meta File to get rid of pts_merge tag
954 metafile = open("%s/%s.meta" % (config.usage.default_path.value,ptsmergeDEST), "w")
955 metafile.write("%s\n%s\n%s\n%i\n" % (servicerefname,eventname.replace("\n", ""),eventtitle.replace("\n", ""),int(eventtime)))
959 if not ptsfilemerged and ptsgetnextfile:
960 Notifications.AddNotification(MessageBox,_("PTS-Plugin: Merging records failed!"), MessageBox.TYPE_ERROR)
962 def ptsCreateAPSCFiles(self, filename):
963 if fileExists(filename, 'r'):
964 if fileExists(filename+".meta", 'r'):
965 # Get Event Info from meta file
966 readmetafile = open(filename+".meta", "r")
967 servicerefname = readmetafile.readline()[0:-1]
968 eventname = readmetafile.readline()[0:-1]
971 JobManager.AddJob(CreateAPSCFilesJob(self, "/usr/lib/enigma2/python/Plugins/Extensions/PermanentTimeshift/createapscfiles \"%s\"" % (filename), eventname))
973 self.ptsSaveTimeshiftFinished()
975 def ptsCreateEITFile(self, filename):
976 if self.pts_curevent_eventid is not None:
979 serviceref = ServiceReference(self.session.nav.getCurrentlyPlayingServiceReference()).ref.toString()
980 eitsave.SaveEIT(serviceref, filename+".eit", self.pts_curevent_eventid, -1, -1)
981 except Exception, errormsg:
982 print "PTS Plugin: %s" % (errormsg)
984 def ptsCopyFilefinished(self, srcfile, destfile):
986 if fileExists(srcfile):
987 self.BgFileEraser.erase(srcfile)
989 # Restart Merge Timer
990 if self.pts_mergeRecords_timer.isActive():
991 self.pts_mergeRecords_timer.stop()
992 self.pts_mergeRecords_timer.start(15000, True)
994 # Create AP and SC Files
995 self.ptsCreateAPSCFiles(destfile)
997 def ptsMergeFilefinished(self, srcfile, destfile):
998 if self.session.nav.RecordTimer.isRecording() or len(JobManager.getPendingJobs()) >= 1:
999 # Rename files and delete them later ...
1000 self.pts_mergeCleanUp_timer.start(120000, True)
1001 os_system("echo \"\" > \"%s.pts.del\"" % (srcfile[0:-3]))
1003 # Delete Instant Record permanently now ... R.I.P.
1004 self.BgFileEraser.erase("%s" % (srcfile))
1005 self.BgFileEraser.erase("%s.ap" % (srcfile))
1006 self.BgFileEraser.erase("%s.sc" % (srcfile))
1007 self.BgFileEraser.erase("%s.meta" % (srcfile))
1008 self.BgFileEraser.erase("%s.cuts" % (srcfile))
1009 self.BgFileEraser.erase("%s.eit" % (srcfile[0:-3]))
1011 # Create AP and SC Files
1012 self.ptsCreateAPSCFiles(destfile)
1014 # Run Merge-Process one more time to check if there are more records to merge
1015 self.pts_mergeRecords_timer.start(10000, True)
1017 def ptsSaveTimeshiftFinished(self):
1018 if not self.pts_mergeCleanUp_timer.isActive():
1019 self.ptsFrontpanelActions("stop")
1020 config.plugins.pts.isRecording.value = False
1022 if Screens.Standby.inTryQuitMainloop:
1023 self.pts_QuitMainloop_timer.start(30000, True)
1025 Notifications.AddNotification(MessageBox, _("Timeshift saved to your harddisk!"), MessageBox.TYPE_INFO, timeout = 5)
1027 def ptsMergePostCleanUp(self):
1028 if self.session.nav.RecordTimer.isRecording() or len(JobManager.getPendingJobs()) >= 1:
1029 config.plugins.pts.isRecording.value = True
1030 self.pts_mergeCleanUp_timer.start(120000, True)
1033 self.ptsFrontpanelActions("stop")
1034 config.plugins.pts.isRecording.value = False
1036 filelist = os_listdir(config.usage.default_path.value)
1037 for filename in filelist:
1038 if filename.endswith(".pts.del"):
1039 srcfile = config.usage.default_path.value + "/" + filename[0:-8] + ".ts"
1040 self.BgFileEraser.erase("%s" % (srcfile))
1041 self.BgFileEraser.erase("%s.ap" % (srcfile))
1042 self.BgFileEraser.erase("%s.sc" % (srcfile))
1043 self.BgFileEraser.erase("%s.meta" % (srcfile))
1044 self.BgFileEraser.erase("%s.cuts" % (srcfile))
1045 self.BgFileEraser.erase("%s.eit" % (srcfile[0:-3]))
1046 self.BgFileEraser.erase("%s.pts.del" % (srcfile[0:-3]))
1048 # Restart QuitMainloop Timer to give BgFileEraser enough time
1049 if Screens.Standby.inTryQuitMainloop and self.pts_QuitMainloop_timer.isActive():
1050 self.pts_QuitMainloop_timer.start(60000, True)
1052 def ptsTryQuitMainloop(self):
1053 if Screens.Standby.inTryQuitMainloop and (len(JobManager.getPendingJobs()) >= 1 or self.pts_mergeCleanUp_timer.isActive()):
1054 self.pts_QuitMainloop_timer.start(60000, True)
1057 if Screens.Standby.inTryQuitMainloop and self.session.ptsmainloopvalue:
1058 self.session.dialog_stack = []
1059 self.session.summary_stack = [None]
1060 self.session.open(TryQuitMainloop, self.session.ptsmainloopvalue)
1062 def ptsGetSeekInfo(self):
1063 s = self.session.nav.getCurrentService()
1064 return s and s.seek()
1066 def ptsGetPosition(self):
1067 seek = self.ptsGetSeekInfo()
1070 pos = seek.getPlayPosition()
1075 def ptsGetLength(self):
1076 seek = self.ptsGetSeekInfo()
1079 length = seek.getLength()
1084 def ptsGetSaveTimeshiftStatus(self):
1085 return self.save_current_timeshift
1087 def ptsSeekPointerOK(self):
1088 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable():
1089 if not self.pvrstate_hide_timer.isActive():
1090 if self.seekstate != self.SEEK_STATE_PLAY:
1091 self.setSeekState(self.SEEK_STATE_PLAY)
1095 length = self.ptsGetLength()
1096 position = self.ptsGetPosition()
1098 if length is None or position is None:
1101 cur_pos = self.pvrStateDialog["PTSSeekPointer"].position
1102 jumptox = int(cur_pos[0]) - int(self.pts_seekpointer_MinX)
1103 jumptoperc = round((jumptox / 400.0) * 100, 0)
1104 jumptotime = int((length / 100) * jumptoperc)
1105 jumptodiff = position - jumptotime
1107 self.doSeekRelative(-jumptodiff)
1111 def ptsSeekPointerLeft(self):
1112 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable():
1113 self.ptsMoveSeekPointer(direction="left")
1117 def ptsSeekPointerRight(self):
1118 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable():
1119 self.ptsMoveSeekPointer(direction="right")
1123 def ptsSeekPointerReset(self):
1124 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled:
1125 self.pvrStateDialog["PTSSeekPointer"].setPosition(self.pts_seekpointer_MinX,self.pvrStateDialog["PTSSeekPointer"].position[1])
1127 def ptsSeekPointerSetCurrentPos(self):
1128 if not self.pts_pvrStateDialog == "PTSTimeshiftState" or not self.timeshift_enabled or not self.isSeekable():
1131 position = self.ptsGetPosition()
1132 length = self.ptsGetLength()
1135 tpixels = int((float(int((position*100)/length))/100)*400)
1136 self.pvrStateDialog["PTSSeekPointer"].setPosition(self.pts_seekpointer_MinX+tpixels, self.pvrStateDialog["PTSSeekPointer"].position[1])
1138 def ptsMoveSeekPointer(self, direction=None):
1139 if direction is None or self.pts_pvrStateDialog != "PTSTimeshiftState":
1143 cur_pos = self.pvrStateDialog["PTSSeekPointer"].position
1144 InfoBarTimeshiftState._mayShow(self)
1146 if direction == "left":
1147 minmaxval = self.pts_seekpointer_MinX
1149 if cur_pos[0]+movepixels > minmaxval:
1151 elif direction == "right":
1152 minmaxval = self.pts_seekpointer_MaxX
1154 if cur_pos[0]+movepixels < minmaxval:
1160 self.pvrStateDialog["PTSSeekPointer"].setPosition(cur_pos[0]+movepixels,cur_pos[1])
1162 self.pvrStateDialog["PTSSeekPointer"].setPosition(minmaxval,cur_pos[1])
1164 def ptsTimeshiftFileChanged(self):
1165 # Reset Seek Pointer
1166 if config.plugins.pts.enabled.value and config.plugins.pts.showinfobar.value:
1167 self.ptsSeekPointerReset()
1169 if self.pts_switchtolive:
1170 self.pts_switchtolive = False
1173 if self.pts_seektoprevfile:
1174 if self.pts_currplaying == 1:
1175 self.pts_currplaying = config.plugins.pts.maxevents.value
1177 self.pts_currplaying -= 1
1179 if self.pts_currplaying == config.plugins.pts.maxevents.value:
1180 self.pts_currplaying = 1
1182 self.pts_currplaying += 1
1184 if not fileExists("%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,self.pts_currplaying), 'r'):
1185 self.pts_currplaying = self.pts_eventcount
1187 # Set Eventname in PTS InfoBar
1188 if config.plugins.pts.enabled.value and config.plugins.pts.showinfobar.value and self.pts_pvrStateDialog == "PTSTimeshiftState":
1190 if self.pts_eventcount != self.pts_currplaying:
1191 readmetafile = open("%s/pts_livebuffer.%s.meta" % (config.usage.timeshift_path.value,self.pts_currplaying), "r")
1192 servicerefname = readmetafile.readline()[0:-1]
1193 eventname = readmetafile.readline()[0:-1]
1194 readmetafile.close()
1195 self.pvrStateDialog["eventname"].setText(eventname)
1197 self.pvrStateDialog["eventname"].setText("")
1198 except Exception, errormsg:
1199 self.pvrStateDialog["eventname"].setText("")
1201 # Get next pts file ...
1202 if self.pts_currplaying+1 > config.plugins.pts.maxevents.value:
1205 nextptsfile = self.pts_currplaying+1
1207 # Seek to previous file
1208 if self.pts_seektoprevfile:
1209 self.pts_seektoprevfile = False
1211 if fileExists("%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,nextptsfile), 'r'):
1212 self.ptsSetNextPlaybackFile("pts_livebuffer.%s" % (nextptsfile))
1214 self.ptsSeekBackHack()
1216 if fileExists("%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,nextptsfile), 'r') and nextptsfile <= self.pts_eventcount:
1217 self.ptsSetNextPlaybackFile("pts_livebuffer.%s" % (nextptsfile))
1218 if nextptsfile == self.pts_currplaying:
1219 self.pts_switchtolive = True
1220 self.ptsSetNextPlaybackFile("")
1222 def ptsSetNextPlaybackFile(self, nexttsfile):
1223 ts = self.getTimeshift()
1228 ts.setNextPlaybackFile("%s/%s" % (config.usage.timeshift_path.value,nexttsfile))
1230 print "PTS-Plugin: setNextPlaybackFile() not supported by OE. Enigma2 too old !?"
1232 def ptsSeekBackHack(self):
1233 if not config.plugins.pts.enabled.value or not self.timeshift_enabled:
1236 self.setSeekState(self.SEEK_STATE_PAUSE)
1237 self.doSeek(-90000*4) # seek ~4s before end
1238 self.pts_SeekBack_timer.start(1000, True)
1240 def ptsSeekBackTimer(self):
1241 if self.pts_lastseekspeed == 0:
1242 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1244 self.setSeekState(self.makeStateBackward(int(-self.pts_lastseekspeed)))
1246 def ptsCheckTimeshiftPath(self):
1247 if self.pts_pathchecked:
1250 if fileExists(config.usage.timeshift_path.value, 'w'):
1251 self.pts_pathchecked = True
1254 Notifications.AddNotification(MessageBox, _("Could not activate Permanent-Timeshift!\nTimeshift-Path does not exist"), MessageBox.TYPE_ERROR, timeout=15)
1255 if self.pts_delay_timer.isActive():
1256 self.pts_delay_timer.stop()
1257 if self.pts_cleanUp_timer.isActive():
1258 self.pts_cleanUp_timer.stop()
1261 def ptsTimerEntryStateChange(self, timer):
1262 if not config.plugins.pts.enabled.value or not config.plugins.pts.stopwhilerecording.value:
1265 self.pts_record_running = self.session.nav.RecordTimer.isRecording()
1267 # Abort here when box is in standby mode
1268 if self.session.screen["Standby"].boolean is True:
1271 # Stop Timeshift when Record started ...
1272 if timer.state == TimerEntry.StateRunning and self.timeshift_enabled and self.pts_record_running:
1273 if self.ptsLiveTVStatus() is False:
1274 self.timeshift_enabled = 0
1275 self.pts_LengthCheck_timer.stop()
1278 if self.seekstate != self.SEEK_STATE_PLAY:
1279 self.setSeekState(self.SEEK_STATE_PLAY)
1281 if self.isSeekable():
1282 Notifications.AddNotification(MessageBox,_("Record started! Stopping timeshift now ..."), MessageBox.TYPE_INFO, timeout=5)
1284 self.stopTimeshiftConfirmed(True, False)
1286 # Restart Timeshift when all records stopped
1287 if timer.state == TimerEntry.StateEnded and not self.timeshift_enabled and not self.pts_record_running:
1288 self.ActivatePermanentTimeshift()
1290 # Restart Merge-Timer when all records stopped
1291 if timer.state == TimerEntry.StateEnded and self.pts_mergeRecords_timer.isActive():
1292 self.pts_mergeRecords_timer.stop()
1293 self.pts_mergeRecords_timer.start(15000, True)
1295 # Restart FrontPanel LED when still copying or merging files
1296 # ToDo: Only do this on PTS Events and not events from other jobs
1297 if timer.state == TimerEntry.StateEnded and (len(JobManager.getPendingJobs()) >= 1 or self.pts_mergeRecords_timer.isActive()):
1298 self.ptsFrontpanelActions("start")
1299 config.plugins.pts.isRecording.value = True
1301 def ptsLiveTVStatus(self):
1302 service = self.session.nav.getCurrentService()
1303 info = service and service.info()
1304 sTSID = info and info.getInfo(iServiceInformation.sTSID) or -1
1306 if sTSID is None or sTSID == -1:
1311 def ptsLengthCheck(self):
1312 # Check if we are in TV Mode ...
1313 if self.ptsLiveTVStatus() is False:
1314 self.timeshift_enabled = 0
1315 self.pts_LengthCheck_timer.stop()
1318 if config.plugins.pts.stopwhilerecording.value and self.pts_record_running:
1322 if config.plugins.pts.enabled.value and self.session.screen["Standby"].boolean is not True and self.timeshift_enabled and (time() - self.pts_starttime) >= (config.plugins.pts.maxlength.value * 60):
1323 if self.save_current_timeshift:
1324 self.saveTimeshiftActions("savetimeshift")
1325 self.ActivatePermanentTimeshift()
1326 self.save_current_timeshift = True
1328 self.ActivatePermanentTimeshift()
1329 Notifications.AddNotification(MessageBox,_("Maximum Timeshift length per Event reached!\nRestarting Timeshift now ..."), MessageBox.TYPE_INFO, timeout=5)
1331 #Replace the InfoBar with our version ;)
1332 Screens.InfoBar.InfoBar = InfoBar
1334 ################################
1335 ##### Class Standby Hack 1 #####
1336 ################################
1337 TryQuitMainloop_getRecordEvent = Screens.Standby.TryQuitMainloop.getRecordEvent
1339 class TryQuitMainloopPTS(TryQuitMainloop):
1340 def __init__(self, session, retvalue=1, timeout=-1, default_yes = True):
1341 TryQuitMainloop.__init__(self, session, retvalue, timeout, default_yes)
1343 self.session.ptsmainloopvalue = retvalue
1345 def getRecordEvent(self, recservice, event):
1346 if event == iRecordableService.evEnd and (config.plugins.pts.isRecording.value or len(JobManager.getPendingJobs()) >= 1):
1349 TryQuitMainloop_getRecordEvent(self, recservice, event)
1351 Screens.Standby.TryQuitMainloop = TryQuitMainloopPTS
1353 ################################
1354 ##### Class Standby Hack 2 #####
1355 ################################
1357 Screens_Standby_Standby = Screens.Standby.Standby
1359 class StandbyPTS(Standby):
1360 def __init__(self, session):
1361 if InfoBar and InfoBar.instance and InfoBar.ptsGetSaveTimeshiftStatus(InfoBar.instance):
1362 self.skin = """<screen position="0,0" size="0,0"/>"""
1363 Screen.__init__(self, session)
1364 self.onFirstExecBegin.append(self.showMessageBox)
1365 self.onHide.append(self.close)
1367 Standby.__init__(self, session)
1368 self.skinName = "Standby"
1370 def showMessageBox(self):
1371 if InfoBar and InfoBar.instance:
1372 InfoBar.saveTimeshiftActions(InfoBar.instance, postaction="standby")
1374 Screens.Standby.Standby = StandbyPTS
1379 InfoBarChannelSelection_zapUp = InfoBarChannelSelection.zapUp
1382 if self.pts_blockZap_timer.isActive():
1385 if self.save_current_timeshift and self.timeshift_enabled:
1386 InfoBar.saveTimeshiftActions(self, postaction="zapUp")
1388 InfoBarChannelSelection_zapUp(self)
1390 InfoBarChannelSelection.zapUp = zapUp
1395 InfoBarChannelSelection_zapDown = InfoBarChannelSelection.zapDown
1398 if self.pts_blockZap_timer.isActive():
1401 if self.save_current_timeshift and self.timeshift_enabled:
1402 InfoBar.saveTimeshiftActions(self, postaction="zapDown")
1404 InfoBarChannelSelection_zapDown(self)
1406 InfoBarChannelSelection.zapDown = zapDown
1411 InfoBarChannelSelection_historyBack = InfoBarChannelSelection.historyBack
1413 def historyBack(self):
1414 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable():
1415 InfoBarTimeshiftState._mayShow(self)
1416 self.pvrStateDialog["PTSSeekPointer"].setPosition(self.pts_seekpointer_MinX, self.pvrStateDialog["PTSSeekPointer"].position[1])
1417 if self.seekstate != self.SEEK_STATE_PLAY:
1418 self.setSeekState(self.SEEK_STATE_PLAY)
1419 self.ptsSeekPointerOK()
1420 elif self.save_current_timeshift and self.timeshift_enabled:
1421 InfoBar.saveTimeshiftActions(self, postaction="historyBack")
1423 InfoBarChannelSelection_historyBack(self)
1425 InfoBarChannelSelection.historyBack = historyBack
1430 InfoBarChannelSelection_historyNext = InfoBarChannelSelection.historyNext
1432 def historyNext(self):
1433 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable():
1434 InfoBarTimeshiftState._mayShow(self)
1435 self.pvrStateDialog["PTSSeekPointer"].setPosition(self.pts_seekpointer_MaxX, self.pvrStateDialog["PTSSeekPointer"].position[1])
1436 if self.seekstate != self.SEEK_STATE_PLAY:
1437 self.setSeekState(self.SEEK_STATE_PLAY)
1438 self.ptsSeekPointerOK()
1439 elif self.save_current_timeshift and self.timeshift_enabled:
1440 InfoBar.saveTimeshiftActions(self, postaction="historyNext")
1442 InfoBarChannelSelection_historyNext(self)
1444 InfoBarChannelSelection.historyNext = historyNext
1446 ######################
1447 #switchChannelUp Hack#
1448 ######################
1449 InfoBarChannelSelection_switchChannelUp = InfoBarChannelSelection.switchChannelUp
1451 def switchChannelUp(self):
1452 if self.save_current_timeshift and self.timeshift_enabled:
1453 InfoBar.saveTimeshiftActions(self, postaction="switchChannelUp")
1455 InfoBarChannelSelection_switchChannelUp(self)
1457 InfoBarChannelSelection.switchChannelUp = switchChannelUp
1459 ########################
1460 #switchChannelDown Hack#
1461 ########################
1462 InfoBarChannelSelection_switchChannelDown = InfoBarChannelSelection.switchChannelDown
1464 def switchChannelDown(self):
1465 if self.save_current_timeshift and self.timeshift_enabled:
1466 InfoBar.saveTimeshiftActions(self, postaction="switchChannelDown")
1468 InfoBarChannelSelection_switchChannelDown(self)
1470 InfoBarChannelSelection.switchChannelDown = switchChannelDown
1472 ######################
1473 #openServiceList Hack#
1474 ######################
1475 InfoBarChannelSelection_openServiceList = InfoBarChannelSelection.openServiceList
1477 def openServiceList(self):
1478 if self.save_current_timeshift and self.timeshift_enabled:
1479 InfoBar.saveTimeshiftActions(self, postaction="openServiceList")
1481 InfoBarChannelSelection_openServiceList(self)
1483 InfoBarChannelSelection.openServiceList = openServiceList
1485 ###########################
1486 #showRadioChannelList Hack#
1487 ###########################
1488 InfoBarChannelSelection_showRadioChannelList = InfoBarChannelSelection.showRadioChannelList
1490 def showRadioChannelList(self, zap=False):
1491 if self.save_current_timeshift and self.timeshift_enabled:
1492 InfoBar.saveTimeshiftActions(self, postaction="showRadioChannelList")
1494 InfoBarChannelSelection_showRadioChannelList(self, zap)
1496 InfoBarChannelSelection.showRadioChannelList = showRadioChannelList
1498 #######################
1499 #InfoBarNumberZap Hack#
1500 #######################
1501 InfoBarNumberZap_keyNumberGlobal = InfoBarNumberZap.keyNumberGlobal
1503 def keyNumberGlobal(self, number):
1504 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable() and number == 0:
1505 InfoBarTimeshiftState._mayShow(self)
1506 self.pvrStateDialog["PTSSeekPointer"].setPosition(self.pts_seekpointer_MaxX/2, self.pvrStateDialog["PTSSeekPointer"].position[1])
1507 if self.seekstate != self.SEEK_STATE_PLAY:
1508 self.setSeekState(self.SEEK_STATE_PLAY)
1509 self.ptsSeekPointerOK()
1512 if self.pts_blockZap_timer.isActive():
1515 if self.save_current_timeshift and self.timeshift_enabled:
1516 InfoBar.saveTimeshiftActions(self)
1519 InfoBarNumberZap_keyNumberGlobal(self, number)
1520 if number and config.plugins.pts.enabled.value and self.timeshift_enabled and not self.isSeekable():
1521 self.session.openWithCallback(self.numberEntered, NumberZap, number)
1523 InfoBarNumberZap.keyNumberGlobal = keyNumberGlobal
1525 #############################
1526 # getNextRecordingTime Hack #
1527 #############################
1528 RecordTimer_getNextRecordingTime = RecordTimer.getNextRecordingTime
1530 def getNextRecordingTime(self):
1531 nextrectime = RecordTimer_getNextRecordingTime(self)
1532 faketime = time()+300
1534 if config.plugins.pts.isRecording.value or len(JobManager.getPendingJobs()) >= 1:
1535 if nextrectime > 0 and nextrectime < faketime:
1542 RecordTimer.getNextRecordingTime = getNextRecordingTime
1544 ############################
1545 #InfoBarTimeshiftState Hack#
1546 ############################
1548 if self.execing and self.timeshift_enabled and self.isSeekable():
1549 InfoBar.ptsSeekPointerSetCurrentPos(self)
1550 self.pvrStateDialog.show()
1552 self.pvrstate_hide_timer = eTimer()
1553 self.pvrstate_hide_timer.callback.append(self.pvrStateDialog.hide)
1555 if self.seekstate == self.SEEK_STATE_PLAY:
1556 idx = config.usage.infobar_timeout.index
1559 self.pvrstate_hide_timer.start(idx*1000, True)
1561 self.pvrstate_hide_timer.stop()
1562 elif self.execing and self.timeshift_enabled and not self.isSeekable():
1563 self.pvrStateDialog.hide()
1565 InfoBarTimeshiftState._mayShow = _mayShow
1570 InfoBarSeek_seekBack = InfoBarSeek.seekBack
1573 InfoBarSeek_seekBack(self)
1574 self.pts_lastseekspeed = self.seekstate[1]
1576 InfoBarSeek.seekBack = seekBack
1578 ####################
1579 #instantRecord Hack#
1580 ####################
1581 InfoBarInstantRecord_instantRecord = InfoBarInstantRecord.instantRecord
1583 def instantRecord(self):
1584 if not config.plugins.pts.enabled.value or not self.timeshift_enabled:
1585 InfoBarInstantRecord_instantRecord(self)
1588 dir = preferredInstantRecordPath()
1589 if not dir or not fileExists(dir, 'w'):
1590 dir = defaultMoviePath()
1594 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1597 if self.isInstantRecordRunning():
1598 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1599 title=_("A recording is currently running.\nWhat do you want to do?"), \
1600 list=((_("stop recording"), "stop"), \
1601 (_("add recording (stop after current event)"), "event"), \
1602 (_("add recording (indefinitely)"), "indefinitely"), \
1603 (_("add recording (enter recording duration)"), "manualduration"), \
1604 (_("add recording (enter recording endtime)"), "manualendtime"), \
1605 (_("change recording (duration)"), "changeduration"), \
1606 (_("change recording (endtime)"), "changeendtime"), \
1607 (_("Timeshift")+" "+_("save recording (stop after current event)"), "savetimeshift"), \
1608 (_("Timeshift")+" "+_("save recording (Select event)"), "savetimeshiftEvent"), \
1609 (_("do nothing"), "no")))
1611 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1612 title=_("Start recording?"), \
1613 list=((_("add recording (stop after current event)"), "event"), \
1614 (_("add recording (indefinitely)"), "indefinitely"), \
1615 (_("add recording (enter recording duration)"), "manualduration"), \
1616 (_("add recording (enter recording endtime)"), "manualendtime"), \
1617 (_("Timeshift")+" "+_("save recording (stop after current event)"), "savetimeshift"), \
1618 (_("Timeshift")+" "+_("save recording (Select event)"), "savetimeshiftEvent"), \
1619 (_("don't record"), "no")))
1621 InfoBarInstantRecord.instantRecord = instantRecord
1623 #############################
1624 #recordQuestionCallback Hack#
1625 #############################
1626 InfoBarInstantRecord_recordQuestionCallback = InfoBarInstantRecord.recordQuestionCallback
1628 def recordQuestionCallback(self, answer):
1629 InfoBarInstantRecord_recordQuestionCallback(self, answer)
1631 if config.plugins.pts.enabled.value:
1632 if answer is not None and answer[1] == "savetimeshift":
1633 if InfoBarSeek.isSeekable(self) and self.pts_eventcount != self.pts_currplaying:
1634 InfoBar.SaveTimeshift(self, timeshiftfile="pts_livebuffer.%s" % self.pts_currplaying)
1636 Notifications.AddNotification(MessageBox,_("Timeshift will get saved at end of event!"), MessageBox.TYPE_INFO, timeout=5)
1637 self.save_current_timeshift = True
1638 config.plugins.pts.isRecording.value = True
1639 if answer is not None and answer[1] == "savetimeshiftEvent":
1640 InfoBar.saveTimeshiftEventPopup(self)
1642 if answer is not None and answer[1].startswith("pts_livebuffer") is True:
1643 InfoBar.SaveTimeshift(self, timeshiftfile=answer[1])
1645 InfoBarInstantRecord.recordQuestionCallback = recordQuestionCallback
1647 ############################
1648 ##### SETTINGS SCREEN #####
1649 ############################
1650 class PermanentTimeShiftSetup(Screen, ConfigListScreen):
1651 def __init__(self, session):
1652 Screen.__init__(self, session)
1653 self.skinName = [ "PTSSetup", "Setup" ]
1654 self.setup_title = _("Permanent Timeshift Settings")
1656 self.onChangedEntry = [ ]
1658 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changedEntry)
1660 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
1662 "ok": self.SaveSettings,
1663 "green": self.SaveSettings,
1668 self["key_green"] = StaticText(_("OK"))
1669 self["key_red"] = StaticText(_("Cancel"))
1672 self.onLayoutFinish.append(self.layoutFinished)
1674 def layoutFinished(self):
1675 self.setTitle(self.setup_title)
1677 def createSetup(self):
1678 self.list = [ getConfigListEntry(_("Permanent Timeshift Enable"), config.plugins.pts.enabled) ]
1679 if config.plugins.pts.enabled.value:
1681 getConfigListEntry(_("Permanent Timeshift Max Events"), config.plugins.pts.maxevents),
1682 getConfigListEntry(_("Permanent Timeshift Max Length"), config.plugins.pts.maxlength),
1683 getConfigListEntry(_("Permanent Timeshift Start Delay"), config.plugins.pts.startdelay),
1684 getConfigListEntry(_("Timeshift-Save Action on zap"), config.plugins.pts.favoriteSaveAction),
1685 getConfigListEntry(_("Stop timeshift while recording?"), config.plugins.pts.stopwhilerecording),
1686 getConfigListEntry(_("Show PTS Infobar while timeshifting?"), config.plugins.pts.showinfobar)
1689 # Permanent Recording Hack
1690 if fileExists("/usr/lib/enigma2/python/Plugins/Extensions/HouseKeeping/plugin.py"):
1691 self.list.append(getConfigListEntry(_("Beta: Enable Permanent Recording?"), config.plugins.pts.permanentrecording))
1693 self["config"].list = self.list
1694 self["config"].setList(self.list)
1697 ConfigListScreen.keyLeft(self)
1698 if self["config"].getCurrent()[1] == config.plugins.pts.enabled:
1702 ConfigListScreen.keyRight(self)
1703 if self["config"].getCurrent()[1] == config.plugins.pts.enabled:
1706 def changedEntry(self):
1707 for x in self.onChangedEntry:
1710 def getCurrentEntry(self):
1711 return self["config"].getCurrent()[0]
1713 def getCurrentValue(self):
1714 return str(self["config"].getCurrent()[1].getText())
1716 def createSummary(self):
1719 def SaveSettings(self):
1720 config.plugins.pts.save()
1727 #################################################
1729 def startSetup(menuid):
1730 if menuid != "system":
1732 return [(_("Timeshift Settings"), PTSSetupMenu, "pts_setup", 50)]
1734 def PTSSetupMenu(session, **kwargs):
1735 session.open(PermanentTimeShiftSetup)
1737 def Plugins(path, **kwargs):
1738 return [ PluginDescriptor(name=_("Permanent Timeshift Settings"), description=_("Permanent Timeshift Settings"), where=PluginDescriptor.WHERE_MENU, fnc=startSetup) ]