4.3.0r13
[enigma2.git] / usr / lib / enigma2 / python / Plugins / Extensions / MediaCenter / AudioPlayer.py
1 from enigma import iPlayableService, iServiceInformation
2 from Components.config import config
3 from Components.ActionMap import HelpableActionMap
4 from Components.Label import Label
5 from Components.ServiceEventTracker import ServiceEventTracker
6
7 from Screens.ChoiceBox import ChoiceBox
8 from Screens.InfoBarGenerics import InfoBarNotifications
9 from Screens.MessageBox import MessageBox
10
11 from Helpers import EasyPixmap
12 from MediaCore import MediaCore, mediaCore
13 from Playlist import Playlist
14 from DatabasePlaylist import DatabasePlaylist
15 from PlaylistPlayer import PlaylistPlayer
16
17 from Tools.Log import Log
18
19 from os import path as os_path
20 from Components.Sources.StaticText import StaticText
21
22 class MediaPixmap(EasyPixmap):
23         def folderCoverArtPath(self, path):
24                 if path.startswith("/"):
25                         path = "%s/" %(os_path.dirname(path))
26                         self.setPicturePath(path)
27
28         def embeddedCoverArt(self):
29                 Log.i("found")
30                 self.pictureFileName = "/tmp/.id3coverart"
31                 self.picload.startDecode(self.pictureFileName)
32
33 class AudioPlayer(PlaylistPlayer, InfoBarNotifications):
34         SEEK_STATE_PLAY = 0
35         SEEK_STATE_PAUSE = 1
36         SEEK_STATE_IDLE = 2
37
38         ITEM_CLEAR_PLAYLIST = 0
39         ITEM_SAVE_PLAYLIST = 1
40         ITEM_LOAD_PLAYLIST = 2
41         ITEM_CREATE_PLAYLIST = 3
42
43         skin = """
44                 <screen position="center,80" size="1200,610" title="Audio Player">
45                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,5" size="200,40" alphatest="on" />
46                         <ePixmap pixmap="skin_default/buttons/green.png" position="210,5" size="200,40" alphatest="on" />
47                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="410,5" size="200,40" alphatest="on" />
48                         <ePixmap pixmap="skin_default/buttons/blue.png" position="610,5" size="200,40" alphatest="on" />
49                         <widget name="red" position="10,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
50                         <widget name="green" position="210,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
51                         <widget name="yellow" position="410,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1"  shadowColor="black" shadowOffset="-2,-2" />
52                         <widget name="blue" position="610,5" size="200,40" zPosition="1" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" shadowColor="black" shadowOffset="-2,-2" />
53                         <widget source="global.CurrentTime" render="Label" position="1130,12" size="60,25" font="Regular;22" halign="right" backgroundColor="background" shadowColor="black" shadowOffset="-2,-2" transparent="1">
54                                 <convert type="ClockToText">Default</convert>
55                         </widget>
56                         <widget source="global.CurrentTime" render="Label" position="820,12" size="300,25" font="Regular;22" halign="right" backgroundColor="background" shadowColor="black" shadowOffset="-2,-2" transparent="1">
57                                 <convert type="ClockToText">Format:%A %d. %B</convert>
58                         </widget>
59                         <eLabel position="10,50" size="1180,1" backgroundColor="grey" />
60                         <eLabel position="380,50" size="1,555" backgroundColor="grey" />
61                         <widget name="coverArt" position="40,55" size="300,300" pixmap="skin_default/no_coverArt.png" />
62                         <widget name="artist" position="30,490" size="330,25" font="Regular;22" backgroundColor="background"/>
63                         <widget name="album" position="30,520" size="330,25" font="Regular;20" />
64                         <widget name="genre" position="30,550" size="330,25" font="Regular;20" />
65                         <widget name="year" position="30,580" size="330,25" font="Regular;20" />
66                         <widget name="title" position="30,365" size="330,50" halign="center" valign="center" font="Regular;22" backgroundColor="background" />
67                         <eLabel position="30,433" size="330,2" backgroundColor="grey" />
68                         <widget source="session.CurrentService" render="Progress" position="30,430" size="330,8" zPosition="1" pixmap="skin_default/progress.png" transparent="1">
69                                 <convert type="ServicePosition">Position</convert>
70                         </widget>
71                         <widget source="session.CurrentService" render="Label" position="30,450" size="70,20" font="Regular;18" noWrap="1">
72                                 <convert type="ServicePosition">Position</convert>
73                         </widget>
74                         <widget source="session.CurrentService" render="Label" position="290,450" size="70,20" font="Regular;18" halign="right" noWrap="1">
75                                 <convert type="ServicePosition">Length</convert>
76                         </widget>
77                         <widget source="playlist" render="Listbox" position="400,70" size="790,530" scrollbarMode="showOnDemand">
78                                 <convert type="TemplatedMultiContent">
79                                         {"templates":{
80                                                 "default": (53, [
81                                                         MultiContentEntryPixmapAlphaTest(pos = (1, 1), size = (53, 53), png = 6),
82                                                         MultiContentEntryPixmapAlphaTest(pos = (740, 3), size = (20, 20), png = 1),
83                                                         MultiContentEntryText(pos = (60, 1), size = (650, 28), font = 0, flags = RT_VALIGN_CENTER, text = 2),
84                                                         MultiContentEntryText(pos = (60, 30), size = (290, 22), font = 1, flags = RT_VALIGN_CENTER, text = 3,color=0xa0a0a0),
85                                                         MultiContentEntryText(pos = (355, 30), size = (290, 22), font = 1, flags = RT_VALIGN_CENTER, text = 4,color=0xa0a0a0),
86                                                         MultiContentEntryText(pos = (670, 30), size = (100, 22), font = 1, flags = RT_VALIGN_CENTER|RT_HALIGN_RIGHT, text = 5,color=0xa0a0a0),
87                                                 ]),
88                                                 "simple": (53, [
89                                                         MultiContentEntryPixmapAlphaTest(pos = (19, 19), size = (16, 16), png = 1),
90                                                         MultiContentEntryText(pos = (25, 0), size = (760, 53), font = 0, flags = RT_VALIGN_CENTER, text = 2),
91                                                 ]),
92                                                 },
93                                         "fonts": [gFont("Regular", 22), gFont("Regular", 18)]
94                                         }
95                                 </convert>
96                         </widget>
97                 </screen>"""
98
99         def __init__(self, session, playlist):
100                 PlaylistPlayer.__init__(self, session, "audio", MediaCore.TYPE_AUDIO)
101                 InfoBarNotifications.__init__(self)
102
103                 self._playlist = None
104                 self._initPlaylist(config.plugins.mediacenter.audio.last_playlist_id.value)
105
106                 self.seekState = self.SEEK_STATE_IDLE
107                 self._repeat = Playlist.REPEAT_NONE
108                 self._browser = None
109
110                 self["artisttext"] = Label(_("Artist") + ':')
111                 self["artist"] = Label("")
112                 self["titletext"] = Label(_("Title") + ':')
113                 self["title"] = Label("")
114                 self["albumtext"] = Label(_("Album") + ':')
115                 self["album"] = Label("")
116                 self["yeartext"] = Label(_("Year") + ':')
117                 self["year"] = Label("")
118                 self["genretext"] = Label(_("Genre") + ':')
119                 self["genre"] = Label("")
120                 self["coverArt"] = MediaPixmap()
121
122                 self["red"] = Label(_("Remove"))
123                 self["green"] = Label(_("Add"))
124                 self["yellow"] = Label(_("Shuffle - Off"))
125                 self["blue"] = Label(_("Repeat - None"))
126                 self._summary_list = StaticText("")
127                 self["summary_list"] = self._summary_list
128
129                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
130                         {
131                                 iPlayableService.evUpdatedInfo: self._evUpdatedInfo,
132                                 iPlayableService.evEOF: self._evEOF,
133                                 iPlayableService.evUser + 10: self._evAudioDecodeError,
134                                 iPlayableService.evUser + 11: self._evVideoDecodeError,
135                                 iPlayableService.evUser + 12: self._evPluginError,
136                                 iPlayableService.evUser + 13: self["coverArt"].embeddedCoverArt
137                         })
138
139                 self["MediaCenterActions"] = HelpableActionMap(self, "MediaCenterActions",
140                 {
141                         "menu" : (self._showMenu, _("Open Menu")),
142                         "ok" : (self.play, _("Play Selected Entry")),
143                         "cancel" : (self._close, _("Close MediaCenter Audioplayer")),
144                         "red" : (self._removeSelectedPlaylistEntry, _("Remove Selected Entry From Playlist")),
145                         "green" : (self.editPlaylist, _("Add file(s) to Playlist")),
146                         "yellow" : (self._toggleShuffle, _("Toggle Shuffle")),
147                         "blue" : (self._toggleRepeat, _("Toggle Repeat-Modes (None / All / Single)")),
148                         "playpause": (self.playpause, _("Play/Pause")),
149                         "stop": (self.stop, _("Stop Playback")),
150                         "next": (self.playNext, _("Play Next Entry")),
151                         "previous":  (self.playPrev, _("Play Previous Entry")),
152                         "moveUp": self._moveUp,
153                         "moveDown" : self._moveDown,
154                         "pageUp": self._pageUp,
155                         "pageDown": self._pageDown,
156                         "channelUp": (self._moveSelectedUp, _("Move selected item up")),
157                         "channelDown": (self._moveSelectedDown, _("Move selected item down")),
158                 }, -2);
159
160                 if playlist != None:
161                         self.addAllToPlaylist(playlist)
162
163         def _evEOF(self):
164                 self.playNext()
165
166         def _evUpdatedInfo(self):
167                 self._readTitleInformation()
168
169         def _evAudioDecodeError(self):
170                 currPlay = self.session.nav.getCurrentService()
171                 sTagAudioCodec = currPlay.info().getInfoString(iServiceInformation.sTagAudioCodec)
172                 Log.i("audio-codec %s can't be decoded by hardware" % (sTagAudioCodec))
173                 self.session.open(MessageBox, _("This Dreambox can't decode %s streams!") % sTagAudioCodec, type=MessageBox.TYPE_INFO, timeout=20)
174
175         def _evVideoDecodeError(self):
176                 currPlay = self.session.nav.getCurrentService()
177                 sTagVideoCodec = currPlay.info().getInfoString(iServiceInformation.sTagVideoCodec)
178                 Log.i("video-codec %s can't be decoded by hardware" % (sTagVideoCodec))
179                 self.session.open(MessageBox, _("This Dreambox can't decode %s streams!") % sTagVideoCodec, type=MessageBox.TYPE_INFO, timeout=20)
180
181         def _evPluginError(self):
182                 currPlay = self.session.nav.getCurrentService()
183                 message = currPlay.info().getInfoString(iServiceInformation.sUser + 12)
184                 Log.i(message)
185                 self.session.open(MessageBox, message, type=MessageBox.TYPE_INFO, timeout=20)
186
187         def _showMenu(self):
188                 menu = ((_("Save Playlist"), self.ITEM_SAVE_PLAYLIST),
189                                 (_("Load Playlist"), self.ITEM_LOAD_PLAYLIST),
190                                 (_("Clear Playlist"), self.ITEM_CLEAR_PLAYLIST),
191                                 (_("Create New Playlist"), self.ITEM_CREATE_PLAYLIST),
192                         )
193                 self.session.openWithCallback(self._onMenuItemSelected, ChoiceBox, list=menu, windowTitle=_("MediaCenter - Menu"))
194
195         def _onMenuItemSelected(self, entry):
196                 if entry != None:
197                         item = entry[1]
198                         if item is self.ITEM_CLEAR_PLAYLIST:
199                                 self.clearPlaylist()
200                         elif item is self.ITEM_LOAD_PLAYLIST:
201                                 self.session.openWithCallback(self._onPlaylistSelected, DatabasePlaylist.PlaylistChoice, MediaCore.TYPE_AUDIO)
202                         elif item is self.ITEM_SAVE_PLAYLIST:
203                                 if self._playlist.save():
204                                         self.session.open(MessageBox, _("Playlist has been saved!"), type=MessageBox.TYPE_INFO)
205                                 else:
206                                         self.session.open(MessageBox, _("An unexpected error occured when trying to save the Playlist!"), type=MessageBox.TYPE_ERROR)
207                         elif item is self.ITEM_CREATE_PLAYLIST:
208                                 DatabasePlaylist.PlaylistCreate(self.session, MediaCore.TYPE_AUDIO, self._onPlaylistCreated)
209                         else:
210                                 self.session.open(MessageBox, _("Not implemented, yet!"), type=MessageBox.TYPE_WARNING)
211
212         def _onPlaylistCreated(self, playlist):
213                 if playlist != None:
214                         self._playlist.load(playlist.id, playlist.name)
215                         config.plugins.mediacenter.audio.last_playlist_id.value = playlist.id
216                         config.plugins.mediacenter.audio.save()
217                 else:
218                         self.session.open(MessageBox, _("This Playlist already exists!"), MessageBox.TYPE_ERROR)
219
220         def _onPlaylistSelected(self, entry):
221                 playlist = entry and entry[1]
222                 if playlist != None:
223                         self._playlist.load(playlist.id, playlist.name)
224                         config.plugins.mediacenter.audio.last_playlist_id.value = playlist.id
225                         config.plugins.mediacenter.audio.save()
226                 else:
227                         Log.w("ERROR LOADING PLAYLIST")
228
229         def lockShow(self):
230                 pass
231
232         def _readTitleInformation(self):
233                 currPlay = self.session.nav.getCurrentService()
234                 if currPlay is not None:
235                         sTitle = currPlay.info().getInfoString(iServiceInformation.sTagTitle)
236                         sAlbum = currPlay.info().getInfoString(iServiceInformation.sTagAlbum)
237                         sGenre = currPlay.info().getInfoString(iServiceInformation.sTagGenre)
238                         sArtist = currPlay.info().getInfoString(iServiceInformation.sTagArtist)
239                         sYear = currPlay.info().getInfoString(iServiceInformation.sTagDate)
240                         self._updateMusicInformation(sArtist, sTitle, sAlbum, sYear, sGenre, clear=False)
241                 else:
242                         self._updateMusicInformation()
243
244         def _updateMusicInformation(self, artist="", title="", album="", year="", genre="", clear=True):
245                 self.updateLabel("artist", artist, clear)
246                 self.updateLabel("title", title, clear)
247                 self.updateLabel("album", album, clear)
248                 self.updateLabel("year", year, clear)
249                 self.updateLabel("genre", genre, clear)
250
251                 if clear:
252                         self["coverArt"].setDefaultPicture()
253
254         def updateLabel(self, name, info, clear):
255                 if info != "" or clear:
256                         if self[name].getText() != info:
257                                 self[name].setText(info)
258
259         def _close(self):
260                 self.close()
261
262         def play(self, playSelected=True):
263                 Log.i("playSelected=%s" % playSelected)
264                 if playSelected:
265                         service = self._playlist.playSelected()
266                         if not self.service or not service or service.toCompareString() != self.service.toCompareString():
267                                 self.service = service
268                                 self._updateMusicInformation(clear=True)
269                         else:
270                                 self.playpause()
271                                 return
272
273                 if self.service:
274                         path = self.service.getPath()
275                         if path.startswith("/"):
276                                 self["coverArt"].folderCoverArtPath(path)
277                         self.seekState = self.SEEK_STATE_PLAY
278                         mediaCore.play(self.service)
279
280         def pause(self):
281                 self.seekState = self.SEEK_STATE_PAUSE
282                 self._playlist.pause()
283                 mediaCore.pause()
284
285         def unpause(self):
286                 self.seekState = self.SEEK_STATE_PLAY
287                 self._playlist.play(resume=True)
288                 mediaCore.unpause()
289
290         def playpause(self):
291                 if self.seekState == self.SEEK_STATE_PLAY:
292                         self.pause()
293                 elif self.seekState == self.SEEK_STATE_IDLE:
294                         self.play()
295                 else:
296                         if self.seekState == self.SEEK_STATE_PAUSE:
297                                 self.unpause()
298                         else:
299                                 self.stop()
300
301         def stop(self):
302                 self.seekState = self.SEEK_STATE_IDLE
303                 self.service = None
304                 self._playlist.stop()
305                 self._updateMusicInformation()
306                 mediaCore.stop()
307
308         def _toggleShuffle(self):
309                 if self._playlist.toggleShuffle():
310                         self["yellow"].setText(_("Shuffle - On") )
311                 else:
312                         self["yellow"].setText(_("Shuffle - Off") )
313
314         def _toggleRepeat(self):
315                 #NONE -> ALL -> SINGLE
316                 if self._repeat == Playlist.REPEAT_NONE:
317                         self._repeat = Playlist.REPEAT_ALL
318                         self["blue"].setText(_("Repeat - All"))
319                 elif self._repeat == Playlist.REPEAT_ALL:
320                         self._repeat = Playlist.REPEAT_SINGLE
321                         self["blue"].setText(_("Repeat - Single"))
322                 else:
323                         self._repeat = Playlist.REPEAT_NONE
324                         self["blue"].setText(_("Repeat - None"))
325                 self._playlist.repeat(self._repeat)
326
327         def _removeSelectedPlaylistEntry(self):
328                 if self.removeSelectedEntry() and self.seekState == self.SEEK_STATE_PLAY:
329                         self.play()