4.2.3r1
[enigma2.git] / usr / lib / enigma2 / python / Plugins / Extensions / MediaCenter / MediaBrowserDB.py
1 from enigma import eListboxPythonMultiContent, gFont, eServiceReference, eMediaDatabase, StringList
2
3 from Components.MenuList import MenuList
4
5 from MediaCore import MediaCore, mediaCore
6 from Tools.Log import Log
7 from MediaBrowser import MediaBrowser, MediaBrowserList, MediaBrowserEntryComponent
8
9 from skin import TemplatedListFonts, componentSizes
10
11 ITEM_KEY_HANDLE = "handle"
12 ITEM_KEY_TITLE = "__item_title"
13 ITEM_KEY_TYPE = "type"
14
15 ITEM_TITLE_ALL = "-- %s --" %_("All")
16
17 #defines the required interface for navigation menu handles (see NavigationHandle and RootNavigationHandle for specific implementations
18 class MediaBrowserNavigationHandle(object):
19         def __init__(self, fnc):
20                 self._fnc = fnc
21                 self._lastItem = None
22
23         def isUpEntry(self, item):
24                 return False
25
26         def isAllEntry(self, item):
27                 return False
28
29         def hasAllHandle(self):
30                 return False
31
32         def canAscend(self):
33                 return False
34
35         def ascend(self, owner):
36                 return False
37
38         def canDescend(self, item):
39                 raise NotImplementedError("[MediaBrowserNavigationHandle] Subclasses have to implement canDescend(self, item)")
40
41         def descend(self, owner, item):
42                 raise NotImplementedError("[MediaBrowserNavigationHandle] Subclasses have to implement descend(self, owner, item)")
43
44         def getItemTitle(self, item):
45                 raise NotImplementedError("[MediaBrowserNavigationHandle] Subclasses have to implement getItemTitle(self, item)")
46
47         def getItems(self, item=None):
48                 raise NotImplementedError("[MediaBrowserNavigationHandle] Subclasses have to implement getItems(self, item)")
49
50         def getAllItems(self, owner, item):
51                 raise NotImplementedError("[MediaBrowserNavigationHandle] Subclasses have to implement getAllItems(self) IF they return 'True' in hasAllHandle(self)")
52
53         def getLastItem(self):
54                 return self._lastItem or {}
55
56 class NavigationHandle(MediaBrowserNavigationHandle):
57         def __init__(self, listFnc, titleKeys=["title"]):
58                 MediaBrowserNavigationHandle.__init__(self, listFnc)
59                 self._descendHandle = None
60                 self._ascendHandle = None
61                 self._allItemHandle = None
62                 self._titleKeys = titleKeys
63
64         def link(self, desc=None, asc=None, all=None):
65                 self._descendHandle = desc
66                 self._ascendHandle = asc
67                 self._allItemHandle = all
68
69         def isUpEntry(self, item):
70                 return item and item.get(ITEM_KEY_TYPE, None) == MediaBrowser.ITEM_TYPE_UP
71
72         def isAllEntry(self, item):
73                 return item and item.get(ITEM_KEY_TYPE, None) == MediaBrowser.ITEM_TYPE_ALL
74
75         def hasAllHandle(self):
76                 return self._allItemHandle is not None
77
78         def canAscend(self):
79                 return self._ascendHandle is not None
80
81         def ascend(self, owner):
82                 if self._ascendHandle:
83                         owner.setNavHandle(self._ascendHandle)
84                         return self._ascendHandle.getItems()
85                 return False
86
87         def canDescend(self, item):
88                 return self._descendHandle is not None
89
90         def descend(self, owner, item):
91                 if self.isUpEntry(item):
92                         return self.ascend(owner)
93
94                 if self.isAllEntry(item):
95                         return self.getAllItems(owner, item)
96
97                 if self._descendHandle:
98                         owner.setNavHandle(self._descendHandle)
99                         return self._descendHandle.getItems(item)
100                 return False
101
102         def getItemTitle(self, item):
103                 title = []
104                 for key in self._titleKeys:
105                         value = item.get(key, None)
106                         if value:
107                                 title.append(value)
108                 if len(title) is 0:
109                         title = ["-- %s --" %_("Unknown")]
110                 return " - ".join(title)
111
112         def getItems(self, item=None):
113                 if item is not None:
114                         self._lastItem = item
115                 self._fnc(self._lastItem)
116
117         def getAllItems(self, owner, item):
118                 if self._allItemHandle:
119                         owner.setNavHandle(self._allItemHandle)
120                         return self._allItemHandle.getItems(item)
121                 return False
122
123 class RootNavigationHandle(MediaBrowserNavigationHandle):
124         def __init__(self, fnc):
125                 MediaBrowserNavigationHandle.__init__(self, fnc)
126
127         def canDescend(self, item):
128                 return True
129
130         def descend(self, owner, item):
131                 handle = item[ITEM_KEY_HANDLE]
132                 owner.setNavHandle(handle)
133                 return handle.getItems(item)
134
135         def getItemTitle(self, item):
136                 return item[ITEM_KEY_TITLE]
137
138         def getItems(self, unused=None):
139                 return self._fnc(unused=unused)
140
141 class MediaBrowserDBList(MenuList, MediaBrowserList):
142         def __init__(self, type):
143                 MenuList.__init__(self, [], True, eListboxPythonMultiContent)
144                 MediaBrowserList.__init__(self, type)
145
146                 tlf = TemplatedListFonts()
147                 self.l.setFont(0, gFont(tlf.face(tlf.MEDIUM), tlf.size(tlf.MEDIUM)))
148                 itemHeight = componentSizes.itemHeight(componentSizes.FILE_LIST, 25)
149                 self.l.setItemHeight(itemHeight)
150                 self.l.setBuildFunc(self._buildListEntry)
151
152                 self._db = eMediaDatabase.getInstance()
153                 self._rootHandle = RootNavigationHandle(self._navRoot)
154
155                 if type == MediaCore.TYPE_AUDIO:
156                         #Artist -> Album -> Titles
157                         self._artistHandle = NavigationHandle(self.__getArtists, [eMediaDatabase.FIELD_ARTIST])
158                         self._artistAlbumHandle = NavigationHandle(self.__getAlbumsByArtist, [eMediaDatabase.FIELD_ARTIST, eMediaDatabase.FIELD_ALBUM])
159                         self._artistAllHandle = NavigationHandle(self.__filterByArtist, [eMediaDatabase.FIELD_ARTIST, eMediaDatabase.FIELD_ALBUM, eMediaDatabase.FIELD_TITLE])
160                         self._artistAlbumTitleHandle = NavigationHandle(self.__filterByArtistAlbum, [eMediaDatabase.FIELD_ARTIST, eMediaDatabase.FIELD_TITLE])
161
162                         self._artistHandle.link(desc=self._artistAlbumHandle, asc=self._rootHandle)
163                         self._artistAlbumHandle.link(desc=self._artistAlbumTitleHandle, asc=self._artistHandle, all=self._artistAllHandle)
164                         self._artistAllHandle.link(asc=self._artistAlbumHandle)
165                         self._artistAlbumTitleHandle.link(asc=self._artistAlbumHandle)
166
167                         #Album -> Titles
168                         self._albumHandle = NavigationHandle(self.__getAllAlbums, [eMediaDatabase.FIELD_ARTIST, eMediaDatabase.FIELD_ALBUM])
169                         self._albumTitleHandle = NavigationHandle(self.__filterByArtistAlbum, [eMediaDatabase.FIELD_ARTIST, eMediaDatabase.FIELD_TITLE])
170                         self._albumHandle.link(desc=self._albumTitleHandle, asc=self._rootHandle)
171                         self._albumTitleHandle.link(asc=self._albumHandle)
172
173                         #All
174                         self._allHandle = NavigationHandle(self.__getAll, [eMediaDatabase.FIELD_ARTIST, eMediaDatabase.FIELD_ALBUM, eMediaDatabase.FIELD_TITLE])
175                         self._allHandle.link(asc=self._rootHandle)
176
177                         #set navigation handle items
178                         self._navitems = [{
179                                         ITEM_KEY_TITLE : _("Artists"),
180                                         ITEM_KEY_HANDLE: self._artistHandle,
181                                 },
182                                 {
183                                         ITEM_KEY_TITLE : _("Albums"),
184                                         ITEM_KEY_HANDLE: self._albumHandle,
185                                 },
186                                 {
187                                         ITEM_KEY_TITLE : _("All"),
188                                         ITEM_KEY_HANDLE: self._allHandle,
189                                 }]
190                 elif type == MediaCore.TYPE_VIDEO:
191                         self._recordingHandle = NavigationHandle(self.__getAllRecordings, [eMediaDatabase.FIELD_TITLE])
192                         self._allVideoHandle = NavigationHandle(self.__getAllVideos, [eMediaDatabase.FIELD_TITLE])
193                         self._unseenVideoHandle = NavigationHandle(self.__getUnseenVideos, [eMediaDatabase.FIELD_TITLE])
194                         self._hdVideoHandle = NavigationHandle(self.__getHDVideos, [eMediaDatabase.FIELD_TITLE])
195                         self._sdVideoHandle = NavigationHandle(self.__getSDVideos, [eMediaDatabase.FIELD_TITLE])
196
197                         self._recordingHandle.link(asc=self._rootHandle)
198                         self._allVideoHandle.link(asc=self._rootHandle)
199                         self._unseenVideoHandle.link(asc=self._rootHandle)
200                         self._hdVideoHandle.link(asc=self._rootHandle)
201                         self._sdVideoHandle.link(asc=self._rootHandle)
202
203                         self._navitems = [{
204                                         ITEM_KEY_TITLE : _("Unseen"),
205                                         ITEM_KEY_HANDLE: self._unseenVideoHandle,
206                                 },
207                                 {
208                                         ITEM_KEY_TITLE : _("Recordings"),
209                                         ITEM_KEY_HANDLE: self._recordingHandle,
210                                 },
211                                 {
212                                         ITEM_KEY_TITLE : _("All Videos"),
213                                         ITEM_KEY_HANDLE: self._allVideoHandle,
214                                 },
215                                 {
216                                         ITEM_KEY_TITLE : _("Only HD"),
217                                         ITEM_KEY_HANDLE: self._hdVideoHandle,
218                                 },
219                                 {
220                                         ITEM_KEY_TITLE : _("Only SD"),
221                                         ITEM_KEY_HANDLE: self._sdVideoHandle,
222                                 },]
223
224                 self.__currentNavHandle = self._rootHandle
225                 self.__prevNavHandle = None
226                 self._currentHandleItem = None
227                 self.__getFolderContent = None
228
229         def _navRoot(self, unused=None):
230                 self.__getFolderContent = None
231                 return self._setList(self._navitems)
232
233         def __setCurrentNavHandle(self, handle):
234                 self.__prevNavHandle = self.__currentNavHandle
235                 self.__currentNavHandle = handle
236
237         def __getCurrentNavHandle(self):
238                 return self.__currentNavHandle
239         _currentNavHandle = property(__getCurrentNavHandle, __setCurrentNavHandle)
240
241         def __getPrevNavHandle(self):
242                 return self.__prevNavHandle
243         _prevNavHandle = property(__getPrevNavHandle)
244
245         def _onShow(self):
246                 self._currentNavHandle.getItems()
247
248         def _onClose(self):
249                 pass
250
251         def _setList(self, l):
252                 lst = []
253                 prev_title = None
254                 if self._prevNavHandle:
255                         prev_title = self._prevNavHandle.getLastItem().get(ITEM_KEY_TITLE, None)
256                 restoreindex = 0
257                 idx = 0
258
259                 if self._currentNavHandle.canAscend():
260                         lst.append(({ITEM_KEY_TITLE : MediaBrowser.ITEM_TEXT_UP, ITEM_KEY_TYPE : MediaBrowser.ITEM_TYPE_UP}, MediaBrowser.ITEM_TYPE_UP))
261                         idx += 1
262
263                 if self._currentNavHandle.hasAllHandle():
264                         lastitem = self._currentNavHandle.getLastItem() or {}
265                         item = dict( lastitem.items() + {ITEM_KEY_TITLE : ITEM_TITLE_ALL, ITEM_KEY_TYPE : MediaBrowser.ITEM_TYPE_ALL}.items() )
266                         lst.append((item, MediaBrowser.ITEM_TYPE_FOLDER))
267                         if prev_title == ITEM_TITLE_ALL:
268                                 restoreindex = idx
269                         idx += 1
270
271                 canDescend = self._currentNavHandle.canDescend(None)
272                 for item in l:
273                         item = dict(item)
274                         title = self._currentNavHandle.getItemTitle(item)
275                         item[ITEM_KEY_TITLE] = title
276
277                         if title == prev_title:
278                                 restoreindex = idx
279
280                         if canDescend:
281                                 item_type = MediaBrowser.ITEM_TYPE_FOLDER
282                         else:
283                                 item_type = MediaBrowser.ITEM_TYPE_AUDIO
284
285                         lst.append((item, item_type))
286                         idx += 1
287                 self.l.setList(lst)
288                 self.moveToIndex(restoreindex)
289
290         def _buildListEntry(self, item, item_type):
291                 return MediaBrowserEntryComponent(item, item[ITEM_KEY_TITLE], item_type)
292
293         def _processResult(self, res):
294                 if res and not res.error():
295                         data = res.data()
296                         self._setList(data)
297                         return
298                 elif res:
299                         Log.w("%s\n%s" %(res.errorDriverText(), res.errorDatabaseText()))
300                 self._setList([])
301
302         def __getArtists(self, item):
303                 self.__getFolderContent = self.__filterByArtist
304                 res = self._db.getAllArtists()
305                 self._processResult(res)
306
307         def __getAllAlbums(self, item):
308                 self.__getFolderContent = self.__filterByArtistAlbum
309                 res = self._db.getAllAlbums()
310                 self._processResult(res)
311
312         def __getAlbumsByArtist(self, item):
313                 self.__getFolderContent = self.__filterByArtistAlbum
314                 artist = item.get(eMediaDatabase.FIELD_ARTIST)
315                 res = self._db.getAlbumsByArtist(artist)
316                 self._processResult(res)
317
318         def __filterByArtistAlbum(self, item, only_get=False):
319                 self.__getFolderContent = None
320                 artist = item.get(eMediaDatabase.FIELD_ARTIST)
321                 album = item.get(eMediaDatabase.FIELD_ALBUM)
322                 res = self._db.filterByArtistAlbum(artist, album)
323                 if only_get:
324                         return res.data()
325                 self._processResult(res)
326
327         def __filterByArtist(self, item, only_get=False):
328                 self.__getFolderContent = None
329                 artist = item.get(eMediaDatabase.FIELD_ARTIST)
330                 res = self._db.filterByArtist(artist)
331                 if only_get:
332                         return res.data()
333                 self._processResult(res)
334
335         def __getAll(self, item):
336                 self.__getFolderContent = None
337                 res = self._db.getAllAudio()
338                 self._processResult(res)
339
340         def __getAllVideos(self, item):
341                 self.__getFolderContent = None
342                 res = self._db.getAllVideos()
343                 self._processResult(res)
344
345         def __getAllRecordings(self, item):
346                 self.__getFolderContent = None
347                 res = self._db.getAllRecordings()
348                 self._processResult(res)
349
350         def __getUnseenVideos(self, item):
351                 self.__getFolderContent = None
352                 res = self._db.query("SELECT * from video WHERE lastplaypos=?;", StringList(["0"]))
353                 self._processResult(res)
354
355         def __getHDVideos(self, item):
356                 self.__getFolderContent = None
357                 res = self._db.query("SELECT * from video WHERE hd=?;", StringList(["1"]))
358                 self._processResult(res)
359
360         def __getSDVideos(self, item):
361                 self.__getFolderContent = None
362                 res = self._db.query("SELECT * from video WHERE hd=?;", StringList(["0"]))
363                 self._processResult(res)
364
365         def setNavHandle(self, handle):
366                 self._currentNavHandle = handle
367
368         def canDescend(self):
369                 item = self.getSelectedItem()
370                 return self._currentNavHandle.canDescend(item) or self._currentNavHandle.isUpEntry(item)
371
372         def ascend(self):
373                 self._currentHandleItem = None
374                 return self._currentNavHandle.ascend(self)
375
376         def descend(self):
377                 self._currentHandleItem = self.getSelectedItem()
378                 return self._currentNavHandle.descend(self, self._currentHandleItem)
379
380         def getSelectedItem(self):
381                 item = self.l.getCurrentSelection()
382                 if item is None:
383                         return None
384                 return item[0]
385
386         def getMeta(self, item):
387                 return self.getSelectedItem()
388
389         def getItemName(self, item=None):
390                 if item is None:
391                         item = self.getSelectedItem()
392                 return self._currentNavHandle.getItemTitle(item)
393
394         def getServiceRef(self, item=None):
395                 if item is None:
396                         item = self.getSelectedItem()
397                 if item is None:
398                         return eServiceReference()
399
400                 file = "%s/%s" %(item[eMediaDatabase.FIELD_PATH], item[eMediaDatabase.FIELD_FILENAME])
401                 ref = eServiceReference(4097, 0, file)
402                 ref.setName(item[eMediaDatabase.FIELD_TITLE])
403                 return ref
404
405         def canAddSelected(self):
406                 item = self.getSelectedItem()
407                 canDescend = self.canDescend()
408                 retVal = canDescend and self.__getFolderContent is not None and not self._currentNavHandle.isAllEntry(item) and not self._currentNavHandle.isUpEntry(item)
409                 return retVal or not canDescend
410
411         def getAllForSelected(self):
412                 if self.__getFolderContent is not None:
413                         return self.__getFolderContent(self.getSelectedItem(), only_get=True)
414                 else:
415                         return []
416
417 class MediaBrowserDB(MediaBrowser):
418         FEATURE_FILTER_TYPE = True
419         FEATURE_ADD_FOLDER = True
420         FEATURE_SEARCH = True
421         TITLE = _("Media Database Browser")
422
423         def __init__(self, session, type=type, player=None):
424                 MediaBrowser.__init__(self, session, type, player=player)
425                 self._setList(MediaBrowserDBList(type=type))
426
427         def addFolderToPlaylist(self, folder, recursive=True):
428                 Log.i("called")
429                 items = self._list.getAllForSelected()
430                 if not items:
431                         return False
432
433                 for item in items:
434                         item = dict(item)
435                         self.addToPlaylist(self._list.getServiceRef(item=item), item)
436                 return True
437
438         def canAddSelected(self):
439                 return self._list.canAddSelected()
440
441         @staticmethod
442         def search(needle):
443                 db = eMediaDatabase.getInstance()
444                 res = db.filterAudio(needle, 0, 250)
445                 if res.error():
446                         return []
447                 items = []
448                 for item in res.data():
449                         filename = "%s/%s" %(item[eMediaDatabase.FIELD_PATH], item[eMediaDatabase.FIELD_FILENAME])
450                         ref = eServiceReference(4097, 0, filename)
451                         ref.setName("%s - %s" %(item[eMediaDatabase.FIELD_ARTIST], item[eMediaDatabase.FIELD_TITLE]))
452                         items.append( (ref.getName(), (ref, dict(item))) )
453                 return items
454
455 mediaCore.registerBrowser(MediaBrowserDB, _("Media Database"), MediaCore.TYPE_AUDIO)
456 mediaCore.registerBrowser(MediaBrowserDB, _("Media Database"), MediaCore.TYPE_VIDEO)