revert local change (just for testing)
[enigma2.git] / lib / python / Screens / MediaPlayer.py
1 from enigma import eTimer, iPlayableService, eServiceCenter, iServiceInformation, eSize
2 from Screens.Screen import Screen
3 from Screens.MessageBox import MessageBox
4 from Components.ActionMap import NumberActionMap
5 from Components.Label import Label
6 from Components.Input import Input
7 from Components.GUIComponent import *
8 from Components.Pixmap import Pixmap
9 from Components.Label import Label
10 from Components.FileList import FileEntryComponent, FileList
11 from Components.MediaPlayer import PlayList, PlaylistEntryComponent
12 from Plugins.Plugin import PluginDescriptor
13 from Tools.Directories import resolveFilename, SCOPE_MEDIA, SCOPE_CONFIG, SCOPE_SKIN_IMAGE
14 from Components.ServicePosition import ServicePositionGauge
15 from Screens.ChoiceBox import ChoiceBox
16 from Components.ServiceEventTracker import ServiceEventTracker
17 from Components.Playlist import PlaylistIOInternal, PlaylistIOM3U, PlaylistIOPLS
18 from Screens.InfoBarGenerics import InfoBarSeek
19 from ServiceReference import ServiceReference
20 from Screens.ChoiceBox import ChoiceBox
21
22 import os
23
24 class MediaPlayer(Screen, InfoBarSeek):
25         ALLOW_SUSPEND = True
26
27         def __init__(self, session, args = None):
28                 Screen.__init__(self, session)
29                 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
30                 self.session.nav.stopService()
31
32                 self.playlistparsers = {}
33                 self.addPlaylistParser(PlaylistIOM3U, "m3u")
34                 self.addPlaylistParser(PlaylistIOPLS, "pls")
35                 self.addPlaylistParser(PlaylistIOInternal, "e2pls")
36
37                 self.filelist = FileList(resolveFilename(SCOPE_MEDIA), matchingPattern = "(?i)^.*\.(mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob)", useServiceRef = True)
38                 self["filelist"] = self.filelist
39
40                 self.playlist = PlayList()
41                 self["playlist"] = self.playlist
42
43                 self["PositionGauge"] = ServicePositionGauge(self.session.nav)
44                 
45                 self["currenttext"] = Label("")
46
47                 self["artisttext"] = Label(_("Artist:"))
48                 self["artist"] = Label("")
49                 self["titletext"] = Label(_("Title:"))
50                 self["title"] = Label("")
51                 self["albumtext"] = Label(_("Album:"))
52                 self["album"] = Label("")
53                 self["yeartext"] = Label(_("Year:"))
54                 self["year"] = Label("")
55                 self["genretext"] = Label(_("Genre:"))
56                 self["genre"] = Label("")
57                 self["coverArt"] = Pixmap()
58                 
59                 self.seek_target = None
60                 
61                 #self["text"] = Input("1234", maxSize=True, type=Input.NUMBER)
62
63                 class MoviePlayerActionMap(NumberActionMap):
64                         def __init__(self, player, contexts = [ ], actions = { }, prio=0):
65                                 NumberActionMap.__init__(self, contexts, actions, prio)
66                                 self.player = player
67
68                         def action(self, contexts, action):
69                                 self.player.show()
70                                 return NumberActionMap.action(self, contexts, action)
71
72                 self["actions"] = MoviePlayerActionMap(self, ["OkCancelActions", "DirectionActions", "NumberActions", "MediaPlayerSeekActions"],
73                 {
74                         "ok": self.ok,
75                         "cancel": self.exit,
76
77                         "right": self.rightDown,
78                         "rightRepeated": self.doNothing,
79                         "rightUp": self.rightUp,
80                         "left": self.leftDown,
81                         "leftRepeated": self.doNothing,
82                         "leftUp": self.leftUp,
83
84                         "up": self.up,
85                         "upRepeated": self.up,
86                         "down": self.down,
87                         "downRepeated": self.down,
88
89                         "play": self.playEntry,
90                         "pause": self.pauseEntry,
91                         "stop": self.stopEntry,
92
93                         "previous": self.previousEntry,
94                         "next": self.nextEntry,
95
96                         "menu": self.showMenu,
97
98                         "1": self.keyNumberGlobal,
99                         "2": self.keyNumberGlobal,
100                         "3": self.keyNumberGlobal,
101                         "4": self.keyNumberGlobal,
102                         "5": self.keyNumberGlobal,
103                         "6": self.keyNumberGlobal,
104                         "7": self.keyNumberGlobal,
105                         "8": self.keyNumberGlobal,
106                         "9": self.keyNumberGlobal,
107                         "0": self.keyNumberGlobal
108                 }, -2)
109
110                 InfoBarSeek.__init__(self)
111
112                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
113                         {
114                                 #iPlayableService.evStart: self.__serviceStarted,
115                                 #iPlayableService.evSeekableStatusChanged: InfoBarSeek.__seekableStatusChanged,
116
117                                 iPlayableService.evEOF: self.__evEOF,
118 #                               iPlayableService.evSOF: self.__evSOF,
119                         })
120
121                 self.onClose.append(self.delMPTimer)
122                 self.onClose.append(self.__onClose)
123
124                 self.righttimer = False
125                 self.rightKeyTimer = eTimer()
126                 self.rightKeyTimer.timeout.get().append(self.rightTimerFire)
127
128                 self.lefttimer = False
129                 self.leftKeyTimer = eTimer()
130                 self.leftKeyTimer.timeout.get().append(self.leftTimerFire)
131
132                 self.infoTimer = eTimer()
133                 self.infoTimer.timeout.get().append(self.infoTimerFire)
134                 self.infoTimer.start(500)
135
136                 self.currList = "filelist"
137
138                 self.coverArtFileName = ""
139
140                 self.playlistIOInternal = PlaylistIOInternal()
141                 list = self.playlistIOInternal.open(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
142                 if list:
143                         for x in list:
144                                 self.playlist.addFile(x.ref)
145                         self.playlist.updateList()
146
147         def doNothing(self):
148                 pass
149
150         def exit(self):
151                 self.playlistIOInternal.clear()
152                 for x in self.playlist.list:
153                         self.playlistIOInternal.addService(ServiceReference(x[0]))
154                 self.playlistIOInternal.save(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
155                 self.close()
156
157         def checkSkipShowHideLock(self):
158                 self.updatedSeekState()
159
160         def __evEOF(self):
161                 self.nextEntry()
162
163         def __onClose(self):
164                 self.session.nav.playService(self.oldService)
165
166         def delMPTimer(self):
167                 del self.rightKeyTimer
168                 del self.leftKeyTimer
169                 del self.infoTimer
170
171         def infoTimerFire(self):
172                 currPlay = self.session.nav.getCurrentService()
173                 if currPlay is not None:
174                         self.updateMusicInformation( artist = currPlay.info().getInfoString(iServiceInformation.sArtist),
175                                                                                  title = currPlay.info().getInfoString(iServiceInformation.sTitle),
176                                                                                  album = currPlay.info().getInfoString(iServiceInformation.sAlbum),
177                                                                                  genre = currPlay.info().getInfoString(iServiceInformation.sGenre),
178                                                                                  clear = True)
179                         self.updateCoverArtPixmap( currPlay.info().getName() )
180                 else:
181                         self.updateMusicInformation()
182                         self.updateCoverArtPixmap( "" )
183
184         def updateMusicInformation(self, artist = "", title = "", album = "", year = "", genre = "", clear = False):
185                 self.updateSingleMusicInformation("artist", artist, clear)
186                 self.updateSingleMusicInformation("title", title, clear)
187                 self.updateSingleMusicInformation("album", album, clear)
188                 self.updateSingleMusicInformation("year", year, clear)
189                 self.updateSingleMusicInformation("genre", genre, clear)
190
191         def updateSingleMusicInformation(self, name, info, clear):
192                 if info != "" or clear:
193                         if self[name].getText() != info:
194                                 self[name].setText(info)
195
196         def updateCoverArtPixmap(self, currentServiceName):
197                 filename = currentServiceName
198                 # The "getName" usually adds something like "MP3 File:" infront of filename
199                 # Get rid of this...by finding the first "/"
200                 filename = filename[filename.find("/"):]
201                 path = os.path.dirname(filename)
202                 pngname = path + "/" + "folder.png"
203                 if not os.path.exists(pngname):
204                         pngname = resolveFilename(SCOPE_SKIN_IMAGE, "no_coverArt.png")
205                 if self.coverArtFileName != pngname:
206                         self.coverArtFileName = pngname
207                         self["coverArt"].instance.setPixmapFromFile(self.coverArtFileName)
208
209         def fwdTimerFire(self):
210                 self.fwdKeyTimer.stop()
211                 self.fwdtimer = False
212                 self.nextEntry()
213
214         def rwdTimerFire(self):
215                 self.rwdKeyTimer.stop()
216                 self.rwdtimer = False
217                 self.previousEntry()
218
219         def leftDown(self):
220                 self.lefttimer = True
221                 self.leftKeyTimer.start(1000)
222
223         def rightDown(self):
224                 self.righttimer = True
225                 self.rightKeyTimer.start(1000)
226
227         def leftUp(self):
228                 if self.lefttimer:
229                         self.leftKeyTimer.stop()
230                         self.lefttimer = False
231                         self[self.currList].pageUp()
232
233         def rightUp(self):
234                 if self.righttimer:
235                         self.rightKeyTimer.stop()
236                         self.righttimer = False
237                         self[self.currList].pageDown()
238
239         def leftTimerFire(self):
240                 self.leftKeyTimer.stop()
241                 self.lefttimer = False
242                 self.switchToFileList()
243
244         def rightTimerFire(self):
245                 self.rightKeyTimer.stop()
246                 self.righttimer = False
247                 self.switchToPlayList()
248
249         def switchToFileList(self):
250                 self.currList = "filelist"
251                 self.filelist.selectionEnabled(1)
252                 self.playlist.selectionEnabled(0)
253                 self.updateCurrentInfo()
254
255         def switchToPlayList(self):
256                 if len(self.playlist) != 0:
257                         self.currList = "playlist"
258                         self.filelist.selectionEnabled(0)
259                         self.playlist.selectionEnabled(1)
260                         self.updateCurrentInfo()
261
262         def up(self):
263                 self[self.currList].up()
264                 self.updateCurrentInfo()
265
266         def down(self):
267                 self[self.currList].down()
268                 self.updateCurrentInfo()
269
270         def updateCurrentInfo(self):
271                 text = ""
272                 if self.currList == "filelist":
273                         if not self.filelist.canDescent():
274                                 text = self.filelist.getServiceRef().getPath()
275                 if self.currList == "playlist":
276                         text = self.playlist.getSelection().getPath()
277
278                 self["currenttext"].setText(os.path.basename(text))
279
280         def ok(self):
281                 if self.currList == "filelist":
282                         if self.filelist.canDescent():
283                                 self.filelist.descent()
284                                 self.updateCurrentInfo()
285                         else:
286                                 self.stopEntry()
287                                 self.playlist.clear()
288                                 self.copyDirectory(os.path.dirname(self.filelist.getSelection()[0].getPath()) + "/", recursive = False)
289                                 self.playServiceRefEntry(self.filelist.getServiceRef())
290                                 
291                 if self.currList == "playlist":
292                         selection = self["playlist"].getSelection()
293                         self.changeEntry(self.playlist.getSelectionIndex())
294
295         def keyNumberGlobal(self, number):
296                 if number == 5: # enable seeking
297                         if self.seek_target is None:
298                                 (len, pos) = self["PositionGauge"].get()
299                                 
300                                 if self.isSeekable() and len != 0:
301                                         self.seek_target = pos
302                         else:
303                                 self.seekAbsolute(self.seek_target)
304                                 self.seek_target = None
305                 elif number == 2: # abort
306                         self.seek_target = None
307                 elif (number == 4 or number == 6) and self.seek_target is not None:
308                         (len, pos) = self["PositionGauge"].get()
309                         
310                         if number == 4:
311                                 self.seek_target -= len / 10
312                         else:
313                                 self.seek_target += len / 10
314                         
315                         if self.seek_target > len * 9 / 10:
316                                 self.seek_target = len * 9 / 10
317                         
318                         if self.seek_target < 0:
319                                 self.seek_target = 0
320
321                 print "seek target is now", self.seek_target
322                 
323                 self.updateSeek()
324         
325         def updateSeek(self):
326                 if self.seek_target is None:
327                         self["PositionGauge"].seek_pointer = False
328                 else:
329                         self["PositionGauge"].seek_pointer = True
330                         self["PositionGauge"].seek_pointer_position = self.seek_target
331
332
333         def showMenu(self):
334                 menu = []
335                 if self.currList == "filelist":
336                         menu.append((_("switch to playlist"), "playlist"))
337                         if self.filelist.canDescent():
338                                 menu.append((_("add directory to playlist"), "copydir"))
339                         else:
340                                 menu.append((_("add file to playlist"), "copy"))
341                 else:
342                         menu.append((_("switch to filelist"), "filelist"))
343                         menu.append((_("delete"), "delete"))
344                         menu.append((_("clear playlist"), "clear"))
345                 menu.append((_("hide player"), "hide"));
346                 self.session.openWithCallback(self.menuCallback, ChoiceBox, title="", list=menu)
347
348         def menuCallback(self, choice):
349                 if choice is None:
350                         return
351
352                 if choice[1] == "copydir":
353                         self.copyDirectory(self.filelist.getSelection()[0])
354                 elif choice[1] == "copy":
355                         self.copyFile()
356                 elif choice[1] == "playlist":
357                         self.switchToPlayList()
358                 elif choice[1] == "filelist":
359                         self.switchToFileList()
360                 elif choice[1] == "delete":
361                         if self.playlist.getSelectionIndex() == self.playlist.getCurrentIndex():
362                                 self.stopEntry()
363                         self.deleteEntry()
364                 elif choice[1] == "clear":
365                         self.stopEntry()
366                         self.playlist.clear()
367                         self.switchToFileList()
368                 elif choice[1] == "hide":
369                         self.hide()
370
371         def copyDirectory(self, directory, recursive = True):
372                 print "copyDirectory", directory
373                 filelist = FileList(directory, useServiceRef = True, isTop = True)
374
375                 for x in filelist.getFileList():
376                         if x[0][1] == True: #isDir
377                                 if recursive:
378                                         self.copyDirectory(x[0][0])
379                         else:
380                                 self.playlist.addFile(x[0][0])
381                 self.playlist.updateList()
382
383         ADDPLAYLIST = 0
384         REPLACEPLAYLIST = 1
385
386         def copyFile(self):
387                 if self.filelist.getServiceRef().type == 4098: # playlist
388                         list = []
389                         list.append((_("Add files to playlist"), (self.ADDPLAYLIST, self.filelist.getServiceRef())))
390                         list.append((_("Replace current playlist"), (self.REPLACEPLAYLIST, self.filelist.getServiceRef())))
391                         self.session.openWithCallback(self.playlistCallback, ChoiceBox, title=_("You selected a playlist"), list = list)
392                 else:
393                         self.playlist.addFile(self.filelist.getServiceRef())
394                         self.playlist.updateList()
395                         if len(self.playlist) == 1:
396                                 self.changeEntry(0)
397
398         def addPlaylistParser(self, parser, extension):
399                 self.playlistparsers[extension] = parser
400
401         def playlistCallback(self, answer):
402                 if answer is not None:
403                         extension = answer[1][1].getPath()[answer[1][1].getPath().rfind('.') + 1:]
404                         print "extension:", extension
405                         if self.playlistparsers.has_key(extension):
406                                 playlist = self.playlistparsers[extension]()
407                                 if answer[1][0] == self.REPLACEPLAYLIST:
408                                         self.stopEntry()
409                                         self.playlist.clear()
410                                         self.switchToFileList()
411                                 if answer[1][0] == self.REPLACEPLAYLIST or answer[1][0] == self.ADDPLAYLIST:
412                                         list = playlist.open(answer[1][1].getPath())
413                                         for x in list:
414                                                 self.playlist.addFile(x.ref)
415
416
417         def nextEntry(self):
418                 next = self.playlist.getCurrentIndex() + 1
419                 if next < len(self.playlist):
420                         self.changeEntry(next)
421
422         def previousEntry(self):
423                 next = self.playlist.getCurrentIndex() - 1
424                 if next >= 0:
425                         self.changeEntry(next)
426
427         def deleteEntry(self):
428                 self.playlist.deleteFile(self.playlist.getSelectionIndex())
429                 self.playlist.updateList()
430                 if len(self.playlist) == 0:
431                         self.switchToFileList()
432
433         def changeEntry(self, index):
434                 self.playlist.setCurrentPlaying(index)
435                 self.playEntry()
436
437         def playServiceRefEntry(self, serviceref):
438                 serviceRefList = self.playlist.getServiceRefList()
439                 for count in range(len(serviceRefList)):
440                         if serviceRefList[count] == serviceref:
441                                 self.changeEntry(count)
442                                 break
443
444         def playEntry(self):
445                 if len(self.playlist.getServiceRefList()):
446                         currref = self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()]
447                         if self.session.nav.getCurrentlyPlayingServiceReference() is None or currref != self.session.nav.getCurrentlyPlayingServiceReference():
448                                 self.session.nav.playService(self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()])
449                                 info = eServiceCenter.getInstance().info(currref)
450                                 description = info and info.getInfoString(currref, iServiceInformation.sDescription) or ""
451                                 self["title"].setText(description)
452                         self.unPauseService()
453
454         def updatedSeekState(self):
455                 if self.seekstate == self.SEEK_STATE_PAUSE:
456                         self.playlist.pauseFile()
457                 elif self.seekstate == self.SEEK_STATE_PLAY:
458                         self.playlist.playFile()
459                 elif self.seekstate in ( self.SEEK_STATE_FF_2X,
460                                                                  self.SEEK_STATE_FF_4X,
461                                                                  self.SEEK_STATE_FF_8X,
462                                                                  self.SEEK_STATE_FF_32X,
463                                                                  self.SEEK_STATE_FF_64X,
464                                                                  self.SEEK_STATE_FF_128X):
465                         self.playlist.forwardFile()
466                 elif self.seekstate in ( self.SEEK_STATE_BACK_16X,
467                                                                  self.SEEK_STATE_BACK_32X,
468                                                                  self.SEEK_STATE_BACK_64X,
469                                                                  self.SEEK_STATE_BACK_128X,):
470                         self.playlist.rewindFile()
471
472         def pauseEntry(self):
473                 self.pauseService()
474
475         def stopEntry(self):
476                 self.playlist.stopFile()
477                 self.session.nav.playService(None)
478                 self.updateMusicInformation(clear=True)
479
480         def unPauseService(self):
481                 self.setSeekState(self.SEEK_STATE_PLAY)