[MerlinSkinThemes] - add support for globals
[enigma2-plugins.git] / serienfilm / src / MovieList.py
1 # -*- coding: utf-8 -*-
2
3 from Components.GUIComponent import GUIComponent
4 from Tools.FuzzyDate import FuzzyTime
5 from ServiceReference import ServiceReference
6 from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest
7 from Components.config import config
8 from Tools.LoadPixmap import LoadPixmap
9 from Components.UsageConfig import preferredPath, defaultMoviePath
10 from enigma import eEnv
11 from glob import glob
12 import copy
13 import os.path
14
15 from enigma import eListboxPythonMultiContent, eListbox, gFont, iServiceInformation, \
16         RT_HALIGN_LEFT, RT_HALIGN_RIGHT, eServiceReference, eServiceCenter
17
18 class MovieList(GUIComponent):
19         SORT_ALPHANUMERIC = 1
20         SORT_RECORDED = 2
21
22         LISTTYPE_ORIGINAL = 0x10
23         LISTTYPE_COMPACT_TAGS = 0x20
24         LISTTYPE_COMPACT_SERVICE = 0x40
25         LISTTYPE_MINIMAL = 0x80
26         LISTTYPE_COMPACT = LISTTYPE_COMPACT_TAGS | LISTTYPE_COMPACT_SERVICE
27
28         HIDE_DESCRIPTION = 1
29         SHOW_DESCRIPTION = 2
30
31         SHOW_NO_TIMES = 0
32         SHOW_RECORDINGTIME = 1
33         SHOW_DURATION = 2
34         SHOW_DIRECTORIES = 4
35
36         # tinfo types:
37         REAL_DIR = 1
38         REAL_UP = 2
39         VIRT_DIR = 4
40         VIRT_UP = 8
41         VIRT_ENTRY = (VIRT_DIR | VIRT_UP)
42
43         MAXTIME = 0x7fffffff
44
45         gsflists = []
46
47
48         def __init__(self, root, list_type=None, sort_type=None, show_times=None, sftitle_episode_separator = None, MovieSelectionSelf = None):
49                 GUIComponent.__init__(self)
50 #               print "[SF-Plugin] class SF:MovieList init, lstt=%x, srt=%x, sht=%s, sft=>%s<, root=%s" % ( list_type, sort_type, show_times, str(sftitle_episode_separator), str(root))
51                 self.list_type = list_type or self.LISTTYPE_MINIMAL
52                 self.show_times = show_times or self.SHOW_DURATION | self.SHOW_DIRECTORIES
53                 self.sort_type = sort_type or self.SORT_RECORDED
54                 self.sftitle_episode_separator = sftitle_episode_separator
55
56                 self.l = eListboxPythonMultiContent()
57
58                 self.tags = set()
59                 self.list = None
60                 self.sflists = None
61                 self.MovieSelectionSelf = MovieSelectionSelf
62                 self.MselTitle = ""
63
64                 if root is not None:
65                         self.reload(root)
66
67                 self.pdirIcon = LoadPixmap(cached=True, path=eEnv.resolve('${libdir}/enigma2/python/Plugins/Extensions/SerienFilm/icons/folder_20.png'))
68                 self.rdirIcon = LoadPixmap(cached=True, path=eEnv.resolve('${libdir}/enigma2/python/Plugins/Extensions/SerienFilm/icons/folder_red.png'))
69                 self.fupIcon = LoadPixmap(cached=True, path=eEnv.resolve('${libdir}/enigma2/python/Plugins/Extensions/SerienFilm/icons/folderup_20.png'))
70                 self.pdirMap = MultiContentEntryPixmapAlphaTest(pos=(0,0), size=(20,20), png=self.pdirIcon)
71                 self.rdirMap = MultiContentEntryPixmapAlphaTest(pos=(0,0), size=(20,20), png=self.rdirIcon)
72                 self.fupMap = MultiContentEntryPixmapAlphaTest(pos=(0,0), size=(20,20), png=self.fupIcon)
73
74                 self.redrawList()
75                 self.l.setBuildFunc(self.buildMovieListEntry)
76
77                 self.onSelectionChanged = [ ]
78
79         def connectSelChanged(self, fnc):
80                 if not fnc in self.onSelectionChanged:
81                         self.onSelectionChanged.append(fnc)
82
83         def disconnectSelChanged(self, fnc):
84                 if fnc in self.onSelectionChanged:
85                         self.onSelectionChanged.remove(fnc)
86
87         def selectionChanged(self):
88                 for x in self.onSelectionChanged:
89 #                       print "[SF-Plugin] MovieList.selectionChanged: " + str(x)
90                         x()
91
92         def setListType(self, type):
93                 self.list_type = type
94                 self.redrawList()
95                 self.l.setList(self.list)                               # redraw
96
97         def setShowTimes(self, val):
98                 self.show_times = val
99
100         def setSortType(self, type):
101                 self.sort_type = type
102
103         def setTitleEpiSep(self, sftitle_episode_separator):
104                 self.sftitle_episode_separator = sftitle_episode_separator
105
106
107         def redrawList(self):
108                 if self.list_type & MovieList.LISTTYPE_ORIGINAL:
109                         self.l.setFont(0, gFont("Regular", 22))
110                         self.l.setFont(1, gFont("Regular", 18))
111                         self.l.setFont(2, gFont("Regular", 16))
112                         self.l.setItemHeight(75)
113                         if self.sflists and self.sflists[0] != self.list:
114                                 self.l.setItemHeight(41)
115                 elif self.list_type & MovieList.LISTTYPE_COMPACT:
116                         self.l.setFont(0, gFont("Regular", 20))
117                         self.l.setFont(1, gFont("Regular", 16))
118                         self.l.setItemHeight(39)
119 #                       self.l.setFont(1, gFont("Regular", 14))
120 #                       self.l.setItemHeight(37)
121                 else:
122                         self.l.setFont(0, gFont("Regular", 20)) # MINIMAL
123                         self.l.setFont(1, gFont("Regular", 16))
124                         self.l.setItemHeight(25)
125
126         #
127         # | name of movie              |
128         #
129         def buildMovieListEntry(self, serviceref, info, begin, tinfo):
130 #               print "[SF-Plugin] SF:MovieList.buildMovieListEntry, lst_type=%x, show_tims=%x" % (self.list_type, self.show_times)
131
132                 width = self.l.getItemSize().width()
133                 len = tinfo[5]                  #tinfo = [type, pixmap, txt, description, service, len]
134
135                 if len <= 0: #recalc len when not already done
136                         cur_idx = self.l.getCurrentSelectionIndex()
137                         x = self.list[cur_idx]
138                         if config.usage.load_length_of_movies_in_moviellist.value:
139                                 len = x[1].getLength(x[0]) #recalc the movie length...
140                         else:
141                                 len = 0         #dont recalc movielist to speedup loading the list
142                         self.list[cur_idx][3][5] = len  #update entry in list... so next time we don't need to recalc
143
144                 if len > 0:
145                         len = "%d:%02d" % (len / 60, len % 60)
146                 else:
147                         len = ""
148
149                 res = [ None ]
150                 begin_string = ""
151                 date_string = ""
152
153                 pixmap = tinfo[1]
154                 typ = tinfo[0]
155                 service = None
156                 if typ & (self.VIRT_UP | self.REAL_UP):
157                         txt = tinfo[3]  # [2] == " " for alpha-sort to top
158                 else:
159                         txt = tinfo[2]
160                         if begin > 0:
161                                 t = FuzzyTime(begin)
162                                 begin_string = t[0] + ", " + t[1]
163                                 date_string = t[0]
164                 if not typ & (self.REAL_DIR | self.VIRT_ENTRY):
165                         service = tinfo[4]
166                 description = tinfo[3]
167                 tags = self.tags and info.getInfoString(serviceref, iServiceInformation.sTags)
168
169                 if isinstance(pixmap, str):
170                         pixmap = MultiContentEntryText(pos=(0, 0), size=(25, 20), font = 0, flags = RT_HALIGN_LEFT, text = pixmap)
171                 if pixmap is not None:
172                         res.append(pixmap)
173
174                 XPOS = 25
175
176                 if self.list_type & MovieList.LISTTYPE_ORIGINAL:
177                         res.append(MultiContentEntryText(pos=(XPOS, 0), size=(width, 30), font = 0, flags = RT_HALIGN_LEFT, text=txt))
178                         line2 = 20
179                         if self.list == self.sflists[0]:
180                                 line2 = 50
181                                 if not typ & (self.REAL_DIR | self.VIRT_ENTRY):
182                                         res.append(MultiContentEntryText(pos=(XPOS, 30), size=(width, 20), font=1, flags=RT_HALIGN_LEFT, text=description))
183                         res.append(MultiContentEntryText(pos=(XPOS, line2), size=(150, 20), font=1, flags=RT_HALIGN_LEFT, text=begin_string))
184                         if service:
185                                 res.append(MultiContentEntryText(pos=(XPOS+150, line2), size=(180, 20), font = 2, flags = RT_HALIGN_RIGHT, text = service))
186                         if tags:
187                                 res.append(MultiContentEntryText(pos=(width - 250, line2), size=(180, 20), font = 2, flags = RT_HALIGN_RIGHT, text = tags))
188                         if not typ & (self.REAL_DIR | self.VIRT_ENTRY):
189                                 res.append(MultiContentEntryText(pos=(width-60, line2), size=(60, 20), font=2, flags=RT_HALIGN_RIGHT, text=len))
190                         return res
191
192                 tslen = 80
193                 if self.show_times & self.SHOW_RECORDINGTIME:
194                         tslen += 50
195                         date_string = begin_string
196                 dusz = 0
197                 if self.show_times & self.SHOW_DURATION and not tinfo[0] & (self.VIRT_ENTRY | self.REAL_UP):
198                         dusz = 57
199
200                 if self.list_type  & MovieList.LISTTYPE_COMPACT:
201                         res.append(MultiContentEntryText(pos=(XPOS, 4), size=(tslen-5, 20), font=1, flags=RT_HALIGN_RIGHT, text=date_string))
202                         res.append(MultiContentEntryText(pos=(XPOS + tslen, 0), size=(width-XPOS-tslen, 20), font = 0, flags = RT_HALIGN_LEFT, text = txt))
203                         other = None
204                         if self.list_type & MovieList.LISTTYPE_COMPACT_TAGS:
205                                 if tags:
206                                         res.append(MultiContentEntryText(pos=(width-dusz-185, 20), size=(180, 17), font=1, flags=RT_HALIGN_RIGHT, text=tags))
207                                 otherend = dusz+185
208                                 other = service
209                         else:
210                                 if service:
211                                         res.append(MultiContentEntryText(pos=(width-dusz-155, 20), size=(153, 17), font=1, flags=RT_HALIGN_RIGHT, text=service))
212                                 otherend = dusz+160
213                                 other = tags
214                         if self.list == self.sflists[0]:
215                                 if not typ & (self.REAL_DIR | self.VIRT_ENTRY):
216                                         res.append(MultiContentEntryText(pos=(XPOS, 20), size=(width-(XPOS+otherend), 17), font=1, flags=RT_HALIGN_LEFT, text=description))
217                                 elif other:
218                                         res.append(MultiContentEntryText(pos=(XPOS, 20), size=(width-(XPOS+otherend), 17), font=1, flags=RT_HALIGN_LEFT, text=other))
219                         if dusz:
220                                 res.append(MultiContentEntryText(pos=(width-dusz, 20), size=(dusz-2, 20), font=1, flags=RT_HALIGN_RIGHT, text=len))
221                 else:
222 #                       assert(self.list_type == MovieList.LISTTYPE_MINIMAL)
223                         res.append(MultiContentEntryText(pos=(XPOS, 3), size=(tslen-5, 20), font=1, flags=RT_HALIGN_RIGHT, text=date_string))
224                         res.append(MultiContentEntryText(pos=(XPOS + tslen, 0), size=(width-XPOS-tslen-dusz, 20), font = 0, flags = RT_HALIGN_LEFT, text = txt))
225                         if dusz:
226                                 res.append(MultiContentEntryText(pos=(width-dusz, 3), size=(dusz, 20), font=1, flags=RT_HALIGN_RIGHT, text=len))
227
228                 return res
229
230         def moveToIndex(self, index):
231                 if index <0:
232                         index += len(self.list)                 # standard python list behaviour
233                 self.instance.moveSelectionTo(index)
234
235         def getCurrentIndex(self):
236                 return self.instance.getCurrentIndex()
237
238         def getCurrentEvent(self):
239                 l = self.l.getCurrentSelection()
240                 return l and l[0] and l[1] and l[1].getEvent(l[0])
241
242         def getCurrent(self):
243                 l = self.l.getCurrentSelection()
244                 return l and l[0]
245
246         GUI_WIDGET = eListbox
247
248         def postWidgetCreate(self, instance):
249                 instance.setContent(self.l)
250                 self.selectionChanged_conn = instance.selectionChanged.connect(self.selectionChanged)
251
252         def preWidgetRemove(self, instance):
253                 instance.setContent(None)
254                 self.selectionChanged_conn = None
255
256         def reload(self, root = None, filter_tags = None):
257                 if root is not None:
258                         self.load(root, filter_tags)
259                 else:
260                         self.load(self.root, filter_tags)
261                 self.l.setList(self.list)
262
263         def removeService(self, service):
264                 for l in self.list[:]:
265                         repnr = tinfo = None
266                         if l[0] == service:
267                                 tinfo = l[3]
268                                 if not service.flags & eServiceReference.canDescent and isinstance(tinfo[1], str) and tinfo[1][0] == "#":
269                                         repnr = int(tinfo[1][1:])
270                                 self.list.remove(l)
271                                 break
272                 self.l.setList(self.list)
273                 if len(self.list) == 1 and self.list[0][3][0] & self.VIRT_UP:   # last movie of a series is gone
274                         service = self.list[0][0]
275                         self.moveTo(service, True)
276                         assert service.flags == eServiceReference.canDescent
277                         self.removeService(service)
278                         return
279                 if repnr is None:
280                         return
281                 repeats = 0             # update repeatcount "#x" of surviving movies
282                 ele0 = 0
283 #               print "[SF-Plugin] removeService: searching " + tinfo[2]
284                 for i in range(1, len(self.list)):
285                         m = self.list[i]
286                         t = m[3]
287 #                       print "[SF-Plugin] removeService try: %x, %s -- %s" % (m[0].flags,  str(t[1]), str(t[2]))
288                         if not m[0].flags & eServiceReference.canDescent and t[2] == tinfo[2] and isinstance(t[1], str) and t[1][0] == "#":
289                                 repeats += 1
290                                 rc = int(t[1][1:])
291                                 if rc > repnr:
292                                         rc -= 1
293                                         t[1] = "#" + str(rc)
294 #                                       print "[SF-Plugin] removeService: %s --> %s" % (t[2], t[1])
295                                 if rc == 0:
296                                         ele0 = i
297                 if ele0 > 0 and repeats == 1:
298                         self.list[ele0][3][1] = None    # remove "#0" from only lonely surviving movie
299 #                       print "[SF-Plugin] removeService: remove #0 from " + self.list[ele0][3][2]
300
301
302         def __len__(self):
303                 return len(self.list)
304
305
306         def playDirectory(self, serviceref):
307                 if serviceref.type == (eServiceReference.idUser | eServiceReference.idDVB) and serviceref.flags == eServiceReference.canDescent:
308                         self.moveTo(serviceref)         # virtual Directory
309                         return ""
310                 if serviceref.flags & eServiceReference.mustDescent:
311                         info = self.serviceHandler.info(serviceref)
312                         if info is None:
313                                 name = ""
314                         else:
315                                 name = info.getName(serviceref)
316 #                       print "[SF-Plugin] MovieList.playDirectory: %s nicht spielbar" ,(name)
317                         return name
318
319         def realDirUp(self, root):
320                 parent = None
321                 info = self.serviceHandler.info(root)
322                 pwd = info and info.getName(root)
323                 print "[SF-Plugin] MovieList.realDirUp: pwd = >%s<" % (str(pwd))
324                 if pwd and os.path.exists(pwd) and not os.path.samefile(pwd, defaultMoviePath()):
325                         parentdir = pwd[:pwd.rfind("/", 0, -1)] + "/"
326                         parent = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + parentdir)
327                         info = self.serviceHandler.info(parent)
328                         if info is not None:
329                                 txt = info.getName(parent)                                                                                                                              # Titel
330                                 service = ServiceReference(info.getInfoString(parent, iServiceInformation.sServiceref)).getServiceName()        # Sender
331                                 description = info.getInfoString(parent, iServiceInformation.sDescription)                              # Beschreibung
332 #                               begin = info.getInfo(root, iServiceInformation.sTimeCreate)
333                                 begin = self.MAXTIME
334                                 parent.flags = eServiceReference.flagDirectory | eServiceReference.sort1
335                                 tinfo = [self.REAL_DIR | self.REAL_UP, self.fupMap, "  0", txt, service, 1]     # "  0" sorts before VIRT_UP
336                                 return ((parent, info, begin, tinfo))
337
338
339
340         def load(self, root, filter_tags):
341                 # this lists our root service, then building a 
342                 # nice list
343
344                 self.serviceHandler = eServiceCenter.getInstance()
345                 parentLstEntry = self.realDirUp(root)
346
347                 self.rootlst = [ ]
348
349                 self.root = root
350                 list = self.serviceHandler.list(root)
351                 if list is None:
352                         print "[SF-Plugin] listing of movies failed"
353                         list = [ ]      
354                         return
355                 tags = set()
356
357                 rootinfo = self.serviceHandler.info(root)
358                 pwd = rootinfo and rootinfo.getName(root)
359
360                 while 1:
361                         serviceref = list.getNext()
362                         if not serviceref.valid():
363                                 break
364                         pixmap = None
365                         type = 0
366                         if serviceref.flags & eServiceReference.mustDescent:
367                                 if not self.show_times & self.SHOW_DIRECTORIES:
368                                         continue                                # hide Directories
369                                 type = self.REAL_DIR            # show Directories
370                                 pixmap = self.rdirMap
371
372                         info = self.serviceHandler.info(serviceref)
373                         if info is None:
374                                 continue
375                         txt = info.getName(serviceref)
376                         if serviceref.flags & eServiceReference.mustDescent:
377                                 files = glob(os.path.join(txt, "*"))
378                                 # skip empty directories
379                                 if not files:
380                                         continue
381                                 # use mtime of latest recording
382                                 begin = sorted((os.path.getmtime(x) for x in files))[-1]
383                                 if pwd and txt.startswith(pwd):
384                                         txt = txt[len(pwd):]
385                                 if txt.endswith(os.path.sep):
386                                         txt = txt[:-len(os.path.sep)]
387                         else:
388                                 begin = info.getInfo(serviceref, iServiceInformation.sTimeCreate)
389                         this_tags = info.getInfoString(serviceref, iServiceInformation.sTags).split(' ')
390
391                         # convert space-seperated list of tags into a set
392                         if this_tags == ['']:
393                                 this_tags = []
394                         this_tags = set(this_tags)
395                         tags |= this_tags
396
397                         # filter_tags is either None (which means no filter at all), or 
398                         # a set. In this case, all elements of filter_tags must be present,
399                         # otherwise the entry will be dropped.                  
400                         if filter_tags is not None and not this_tags.issuperset(filter_tags):
401                                 continue
402
403                         service = ServiceReference(info.getInfoString(serviceref, iServiceInformation.sServiceref)).getServiceName()    # Sender
404                         description = info.getInfoString(serviceref, iServiceInformation.sDescription)
405                         tinfo = [type, pixmap, txt, description, service, -1]
406
407                         self.rootlst.append((serviceref, info, begin, tinfo))
408
409                 self.rootlst.sort(key=lambda x: -x[2])                                          # movies of same name stay sortet by time
410                 self.rootlst.sort(key=lambda x: (x[3][2]+x[3][3]).lower())
411                 self.list = self.rootlst
412                 self.createSublists()
413
414
415                 if self.sort_type == self.SORT_RECORDED:
416                         self.sortLists()
417
418                 # finally, store a list of all tags which were found. these can be presented
419                 # to the user to filter the list
420                 self.tags = tags
421                 if parentLstEntry:
422 #                       print "[SF-Plugin] SF:MovieList.load: parentLstEntry %s" % (self.debPrtEref(parentLstEntry[0]))
423                         self.list.insert(0, parentLstEntry)
424
425         def moveTo(self, serviceref, descend_virtdirs=True, search_all_lists=True):
426                 count = 0
427                 for x in self.list:
428                         if x[0] == serviceref:
429
430                                 if descend_virtdirs:
431                                         l = self.list[count]
432                                         tinfo = l[3]
433                                         if tinfo[0] & self.VIRT_ENTRY:
434                                                 assert tinfo[4][:6] == "SFLIDX"
435                                                 self.list = self.sflists[int(tinfo[4][6:])]
436                                                 self.l.setList(self.list)
437                                                 self.MovieSelectionSelf.setTitle(self.MselTitle)
438                                                 self.redrawList()
439                                         if tinfo[0] & self.VIRT_DIR:
440                                                 count = 0                                                       # select VIRT_UP in sublist
441                                                 self.MovieSelectionSelf.setTitle("%s: %s" % (_("Series"), tinfo[2]))
442                                         elif tinfo[0] & self.VIRT_UP:
443                                                 rv = self.moveTo(serviceref, False)
444                                                 return rv
445
446                                 self.instance.moveSelectionTo(count)
447                                 return True
448                         count += 1
449         # InfoBar:leavePlayerConfirmed(movielist) should find movies in virtual directories
450                 if search_all_lists and descend_virtdirs and self.sflists:
451                         savelist = self.list
452                         for l in self.sflists:
453                                 if l == savelist:
454                                         continue
455                                 self.list = l
456                                 self.l.setList(l)
457                                 if self.moveTo(serviceref, descend_virtdirs=True, search_all_lists=False):
458                                         return True
459                         self.list = savelist
460                         self.l.setList(self.list)
461
462 # enigmas list:         (serviceref, info, begin, len)  # len is replaced by tinfo
463 # tinfo:                        [type, pixmap, txt, description, service, len]
464
465 # pixmap:                       pixmap (DIR_UP...) or String (#0, #1 ... for multiple recordings)
466 # SFLIDX0...999         entry# in serlst, 0 == rootlist
467
468
469         def serflm(self, film, episode):
470                 fdate = film[2]
471                 tinfo = film[3]
472                 dsc = tinfo[3]
473                 service = tinfo[4]
474                 epi = len(episode) == 2 and episode[1]
475                 if epi:
476                         txt = ": ".join((epi, dsc))
477                 else:
478                         txt = dsc or service
479                 if self.serdate < fdate:
480                         self.serdate = fdate
481                 tinfo[2] = txt
482                 tinfo[3] = dsc
483                 return film
484
485         def update_repcnt(self, serlst, repcnt):
486                 for i in range(repcnt + 1):
487                         serlst[-( i+1 )][3][1] =  "#" + str(i)
488
489         def createSublists(self):
490                 self.serdate = 0
491                 serie = serlst = None
492                 self.sflists = [self.rootlst]
493                 txt = ("", "")
494                 rootlidx = repcnt = 0
495                 global gsflists
496                 sflidx = 0
497                 if self.sftitle_episode_separator:
498                         splitTitle = lambda s: s.split(self.sftitle_episode_separator, 1)
499                 else:
500                         splitTitle = lambda s: [s]
501 #               print "[SF-Plugin] MovieList.createSublists: self.sftitle_episode_separator = %d = >%s<" % (len(self.sftitle_episode_separator), self.sftitle_episode_separator)
502                 for tinfo in self.rootlst[:]:
503 #                       ts = tinfo[3][2].split(": ", 1)
504                         ts = splitTitle(tinfo[3][2])
505                         if txt[0] == ts[0]:
506                                 if txt[0] != serie:                             # neue Serie
507                                         sflidx += 1
508                                         serie = txt[0]
509                                         ser_serviceref = eServiceReference(eServiceReference.idUser | eServiceReference.idDVB, 
510                                                         eServiceReference.canDescent, "SFLIDX" + str(sflidx))
511                                         ser_info = self.serviceHandler.info(ser_serviceref)
512                                         # VIRT_UP should sort first, but after REAL_UP: MAXTIME-1 resp. "  1"
513                                         serlst = [(ser_serviceref, ser_info, MovieList.MAXTIME-1,
514                                                 [self.VIRT_UP, self.fupMap, "  1", txt[0], "SFLIDX0", 1])]
515                                         self.sflists.append(serlst)
516                                         serlst.append(self.serflm(self.rootlst[rootlidx-1], txt))
517                                         parent_list_index = rootlidx-1
518                                 film = self.rootlst.pop(rootlidx)
519                                 rootlidx -= 1
520                                 film = self.serflm(film, ts)
521                                 samefilm = False
522                                 if serlst:
523                                         if serlst and film[3][3] != "" and film[3][2] == serlst[-1][3][2]:              # perhaps same Movie?
524                                                 event1 = film[1].getEvent(film[0])
525                                                 event2 = serlst[-1][1].getEvent(serlst[-1][0])
526                                                 if event1 and event2 and event1.getExtendedDescription() == event2.getExtendedDescription():
527                                                         samefilm = True
528                                         if samefilm:
529                                                 repcnt += 1
530                                         elif repcnt:
531                                                 self.update_repcnt(serlst, repcnt)
532                                                 repcnt = 0
533                                         serlst.append(film)
534                         elif serlst:
535                                 self.rootlst[parent_list_index] = (ser_serviceref, ser_info, self.serdate, 
536                                         [self.VIRT_DIR, self.pdirMap, txt[0], "", "SFLIDX" + str(sflidx), 1])
537                                 self.serdate = 0
538                                 if repcnt:
539                                         self.update_repcnt(serlst, repcnt)
540                                         repcnt = 0
541                                 serlst = None
542                         rootlidx += 1
543                         txt = ts
544                 if serlst:
545                         self.rootlst[parent_list_index] = (ser_serviceref, ser_info, self.serdate, 
546                                 [self.VIRT_DIR, self.pdirMap, txt[0], "", "SFLIDX" + str(sflidx), None, 1])
547                         if repcnt:
548                                 self.update_repcnt(serlst, repcnt)
549 #               print "[SF-Plugin] sflist has %d entries" % (len(self.sflists))
550                 gsflists = self.sflists
551
552
553
554
555         def sortLists(self):
556                 if self.sort_type == self.SORT_ALPHANUMERIC:
557                         key = lambda x: (x[3][2]+x[3][3]).lower()
558                 else: key=lambda x: -x[2]
559                 if self.sflists:
560                         for list in self.sflists:
561                                 list.sort(key=key)
562                         return True
563
564         def toggleSort(self):
565                 save_list = self.list
566                 current = self.getCurrent()
567                 self.sort_type ^= (self.SORT_ALPHANUMERIC | self.SORT_RECORDED)
568                 self.sortLists()
569                 self.list = save_list
570                 self.l.setList(self.list)                               # redraw
571                 self.moveTo(current, False)
572
573 #       def toggleTags(self, toggle):
574 #               if toggle and self.list_type & (MovieList.LISTTYPE_COMPACT | MovieList.LISTTYPE_MINIMAL):
575 #                       self.showtags ^= MovieList.LISTTYPE_COMPACT
576 #               else:
577 #                       self.showtags = 0
578 #               self.redrawList()
579 #               self.l.setList(self.list)                               # redraw
580
581         def saveTitle(self, title):
582                 self.MselTitle = title
583
584         def getVirtDirList(self, name):
585                 return name[:6] == "SFLIDX" and self.sflists[int(name[6:])]
586
587         @staticmethod
588         def getVirtDirStatistics(name):
589                 if name[:6] == "SFLIDX":
590                         list = gsflists[int(name[6:])]
591                         repcnt = 0
592                         for l in list:
593                                 if isinstance(l[3][1], str) and l[3][1][0] == "#" and l[3][1] != "#0":
594                                         repcnt += 1
595                         s = "%d %s" % (len(list)-1, _("Movies"))
596                         if repcnt:
597                                 s += ", %d %s" % (repcnt, _("duplicated"))
598                         return s
599
600