tiny speedups and cleanup
[enigma2-plugins.git] / tageditor / src / plugin.py
1 from Plugins.Plugin import PluginDescriptor
2 from Screens.Screen import Screen
3 from Screens.InputBox import InputBox
4 from Screens.ChoiceBox import ChoiceBox
5 from Screens.MessageBox import MessageBox
6 from Components.config import config
7 from Components.ActionMap import ActionMap
8 from Components.Button import Button
9 from Components.SelectionList import SelectionList
10 from enigma import eServiceReference, eServiceCenter, iServiceInformation
11 import os
12
13 def main(session, service, **kwargs):
14         session.open(MovieTagEditor, service, session.current_dialog, **kwargs)
15
16 def Plugins(**kwargs):
17         try:
18                 from Screens.MovieSelection import setPreferredTagEditor
19                 setPreferredTagEditor(TagEditor)
20         except:
21                 pass
22         return PluginDescriptor(name="TagEditor", description=_("Edit tags..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main)
23
24
25 class TagEditor(Screen):
26         skin = """
27         <screen name="TagEditor" title="Edit Tags" position="75,155" size="565,280">
28                 <widget name="list" position="5,5" size="555,225" scrollbarMode="showOnDemand" />
29                 <ePixmap position="0,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
30                 <ePixmap position="140,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
31                 <ePixmap position="280,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
32                 <ePixmap position="420,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
33                 <widget name="key_red" position="0,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
34                 <widget name="key_green" position="140,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
35                 <widget name="key_yellow" position="280,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
36                 <widget name="key_blue" position="420,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
37         </screen>"""
38
39         def __init__(self, session, tags, txt = None, args = 0):
40                 Screen.__init__(self, session, args)
41
42                 # Initialize Buttons
43                 self["key_red"] = Button(_("Cancel"))
44                 self["key_green"] = Button(_("OK"))
45                 self["key_yellow"] = Button(_("New"))
46                 self["key_blue"] = Button(_("Load"))
47
48                 self["list"] = SelectionList()
49
50                 allTags = self.loadTagsFile()
51                 self.joinTags(allTags, tags)
52                 self.updateMenuList(allTags, tags)
53
54                 self.ghostlist = tags[:]
55                 self.ghosttags = allTags[:]
56                 self.origtags = allTags[:]
57                 self.tags = allTags
58
59                 # Define Actions
60                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "MenuActions"],
61                 {
62                         "ok": self["list"].toggleSelection,
63                         "cancel": self.cancel,
64                         "red": self.cancel,
65                         "green": self.accept,
66                         "yellow": self.addCustom,
67                         "blue": self.loadFromHdd,
68                         "menu": self.showMenu
69                 }, -1)
70
71         def addCustom(self):
72                 self.session.openWithCallback(
73                         self.addCustomCallback,
74                         InputBox,
75                         title = _("Please enter the new tag")
76                 )
77
78         def addCustomCallback(self, ret):
79                 ret = ret and ret.strip().replace(" ","_").capitalize()
80                 if ret and ret not in self.tags:
81                         self.tags.append(ret)
82                         self.updateMenuList(self.tags, [ret])
83
84         def loadTagsFile(self):
85                 try:
86                         file = open("/etc/enigma2/movietags")
87                         tags = [x.rstrip() for x in file.readlines()]
88                         while "" in tags:
89                                 tags.remove("")
90                         file.close()
91                 except IOError, ioe:
92                         tags = []
93                 return tags
94
95         def saveTagsFile(self, tags):
96                 try:
97                         file = open("/etc/enigma2/movietags", "w")
98                         file.write("\n".join(tags)+"\n")
99                         file.close()
100                 except IOError, ioe:
101                         pass
102
103         def joinTags(self, taglist, newtags):
104                 for tag in newtags:
105                         if not tag in taglist:
106                                 taglist.append(tag)
107
108         def setTimerTags(self, timer, tags):
109                 if hasattr(timer, "tags") and timer.tags != tags:
110                         timer.tags = tags
111                         self.timerdirty = True
112
113         def setMovieTags(self, ref, tags):
114                 file = ref.getPath()
115                 if file.endswith(".ts"):
116                         file = file + ".meta"
117                 else:
118                         file = file + ".ts.meta"
119                 if os.path.exists(file):
120                         metafile = open(file, "r")
121                         sid = metafile.readline()
122                         title = metafile.readline()
123                         descr = metafile.readline()
124                         time = metafile.readline()
125                         oldtags = metafile.readline().rstrip()
126                         metafile.close()
127                         tags = " ".join(tags)
128                         if tags != oldtags:
129                                 metafile = open(file, "w")
130                                 metafile.write("%s%s%s%s%s" %(sid, title, descr, time, tags))
131                                 metafile.close()
132
133         def foreachTimerTags(self, func):
134                 self.timerdirty = False
135                 for timer in self.session.nav.RecordTimer.timer_list + self.session.nav.RecordTimer.processed_timers:
136                         if hasattr(timer, "tags") and timer.tags:
137                                 func(timer, timer.tags[:])
138                 if self.timerdirty:
139                         self.session.nav.RecordTimer.saveTimer()
140
141         def foreachMovieTags(self, func):
142                 serviceHandler = eServiceCenter.getInstance()
143                 for dir in config.movielist.videodirs.value:
144                         if os.path.isdir(dir):
145                                 root = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + dir)
146                                 list = serviceHandler.list(root)
147                                 if list is None:
148                                         continue
149                                 while 1:
150                                         serviceref = list.getNext()
151                                         if not serviceref.valid():
152                                                 break
153                                         if (serviceref.flags & eServiceReference.mustDescent):
154                                                 continue
155                                         info = serviceHandler.info(serviceref)
156                                         if info is None:
157                                                 continue
158                                         tags = info.getInfoString(serviceref, iServiceInformation.sTags).split(' ')
159                                         if tags == ['']:
160                                                 tags = []
161                                         if tags:
162                                                 func(serviceref, tags)
163
164         def updateMenuList(self, tags, extrasel = []):
165                 seltags = [x[1] for x in self["list"].getSelectionsList()] + extrasel
166                 tags.sort()
167                 self["list"].setList([])
168                 for tag in tags:
169                         self["list"].addSelection(tag, tag, 0, tag in seltags)
170                 
171         def loadFromHdd(self):
172                 tags = self.tags[:]
173                 self.foreachTimerTags(lambda t, tg: self.joinTags(tags, tg))
174                 self.foreachMovieTags(lambda r, tg: self.joinTags(tags, tg))
175                 self.updateMenuList(tags)
176                 self.tags = tags
177
178         def removeUnused(self):
179                 tags = [x[1] for x in self["list"].getSelectionsList()]
180                 self.foreachTimerTags(lambda t, tg: self.joinTags(tags, tg))
181                 self.foreachMovieTags(lambda r, tg: self.joinTags(tags, tg))
182                 self.updateMenuList(tags)
183                 self.tags = tags
184
185         def listReplace(self, lst, fr, to=None):
186                 if fr in lst:
187                         lst.remove(fr)
188                         if to != None and not to in lst:
189                                 lst.append(to)
190                                 lst.sort()
191                 return lst
192
193         def renameTag(self):
194                 self.thistag = self["list"].list[self["list"].getSelectedIndex()][0]
195                 self.session.openWithCallback(
196                         self.renameTagCallback,
197                         InputBox,
198                         title = _("Replace tag \"%s\" everywhere with:   (Note that 'Cancel' will not undo this!)") % (self.thistag[1]),
199                         text = self.thistag[1]
200                 )
201
202         def renameTagCallback(self, res):
203                 res = res and res.strip().replace(" ", "_").capitalize()
204                 if res and len(res) and res != self.thistag[1]:
205                         thistag = self.thistag[1]
206                         self.foreachTimerTags(lambda t, tg: (thistag in tg) and self.setTimerTags(t, self.listReplace(tg, thistag, res)))
207                         self.foreachMovieTags(lambda r, tg: (thistag in tg) and self.setMovieTags(r, self.listReplace(tg, thistag, res)))
208                         self.listReplace(self.tags, thistag, res)
209                         self.listReplace(self.ghosttags, thistag, res)
210                         self.listReplace(self.ghostlist, thistag, res)
211                         self.updateMenuList(self.tags, self.thistag[3] and [res] or [])
212
213         def removeTag(self):
214                 self.thistag = self["list"].list[self["list"].getSelectedIndex()][0]
215                 self.session.openWithCallback(
216                         self.removeTagCallback,
217                         MessageBox,
218                         _("Do you really want to delete tag \"%s\" everywhere?\n(Note that 'Cancel' will not undo this!)") % (self.thistag[1])
219                 )
220
221         def removeTagCallback(self, res):
222                 if res:
223                         thistag = self.thistag[1]
224                         self.foreachTimerTags(lambda t, tg: (thistag in tg) and self.setTimerTags(t, self.listReplace(tg, thistag)))
225                         self.foreachMovieTags(lambda r, tg: (thistag in tg) and self.setMovieTags(r, self.listReplace(tg, thistag)))
226                         self.listReplace(self.tags, thistag)
227                         self.listReplace(self.ghosttags, thistag)
228                         self.listReplace(self.ghostlist, thistag)
229                         self.updateMenuList(self.tags)
230
231         def removeAll(self):
232                 self.session.openWithCallback(
233                         self.removeAllCallback,
234                         MessageBox,
235                         _("Do you really want to delete all tags everywhere?\n(Note that 'Cancel' will not undo this!)")
236                 )
237
238         def removeAllCallback(self, res):
239                 if res:
240                         self.foreachTimerTags(lambda t, tg: tg and self.setTimerTags(t, []))
241                         self.foreachMovieTags(lambda r, tg: tg and self.setMovieTags(r, []))
242                         self.tags = []
243                         self.ghosttags = []
244                         self.ghostlist = []
245                         self.updateMenuList(self.tags)
246
247         def showMenu(self):
248                 menu = []
249                 menu.append((_("Add new tag"), self.addCustom))
250                 menu.append((_("Rename this tag"), self.renameTag))
251                 menu.append((_("Delete this tag"), self.removeTag))
252                 menu.append((_("Delete unused tags"), self.removeUnused))
253                 menu.append((_("Delete all tags"), self.removeAll))
254                 self.session.openWithCallback(self.menuCallback, ChoiceBox, title="", list=menu)
255
256         def menuCallback(self, choice):
257                 if choice is None:
258                         return
259                 choice[1]()
260
261         def cancel(self):
262                 if not self.origtags == self.ghosttags:
263                         self.saveTagsFile(self.ghosttags)
264                         self.close(self.ghostlist)
265                 else:
266                         self.close(None)
267
268         def accept(self):
269                 list = [x[1] for x in self["list"].getSelectionsList()]
270                 if not self.origtags == self.tags:
271                         self.saveTagsFile(self.tags)
272                 self.close(list)
273
274 class MovieTagEditor(TagEditor):
275         def __init__(self, session, service, parent, args = 0):
276                 self.service = service
277                 self.parentscreen = parent
278                 serviceHandler = eServiceCenter.getInstance()
279                 info = serviceHandler.info(service)
280                 path = service.getPath()
281                 if path.endswith(".ts"):
282                         path = path[:-3]
283                 self.path = path
284                 tags = info.getInfoString(service, iServiceInformation.sTags)
285                 if tags:
286                         tags = tags.split(' ')
287                 else:
288                         tags = []
289                 TagEditor.__init__(self, session, tags, args)
290
291         def saveTags(self, file, tags):
292                 if os.path.exists(file + ".ts.meta"):
293                         metafile = open(file + ".ts.meta", "r")
294                         sid = metafile.readline()
295                         title = metafile.readline()
296                         descr = metafile.readline()
297                         time = metafile.readline()
298                         oldtags = metafile.readline().rstrip()
299                         metafile.close()
300                         tags = " ".join(tags)
301                         if tags != oldtags:
302                                 metafile = open(file + ".ts.meta", "w")
303                                 metafile.write("%s%s%s%s%s" %(sid, title, descr, time, tags))
304                                 metafile.close()
305
306         def cancel(self):
307                 if not self.origtags == self.ghosttags:
308                         self.saveTagsFile(self.ghosttags)
309                         self.exitDialog()
310                 else:
311                         self.close()
312
313         def accept(self):
314                 list = [x[1] for x in self["list"].getSelectionsList()]
315                 if not self.origtags == self.tags:
316                         self.saveTagsFile(self.tags)
317                 self.saveTags(self.path, list)
318                 self.exitDialog()
319
320         def exitDialog(self):
321                 self.close()
322                 # This will try to get back to an updated movie list.
323                 # A proper way to do this should be provided in enigma2.
324                 try:
325                         self.parentscreen.csel.reloadList()
326                         self.parentscreen.close()
327                 except AttributeError:
328                         pass