Make seekFwd and seekBack work again
[enigma2-plugins.git] / vlcplayer / src / VlcPlayer.py
1 # -*- coding: ISO-8859-1 -*-
2 #===============================================================================
3 # VLC Player Plugin by A. Lštsch 2007
4 #                   modified by Volker Christian 2008
5 #
6 # This is free software; you can redistribute it and/or modify it under
7 # the terms of the GNU General Public License as published by the Free
8 # Software Foundation; either version 2, or (at your option) any later
9 # version.
10 #===============================================================================
11
12
13 from time import time
14
15 from enigma import iPlayableServicePtr
16 from enigma import iPlayableService
17 from enigma import iServiceInformation
18 from enigma import iSeekableService
19 from enigma import eServiceReference
20 from enigma import eServiceCenter
21 from enigma import eTimer
22 from Screens.Screen import Screen
23 from Screens.MessageBox import MessageBox
24 from Screens.InfoBarGenerics import InfoBarNotifications, InfoBarAudioSelection
25 from Components.Sources.Source import Source
26 from Components.ServiceEventTracker import ServiceEventTracker
27 from Components.ActionMap import ActionMap
28 from Components.config import config
29 from Screens.MinuteInput import MinuteInput
30
31
32 def isValidServiceId(id):
33         testSRef = eServiceReference(id, 0, "Just a TestReference")
34         info = eServiceCenter.getInstance().info(testSRef)
35         return info is not None
36
37 ENIGMA_SERVICEGS_ID = 0x1001
38 ENIGMA_SERVICETS_ID = 0x1002
39
40 ENIGMA_SERVICE_ID = 0
41
42 print "[VLC] Checking for buildin servicets ... ",
43 if isValidServiceId(ENIGMA_SERVICETS_ID):
44         print "yes"
45         ENIGMA_SERVICE_ID = ENIGMA_SERVICETS_ID
46         STOP_BEFORE_UNPAUSE = False
47 else:
48         print "no"
49         print "[VLC] Checking for existing and usable servicets.so ... ",
50         try:
51                 import servicets
52         except Exception, e:
53                 print e
54                 print "[VLC] Checking for usable gstreamer service ... ",
55                 if isValidServiceId(ENIGMA_SERVICEGS_ID):
56                         print "yes"
57                         ENIGMA_SERVICE_ID = ENIGMA_SERVICEGS_ID
58                         STOP_BEFORE_UNPAUSE = True
59                 else:
60                         print "no"
61                         print "[VLC] No valid VLC-Service found - VLC-streaming not supported"
62         else:
63                 print "yes"
64                 ENIGMA_SERVICE_ID = ENIGMA_SERVICETS_ID
65                 STOP_BEFORE_UNPAUSE = False
66
67 DEFAULT_VIDEO_PID = 0x44
68 DEFAULT_AUDIO_PID = 0x45
69
70
71 def isDvdUrl(url):
72         return url.startswith("dvd://") or url.startswith("dvdsimple://")
73
74
75 def splitDvdUrl(url):
76         pos = url.rfind("@", len(url)-8)
77         if pos > 0:
78                 track = url[pos+1:]
79                 url = url[0:pos]
80                 if track.find(":") >= 0:
81                         track, chapter = track.split(":")
82                 else:
83                         chapter = None
84         else:
85                 track, chapter = (None, None)
86         return (url, track, chapter)
87
88
89 class VlcService(Source, iPlayableServicePtr, iSeekableService):
90         refreshInterval = 3000
91
92         class Info(iServiceInformation):
93                 def __init__(self, name=""):
94                         self.name = name
95
96                 def getName(self):
97                         return self.name
98
99                 def getInfoObject(self, *args, **kwargs):
100                         return { }
101
102                 def getInfo(self, what):
103                         return -1
104
105                 def getInfoString(self, *args, **kwargs):
106                         return self.name
107
108                 def isPlayable(self):
109                         return True
110
111                 def getEvent(self, what):
112                         return None
113
114         def __init__(self, player):
115                 Source.__init__(self)
116                 self.__info = VlcService.Info()
117                 self.server = None
118                 self.service = self
119                 self.player = player
120                 self.lastrefresh = time()
121                 self.stats = None
122                 self.refreshTimer = eTimer()
123                 self.refreshTimer.timeout.get().append(self.__onRefresh)
124                 self.refreshTimer.start(self.refreshInterval)
125
126         def setName(self, name):
127                 i = name.rfind("/")
128                 if i >= 0:
129                         name = name[i+1:]
130                 i = name.rfind("\\")
131                 if i >= 0:
132                         name = name[i+1:]
133                 self.__info.name = name
134                 self.setChanged()
135
136         def setChanged(self):
137                 self.changed( (self.CHANGED_SPECIFIC, iPlayableService.evStart) )
138
139         def setServer(self, server):
140                 self.server = server
141
142         def __onRefresh(self):
143                 if self.server is None:
144                         self.stats = None
145                         return
146                 print "[VLC] refresh"
147                 try:
148                         self.stats = self.server.status()
149                         self.lastrefresh = time()
150                 except Exception, e:
151                         print e
152
153         def refresh(self):
154                 self.__onRefresh()
155
156         def info(self):
157                 return self.__info
158
159         # iSeekableService
160         def seek(self):
161                 return self
162
163         def getPlayPosition(self):
164                 if self.stats and self.stats.has_key("time"):
165                         pos = float(self.stats["time"])
166                         if self.player.state == VlcPlayer.STATE_PLAYING:
167                                 pos += time() - self.lastrefresh
168                         return (False, int(pos*90000))
169                 else:
170                         return (True, 0)
171
172         def getLength(self):
173                 if self.stats and self.stats.has_key("length"):
174                         return (False, int(self.stats["length"])*90000)
175                 else:
176                         return (True, 0)
177
178         # iPlayableService
179         def cueSheet(self): return None
180         def pause(self): return self.player
181         def audioTracks(self):
182                 return self.player.audioTracks()
183
184         def audioChannel(self): return None
185         def subServices(self): return None
186         def frontendInfo(self): return None
187         def timeshift(self): return None
188         def subtitle(self): return None
189         def audioDelay(self): return None
190         def rdsDecoder(self): return None
191         def stream(self): return None
192         def start(self):
193                 self.player.play()
194         def stop(self):
195                 self.player.stop()
196
197
198 class VlcPlayerSummary(Screen):
199         skin = """
200         <screen name="InfoBarMoviePlayerSummary" position="0,0" size="132,64">
201                 <widget source="session.CurrentService" render="Label" position="6,0" size="120,25" font="Regular;14" halign="center" valign="center" >
202                         <convert type="ServiceName">Name</convert>
203                 </widget>
204                 <widget source="session.CurrentService" render="Progress" position="16,27" size="100,5" borderWidth="1">
205                         <convert type="ServicePosition">Position</convert>
206                 </widget>
207                 <widget source="global.CurrentTime" render="Label" position="6,32" size="120,32" font="Regular;32" halign="center" valign="center">
208                         <convert type="ClockToText">Format:%H:%M</convert>
209                 </widget>
210                 <widget source="session.RecordState" render="FixedLabel" text=" " position="6,32" zPosition="1" size="120,32">
211                         <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
212                         <convert type="ConditionalShowHide">Blink</convert>
213                 </widget>
214         </screen>"""
215
216         def __init__(self, session, parent):
217                 Screen.__init__(self, session)
218                 self.skinName = "InfoBarMoviePlayerSummary"
219
220
221 class VlcPlayer(Screen, InfoBarNotifications, InfoBarAudioSelection):
222         screen_timeout = 5000
223
224         STATE_IDLE = 0
225         STATE_PLAYING = 1
226         STATE_PAUSED = 2
227
228         def __init__(self, session, server, currentList):
229                 Screen.__init__(self, session)
230                 InfoBarNotifications.__init__(self)
231                 InfoBarAudioSelection.__init__(self)
232                 self.server = server
233                 self.currentList = currentList
234                 self.skinName = "MoviePlayer"
235                 self.state = self.STATE_IDLE
236                 self.oldservice = self.session.screen["CurrentService"]
237                 self.oldNavService = self.session.nav.getCurrentlyPlayingServiceReference()
238                 self.session.nav.stopService()
239                 self.vlcservice = VlcService(self)
240                 self.session.screen["CurrentService"] = self.vlcservice
241                 self.hidetimer = eTimer()
242                 self.hidetimer.timeout.get().append(self.ok)
243                 self.onClose.append(self.__onClose)
244
245                 class VlcPlayerActionMap(ActionMap):
246                         def __init__(self, player, contexts = [ ], actions = { }, prio=0):
247                                 ActionMap.__init__(self, contexts, actions, prio)
248                                 self.player = player
249
250                         def action(self, contexts, action):
251                                 if action[:5] == "seek:":
252                                         time = int(action[5:])
253                                         self.player.seekRelative(time)
254                                         return 1
255                                 elif action[:8] == "seekdef:":
256                                         key = int(action[8:])
257                                         time = [-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
258                                                         -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
259                                                         -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value][key-1]
260                                         self.player.seekRelative(time)
261                                         return 1
262                                 else:
263                                         return ActionMap.action(self, contexts, action)
264
265                 self["actions"] = VlcPlayerActionMap(self, ["OkCancelActions", "TvRadioActions", "InfobarSeekActions", "MediaPlayerActions"],
266                 {
267                                 "ok": self.ok,
268                                 "cancel": self.stop,
269                                 "keyTV": self.stop,
270                                 "pauseService": self.pause,
271                                 "unPauseService": self.play,
272                                 "play": self.play,
273                                 "seekFwd": self.seekFwd,
274                                 "seekBack": self.seekBack,
275                                 "seekFwdDown": self.seekFwd,
276                                 "seekBackDown": self.seekBack,
277                                 "seekFwdManual": self.seekManual,
278                                 "seekBackManual": self.seekManual,
279                                 "next": self.playNextFile,
280                                 "previous": self.playPrevFile
281                         }, -2)
282
283                 print "[VLC] evEOF=%d" % iPlayableService.evEOF
284                 self.__event_tracker = ServiceEventTracker(screen = self, eventmap=
285                         {
286                                 iPlayableService.evEOF: self.__evEOF,
287                         })
288
289         def createSummary(self):
290                 print "[VLC] createSummary"
291                 return VlcPlayerSummary
292
293         def __onClose(self):
294                 self.session.screen["CurrentService"] = self.oldservice
295                 self.session.nav.playService(self.oldNavService)
296
297         def __evEOF(self):
298                 print "[VLC] Event EOF"
299                 self.stop()
300
301         def playfile(self, path, name):
302                 if self.state != self.STATE_IDLE:
303                         self.stopCurrent()
304                 self.filename = path
305                 self.vlcservice.setName(name)
306                 self.play()
307
308         def play(self):
309                 if self.state == self.STATE_PAUSED:
310                         self.unpause()
311                         return
312                 print "[VLC] setupStream: " + self.filename
313                 if ENIGMA_SERVICE_ID == 0:
314                         self.hide()
315                         self.session.open(
316                                         MessageBox, _("No valid Enigma-Service to play a VLC-Stream\nCheck your installation and try again!"), MessageBox.TYPE_ERROR
317                         )
318                         self.close()
319                         return
320                 try:
321                         url = self.server.playFile(self.filename, DEFAULT_VIDEO_PID, DEFAULT_AUDIO_PID)
322                         print "[VLC] url: " + url
323                 except Exception, e:
324                         self.hide()
325                         self.session.open(
326                                         MessageBox, _("Error with VLC server:\n%s" % e), MessageBox.TYPE_ERROR
327                         )
328                         self.close()
329                         return
330                 if url is not None:
331                         sref = eServiceReference(ENIGMA_SERVICE_ID, 0, url)
332                         print "sref valid=", sref.valid()
333                         sref.setData(0, DEFAULT_VIDEO_PID)
334                         sref.setData(1, DEFAULT_AUDIO_PID)
335                         self.session.nav.playService(sref)
336                         self.state = self.STATE_PLAYING
337                         if self.shown:
338                                 self.__setHideTimer()
339                         self.vlcservice.setServer(self.server)
340                         self.vlcservice.refresh()
341
342         def pause(self):
343                 print "[VLC] pause"
344                 if self.state == self.STATE_PLAYING:
345                         self.session.nav.pause(True)
346                         self.server.pause()
347                         self.state = self.STATE_PAUSED
348                         self.vlcservice.refresh()
349                         if not self.shown:
350                                 self.hidetimer.stop()
351                                 self.show()
352                 elif self.state == self.STATE_PAUSED:
353                         self.unpause()
354
355         def unpause(self):
356                 print "[VLC] unpause"
357                 try:
358                         self.server.seek("-2")
359                         self.server.play()
360                 except Exception, e:
361                         self.session.open(
362                                 MessageBox, _("Error with VLC server:\n%s" % e), MessageBox.TYPE_ERROR
363                         )
364                         self.stop()
365                         return
366                 if STOP_BEFORE_UNPAUSE:
367                         self.session.nav.stopService()
368                         sref = self.session.nav.getCurrentlyPlayingServiceReference()
369                         sref.setData(0, DEFAULT_VIDEO_PID)
370                         sref.setData(1, DEFAULT_AUDIO_PID)
371                         self.session.nav.playService(sref)
372                 else:
373                         self.session.nav.pause(False)
374                 self.state = self.STATE_PLAYING
375                 self.vlcservice.refresh()
376                 if self.shown:
377                         self.__setHideTimer()
378
379         def stopCurrent(self):
380                 print "[VLC] stopCurrent"
381                 self.session.nav.stopService()
382                 if self.state == self.STATE_IDLE:
383                         self.close()
384                         return
385                 try:
386                         self.server.stop()
387                         self.server.deleteCurrentTree()
388                 except Exception, e:
389                         self.session.open(
390                                 MessageBox, _("Error with VLC server:\n%s" % e), MessageBox.TYPE_ERROR
391                         )
392                 self.state = self.STATE_IDLE
393                 self.vlcservice.setServer(None)
394                 self.vlcservice.refresh()
395
396         def stop(self):
397                 print "[VLC] stop"
398                 self.stopCurrent()
399                 self.close()
400
401         def __setHideTimer(self):
402                 self.hidetimer.start(self.screen_timeout)
403
404         def showInfobar(self):
405                 self.vlcservice.refresh()
406                 self.show()
407                 if self.state == self.STATE_PLAYING:
408                         self.__setHideTimer()
409                 else:
410                         self.vlcservice.refreshTimer.start(self.vlcservice.refreshInterval)
411
412         def hideInfobar(self):
413                 self.hide()
414                 self.hidetimer.stop()
415                 self.vlcservice.refreshTimer.stop()
416
417         def ok(self):
418                 if self.shown:
419                         self.hideInfobar()
420                 else:
421                         self.showInfobar()
422
423         def playNextFile(self):
424                 print "[VLC] playNextFile"
425                 if isDvdUrl(self.filename):
426                         url, track, chapter = splitDvdUrl(self.filename)
427                         if track is None:
428                                 track = 1
429                         else:
430                                 track = int(track)
431                         if chapter is None:
432                                 chapter = 2
433                         else:
434                                 chapter = int(chapter) + 1
435                         url = "%s@%d:%d" % (url, track, chapter)
436                         self.playfile(url, "DVD")
437                         self.showInfobar()
438                 else:
439                         media, name = self.currentList.getNextFile()
440                         if media is None:
441                                 self.session.open(
442                                                 MessageBox, _("No more files in this directory"), MessageBox.TYPE_INFO
443                                 )
444                                 self.close()
445                         else:
446                                 self.playfile(media, name)
447                                 self.showInfobar()
448
449         def playPrevFile(self):
450                 print "[VLC] playPrevFile"
451                 if isDvdUrl(self.filename):
452                         url, track, chapter = splitDvdUrl(self.filename)
453                         if track is None:
454                                 track = 1
455                         else:
456                                 track = int(track)
457                         if chapter is not None and int(chapter) > 1:
458                                 chapter = int(chapter) - 1
459                         else:
460                                 chapter = 1
461                         url = "%s@%d:%d" % (url, track, chapter)
462                         self.playfile(url, "DVD")
463                         self.showInfobar()
464                 else:
465                         media, name = self.currentList.getPrevFile()
466                         if media is None:
467                                 self.session.open(
468                                                 MessageBox, _("No previous file in this directory"), MessageBox.TYPE_INFO
469                                         )
470                                 self.close()
471                         else:
472                                 self.playfile(media, name)
473                                 self.showInfobar()
474
475         def audioTracks(self):
476                 return self.session.nav.getCurrentService() and self.session.nav.getCurrentService().audioTracks();
477
478         def seekRelative(self, delta):
479                 """delta is seconds as integer number
480                 positive=forwards, negative=backwards"""
481                 if self.state != self.STATE_IDLE:
482                         if (delta >= 0):
483                                 self.server.seek("+" + str(delta))
484                         else:
485                                 self.server.seek(str(delta))
486                 self.vlcservice.refresh()
487                 if not self.shown:
488                         self.show()
489                         self.__setHideTimer()
490
491         def seekFwd(self):
492                 if isDvdUrl(self.filename):
493                         url, track, chapter = splitDvdUrl(self.filename)
494                         if track is None:
495                                 track = 2
496                         else:
497                                 track = int(track) + 1
498                         url = "%s@%d" % (url, track)
499                         self.playfile(url, "DVD")
500                         self.showInfobar()
501                 else:
502                         self.seekRelative(600)
503
504         def seekBack(self):
505                 if isDvdUrl(self.filename):
506                         url, track, chapter = splitDvdUrl(self.filename)
507                         if track is not None and int(track) > 2:
508                                 track = int(track) - 1
509                                 url = "%s@%d" % (url, track)
510                         else:
511                                 track = 1
512                         self.playfile(url, "DVD")
513                         self.showInfobar()
514                 else:
515                         self.seekRelative(-600)
516
517         def seekToMinute(self, minutes):
518                 self.server.seek(str(int(minutes)*60))
519                 self.vlcservice.refresh()
520                 if not self.shown:
521                         self.show()
522                         self.__setHideTimer()
523
524         def seekManual(self):
525                 self.session.openWithCallback(self.seekToMinute, MinuteInput)