Merge branch 'master' of fraxinas@git.opendreambox.org:/git/enigma2
[enigma2.git] / lib / python / Plugins / Extensions / MediaPlayer / plugin.py
1 from os import path as os_path, remove as os_remove, listdir as os_listdir
2 from time import strftime
3 from enigma import iPlayableService, eTimer, eServiceCenter, iServiceInformation, ePicLoad
4 from ServiceReference import ServiceReference
5 from Screens.Screen import Screen
6 from Screens.HelpMenu import HelpableScreen
7 from Screens.MessageBox import MessageBox
8 from Screens.InputBox import InputBox
9 from Screens.ChoiceBox import ChoiceBox
10 from Screens.InfoBarGenerics import InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSupport, InfoBarNotifications, InfoBarSubtitleSupport
11 from Components.ActionMap import NumberActionMap, HelpableActionMap
12 from Components.Label import Label
13 from Components.Pixmap import Pixmap,MultiPixmap
14 from Components.FileList import FileList
15 from Components.MediaPlayer import PlayList
16 from Components.ServicePosition import ServicePositionGauge
17 from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase
18 from Components.Playlist import PlaylistIOInternal, PlaylistIOM3U, PlaylistIOPLS
19 from Components.AVSwitch import AVSwitch
20 from Components.Harddisk import harddiskmanager
21 from Components.config import config
22 from Tools.Directories import fileExists, pathExists, resolveFilename, SCOPE_CONFIG, SCOPE_PLAYLIST, SCOPE_SKIN_IMAGE
23 from settings import MediaPlayerSettings
24 from Plugins.SystemPlugins.Hotplug.plugin import hotplugNotifier
25 import random
26
27 class MyPlayList(PlayList):
28         def __init__(self):
29                 PlayList.__init__(self)
30
31         def PlayListShuffle(self):
32                 random.shuffle(self.list)
33                 self.l.setList(self.list)
34                 self.currPlaying = -1
35                 self.oldCurrPlaying = -1
36
37 class MediaPixmap(Pixmap):
38         def __init__(self):
39                 Pixmap.__init__(self)
40                 self.coverArtFileName = ""
41                 self.picload = ePicLoad()
42                 self.picload.PictureData.get().append(self.paintCoverArtPixmapCB)
43                 self.coverFileNames = ["folder.png", "folder.jpg"]
44
45         def applySkin(self, desktop, screen):
46                 from Tools.LoadPixmap import LoadPixmap
47                 if self.skinAttributes is not None:
48                         for (attrib, value) in self.skinAttributes:
49                                 if attrib == "pixmap":
50                                         noCoverFile = value
51                                         break
52                 if noCoverFile is None:
53                         noCoverFile = resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/no_coverArt.png")
54                 self.noCoverPixmap = LoadPixmap(noCoverFile)
55                 return Pixmap.applySkin(self, desktop, screen)
56
57         def onShow(self):
58                 Pixmap.onShow(self)
59                 sc = AVSwitch().getFramebufferScale()
60                 #0=Width 1=Height 2=Aspect 3=use_cache 4=resize_type 5=Background(#AARRGGBB)
61                 self.picload.setPara((self.instance.size().width(), self.instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
62
63         def paintCoverArtPixmapCB(self, picInfo=None):
64                 ptr = self.picload.getData()
65                 if ptr != None:
66                         self.instance.setPixmap(ptr.__deref__())
67
68         def updateCoverArt(self, path):
69                 while not path.endswith("/"):
70                         path = path[:-1]
71                 new_coverArtFileName = None
72                 for filename in self.coverFileNames:
73                         if fileExists(path + filename):
74                                 new_coverArtFileName = path + filename
75                 if self.coverArtFileName != new_coverArtFileName:
76                         self.coverArtFileName = new_coverArtFileName
77                         if new_coverArtFileName:
78                                 self.picload.startDecode(self.coverArtFileName)
79                         else:
80                                 self.showDefaultCover()
81
82         def showDefaultCover(self):
83                 self.instance.setPixmap(self.noCoverPixmap)
84
85         def embeddedCoverArt(self):
86                 print "[embeddedCoverArt] found"
87                 self.coverArtFileName = "/tmp/.id3coverart"
88                 self.picload.startDecode(self.coverArtFileName)
89
90 class MediaPlayer(Screen, InfoBarBase, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSupport, InfoBarNotifications, InfoBarSubtitleSupport, HelpableScreen):
91         ALLOW_SUSPEND = True
92         ENABLE_RESUME_SUPPORT = True
93
94         def __init__(self, session, args = None):
95                 Screen.__init__(self, session)
96                 InfoBarAudioSelection.__init__(self)
97                 InfoBarCueSheetSupport.__init__(self, actionmap = "MediaPlayerCueSheetActions")
98                 InfoBarNotifications.__init__(self)
99                 InfoBarBase.__init__(self)
100                 InfoBarSubtitleSupport.__init__(self)
101                 HelpableScreen.__init__(self)
102                 self.summary = None
103                 self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
104                 self.session.nav.stopService()
105
106                 self.playlistparsers = {}
107                 self.addPlaylistParser(PlaylistIOM3U, "m3u")
108                 self.addPlaylistParser(PlaylistIOPLS, "pls")
109                 self.addPlaylistParser(PlaylistIOInternal, "e2pls")
110
111                 # 'None' is magic to start at the list of mountpoints
112                 defaultDir = config.mediaplayer.defaultDir.getValue()
113                 self.filelist = FileList(defaultDir, matchingPattern = "(?i)^.*\.(mp2|mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob|avi|mkv|mp4|dat|flac)", useServiceRef = True, additionalExtensions = "4098:m3u 4098:e2pls 4098:pls")
114                 self["filelist"] = self.filelist
115
116                 self.playlist = MyPlayList()
117                 self.is_closing = False
118                 self.delname = ""
119                 self["playlist"] = self.playlist
120
121                 self["PositionGauge"] = ServicePositionGauge(self.session.nav)
122
123                 self["currenttext"] = Label("")
124
125                 self["artisttext"] = Label(_("Artist:"))
126                 self["artist"] = Label("")
127                 self["titletext"] = Label(_("Title:"))
128                 self["title"] = Label("")
129                 self["albumtext"] = Label(_("Album:"))
130                 self["album"] = Label("")
131                 self["yeartext"] = Label(_("Year:"))
132                 self["year"] = Label("")
133                 self["genretext"] = Label(_("Genre:"))
134                 self["genre"] = Label("")
135                 self["coverArt"] = MediaPixmap()
136                 self["repeat"] = MultiPixmap()
137
138                 self.seek_target = None
139                 hotplugNotifier.append(self.hotplugCB)
140
141                 class MoviePlayerActionMap(NumberActionMap):
142                         def __init__(self, player, contexts = [ ], actions = { }, prio=0):
143                                 NumberActionMap.__init__(self, contexts, actions, prio)
144                                 self.player = player
145
146                         def action(self, contexts, action):
147                                 self.player.show()
148                                 return NumberActionMap.action(self, contexts, action)
149
150
151                 self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions", 
152                         {
153                                 "ok": (self.ok, _("add file to playlist")),
154                                 "cancel": (self.exit, _("exit mediaplayer")),
155                         }, -2)
156
157                 self["MediaPlayerActions"] = HelpableActionMap(self, "MediaPlayerActions", 
158                         {
159                                 "play": (self.xplayEntry, _("play entry")),
160                                 "pause": (self.pauseEntry, _("pause")),
161                                 "stop": (self.stopEntry, _("stop entry")),
162                                 "previous": (self.previousMarkOrEntry, _("play from previous mark or playlist entry")),
163                                 "next": (self.nextMarkOrEntry, _("play from next mark or playlist entry")),
164                                 "menu": (self.showMenu, _("menu")),
165                                 "skipListbegin": (self.skip_listbegin, _("jump to listbegin")),
166                                 "skipListend": (self.skip_listend, _("jump to listend")),
167                                 "prevBouquet": (self.switchToPlayList, _("switch to playlist")),
168                                 "nextBouquet": (self.switchToFileList, _("switch to filelist")),
169                                 "delete": (self.deletePlaylistEntry, _("delete playlist entry")),
170                                 "shift_stop": (self.clear_playlist, _("clear playlist")),
171                                 "shift_record": (self.playlist.PlayListShuffle, _("shuffle playlist")),
172                                 "subtitles": (self.subtitleSelection, _("Subtitle selection")),
173                         }, -2)
174
175                 self["InfobarEPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
176                         {
177                                 "showEventInfo": (self.showEventInformation, _("show event details")),
178                         })
179
180                 self["actions"] = MoviePlayerActionMap(self, ["DirectionActions"], 
181                 {
182                         "right": self.rightDown,
183                         "rightRepeated": self.doNothing,
184                         "rightUp": self.rightUp,
185                         "left": self.leftDown,
186                         "leftRepeated": self.doNothing,
187                         "leftUp": self.leftUp,
188
189                         "up": self.up,
190                         "upRepeated": self.up,
191                         "upUp": self.doNothing,
192                         "down": self.down,
193                         "downRepeated": self.down,
194                         "downUp": self.doNothing,
195                 }, -2)
196
197                 InfoBarSeek.__init__(self, actionmap = "MediaPlayerSeekActions")
198
199                 self.onClose.append(self.delMPTimer)
200                 self.onClose.append(self.__onClose)
201
202                 self.righttimer = False
203                 self.rightKeyTimer = eTimer()
204                 self.rightKeyTimer.callback.append(self.rightTimerFire)
205
206                 self.lefttimer = False
207                 self.leftKeyTimer = eTimer()
208                 self.leftKeyTimer.callback.append(self.leftTimerFire)
209
210                 self.currList = "filelist"
211                 self.isAudioCD = False
212                 self.AudioCD_albuminfo = {}
213                 self.cdAudioTrackFiles = []
214                 self.applySettings()
215
216                 self.playlistIOInternal = PlaylistIOInternal()
217                 list = self.playlistIOInternal.open(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
218                 if list:
219                         for x in list:
220                                 self.playlist.addFile(x.ref)
221                         self.playlist.updateList()
222
223                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
224                         {
225                                 iPlayableService.evUpdatedInfo: self.__evUpdatedInfo,
226                                 iPlayableService.evUser+11: self.__evDecodeError,
227                                 iPlayableService.evUser+12: self.__evPluginError,
228                                 iPlayableService.evUser+13: self["coverArt"].embeddedCoverArt
229                         })
230
231         def doNothing(self):
232                 pass
233
234         def createSummary(self):
235                 return MediaPlayerLCDScreen
236
237         def exit(self):
238                 self.playlistIOInternal.clear()
239                 for x in self.playlist.list:
240                         self.playlistIOInternal.addService(ServiceReference(x[0]))
241                 if self.savePlaylistOnExit:
242                         self.playlistIOInternal.save(resolveFilename(SCOPE_CONFIG, "playlist.e2pls"))
243                 if config.mediaplayer.saveDirOnExit.getValue():
244                         config.mediaplayer.defaultDir.setValue(self.filelist.getCurrentDirectory())
245                         config.mediaplayer.defaultDir.save()
246                 hotplugNotifier.remove(self.hotplugCB)
247                 del self["coverArt"].picload
248                 self.close()
249
250         def checkSkipShowHideLock(self):
251                 self.updatedSeekState()
252
253         def doEofInternal(self, playing):
254                 if playing:
255                         self.nextEntry()
256                 else:
257                         self.show()
258
259         def __onClose(self):
260                 self.session.nav.playService(self.oldService)
261
262         def __evUpdatedInfo(self):
263                 currPlay = self.session.nav.getCurrentService()
264                 currenttitle = currPlay.info().getInfo(iServiceInformation.sCurrentTitle)
265                 totaltitles = currPlay.info().getInfo(iServiceInformation.sTotalTitles)
266                 sTitle = currPlay.info().getInfoString(iServiceInformation.sTitle)
267                 print "[__evUpdatedInfo] title %d of %d (%s)" % (currenttitle, totaltitles, sTitle)
268                 self.readTitleInformation()
269
270         def __evDecodeError(self):
271                 currPlay = self.session.nav.getCurrentService()
272                 sVideoType = currPlay.info().getInfoString(iServiceInformation.sVideoType)
273                 print "[__evDecodeError] video-codec %s can't be decoded by hardware" % (sVideoType)
274                 self.session.open(MessageBox, _("This Dreambox can't decode %s video streams!") % sVideoType, type = MessageBox.TYPE_INFO,timeout = 20 )
275
276         def __evPluginError(self):
277                 currPlay = self.session.nav.getCurrentService()
278                 message = currPlay.info().getInfoString(iServiceInformation.sUser+12)
279                 print "[__evPluginError]" , message
280                 self.session.open(MessageBox, message, type = MessageBox.TYPE_INFO,timeout = 20 )
281
282         def delMPTimer(self):
283                 del self.rightKeyTimer
284                 del self.leftKeyTimer
285
286         def readTitleInformation(self):
287                 currPlay = self.session.nav.getCurrentService()
288                 if currPlay is not None:
289                         sTitle = currPlay.info().getInfoString(iServiceInformation.sTitle)
290                         sAlbum = currPlay.info().getInfoString(iServiceInformation.sAlbum)
291                         sGenre = currPlay.info().getInfoString(iServiceInformation.sGenre)
292                         sArtist = currPlay.info().getInfoString(iServiceInformation.sArtist)
293                         sYear = currPlay.info().getInfoString(iServiceInformation.sTimeCreate)
294
295                         if sTitle == "":
296                                 if not self.isAudioCD:
297                                         sTitle = currPlay.info().getName().split('/')[-1]
298                                 else:
299                                         sTitle = self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()].getName()
300
301                         if self.AudioCD_albuminfo:
302                                 if sAlbum == "" and "title" in self.AudioCD_albuminfo:
303                                         sAlbum = self.AudioCD_albuminfo["title"]
304                                 if sGenre == "" and "genre" in self.AudioCD_albuminfo:
305                                         sGenre = self.AudioCD_albuminfo["genre"]
306                                 if sArtist == "" and "artist" in self.AudioCD_albuminfo:
307                                         sArtist = self.AudioCD_albuminfo["artist"]
308                                 if "year" in self.AudioCD_albuminfo:
309                                         sYear = self.AudioCD_albuminfo["year"]
310
311                         self.updateMusicInformation( sArtist, sTitle, sAlbum, sYear, sGenre, clear = True )
312                 else:
313                         self.updateMusicInformation()
314
315         def updateMusicInformation(self, artist = "", title = "", album = "", year = "", genre = "", clear = False):
316                 self.updateSingleMusicInformation("artist", artist, clear)
317                 self.updateSingleMusicInformation("title", title, clear)
318                 self.updateSingleMusicInformation("album", album, clear)
319                 self.updateSingleMusicInformation("year", year, clear)
320                 self.updateSingleMusicInformation("genre", genre, clear)
321
322         def updateSingleMusicInformation(self, name, info, clear):
323                 if info != "" or clear:
324                         if self[name].getText() != info:
325                                 self[name].setText(info)
326
327         def leftDown(self):
328                 self.lefttimer = True
329                 self.leftKeyTimer.start(1000)
330
331         def rightDown(self):
332                 self.righttimer = True
333                 self.rightKeyTimer.start(1000)
334
335         def leftUp(self):
336                 if self.lefttimer:
337                         self.leftKeyTimer.stop()
338                         self.lefttimer = False
339                         self[self.currList].pageUp()
340                         self.updateCurrentInfo()
341
342         def rightUp(self):
343                 if self.righttimer:
344                         self.rightKeyTimer.stop()
345                         self.righttimer = False
346                         self[self.currList].pageDown()
347                         self.updateCurrentInfo()
348
349         def leftTimerFire(self):
350                 self.leftKeyTimer.stop()
351                 self.lefttimer = False
352                 self.switchToFileList()
353
354         def rightTimerFire(self):
355                 self.rightKeyTimer.stop()
356                 self.righttimer = False
357                 self.switchToPlayList()
358
359         def switchToFileList(self):
360                 self.currList = "filelist"
361                 self.filelist.selectionEnabled(1)
362                 self.playlist.selectionEnabled(0)
363                 self.updateCurrentInfo()
364
365         def switchToPlayList(self):
366                 if len(self.playlist) != 0:
367                         self.currList = "playlist"
368                         self.filelist.selectionEnabled(0)
369                         self.playlist.selectionEnabled(1)
370                         self.updateCurrentInfo()
371
372         def up(self):
373                 self[self.currList].up()
374                 self.updateCurrentInfo()
375
376         def down(self):
377                 self[self.currList].down()
378                 self.updateCurrentInfo()
379
380         def showAfterSeek(self):
381                 self.show()
382
383         def showAfterCuesheetOperation(self):
384                 self.show()
385
386         def hideAfterResume(self):
387                 self.hide()
388
389         def getIdentifier(self, ref):
390                 if self.isAudioCD:
391                         return ref.getName()
392                 else:
393                         text = ref.getPath()
394                         return text.split('/')[-1]
395
396         # FIXME: maybe this code can be optimized 
397         def updateCurrentInfo(self):
398                 text = ""
399                 if self.currList == "filelist":
400                         idx = self.filelist.getSelectionIndex()
401                         r = self.filelist.list[idx]
402                         text = r[1][7]
403                         if r[0][1] == True:
404                                 if len(text) < 2:
405                                         text += " "
406                                 if text[:2] != "..":
407                                         text = "/" + text
408                         self.summaries.setText(text,1)
409
410                         idx += 1
411                         if idx < len(self.filelist.list):
412                                 r = self.filelist.list[idx]
413                                 text = r[1][7]
414                                 if r[0][1] == True:
415                                         text = "/" + text
416                                 self.summaries.setText(text,3)
417                         else:
418                                 self.summaries.setText(" ",3)
419
420                         idx += 1
421                         if idx < len(self.filelist.list):
422                                 r = self.filelist.list[idx]
423                                 text = r[1][7]
424                                 if r[0][1] == True:
425                                         text = "/" + text
426                                 self.summaries.setText(text,4)
427                         else:
428                                 self.summaries.setText(" ",4)
429
430                         text = ""
431                         if not self.filelist.canDescent():
432                                 r = self.filelist.getServiceRef()
433                                 if r is None:
434                                         return
435                                 text = r.getPath()
436                                 self["currenttext"].setText(os_path.basename(text))
437
438                 if self.currList == "playlist":
439                         t = self.playlist.getSelection()
440                         if t is None:
441                                 return
442                         #display current selected entry on LCD
443                         text = self.getIdentifier(t)
444                         self.summaries.setText(text,1)
445                         self["currenttext"].setText(text)
446                         idx = self.playlist.getSelectionIndex()
447                         idx += 1
448                         if idx < len(self.playlist):
449                                 currref = self.playlist.getServiceRefList()[idx]
450                                 text = self.getIdentifier(currref)
451                                 self.summaries.setText(text,3)
452                         else:
453                                 self.summaries.setText(" ",3)
454
455                         idx += 1
456                         if idx < len(self.playlist):
457                                 currref = self.playlist.getServiceRefList()[idx]
458                                 text = self.getIdentifier(currref)
459                                 self.summaries.setText(text,4)
460                         else:
461                                 self.summaries.setText(" ",4)
462
463         def ok(self):
464                 if self.currList == "filelist":
465                         if self.filelist.canDescent():
466                                 self.filelist.descent()
467                                 self.updateCurrentInfo()
468                         else:
469                                 self.copyFile()
470
471                 if self.currList == "playlist":
472                         selection = self["playlist"].getSelection()
473                         self.changeEntry(self.playlist.getSelectionIndex())
474
475         def showMenu(self):
476                 menu = []
477                 if len(self.cdAudioTrackFiles):
478                         menu.insert(0,(_("Play Audio-CD..."), "audiocd"))
479                 if self.currList == "filelist":
480                         if self.filelist.canDescent():
481                                 menu.append((_("add directory to playlist"), "copydir"))
482                         else:
483                                 menu.append((_("add files to playlist"), "copyfiles"))
484                         menu.append((_("switch to playlist"), "playlist"))
485                         menu.append((_("delete file"), "deletefile"))
486                 else:
487                         menu.append((_("switch to filelist"), "filelist"))
488                         menu.append((_("shuffle playlist"), "shuffle"))
489                         menu.append((_("Delete entry"), "deleteentry"))
490                         menu.append((_("clear playlist"), "clear"))
491                 menu.append((_("hide player"), "hide"));
492                 menu.append((_("save playlist"), "saveplaylist"));
493                 menu.append((_("load playlist"), "loadplaylist"));
494                 menu.append((_("delete saved playlist"), "deleteplaylist"));
495                 menu.append((_("Edit settings"), "settings"))
496                 self.session.openWithCallback(self.menuCallback, ChoiceBox, title="", list=menu)
497
498         def menuCallback(self, choice):
499                 if choice is None:
500                         return
501
502                 if choice[1] == "copydir":
503                         self.copyDirectory(self.filelist.getSelection()[0])
504                 elif choice[1] == "copyfiles":
505                         self.stopEntry()
506                         self.playlist.clear()
507                         self.isAudioCD = False
508                         self.copyDirectory(os_path.dirname(self.filelist.getSelection()[0].getPath()) + "/", recursive = False)
509                         self.playServiceRefEntry(self.filelist.getServiceRef())
510                 elif choice[1] == "playlist":
511                         self.switchToPlayList()
512                 elif choice[1] == "filelist":
513                         self.switchToFileList()
514                 elif choice[1] == "deleteentry":
515                         if self.playlist.getSelectionIndex() == self.playlist.getCurrentIndex():
516                                 self.stopEntry()
517                         self.deleteEntry()
518                 elif choice[1] == "clear":
519                         self.clear_playlist()
520                 elif choice[1] == "hide":
521                         self.hide()
522                 elif choice[1] == "saveplaylist":
523                         self.save_playlist()
524                 elif choice[1] == "loadplaylist":
525                         self.load_playlist()
526                 elif choice[1] == "deleteplaylist":
527                         self.delete_saved_playlist()
528                 elif choice[1] == "shuffle":
529                         self.playlist.PlayListShuffle()
530                 elif choice[1] == "deletefile":
531                         self.deleteFile()
532                 elif choice[1] == "settings":
533                         self.session.openWithCallback(self.applySettings, MediaPlayerSettings, self)
534                 elif choice[1] == "audiocd":
535                         self.playAudioCD()
536
537         def playAudioCD(self):
538                 from enigma import eServiceReference
539                 from Plugins.Extensions.CDInfo.plugin import Query
540
541                 if len(self.cdAudioTrackFiles):
542                         self.playlist.clear()
543                         self.savePlaylistOnExit = False
544                         self.isAudioCD = True
545                         for file in self.cdAudioTrackFiles:
546                                 ref = eServiceReference(4097, 0, file)
547                                 self.playlist.addFile(ref)
548                         cdinfo = Query(self)
549                         cdinfo.scan()
550                         self.changeEntry(0)
551                         self.switchToPlayList()
552
553         def applySettings(self):
554                 self.savePlaylistOnExit = config.mediaplayer.savePlaylistOnExit.getValue()
555                 if config.mediaplayer.repeat.getValue() == True:
556                         self["repeat"].setPixmapNum(1)
557                 else:
558                         self["repeat"].setPixmapNum(0)
559
560         def showEventInformation(self):
561                 from Screens.EventView import EventViewSimple
562                 from ServiceReference import ServiceReference
563                 evt = self[self.currList].getCurrentEvent()
564                 if evt:
565                         self.session.open(EventViewSimple, evt, ServiceReference(self.getCurrent()))
566
567         # also works on filelist (?)
568         def getCurrent(self):
569                 return self["playlist"].getCurrent()
570
571         def deletePlaylistEntry(self):
572                 if self.currList == "playlist":
573                         if self.playlist.getSelectionIndex() == self.playlist.getCurrentIndex():
574                                 self.stopEntry()
575                         self.deleteEntry()
576
577         def skip_listbegin(self):
578                 if self.currList == "filelist":
579                         self.filelist.moveToIndex(0)
580                 else:
581                         self.playlist.moveToIndex(0)
582                 self.updateCurrentInfo()
583
584         def skip_listend(self):
585                 if self.currList == "filelist":
586                         idx = len(self.filelist.list)
587                         self.filelist.moveToIndex(idx - 1)
588                 else:
589                         self.playlist.moveToIndex(len(self.playlist)-1)
590                 self.updateCurrentInfo()
591
592         def save_playlist(self):
593                 self.session.openWithCallback(self.save_playlist2,InputBox, title=_("Please enter filename (empty = use current date)"),windowTitle = _("Save Playlist"))
594
595         def save_playlist2(self, name):
596                 if name is not None:
597                         name = name.strip()
598                         if name == "":
599                                 name = strftime("%y%m%d_%H%M%S")
600                         name += ".e2pls"
601                         self.playlistIOInternal.clear()
602                         for x in self.playlist.list:
603                                 self.playlistIOInternal.addService(ServiceReference(x[0]))
604                         self.playlistIOInternal.save(resolveFilename(SCOPE_PLAYLIST) + name)
605
606         def load_playlist(self):
607                 listpath = []
608                 playlistdir = resolveFilename(SCOPE_PLAYLIST)
609                 try:
610                         for i in os_listdir(playlistdir):
611                                 listpath.append((i,playlistdir + i))
612                 except IOError,e:
613                         print "Error while scanning subdirs ",e
614                 self.session.openWithCallback(self.PlaylistSelected, ChoiceBox, title=_("Please select a playlist..."), list = listpath)
615
616         def PlaylistSelected(self,path):
617                 if path is not None:
618                         self.clear_playlist()
619                         extension = path[0].rsplit('.',1)[-1]
620                         if self.playlistparsers.has_key(extension):
621                                 playlist = self.playlistparsers[extension]()
622                                 list = playlist.open(path[1])
623                                 for x in list:
624                                         self.playlist.addFile(x.ref)
625                         self.playlist.updateList()
626
627         def delete_saved_playlist(self):
628                 listpath = []
629                 playlistdir = resolveFilename(SCOPE_PLAYLIST)
630                 try:
631                         for i in os_listdir(playlistdir):
632                                 listpath.append((i,playlistdir + i))
633                 except IOError,e:
634                         print "Error while scanning subdirs ",e
635                 self.session.openWithCallback(self.DeletePlaylistSelected, ChoiceBox, title=_("Please select a playlist to delete..."), list = listpath)
636
637         def DeletePlaylistSelected(self,path):
638                 if path is not None:
639                         self.delname = path[1]
640                         self.session.openWithCallback(self.deleteConfirmed, MessageBox, _("Do you really want to delete %s?") % (path[1]))
641
642         def deleteConfirmed(self, confirmed):
643                 if confirmed:
644                         try:
645                                 os_remove(self.delname)
646                         except OSError,e:
647                                 print "delete failed:", e
648                                 self.session.open(MessageBox, _("Delete failed!"), MessageBox.TYPE_ERROR)
649
650         def clear_playlist(self):
651                 self.isAudioCD = False
652                 self.stopEntry()
653                 self.playlist.clear()
654                 self.switchToFileList()
655
656         def copyDirectory(self, directory, recursive = True):
657                 print "copyDirectory", directory
658                 filelist = FileList(directory, useServiceRef = True, isTop = True)
659
660                 for x in filelist.getFileList():
661                         if x[0][1] == True: #isDir
662                                 if recursive:
663                                         self.copyDirectory(x[0][0])
664                         elif filelist.getServiceRef() and filelist.getServiceRef().type == 4097:
665                                 self.playlist.addFile(x[0][0])
666                 self.playlist.updateList()
667
668         def deleteFile(self):
669                 if self.currList == "filelist":
670                         self.service = self.filelist.getServiceRef()
671                 else:
672                         self.service = self.playlist.getSelection()
673                 if self.service is None:
674                         return
675                 if self.service.type != 4098 and self.session.nav.getCurrentlyPlayingServiceReference() is not None:
676                         if self.service == self.session.nav.getCurrentlyPlayingServiceReference():
677                                 self.stopEntry()
678
679                 serviceHandler = eServiceCenter.getInstance()
680                 offline = serviceHandler.offlineOperations(self.service)
681                 info = serviceHandler.info(self.service)
682                 name = info and info.getName(self.service)
683                 result = False
684                 if offline is not None:
685                         # simulate first
686                         if not offline.deleteFromDisk(1):
687                                 result = True
688                 if result == True:
689                         self.session.openWithCallback(self.deleteConfirmed_offline, MessageBox, _("Do you really want to delete %s?") % (name))
690                 else:
691                         self.session.openWithCallback(self.close, MessageBox, _("You cannot delete this!"), MessageBox.TYPE_ERROR)      
692
693         def deleteConfirmed_offline(self, confirmed):
694                 if confirmed:
695                         serviceHandler = eServiceCenter.getInstance()
696                         offline = serviceHandler.offlineOperations(self.service)
697                         result = False
698                         if offline is not None:
699                                 # really delete!
700                                 if not offline.deleteFromDisk(0):
701                                         result = True
702                         if result == False:
703                                 self.session.open(MessageBox, _("Delete failed!"), MessageBox.TYPE_ERROR)
704                         else:
705                                 self.removeListEntry()
706
707         def removeListEntry(self):
708                 currdir = self.filelist.getCurrentDirectory()
709                 self.filelist.changeDir(currdir)
710                 deleteend = False
711                 while not deleteend:
712                         index = 0
713                         deleteend = True
714                         if len(self.playlist) > 0:
715                                 for x in self.playlist.list:
716                                         if self.service == x[0]:
717                                                 self.playlist.deleteFile(index)
718                                                 deleteend = False
719                                                 break
720                                         index += 1
721                 self.playlist.updateList()
722                 if self.currList == "playlist":
723                         if len(self.playlist) == 0:
724                                 self.switchToFileList()
725
726         def copyFile(self):
727                 if self.filelist.getServiceRef().type == 4098: # playlist
728                         ServiceRef = self.filelist.getServiceRef()
729                         extension = ServiceRef.getPath()[ServiceRef.getPath().rfind('.') + 1:]
730                         if self.playlistparsers.has_key(extension):
731                                 playlist = self.playlistparsers[extension]()
732                                 list = playlist.open(ServiceRef.getPath())
733                                 for x in list:
734                                         self.playlist.addFile(x.ref)
735                         self.playlist.updateList()
736                 else:
737                         self.playlist.addFile(self.filelist.getServiceRef())
738                         self.playlist.updateList()
739                         if len(self.playlist) == 1:
740                                 self.changeEntry(0)
741
742         def addPlaylistParser(self, parser, extension):
743                 self.playlistparsers[extension] = parser
744
745         def nextEntry(self):
746                 next = self.playlist.getCurrentIndex() + 1
747                 if next < len(self.playlist):
748                         self.changeEntry(next)
749                 elif ( len(self.playlist) > 0 ) and ( config.mediaplayer.repeat.getValue() == True ):
750                         self.stopEntry()
751                         self.changeEntry(0)
752
753         def nextMarkOrEntry(self):
754                 if not self.jumpPreviousNextMark(lambda x: x):
755                         next = self.playlist.getCurrentIndex() + 1
756                         if next < len(self.playlist):
757                                 self.changeEntry(next)
758                         else:
759                                 self.doSeek(-1)
760
761         def previousMarkOrEntry(self):
762                 if not self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True):
763                         next = self.playlist.getCurrentIndex() - 1
764                         if next >= 0:
765                                 self.changeEntry(next)
766
767         def deleteEntry(self):
768                 self.playlist.deleteFile(self.playlist.getSelectionIndex())
769                 self.playlist.updateList()
770                 if len(self.playlist) == 0:
771                         self.switchToFileList()
772
773         def changeEntry(self, index):
774                 self.playlist.setCurrentPlaying(index)
775                 self.playEntry()
776
777         def playServiceRefEntry(self, serviceref):
778                 serviceRefList = self.playlist.getServiceRefList()
779                 for count in range(len(serviceRefList)):
780                         if serviceRefList[count] == serviceref:
781                                 self.changeEntry(count)
782                                 break
783                         
784         def xplayEntry(self):
785                 if self.currList == "playlist":
786                         self.playEntry()
787                 else:
788                         self.stopEntry()
789                         self.playlist.clear()
790                         self.isAudioCD = False
791                         sel = self.filelist.getSelection()
792                         if sel:
793                                 if sel[1]: # can descent
794                                         # add directory to playlist
795                                         self.copyDirectory(sel[0])
796                                 else:
797                                         # add files to playlist
798                                         self.copyDirectory(os_path.dirname(sel[0].getPath()) + "/", recursive = False)
799                         if len(self.playlist) > 0:
800                                 self.changeEntry(0)
801         
802         def playEntry(self):
803                 if len(self.playlist.getServiceRefList()):
804                         needsInfoUpdate = False
805                         currref = self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()]
806                         if self.session.nav.getCurrentlyPlayingServiceReference() is None or currref != self.session.nav.getCurrentlyPlayingServiceReference():
807                                 self.session.nav.playService(self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()])
808                                 info = eServiceCenter.getInstance().info(currref)
809                                 description = info and info.getInfoString(currref, iServiceInformation.sDescription) or ""
810                                 self["title"].setText(description)
811                                 # display just playing musik on LCD
812                                 idx = self.playlist.getCurrentIndex()
813                                 currref = self.playlist.getServiceRefList()[idx]
814                                 text = self.getIdentifier(currref)
815                                 text = ">"+text
816                                 ext = text[-4:].lower()
817
818                                 # FIXME: the information if the service contains video (and we should hide our window) should com from the service instead 
819                                 if ext not in [".mp2", ".mp3", ".wav", ".ogg", "flac"] and not self.isAudioCD:
820                                         self.hide()
821                                 else:
822                                         needsInfoUpdate = True
823                                 self.summaries.setText(text,1)
824
825                                 # get the next two entries
826                                 idx += 1
827                                 if idx < len(self.playlist):
828                                         currref = self.playlist.getServiceRefList()[idx]
829                                         text = self.getIdentifier(currref)
830                                         self.summaries.setText(text,3)
831                                 else:
832                                         self.summaries.setText(" ",3)
833
834                                 idx += 1
835                                 if idx < len(self.playlist):
836                                         currref = self.playlist.getServiceRefList()[idx]
837                                         text = self.getIdentifier(currref)
838                                         self.summaries.setText(text,4)
839                                 else:
840                                         self.summaries.setText(" ",4)
841                         else:
842                                 idx = self.playlist.getCurrentIndex()
843                                 currref = self.playlist.getServiceRefList()[idx]
844                                 text = currref.getPath()
845                                 ext = text[-4:].lower()
846                                 if ext not in [".mp2", ".mp3", ".wav", ".ogg", "flac"] and not self.isAudioCD:
847                                         self.hide()
848                                 else:
849                                         needsInfoUpdate = True
850
851                         self.unPauseService()
852                         if needsInfoUpdate == True:
853                                 path = self.playlist.getServiceRefList()[self.playlist.getCurrentIndex()].getPath()
854                                 self["coverArt"].updateCoverArt(path)
855                         else:
856                                 self["coverArt"].showDefaultCover()
857                         self.readTitleInformation()
858
859         def updatedSeekState(self):
860                 if self.seekstate == self.SEEK_STATE_PAUSE:
861                         self.playlist.pauseFile()
862                 elif self.seekstate == self.SEEK_STATE_PLAY:
863                         self.playlist.playFile()
864                 elif self.isStateForward(self.seekstate):
865                         self.playlist.forwardFile()
866                 elif self.isStateBackward(self.seekstate):
867                         self.playlist.rewindFile()
868
869         def pauseEntry(self):
870                 self.pauseService()
871                 if self.seekstate == self.SEEK_STATE_PAUSE:
872                         self.show()
873                 else:
874                         self.hide()
875
876         def stopEntry(self):
877                 self.playlist.stopFile()
878                 self.session.nav.playService(None)
879                 self.updateMusicInformation(clear=True)
880                 self.show()
881
882         def unPauseService(self):
883                 self.setSeekState(self.SEEK_STATE_PLAY)
884                 
885         def subtitleSelection(self):
886                 from Screens.Subtitles import Subtitles
887                 self.session.open(Subtitles)
888         
889         def hotplugCB(self, dev, media_state):
890                 if dev == harddiskmanager.getCD():
891                         if media_state == "1":
892                                 from Components.Scanner import scanDevice
893                                 devpath = harddiskmanager.getAutofsMountpoint(harddiskmanager.getCD())
894                                 self.cdAudioTrackFiles = []
895                                 res = scanDevice(devpath)
896                                 list = [ (r.description, r, res[r], self.session) for r in res ]
897                                 if list:
898                                         (desc, scanner, files, session) = list[0]
899                                         for file in files:
900                                                 if file.mimetype == "audio/x-cda":
901                                                         self.cdAudioTrackFiles.append(file.path)
902                         else:
903                                 self.cdAudioTrackFiles = []
904                                 if self.isAudioCD:
905                                         self.clear_playlist()
906
907 class MediaPlayerLCDScreen(Screen):
908         skin = """
909         <screen position="0,0" size="132,64" title="LCD Text">
910                 <widget name="text1" position="4,0" size="132,35" font="Regular;16"/>
911                 <widget name="text3" position="4,36" size="132,14" font="Regular;10"/>
912                 <widget name="text4" position="4,49" size="132,14" font="Regular;10"/>
913         </screen>"""
914
915         def __init__(self, session, parent):
916                 Screen.__init__(self, session)
917                 self["text1"] = Label("Mediaplayer")
918                 self["text3"] = Label("")
919                 self["text4"] = Label("")
920
921         def setText(self, text, line):
922                 if len(text) > 10:
923                         if text[-4:] == ".mp3":
924                                 text = text[:-4]
925                 textleer = "    "
926                 text = text + textleer*10
927                 if line == 1:
928                         self["text1"].setText(text)
929                 elif line == 3:
930                         self["text3"].setText(text)
931                 elif line == 4:
932                         self["text4"].setText(text)
933
934 def main(session, **kwargs):
935         session.open(MediaPlayer)
936
937 def menu(menuid, **kwargs):
938         if menuid == "mainmenu":
939                 return [(_("Media player"), main, "media_player", 45)]
940         return []
941
942 def filescan_open(list, session, **kwargs):
943         from enigma import eServiceReference
944
945         mp = session.open(MediaPlayer)
946         mp.playlist.clear()
947         mp.savePlaylistOnExit = False
948
949         for file in list:
950                 if file.mimetype == "video/MP2T":
951                         stype = 1
952                 else:
953                         stype = 4097
954                 ref = eServiceReference(stype, 0, file.path)
955                 mp.playlist.addFile(ref)
956
957         mp.changeEntry(0)
958         mp.switchToPlayList()
959
960 def audioCD_open(list, session, **kwargs):
961         from enigma import eServiceReference
962
963         mp = session.open(MediaPlayer)
964         mp.cdAudioTrackFiles = []
965         for file in list:
966                 mp.cdAudioTrackFiles.append(file.path)
967         mp.playAudioCD()
968
969 def filescan(**kwargs):
970         from Components.Scanner import Scanner, ScanPath
971         mediatypes = [
972                 Scanner(mimetypes = ["video/mpeg", "video/MP2T", "video/x-msvideo"],
973                         paths_to_scan =
974                                 [
975                                         ScanPath(path = "", with_subdirs = False),
976                                 ],
977                         name = "Movie",
978                         description = "View Movies...",
979                         openfnc = filescan_open,
980                 ),
981                 Scanner(mimetypes = ["video/x-vcd"],
982                         paths_to_scan =
983                                 [
984                                         ScanPath(path = "mpegav", with_subdirs = False),
985                                         ScanPath(path = "MPEGAV", with_subdirs = False),
986                                 ],
987                         name = "Video CD",
988                         description = "View Video CD...",
989                         openfnc = filescan_open,
990                 ),
991                 Scanner(mimetypes = ["audio/mpeg", "audio/x-wav", "application/ogg", "audio/x-flac"],
992                         paths_to_scan =
993                                 [
994                                         ScanPath(path = "", with_subdirs = False),
995                                 ],
996                         name = "Music",
997                         description = "Play Music...",
998                         openfnc = filescan_open,
999                 )]
1000         try:
1001                 from Plugins.Extensions.CDInfo.plugin import Query
1002                 mediatypes.append(
1003                 Scanner(mimetypes = ["audio/x-cda"],
1004                         paths_to_scan =
1005                                 [
1006                                         ScanPath(path = "", with_subdirs = False),
1007                                 ],
1008                         name = "Audio-CD",
1009                         description = "Play Audio-CD...",
1010                         openfnc = audioCD_open,
1011                 ))
1012                 return mediatypes
1013         except ImportError:
1014                 return mediatypes
1015
1016 from Plugins.Plugin import PluginDescriptor
1017 def Plugins(**kwargs):
1018         return [
1019                 PluginDescriptor(name = "MediaPlayer", description = "Play back media files", where = PluginDescriptor.WHERE_MENU, fnc = menu),
1020                 PluginDescriptor(name = "MediaPlayer", where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)
1021         ]