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