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