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, remove as os_remove, 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("PermanentTimeshift", "%s%s" % (resolveFilename(SCOPE_PLUGINS), "Extensions/PermanentTimeshift/locale/"))
56 t = gettext.dgettext("PermanentTimeshift", 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
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 eraseFile(self, filePath):
414 # We only want to use E2 Background Eraser if file has no other links on it.
415 # Early Enigma2 3.20 Releases had a bug that also affected the hardlinked files.
416 if fileExists(filePath):
417 if os_stat(filePath).st_nlink != 1:
420 self.BgFileEraser.erase(filePath)
422 def eraseTimeshiftFile(self):
423 for filename in os_listdir(config.usage.timeshift_path.value):
424 if filename.startswith("timeshift.") and not filename.endswith(".del") and not filename.endswith(".copy"):
425 self.eraseFile("%s/%s" % (config.usage.timeshift_path.value,filename))
427 def activatePermanentTimeshift(self):
428 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):
431 # Replace PVR Timeshift State Icon
432 if config.plugins.pts.showinfobar.value:
433 if self.pts_pvrStateDialog != "PTSTimeshiftState":
434 self.pts_pvrStateDialog = "PTSTimeshiftState"
435 self.pvrStateDialog = self.session.instantiateDialog(PTSTimeshiftState)
436 elif not config.plugins.pts.showinfobar.value and self.pts_pvrStateDialog != "TimeshiftState":
437 self.pts_pvrStateDialog = "TimeshiftState"
438 self.pvrStateDialog = self.session.instantiateDialog(TimeshiftState)
440 # Set next-file on event change only when watching latest timeshift ...
441 if self.isSeekable() and self.pts_eventcount == self.pts_currplaying:
442 pts_setnextfile = True
444 pts_setnextfile = False
446 # Update internal Event Counter
447 if self.pts_eventcount >= config.plugins.pts.maxevents.value:
448 self.pts_eventcount = 0
450 self.pts_eventcount += 1
452 # Do not switch back to LiveTV while timeshifting
453 if self.isSeekable():
458 # setNextPlaybackFile() on event change while timeshifting
459 if self.pts_eventcount > 1 and self.isSeekable() and pts_setnextfile:
460 self.ptsSetNextPlaybackFile("pts_livebuffer.%s" % (self.pts_eventcount))
462 # (Re)start Timeshift now
463 self.stopTimeshiftConfirmed(True, switchToLive)
464 ts = self.getTimeshift()
465 if ts and not ts.startTimeshift():
466 self.pts_starttime = time()
467 self.pts_LengthCheck_timer.start(120000)
468 self.timeshift_enabled = 1
469 self.save_timeshift_postaction = None
470 self.ptsGetEventInfo()
471 self.ptsCreateHardlink()
472 self.__seekableStatusChanged()
474 self.pts_eventcount = 0
476 def startTimeshift(self):
477 if config.plugins.pts.enabled.value:
478 self.pts_delay_timer.stop()
479 self.activatePermanentTimeshift()
480 self.activateTimeshiftEndAndPause()
482 InfoBarOrg.startTimeshift(self)
484 def stopTimeshift(self):
485 if not self.timeshift_enabled:
488 # Jump Back to Live TV
489 if config.plugins.pts.enabled.value and self.timeshift_enabled:
490 if self.isSeekable():
491 self.pts_switchtolive = True
492 self.ptsSetNextPlaybackFile("")
493 self.setSeekState(self.SEEK_STATE_PAUSE)
494 if self.seekstate != self.SEEK_STATE_PLAY:
495 self.setSeekState(self.SEEK_STATE_PLAY)
496 self.doSeek(-1) # seek 1 gop before end
497 self.seekFwd() # seekFwd to switch to live TV
500 InfoBarOrg.stopTimeshift(self)
502 def stopTimeshiftConfirmed(self, confirmed, switchToLive=True):
503 was_enabled = self.timeshift_enabled
507 ts = self.getTimeshift()
511 # Get rid of old timeshift file before E2 truncates its filesize
512 self.eraseTimeshiftFile()
516 ts.stopTimeshift(switchToLive)
520 self.timeshift_enabled = 0
521 self.__seekableStatusChanged()
523 if was_enabled and not self.timeshift_enabled:
524 self.timeshift_enabled = 0
525 self.pts_LengthCheck_timer.stop()
527 def restartTimeshift(self):
528 self.activatePermanentTimeshift()
529 Notifications.AddNotification(MessageBox, _("PTS-Plugin: Restarting Timeshift!"), MessageBox.TYPE_INFO, timeout=5)
531 def saveTimeshiftPopup(self):
532 self.session.openWithCallback(self.saveTimeshiftPopupCallback, ChoiceBox, \
533 title=_("The Timeshift record was not saved yet!\nWhat do you want to do now with the timeshift file?"), \
534 list=((_("Save Timeshift as Movie and stop recording"), "savetimeshift"), \
535 (_("Save Timeshift as Movie and continue recording"), "savetimeshiftandrecord"), \
536 (_("Don't save Timeshift as Movie"), "noSave")))
538 def saveTimeshiftPopupCallback(self, answer):
542 if answer[1] == "savetimeshift":
543 self.saveTimeshiftActions("savetimeshift", self.save_timeshift_postaction)
544 elif answer[1] == "savetimeshiftandrecord":
545 self.saveTimeshiftActions("savetimeshiftandrecord", self.save_timeshift_postaction)
546 elif answer[1] == "noSave":
547 self.save_current_timeshift = False
548 self.saveTimeshiftActions("noSave", self.save_timeshift_postaction)
550 def saveTimeshiftEventPopup(self):
553 entrylist.append((_("Current Event:")+" %s" % (self.pts_curevent_name), "savetimeshift"))
555 filelist = os_listdir(config.usage.timeshift_path.value)
557 if filelist is not None:
560 for filename in filelist:
561 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):
562 statinfo = os_stat("%s/%s" % (config.usage.timeshift_path.value,filename))
563 if statinfo.st_mtime < (time()-5.0):
564 # Get Event Info from meta file
565 readmetafile = open("%s/%s.meta" % (config.usage.timeshift_path.value,filename), "r")
566 servicerefname = readmetafile.readline()[0:-1]
567 eventname = readmetafile.readline()[0:-1]
568 description = readmetafile.readline()[0:-1]
569 begintime = readmetafile.readline()[0:-1]
574 entrylist.append((_("Record") + " #%s (%s): %s" % (filecount,strftime("%H:%M",localtime(int(begintime))),eventname), "%s" % filename))
576 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("Which event do you want to save permanently?"), list=entrylist)
578 def saveTimeshiftActions(self, action=None, postaction=None):
579 self.save_timeshift_postaction = postaction
582 if config.plugins.pts.favoriteSaveAction.value == "askuser":
583 self.saveTimeshiftPopup()
585 elif config.plugins.pts.favoriteSaveAction.value == "savetimeshift":
587 elif config.plugins.pts.favoriteSaveAction.value == "savetimeshiftandrecord":
588 if self.pts_curevent_end > time():
589 self.SaveTimeshift(mergelater=True)
590 self.ptsRecordCurrentEvent()
593 elif config.plugins.pts.favoriteSaveAction.value == "noSave":
594 config.plugins.pts.isRecording.value = False
595 self.save_current_timeshift = False
596 elif action == "savetimeshift":
598 elif action == "savetimeshiftandrecord":
599 if self.pts_curevent_end > time():
600 self.SaveTimeshift(mergelater=True)
601 self.ptsRecordCurrentEvent()
604 elif action == "noSave":
605 config.plugins.pts.isRecording.value = False
606 self.save_current_timeshift = False
608 # Get rid of old timeshift file before E2 truncates its filesize
609 if self.save_timeshift_postaction is not None:
610 self.eraseTimeshiftFile()
612 # Post PTS Actions like ZAP or whatever the user requested
613 if self.save_timeshift_postaction == "zapUp":
614 InfoBarChannelSelection.zapUp(self)
615 elif self.save_timeshift_postaction == "zapDown":
616 InfoBarChannelSelection.zapDown(self)
617 elif self.save_timeshift_postaction == "historyBack":
618 InfoBarChannelSelection.historyBack(self)
619 elif self.save_timeshift_postaction == "historyNext":
620 InfoBarChannelSelection.historyNext(self)
621 elif self.save_timeshift_postaction == "switchChannelUp":
622 InfoBarChannelSelection.switchChannelUp(self)
623 elif self.save_timeshift_postaction == "switchChannelDown":
624 InfoBarChannelSelection.switchChannelDown(self)
625 elif self.save_timeshift_postaction == "openServiceList":
626 InfoBarChannelSelection.openServiceList(self)
627 elif self.save_timeshift_postaction == "showRadioChannelList":
628 InfoBarChannelSelection.showRadioChannelList(self, zap=True)
629 elif self.save_timeshift_postaction == "standby":
630 Notifications.AddNotification(Screens_Standby_Standby)
632 def SaveTimeshift(self, timeshiftfile=None, mergelater=False):
633 self.save_current_timeshift = False
636 if timeshiftfile is not None:
637 savefilename = timeshiftfile
639 if savefilename is None:
640 for filename in os_listdir(config.usage.timeshift_path.value):
641 if filename.startswith("timeshift.") and not filename.endswith(".del") and not filename.endswith(".copy"):
643 statinfo = os_stat("%s/%s" % (config.usage.timeshift_path.value,filename))
644 if statinfo.st_mtime > (time()-5.0):
645 savefilename=filename
646 except Exception, errormsg:
647 Notifications.AddNotification(MessageBox, _("PTS Plugin Error: %s" % (errormsg)), MessageBox.TYPE_ERROR)
649 if savefilename is None:
650 Notifications.AddNotification(MessageBox, _("No Timeshift found to save as recording!"), MessageBox.TYPE_ERROR)
652 timeshift_saved = True
653 timeshift_saveerror1 = ""
654 timeshift_saveerror2 = ""
657 config.plugins.pts.isRecording.value = True
660 self.pts_mergeRecords_timer.start(120000, True)
661 metamergestring = "pts_merge\n"
664 if timeshiftfile is None:
665 # Save Current Event by creating hardlink to ts file
666 if self.pts_starttime >= (time()-60):
667 self.pts_starttime -= 60
669 ptsfilename = "%s - %s - %s" % (strftime("%Y%m%d %H%M",localtime(self.pts_starttime)),self.pts_curevent_station,self.pts_curevent_name)
671 if config.usage.setup_level.index >= 2:
672 if config.recording.filename_composition.value == "long" and self.pts_curevent_name != pts_curevent_description:
673 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)
674 elif config.recording.filename_composition.value == "short":
675 ptsfilename = "%s - %s" % (strftime("%Y%m%d",localtime(self.pts_starttime)),self.pts_curevent_name)
676 except Exception, errormsg:
677 print "PTS-Plugin: Using default filename"
679 if config.recording.ascii_filenames.value:
680 ptsfilename = ASCIItranslit.legacyEncode(ptsfilename)
682 fullname = Directories.getRecordingFilename(ptsfilename,config.usage.default_path.value)
683 os_link("%s/%s" % (config.usage.timeshift_path.value,savefilename), "%s.ts" % (fullname))
684 metafile = open("%s.ts.meta" % (fullname), "w")
685 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))
687 self.ptsCreateEITFile(fullname)
688 elif timeshiftfile.startswith("pts_livebuffer"):
689 # Save stored timeshift by creating hardlink to ts file
690 readmetafile = open("%s/%s.meta" % (config.usage.timeshift_path.value,timeshiftfile), "r")
691 servicerefname = readmetafile.readline()[0:-1]
692 eventname = readmetafile.readline()[0:-1]
693 description = readmetafile.readline()[0:-1]
694 begintime = readmetafile.readline()[0:-1]
697 ptsfilename = "%s - %s - %s" % (strftime("%Y%m%d %H%M",localtime(int(begintime))),self.pts_curevent_station,eventname)
699 if config.usage.setup_level.index >= 2:
700 if config.recording.filename_composition.value == "long" and eventname != description:
701 ptsfilename = "%s - %s - %s - %s" % (strftime("%Y%m%d %H%M",localtime(int(begintime))),self.pts_curevent_station,eventname,description)
702 elif config.recording.filename_composition.value == "short":
703 ptsfilename = "%s - %s" % (strftime("%Y%m%d",localtime(int(begintime))),eventname)
704 except Exception, errormsg:
705 print "PTS-Plugin: Using default filename"
707 if config.recording.ascii_filenames.value:
708 ptsfilename = ASCIItranslit.legacyEncode(ptsfilename)
710 fullname=Directories.getRecordingFilename(ptsfilename,config.usage.default_path.value)
711 os_link("%s/%s" % (config.usage.timeshift_path.value,timeshiftfile),"%s.ts" % (fullname))
712 os_link("%s/%s.meta" % (config.usage.timeshift_path.value,timeshiftfile),"%s.ts.meta" % (fullname))
713 if fileExists("%s/%s.eit" % (config.usage.timeshift_path.value,timeshiftfile)):
714 os_link("%s/%s.eit" % (config.usage.timeshift_path.value,timeshiftfile),"%s.eit" % (fullname))
716 # Add merge-tag to metafile
718 metafile = open("%s.ts.meta" % (fullname), "a")
719 metafile.write("%s\n" % (metamergestring))
722 # Create AP and SC Files when not merging
724 self.ptsCreateAPSCFiles(fullname+".ts")
726 except Exception, errormsg:
727 timeshift_saved = False
728 timeshift_saveerror1 = errormsg
730 # Hmpppf! Saving Timeshift via Hardlink-Method failed. Probably other device?
731 # Let's try to copy the file in background now! This might take a while ...
732 if not timeshift_saved:
734 stat = statvfs(config.usage.default_path.value)
735 freespace = stat.f_bfree / 1000 * stat.f_bsize / 1000
736 randomint = randint(1, 999)
738 if timeshiftfile is None:
739 # Get Filesize for Free Space Check
740 filesize = int(os_path.getsize("%s/%s" % (config.usage.timeshift_path.value,savefilename)) / (1024*1024))
742 # Save Current Event by copying it to the other device
743 if filesize <= freespace:
744 os_link("%s/%s" % (config.usage.timeshift_path.value,savefilename), "%s/%s.%s.copy" % (config.usage.timeshift_path.value,savefilename,randomint))
745 copy_file = savefilename
746 metafile = open("%s.ts.meta" % (fullname), "w")
747 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))
749 self.ptsCreateEITFile(fullname)
750 elif timeshiftfile.startswith("pts_livebuffer"):
751 # Get Filesize for Free Space Check
752 filesize = int(os_path.getsize("%s/%s" % (config.usage.timeshift_path.value, timeshiftfile)) / (1024*1024))
754 # Save stored timeshift by copying it to the other device
755 if filesize <= freespace:
756 os_link("%s/%s" % (config.usage.timeshift_path.value,timeshiftfile), "%s/%s.%s.copy" % (config.usage.timeshift_path.value,timeshiftfile,randomint))
757 copyfile("%s/%s.meta" % (config.usage.timeshift_path.value,timeshiftfile),"%s.ts.meta" % (fullname))
758 if fileExists("%s/%s.eit" % (config.usage.timeshift_path.value,timeshiftfile)):
759 copyfile("%s/%s.eit" % (config.usage.timeshift_path.value,timeshiftfile),"%s.eit" % (fullname))
760 copy_file = timeshiftfile
762 # Add merge-tag to metafile
764 metafile = open("%s.ts.meta" % (fullname), "a")
765 metafile.write("%s\n" % (metamergestring))
768 # Only copy file when enough disk-space available!
769 if filesize <= freespace:
770 timeshift_saved = True
771 copy_file = copy_file+"."+str(randomint)
773 # Get Event Info from meta file
774 if fileExists("%s.ts.meta" % (fullname)):
775 readmetafile = open("%s.ts.meta" % (fullname), "r")
776 servicerefname = readmetafile.readline()[0:-1]
777 eventname = readmetafile.readline()[0:-1]
781 JobManager.AddJob(CopyTimeshiftJob(self, "cp \"%s/%s.copy\" \"%s.ts\"" % (config.usage.timeshift_path.value,copy_file,fullname), copy_file, fullname, eventname))
782 if not Screens.Standby.inTryQuitMainloop and not Screens.Standby.inStandby and not mergelater and self.save_timeshift_postaction != "standby":
783 Notifications.AddNotification(MessageBox, _("Saving timeshift as movie now. This might take a while!"), MessageBox.TYPE_INFO, timeout=5)
785 timeshift_saved = False
786 timeshift_saveerror1 = ""
787 timeshift_saveerror2 = _("Not enough free Diskspace!\n\nFilesize: %sMB\nFree Space: %sMB\nPath: %s" % (filesize,freespace,config.usage.default_path.value))
789 except Exception, errormsg:
790 timeshift_saved = False
791 timeshift_saveerror2 = errormsg
793 if not timeshift_saved:
794 config.plugins.pts.isRecording.value = False
795 self.save_timeshift_postaction = None
796 errormessage = str(timeshift_saveerror1) + "\n" + str(timeshift_saveerror2)
797 Notifications.AddNotification(MessageBox, _("Timeshift save failed!")+"\n\n%s" % errormessage, MessageBox.TYPE_ERROR)
799 def ptsCleanTimeshiftFolder(self):
800 if not config.plugins.pts.enabled.value or self.ptsCheckTimeshiftPath() is False or self.session.screen["Standby"].boolean is True:
804 for filename in os_listdir(config.usage.timeshift_path.value):
805 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):
807 statinfo = os_stat("%s/%s" % (config.usage.timeshift_path.value,filename))
808 # if no write for 5 sec = stranded timeshift
809 if statinfo.st_mtime < (time()-5.0):
810 print "PTS-Plugin: Erasing stranded timeshift %s" % filename
811 self.eraseFile("%s/%s" % (config.usage.timeshift_path.value,filename))
813 # Delete Meta and EIT File too
814 if filename.startswith("pts_livebuffer.") is True:
815 self.BgFileEraser.erase("%s/%s.meta" % (config.usage.timeshift_path.value,filename))
816 self.BgFileEraser.erase("%s/%s.eit" % (config.usage.timeshift_path.value,filename))
818 print "PTS: IO-Error while cleaning Timeshift Folder ..."
820 def ptsGetEventInfo(self):
823 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
824 serviceHandler = eServiceCenter.getInstance()
825 info = serviceHandler.info(serviceref)
827 self.pts_curevent_servicerefname = serviceref.toString()
828 self.pts_curevent_station = info.getName(serviceref)
830 service = self.session.nav.getCurrentService()
831 info = service and service.info()
832 event = info and info.getEvent(0)
833 except Exception, errormsg:
834 Notifications.AddNotification(MessageBox, _("Getting Event Info failed!")+"\n\n%s" % errormsg, MessageBox.TYPE_ERROR, timeout=10)
836 if event is not None:
837 curEvent = parseEvent(event)
838 self.pts_curevent_begin = int(curEvent[0])
839 self.pts_curevent_end = int(curEvent[1])
840 self.pts_curevent_name = curEvent[2]
841 self.pts_curevent_description = curEvent[3]
842 self.pts_curevent_eventid = curEvent[4]
844 def ptsFrontpanelActions(self, action=None):
845 if self.session.nav.RecordTimer.isRecording() or SystemInfo.get("NumFrontpanelLEDs", 0) == 0:
849 if action == "start":
850 if fileExists("/proc/stb/fp/led_set_pattern"):
851 open("/proc/stb/fp/led_set_pattern", "w").write("0xa7fccf7a")
852 elif fileExists("/proc/stb/fp/led0_pattern"):
853 open("/proc/stb/fp/led0_pattern", "w").write("0x55555555")
854 if fileExists("/proc/stb/fp/led_pattern_speed"):
855 open("/proc/stb/fp/led_pattern_speed", "w").write("20")
856 elif fileExists("/proc/stb/fp/led_set_speed"):
857 open("/proc/stb/fp/led_set_speed", "w").write("20")
858 elif action == "stop":
859 if fileExists("/proc/stb/fp/led_set_pattern"):
860 open("/proc/stb/fp/led_set_pattern", "w").write("0")
861 elif fileExists("/proc/stb/fp/led0_pattern"):
862 open("/proc/stb/fp/led0_pattern", "w").write("0")
863 except Exception, errormsg:
864 print "PTS Plugin: %s" % (errormsg)
866 def ptsCreateHardlink(self):
867 for filename in os_listdir(config.usage.timeshift_path.value):
868 if filename.startswith("timeshift.") and not filename.endswith(".del") and not filename.endswith(".copy"):
870 statinfo = os_stat("%s/%s" % (config.usage.timeshift_path.value,filename))
871 if statinfo.st_mtime > (time()-5.0):
873 self.eraseFile("%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,self.pts_eventcount))
874 self.BgFileEraser.erase("%s/pts_livebuffer.%s.meta" % (config.usage.timeshift_path.value,self.pts_eventcount))
875 except Exception, errormsg:
876 print "PTS Plugin: %s" % (errormsg)
879 # Create link to pts_livebuffer file
880 os_link("%s/%s" % (config.usage.timeshift_path.value,filename), "%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,self.pts_eventcount))
883 metafile = open("%s/pts_livebuffer.%s.meta" % (config.usage.timeshift_path.value,self.pts_eventcount), "w")
884 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)))
886 except Exception, errormsg:
887 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)
890 self.ptsCreateEITFile("%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,self.pts_eventcount))
892 # Permanent Recording Hack
893 if config.plugins.pts.permanentrecording.value:
895 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)
896 os_link("%s/%s" % (config.usage.timeshift_path.value,filename), "%s.ts" % (fullname))
898 metafile = open("%s.ts.meta" % (fullname), "w")
899 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)))
901 except Exception, errormsg:
902 print "PTS Plugin: %s" % (errormsg)
903 except Exception, errormsg:
904 errormsg = str(errormsg)
905 if errormsg.find('Input/output error') != -1:
906 errormsg += _("\nAn Input/output error usually indicates a corrupted filesystem! Please check the filesystem of your timeshift-device!")
907 Notifications.AddNotification(MessageBox, _("Creating Hardlink to Timeshift file failed!")+"\n%s" % (errormsg), MessageBox.TYPE_ERROR)
909 def ptsRecordCurrentEvent(self):
910 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)
911 recording.dontSave = True
912 self.session.nav.RecordTimer.record(recording)
913 self.recording.append(recording)
915 def ptsMergeRecords(self):
916 if self.session.nav.RecordTimer.isRecording():
917 self.pts_mergeRecords_timer.start(120000, True)
922 ptsmergeeventname = ""
923 ptsgetnextfile = False
924 ptsfilemerged = False
926 filelist = os_listdir(config.usage.default_path.value)
928 if filelist is not None:
931 for filename in filelist:
932 if filename.endswith(".meta"):
933 # Get Event Info from meta file
934 readmetafile = open("%s/%s" % (config.usage.default_path.value,filename), "r")
935 servicerefname = readmetafile.readline()[0:-1]
936 eventname = readmetafile.readline()[0:-1]
937 eventtitle = readmetafile.readline()[0:-1]
938 eventtime = readmetafile.readline()[0:-1]
939 eventtag = readmetafile.readline()[0:-1]
943 ptsgetnextfile = False
944 ptsmergeSRC = filename[0:-5]
946 if ASCIItranslit.legacyEncode(eventname) == ASCIItranslit.legacyEncode(ptsmergeeventname):
948 if fileExists("%s/%s.eit" % (config.usage.default_path.value, ptsmergeSRC[0:-3])):
949 copyfile("%s/%s.eit" % (config.usage.default_path.value, ptsmergeSRC[0:-3]),"%s/%s.eit" % (config.usage.default_path.value, ptsmergeDEST[0:-3]))
951 # Delete AP and SC Files
952 self.BgFileEraser.erase("%s/%s.ap" % (config.usage.default_path.value, ptsmergeDEST))
953 self.BgFileEraser.erase("%s/%s.sc" % (config.usage.default_path.value, ptsmergeDEST))
955 # Add Merge Job to JobManager
956 JobManager.AddJob(MergeTimeshiftJob(self, "cat \"%s/%s\" >> \"%s/%s\"" % (config.usage.default_path.value,ptsmergeSRC,config.usage.default_path.value,ptsmergeDEST), ptsmergeSRC, ptsmergeDEST, eventname))
957 config.plugins.pts.isRecording.value = True
960 ptsgetnextfile = True
962 if eventtag == "pts_merge" and not ptsgetnextfile:
963 ptsgetnextfile = True
964 ptsmergeDEST = filename[0:-5]
965 ptsmergeeventname = eventname
966 ptsfilemerged = False
968 # If still recording or transfering, try again later ...
969 if fileExists("%s/%s" % (config.usage.default_path.value,ptsmergeDEST)):
970 statinfo = os_stat("%s/%s" % (config.usage.default_path.value,ptsmergeDEST))
971 if statinfo.st_mtime > (time()-10.0):
972 self.pts_mergeRecords_timer.start(120000, True)
975 # Rewrite Meta File to get rid of pts_merge tag
976 metafile = open("%s/%s.meta" % (config.usage.default_path.value,ptsmergeDEST), "w")
977 metafile.write("%s\n%s\n%s\n%i\n" % (servicerefname,eventname.replace("\n", ""),eventtitle.replace("\n", ""),int(eventtime)))
981 if not ptsfilemerged and ptsgetnextfile:
982 Notifications.AddNotification(MessageBox,_("PTS-Plugin: Merging records failed!"), MessageBox.TYPE_ERROR)
984 def ptsCreateAPSCFiles(self, filename):
985 if fileExists(filename, 'r'):
986 if fileExists(filename+".meta", 'r'):
987 # Get Event Info from meta file
988 readmetafile = open(filename+".meta", "r")
989 servicerefname = readmetafile.readline()[0:-1]
990 eventname = readmetafile.readline()[0:-1]
993 JobManager.AddJob(CreateAPSCFilesJob(self, "/usr/lib/enigma2/python/Plugins/Extensions/PermanentTimeshift/createapscfiles \"%s\"" % (filename), eventname))
995 self.ptsSaveTimeshiftFinished()
997 def ptsCreateEITFile(self, filename):
998 if self.pts_curevent_eventid is not None:
1001 serviceref = ServiceReference(self.session.nav.getCurrentlyPlayingServiceReference()).ref.toString()
1002 eitsave.SaveEIT(serviceref, filename+".eit", self.pts_curevent_eventid, -1, -1)
1003 except Exception, errormsg:
1004 print "PTS Plugin: %s" % (errormsg)
1006 def ptsCopyFilefinished(self, srcfile, destfile):
1008 if fileExists(srcfile):
1009 self.eraseFile(srcfile)
1011 # Restart Merge Timer
1012 if self.pts_mergeRecords_timer.isActive():
1013 self.pts_mergeRecords_timer.stop()
1014 self.pts_mergeRecords_timer.start(15000, True)
1016 # Create AP and SC Files
1017 self.ptsCreateAPSCFiles(destfile)
1019 def ptsMergeFilefinished(self, srcfile, destfile):
1020 if self.session.nav.RecordTimer.isRecording() or len(JobManager.getPendingJobs()) >= 1:
1021 # Rename files and delete them later ...
1022 self.pts_mergeCleanUp_timer.start(120000, True)
1023 os_system("echo \"\" > \"%s.pts.del\"" % (srcfile[0:-3]))
1025 # Delete Instant Record permanently now ... R.I.P.
1026 self.eraseFile("%s" % (srcfile))
1027 self.BgFileEraser.erase("%s.ap" % (srcfile))
1028 self.BgFileEraser.erase("%s.sc" % (srcfile))
1029 self.BgFileEraser.erase("%s.meta" % (srcfile))
1030 self.BgFileEraser.erase("%s.cuts" % (srcfile))
1031 self.BgFileEraser.erase("%s.eit" % (srcfile[0:-3]))
1033 # Create AP and SC Files
1034 self.ptsCreateAPSCFiles(destfile)
1036 # Run Merge-Process one more time to check if there are more records to merge
1037 self.pts_mergeRecords_timer.start(10000, True)
1039 def ptsSaveTimeshiftFinished(self):
1040 if not self.pts_mergeCleanUp_timer.isActive():
1041 self.ptsFrontpanelActions("stop")
1042 config.plugins.pts.isRecording.value = False
1044 if Screens.Standby.inTryQuitMainloop:
1045 self.pts_QuitMainloop_timer.start(30000, True)
1047 Notifications.AddNotification(MessageBox, _("Timeshift saved to your harddisk!"), MessageBox.TYPE_INFO, timeout = 5)
1049 def ptsMergePostCleanUp(self):
1050 if self.session.nav.RecordTimer.isRecording() or len(JobManager.getPendingJobs()) >= 1:
1051 config.plugins.pts.isRecording.value = True
1052 self.pts_mergeCleanUp_timer.start(120000, True)
1055 self.ptsFrontpanelActions("stop")
1056 config.plugins.pts.isRecording.value = False
1058 filelist = os_listdir(config.usage.default_path.value)
1059 for filename in filelist:
1060 if filename.endswith(".pts.del"):
1061 srcfile = config.usage.default_path.value + "/" + filename[0:-8] + ".ts"
1062 self.eraseFile("%s" % (srcfile))
1063 self.BgFileEraser.erase("%s.ap" % (srcfile))
1064 self.BgFileEraser.erase("%s.sc" % (srcfile))
1065 self.BgFileEraser.erase("%s.meta" % (srcfile))
1066 self.BgFileEraser.erase("%s.cuts" % (srcfile))
1067 self.BgFileEraser.erase("%s.eit" % (srcfile[0:-3]))
1068 self.BgFileEraser.erase("%s.pts.del" % (srcfile[0:-3]))
1070 # Restart QuitMainloop Timer to give BgFileEraser enough time
1071 if Screens.Standby.inTryQuitMainloop and self.pts_QuitMainloop_timer.isActive():
1072 self.pts_QuitMainloop_timer.start(60000, True)
1074 def ptsTryQuitMainloop(self):
1075 if Screens.Standby.inTryQuitMainloop and (len(JobManager.getPendingJobs()) >= 1 or self.pts_mergeCleanUp_timer.isActive()):
1076 self.pts_QuitMainloop_timer.start(60000, True)
1079 if Screens.Standby.inTryQuitMainloop and self.session.ptsmainloopvalue:
1080 self.session.dialog_stack = []
1081 self.session.summary_stack = [None]
1082 self.session.open(TryQuitMainloop, self.session.ptsmainloopvalue)
1084 def ptsGetSeekInfo(self):
1085 s = self.session.nav.getCurrentService()
1086 return s and s.seek()
1088 def ptsGetPosition(self):
1089 seek = self.ptsGetSeekInfo()
1092 pos = seek.getPlayPosition()
1097 def ptsGetLength(self):
1098 seek = self.ptsGetSeekInfo()
1101 length = seek.getLength()
1106 def ptsGetSaveTimeshiftStatus(self):
1107 return self.save_current_timeshift
1109 def ptsSeekPointerOK(self):
1110 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable():
1111 if not self.pvrstate_hide_timer.isActive():
1112 if self.seekstate != self.SEEK_STATE_PLAY:
1113 self.setSeekState(self.SEEK_STATE_PLAY)
1117 length = self.ptsGetLength()
1118 position = self.ptsGetPosition()
1120 if length is None or position is None:
1123 cur_pos = self.pvrStateDialog["PTSSeekPointer"].position
1124 jumptox = int(cur_pos[0]) - int(self.pts_seekpointer_MinX)
1125 jumptoperc = round((jumptox / 400.0) * 100, 0)
1126 jumptotime = int((length / 100) * jumptoperc)
1127 jumptodiff = position - jumptotime
1129 self.doSeekRelative(-jumptodiff)
1133 def ptsSeekPointerLeft(self):
1134 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable():
1135 self.ptsMoveSeekPointer(direction="left")
1139 def ptsSeekPointerRight(self):
1140 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable():
1141 self.ptsMoveSeekPointer(direction="right")
1145 def ptsSeekPointerReset(self):
1146 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled:
1147 self.pvrStateDialog["PTSSeekPointer"].setPosition(self.pts_seekpointer_MinX,self.pvrStateDialog["PTSSeekPointer"].position[1])
1149 def ptsSeekPointerSetCurrentPos(self):
1150 if not self.pts_pvrStateDialog == "PTSTimeshiftState" or not self.timeshift_enabled or not self.isSeekable():
1153 position = self.ptsGetPosition()
1154 length = self.ptsGetLength()
1157 tpixels = int((float(int((position*100)/length))/100)*400)
1158 self.pvrStateDialog["PTSSeekPointer"].setPosition(self.pts_seekpointer_MinX+tpixels, self.pvrStateDialog["PTSSeekPointer"].position[1])
1160 def ptsMoveSeekPointer(self, direction=None):
1161 if direction is None or self.pts_pvrStateDialog != "PTSTimeshiftState":
1165 cur_pos = self.pvrStateDialog["PTSSeekPointer"].position
1166 InfoBarTimeshiftState._mayShow(self)
1168 if direction == "left":
1169 minmaxval = self.pts_seekpointer_MinX
1171 if cur_pos[0]+movepixels > minmaxval:
1173 elif direction == "right":
1174 minmaxval = self.pts_seekpointer_MaxX
1176 if cur_pos[0]+movepixels < minmaxval:
1182 self.pvrStateDialog["PTSSeekPointer"].setPosition(cur_pos[0]+movepixels,cur_pos[1])
1184 self.pvrStateDialog["PTSSeekPointer"].setPosition(minmaxval,cur_pos[1])
1186 def ptsTimeshiftFileChanged(self):
1187 # Reset Seek Pointer
1188 if config.plugins.pts.enabled.value and config.plugins.pts.showinfobar.value:
1189 self.ptsSeekPointerReset()
1191 if self.pts_switchtolive:
1192 self.pts_switchtolive = False
1195 if self.pts_seektoprevfile:
1196 if self.pts_currplaying == 1:
1197 self.pts_currplaying = config.plugins.pts.maxevents.value
1199 self.pts_currplaying -= 1
1201 if self.pts_currplaying == config.plugins.pts.maxevents.value:
1202 self.pts_currplaying = 1
1204 self.pts_currplaying += 1
1206 if not fileExists("%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,self.pts_currplaying), 'r'):
1207 self.pts_currplaying = self.pts_eventcount
1209 # Set Eventname in PTS InfoBar
1210 if config.plugins.pts.enabled.value and config.plugins.pts.showinfobar.value and self.pts_pvrStateDialog == "PTSTimeshiftState":
1212 if self.pts_eventcount != self.pts_currplaying:
1213 readmetafile = open("%s/pts_livebuffer.%s.meta" % (config.usage.timeshift_path.value,self.pts_currplaying), "r")
1214 servicerefname = readmetafile.readline()[0:-1]
1215 eventname = readmetafile.readline()[0:-1]
1216 readmetafile.close()
1217 self.pvrStateDialog["eventname"].setText(eventname)
1219 self.pvrStateDialog["eventname"].setText("")
1220 except Exception, errormsg:
1221 self.pvrStateDialog["eventname"].setText("")
1223 # Get next pts file ...
1224 if self.pts_currplaying+1 > config.plugins.pts.maxevents.value:
1227 nextptsfile = self.pts_currplaying+1
1229 # Seek to previous file
1230 if self.pts_seektoprevfile:
1231 self.pts_seektoprevfile = False
1233 if fileExists("%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,nextptsfile), 'r'):
1234 self.ptsSetNextPlaybackFile("pts_livebuffer.%s" % (nextptsfile))
1236 self.ptsSeekBackHack()
1238 if fileExists("%s/pts_livebuffer.%s" % (config.usage.timeshift_path.value,nextptsfile), 'r') and nextptsfile <= self.pts_eventcount:
1239 self.ptsSetNextPlaybackFile("pts_livebuffer.%s" % (nextptsfile))
1240 if nextptsfile == self.pts_currplaying:
1241 self.pts_switchtolive = True
1242 self.ptsSetNextPlaybackFile("")
1244 def ptsSetNextPlaybackFile(self, nexttsfile):
1245 ts = self.getTimeshift()
1250 ts.setNextPlaybackFile("%s/%s" % (config.usage.timeshift_path.value,nexttsfile))
1252 print "PTS-Plugin: setNextPlaybackFile() not supported by OE. Enigma2 too old !?"
1254 def ptsSeekBackHack(self):
1255 if not config.plugins.pts.enabled.value or not self.timeshift_enabled:
1258 self.setSeekState(self.SEEK_STATE_PAUSE)
1259 self.doSeek(-90000*4) # seek ~4s before end
1260 self.pts_SeekBack_timer.start(1000, True)
1262 def ptsSeekBackTimer(self):
1263 if self.pts_lastseekspeed == 0:
1264 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1266 self.setSeekState(self.makeStateBackward(int(-self.pts_lastseekspeed)))
1268 def ptsCheckTimeshiftPath(self):
1269 if self.pts_pathchecked:
1272 if fileExists(config.usage.timeshift_path.value, 'w'):
1273 self.pts_pathchecked = True
1276 Notifications.AddNotification(MessageBox, _("Could not activate Permanent-Timeshift!\nTimeshift-Path does not exist"), MessageBox.TYPE_ERROR, timeout=15)
1277 if self.pts_delay_timer.isActive():
1278 self.pts_delay_timer.stop()
1279 if self.pts_cleanUp_timer.isActive():
1280 self.pts_cleanUp_timer.stop()
1283 def ptsTimerEntryStateChange(self, timer):
1284 if not config.plugins.pts.enabled.value or not config.plugins.pts.stopwhilerecording.value:
1287 self.pts_record_running = self.session.nav.RecordTimer.isRecording()
1289 # Abort here when box is in standby mode
1290 if self.session.screen["Standby"].boolean is True:
1293 # Stop Timeshift when Record started ...
1294 if timer.state == TimerEntry.StateRunning and self.timeshift_enabled and self.pts_record_running:
1295 if self.ptsLiveTVStatus() is False:
1296 self.timeshift_enabled = 0
1297 self.pts_LengthCheck_timer.stop()
1300 if self.seekstate != self.SEEK_STATE_PLAY:
1301 self.setSeekState(self.SEEK_STATE_PLAY)
1303 if self.isSeekable():
1304 Notifications.AddNotification(MessageBox,_("Record started! Stopping timeshift now ..."), MessageBox.TYPE_INFO, timeout=5)
1306 self.stopTimeshiftConfirmed(True, False)
1308 # Restart Timeshift when all records stopped
1309 if timer.state == TimerEntry.StateEnded and not self.timeshift_enabled and not self.pts_record_running:
1310 self.activatePermanentTimeshift()
1312 # Restart Merge-Timer when all records stopped
1313 if timer.state == TimerEntry.StateEnded and self.pts_mergeRecords_timer.isActive():
1314 self.pts_mergeRecords_timer.stop()
1315 self.pts_mergeRecords_timer.start(15000, True)
1317 # Restart FrontPanel LED when still copying or merging files
1318 # ToDo: Only do this on PTS Events and not events from other jobs
1319 if timer.state == TimerEntry.StateEnded and (len(JobManager.getPendingJobs()) >= 1 or self.pts_mergeRecords_timer.isActive()):
1320 self.ptsFrontpanelActions("start")
1321 config.plugins.pts.isRecording.value = True
1323 def ptsLiveTVStatus(self):
1324 service = self.session.nav.getCurrentService()
1325 info = service and service.info()
1326 sTSID = info and info.getInfo(iServiceInformation.sTSID) or -1
1328 if sTSID is None or sTSID == -1:
1333 def ptsLengthCheck(self):
1334 # Check if we are in TV Mode ...
1335 if self.ptsLiveTVStatus() is False:
1336 self.timeshift_enabled = 0
1337 self.pts_LengthCheck_timer.stop()
1340 if config.plugins.pts.stopwhilerecording.value and self.pts_record_running:
1344 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):
1345 if self.save_current_timeshift:
1346 self.saveTimeshiftActions("savetimeshift")
1347 self.activatePermanentTimeshift()
1348 self.save_current_timeshift = True
1350 self.activatePermanentTimeshift()
1351 Notifications.AddNotification(MessageBox,_("Maximum Timeshift length per Event reached!\nRestarting Timeshift now ..."), MessageBox.TYPE_INFO, timeout=5)
1353 #Replace the InfoBar with our version ;)
1354 Screens.InfoBar.InfoBar = InfoBar
1356 ################################
1357 ##### Class Standby Hack 1 #####
1358 ################################
1359 TryQuitMainloop_getRecordEvent = Screens.Standby.TryQuitMainloop.getRecordEvent
1361 class TryQuitMainloopPTS(TryQuitMainloop):
1362 def __init__(self, session, retvalue=1, timeout=-1, default_yes = True):
1363 TryQuitMainloop.__init__(self, session, retvalue, timeout, default_yes)
1365 self.session.ptsmainloopvalue = retvalue
1367 def getRecordEvent(self, recservice, event):
1368 if event == iRecordableService.evEnd and (config.plugins.pts.isRecording.value or len(JobManager.getPendingJobs()) >= 1):
1371 TryQuitMainloop_getRecordEvent(self, recservice, event)
1373 Screens.Standby.TryQuitMainloop = TryQuitMainloopPTS
1375 ################################
1376 ##### Class Standby Hack 2 #####
1377 ################################
1379 Screens_Standby_Standby = Screens.Standby.Standby
1381 class StandbyPTS(Standby):
1382 def __init__(self, session):
1383 if InfoBar and InfoBar.instance and InfoBar.ptsGetSaveTimeshiftStatus(InfoBar.instance):
1384 self.skin = """<screen position="0,0" size="0,0"/>"""
1385 Screen.__init__(self, session)
1386 self.onFirstExecBegin.append(self.showMessageBox)
1387 self.onHide.append(self.close)
1389 Standby.__init__(self, session)
1390 self.skinName = "Standby"
1392 def showMessageBox(self):
1393 if InfoBar and InfoBar.instance:
1394 InfoBar.saveTimeshiftActions(InfoBar.instance, postaction="standby")
1396 Screens.Standby.Standby = StandbyPTS
1401 InfoBarChannelSelection_zapUp = InfoBarChannelSelection.zapUp
1404 if self.pts_blockZap_timer.isActive():
1407 if self.save_current_timeshift and self.timeshift_enabled:
1408 InfoBar.saveTimeshiftActions(self, postaction="zapUp")
1410 InfoBarChannelSelection_zapUp(self)
1412 InfoBarChannelSelection.zapUp = zapUp
1417 InfoBarChannelSelection_zapDown = InfoBarChannelSelection.zapDown
1420 if self.pts_blockZap_timer.isActive():
1423 if self.save_current_timeshift and self.timeshift_enabled:
1424 InfoBar.saveTimeshiftActions(self, postaction="zapDown")
1426 InfoBarChannelSelection_zapDown(self)
1428 InfoBarChannelSelection.zapDown = zapDown
1433 InfoBarChannelSelection_historyBack = InfoBarChannelSelection.historyBack
1435 def historyBack(self):
1436 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable():
1437 InfoBarTimeshiftState._mayShow(self)
1438 self.pvrStateDialog["PTSSeekPointer"].setPosition(self.pts_seekpointer_MinX, self.pvrStateDialog["PTSSeekPointer"].position[1])
1439 if self.seekstate != self.SEEK_STATE_PLAY:
1440 self.setSeekState(self.SEEK_STATE_PLAY)
1441 self.ptsSeekPointerOK()
1442 elif self.save_current_timeshift and self.timeshift_enabled:
1443 InfoBar.saveTimeshiftActions(self, postaction="historyBack")
1445 InfoBarChannelSelection_historyBack(self)
1447 InfoBarChannelSelection.historyBack = historyBack
1452 InfoBarChannelSelection_historyNext = InfoBarChannelSelection.historyNext
1454 def historyNext(self):
1455 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable():
1456 InfoBarTimeshiftState._mayShow(self)
1457 self.pvrStateDialog["PTSSeekPointer"].setPosition(self.pts_seekpointer_MaxX, self.pvrStateDialog["PTSSeekPointer"].position[1])
1458 if self.seekstate != self.SEEK_STATE_PLAY:
1459 self.setSeekState(self.SEEK_STATE_PLAY)
1460 self.ptsSeekPointerOK()
1461 elif self.save_current_timeshift and self.timeshift_enabled:
1462 InfoBar.saveTimeshiftActions(self, postaction="historyNext")
1464 InfoBarChannelSelection_historyNext(self)
1466 InfoBarChannelSelection.historyNext = historyNext
1468 ######################
1469 #switchChannelUp Hack#
1470 ######################
1471 InfoBarChannelSelection_switchChannelUp = InfoBarChannelSelection.switchChannelUp
1473 def switchChannelUp(self):
1474 if self.save_current_timeshift and self.timeshift_enabled:
1475 InfoBar.saveTimeshiftActions(self, postaction="switchChannelUp")
1477 InfoBarChannelSelection_switchChannelUp(self)
1479 InfoBarChannelSelection.switchChannelUp = switchChannelUp
1481 ########################
1482 #switchChannelDown Hack#
1483 ########################
1484 InfoBarChannelSelection_switchChannelDown = InfoBarChannelSelection.switchChannelDown
1486 def switchChannelDown(self):
1487 if self.save_current_timeshift and self.timeshift_enabled:
1488 InfoBar.saveTimeshiftActions(self, postaction="switchChannelDown")
1490 InfoBarChannelSelection_switchChannelDown(self)
1492 InfoBarChannelSelection.switchChannelDown = switchChannelDown
1494 ######################
1495 #openServiceList Hack#
1496 ######################
1497 InfoBarChannelSelection_openServiceList = InfoBarChannelSelection.openServiceList
1499 def openServiceList(self):
1500 if self.save_current_timeshift and self.timeshift_enabled:
1501 InfoBar.saveTimeshiftActions(self, postaction="openServiceList")
1503 InfoBarChannelSelection_openServiceList(self)
1505 InfoBarChannelSelection.openServiceList = openServiceList
1507 ###########################
1508 #showRadioChannelList Hack#
1509 ###########################
1510 InfoBarChannelSelection_showRadioChannelList = InfoBarChannelSelection.showRadioChannelList
1512 def showRadioChannelList(self, zap=False):
1513 if self.save_current_timeshift and self.timeshift_enabled:
1514 InfoBar.saveTimeshiftActions(self, postaction="showRadioChannelList")
1516 InfoBarChannelSelection_showRadioChannelList(self, zap)
1518 InfoBarChannelSelection.showRadioChannelList = showRadioChannelList
1520 #######################
1521 #InfoBarNumberZap Hack#
1522 #######################
1523 InfoBarNumberZap_keyNumberGlobal = InfoBarNumberZap.keyNumberGlobal
1525 def keyNumberGlobal(self, number):
1526 if self.pts_pvrStateDialog == "PTSTimeshiftState" and self.timeshift_enabled and self.isSeekable() and number == 0:
1527 InfoBarTimeshiftState._mayShow(self)
1528 self.pvrStateDialog["PTSSeekPointer"].setPosition(self.pts_seekpointer_MaxX/2, self.pvrStateDialog["PTSSeekPointer"].position[1])
1529 if self.seekstate != self.SEEK_STATE_PLAY:
1530 self.setSeekState(self.SEEK_STATE_PLAY)
1531 self.ptsSeekPointerOK()
1534 if self.pts_blockZap_timer.isActive():
1537 if self.save_current_timeshift and self.timeshift_enabled:
1538 InfoBar.saveTimeshiftActions(self)
1541 InfoBarNumberZap_keyNumberGlobal(self, number)
1542 if number and config.plugins.pts.enabled.value and self.timeshift_enabled and not self.isSeekable():
1543 self.session.openWithCallback(self.numberEntered, NumberZap, number)
1545 InfoBarNumberZap.keyNumberGlobal = keyNumberGlobal
1547 #############################
1548 # getNextRecordingTime Hack #
1549 #############################
1550 RecordTimer_getNextRecordingTime = RecordTimer.getNextRecordingTime
1552 def getNextRecordingTime(self):
1553 nextrectime = RecordTimer_getNextRecordingTime(self)
1554 faketime = time()+300
1556 if config.plugins.pts.isRecording.value or len(JobManager.getPendingJobs()) >= 1:
1557 if nextrectime > 0 and nextrectime < faketime:
1564 RecordTimer.getNextRecordingTime = getNextRecordingTime
1566 ############################
1567 #InfoBarTimeshiftState Hack#
1568 ############################
1570 if self.execing and self.timeshift_enabled and self.isSeekable():
1571 InfoBar.ptsSeekPointerSetCurrentPos(self)
1572 self.pvrStateDialog.show()
1574 self.pvrstate_hide_timer = eTimer()
1575 self.pvrstate_hide_timer.callback.append(self.pvrStateDialog.hide)
1577 if self.seekstate == self.SEEK_STATE_PLAY:
1578 idx = config.usage.infobar_timeout.index
1581 self.pvrstate_hide_timer.start(idx*1000, True)
1583 self.pvrstate_hide_timer.stop()
1584 elif self.execing and self.timeshift_enabled and not self.isSeekable():
1585 self.pvrStateDialog.hide()
1587 InfoBarTimeshiftState._mayShow = _mayShow
1592 InfoBarSeek_seekBack = InfoBarSeek.seekBack
1595 InfoBarSeek_seekBack(self)
1596 self.pts_lastseekspeed = self.seekstate[1]
1598 InfoBarSeek.seekBack = seekBack
1600 ####################
1601 #instantRecord Hack#
1602 ####################
1603 InfoBarInstantRecord_instantRecord = InfoBarInstantRecord.instantRecord
1605 def instantRecord(self):
1606 if not config.plugins.pts.enabled.value or not self.timeshift_enabled:
1607 InfoBarInstantRecord_instantRecord(self)
1610 dir = preferredInstantRecordPath()
1611 if not dir or not fileExists(dir, 'w'):
1612 dir = defaultMoviePath()
1616 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1619 if self.isInstantRecordRunning():
1620 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1621 title=_("A recording is currently running.\nWhat do you want to do?"), \
1622 list=((_("stop recording"), "stop"), \
1623 (_("add recording (stop after current event)"), "event"), \
1624 (_("add recording (indefinitely)"), "indefinitely"), \
1625 (_("add recording (enter recording duration)"), "manualduration"), \
1626 (_("add recording (enter recording endtime)"), "manualendtime"), \
1627 (_("change recording (duration)"), "changeduration"), \
1628 (_("change recording (endtime)"), "changeendtime"), \
1629 (_("Timeshift")+" "+_("save recording (stop after current event)"), "savetimeshift"), \
1630 (_("Timeshift")+" "+_("save recording (Select event)"), "savetimeshiftEvent"), \
1631 (_("do nothing"), "no")))
1633 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1634 title=_("Start recording?"), \
1635 list=((_("add recording (stop after current event)"), "event"), \
1636 (_("add recording (indefinitely)"), "indefinitely"), \
1637 (_("add recording (enter recording duration)"), "manualduration"), \
1638 (_("add recording (enter recording endtime)"), "manualendtime"), \
1639 (_("Timeshift")+" "+_("save recording (stop after current event)"), "savetimeshift"), \
1640 (_("Timeshift")+" "+_("save recording (Select event)"), "savetimeshiftEvent"), \
1641 (_("don't record"), "no")))
1643 InfoBarInstantRecord.instantRecord = instantRecord
1645 #############################
1646 #recordQuestionCallback Hack#
1647 #############################
1648 InfoBarInstantRecord_recordQuestionCallback = InfoBarInstantRecord.recordQuestionCallback
1650 def recordQuestionCallback(self, answer):
1651 InfoBarInstantRecord_recordQuestionCallback(self, answer)
1653 if config.plugins.pts.enabled.value:
1654 if answer is not None and answer[1] == "savetimeshift":
1655 if InfoBarSeek.isSeekable(self) and self.pts_eventcount != self.pts_currplaying:
1656 InfoBar.SaveTimeshift(self, timeshiftfile="pts_livebuffer.%s" % self.pts_currplaying)
1658 Notifications.AddNotification(MessageBox,_("Timeshift will get saved at end of event!"), MessageBox.TYPE_INFO, timeout=5)
1659 self.save_current_timeshift = True
1660 config.plugins.pts.isRecording.value = True
1661 if answer is not None and answer[1] == "savetimeshiftEvent":
1662 InfoBar.saveTimeshiftEventPopup(self)
1664 if answer is not None and answer[1].startswith("pts_livebuffer") is True:
1665 InfoBar.SaveTimeshift(self, timeshiftfile=answer[1])
1667 InfoBarInstantRecord.recordQuestionCallback = recordQuestionCallback
1669 ############################
1670 ##### SETTINGS SCREEN #####
1671 ############################
1672 class PermanentTimeShiftSetup(Screen, ConfigListScreen):
1673 def __init__(self, session):
1674 Screen.__init__(self, session)
1675 self.skinName = [ "PTSSetup", "Setup" ]
1676 self.setup_title = _("Permanent Timeshift Settings")
1678 self.onChangedEntry = [ ]
1680 ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changedEntry)
1682 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
1684 "ok": self.SaveSettings,
1685 "green": self.SaveSettings,
1690 self["key_green"] = StaticText(_("OK"))
1691 self["key_red"] = StaticText(_("Cancel"))
1694 self.onLayoutFinish.append(self.layoutFinished)
1696 def layoutFinished(self):
1697 self.setTitle(self.setup_title)
1699 def createSetup(self):
1700 self.list = [ getConfigListEntry(_("Permanent Timeshift Enable"), config.plugins.pts.enabled) ]
1701 if config.plugins.pts.enabled.value:
1703 getConfigListEntry(_("Permanent Timeshift Max Events"), config.plugins.pts.maxevents),
1704 getConfigListEntry(_("Permanent Timeshift Max Length"), config.plugins.pts.maxlength),
1705 getConfigListEntry(_("Permanent Timeshift Start Delay"), config.plugins.pts.startdelay),
1706 getConfigListEntry(_("Timeshift-Save Action on zap"), config.plugins.pts.favoriteSaveAction),
1707 getConfigListEntry(_("Stop timeshift while recording?"), config.plugins.pts.stopwhilerecording),
1708 getConfigListEntry(_("Show PTS Infobar while timeshifting?"), config.plugins.pts.showinfobar)
1711 # Permanent Recording Hack
1712 if fileExists("/usr/lib/enigma2/python/Plugins/Extensions/HouseKeeping/plugin.py"):
1713 self.list.append(getConfigListEntry(_("Beta: Enable Permanent Recording?"), config.plugins.pts.permanentrecording))
1715 self["config"].list = self.list
1716 self["config"].setList(self.list)
1719 ConfigListScreen.keyLeft(self)
1720 if self["config"].getCurrent()[1] == config.plugins.pts.enabled:
1724 ConfigListScreen.keyRight(self)
1725 if self["config"].getCurrent()[1] == config.plugins.pts.enabled:
1728 def changedEntry(self):
1729 for x in self.onChangedEntry:
1732 def getCurrentEntry(self):
1733 return self["config"].getCurrent()[0]
1735 def getCurrentValue(self):
1736 return str(self["config"].getCurrent()[1].getText())
1738 def createSummary(self):
1741 def SaveSettings(self):
1742 config.plugins.pts.save()
1749 #################################################
1751 def startSetup(menuid):
1752 if menuid != "system":
1754 return [(_("Timeshift Settings"), PTSSetupMenu, "pts_setup", 50)]
1756 def PTSSetupMenu(session, **kwargs):
1757 session.open(PermanentTimeShiftSetup)
1759 def Plugins(path, **kwargs):
1760 return [ PluginDescriptor(name=_("Permanent Timeshift Settings"), description=_("Permanent Timeshift Settings"), where=PluginDescriptor.WHERE_MENU, fnc=startSetup) ]