Initial revision
[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 NumberActionMap
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, **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.tags = self.loadTagsFile()
49                 self.origtags = self.tags + []
50                 self.ghosttags = self.tags + []
51                 self.joinTags(self.tags, tags)
52                 
53                 self["list"] = SelectionList()
54                 self.updateMenuList(self.tags, tags)
55
56                 # Define Actions
57                 self["actions"] = NumberActionMap(["OkCancelActions", "ColorActions", "MenuActions"],
58                 {
59                         "ok": self["list"].toggleSelection,
60                         "cancel": self.cancel,
61                         "red": self.cancel,
62                         "green": self.accept,
63                         "yellow": self.addCustom,
64                         "blue": self.loadFromHdd,
65                         "menu": self.showMenu
66                 }, -1)
67
68         def addCustom(self):
69                 self.session.openWithCallback(
70                         self.addCustomCallback,
71                         InputBox,
72                         title = _("Please enter the new tag")
73                 )
74
75         def addCustomCallback(self, ret):
76                 ret = ret and ret.strip().replace(" ","_").capitalize()
77                 if ret and ret not in self.tags:
78                         self.tags.append(ret)
79                         self.updateMenuList(self.tags, [ret])
80
81         def loadTagsFile(self):
82                 try:
83                         file = open("/etc/enigma2/movietags")
84                         tags = [x.rstrip() for x in file.readlines()]
85                         while "" in tags:
86                                 tags.remove("")
87                         file.close()
88                 except IOError, ioe:
89                         tags = []
90                 return tags
91
92         def saveTagsFile(self, tags):
93                 try:
94                         file = open("/etc/enigma2/movietags", "w")
95                         file.write("\n".join(tags)+"\n")
96                         file.close()
97                 except IOError, ioe:
98                         pass
99
100         def joinTags(self, taglist, newtags):
101                 for tag in newtags:
102                         if not tag in taglist:
103                                 taglist.append(tag)
104
105         def setTimerTags(self, timer, tags):
106                 if timer.tags != tags:
107                         timer.tags = tags
108                         self.timerdirty = True
109
110         def setMovieTags(self, ref, tags):
111                 file = ref.getPath()
112                 if file.endswith(".ts") is True:
113                         file = file + ".meta"
114                 else:
115                         file = file + ".ts.meta"
116                 if os.path.exists(file):
117                         metafile = open(file, "r")
118                         sid = metafile.readline()
119                         title = metafile.readline()
120                         descr = metafile.readline()
121                         time = metafile.readline()
122                         oldtags = metafile.readline().rstrip()
123                         metafile.close()
124                         tags = " ".join(tags)
125                         if tags != oldtags:
126                                 metafile = open(file, "w")
127                                 metafile.write("%s%s%s%s%s" %(sid, title, descr, time, tags))
128                                 metafile.close()
129
130         def foreachTimerTags(self, func):
131                 self.timerdirty = False
132                 for timer in self.session.nav.RecordTimer.timer_list + self.session.nav.RecordTimer.processed_timers:
133                         if timer.tags:
134                                 func(timer, timer.tags+[])
135                 if self.timerdirty:
136                         self.session.nav.RecordTimer.saveTimer()
137
138         def foreachMovieTags(self, func):
139                 serviceHandler = eServiceCenter.getInstance()
140                 for dir in config.movielist.videodirs.value:
141                         if os.path.isdir(dir):
142                                 root = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + dir)
143                                 list = serviceHandler.list(root)
144                                 if list is None:
145                                         continue
146                                 while 1:
147                                         serviceref = list.getNext()
148                                         if not serviceref.valid():
149                                                 break
150                                         if (serviceref.flags & eServiceReference.mustDescent):
151                                                 continue
152                                         info = serviceHandler.info(serviceref)
153                                         if info is None:
154                                                 continue
155                                         tags = info.getInfoString(serviceref, iServiceInformation.sTags).split(' ')
156                                         if tags == ['']:
157                                                 tags = []
158                                         if tags:
159                                                 func(serviceref, tags)
160
161         def updateMenuList(self, tags, extrasel = []):
162                 seltags = [x[1] for x in self["list"].getSelectionsList()] + extrasel
163                 tags.sort()
164                 self["list"].setList([])
165                 for tag in tags:
166                         self["list"].addSelection(tag, tag, 0, tag in seltags)
167                 
168         def loadFromHdd(self):
169                 tags = self.tags + []
170                 self.foreachTimerTags(lambda t, tg: self.joinTags(tags, tg))
171                 self.foreachMovieTags(lambda r, tg: self.joinTags(tags, tg))
172                 self.updateMenuList(tags)
173                 self.tags = tags
174
175         def removeUnused(self):
176                 tags = [x[1] for x in self["list"].getSelectionsList()]
177                 self.foreachTimerTags(lambda t, tg: self.joinTags(tags, tg))
178                 self.foreachMovieTags(lambda r, tg: self.joinTags(tags, tg))
179                 self.updateMenuList(tags)
180                 self.tags = tags
181
182         def listReplace(self, lst, fr, to=None):
183                 if fr in lst:
184                         lst.remove(fr)
185                         if to != None and not to in lst:
186                                 lst.append(to)
187                                 lst.sort()
188                 return lst
189
190         def renameTag(self):
191                 self.thistag = self["list"].list[self["list"].getSelectedIndex()][0]
192                 self.session.openWithCallback(
193                         self.renameTagCallback,
194                         InputBox,
195                         title = _("Replace tag \"%s\" everywhere with:   (Note that 'Cancel' will not undo this!)") % (self.thistag[1]),
196                         text = self.thistag[1]
197                 )
198
199         def renameTagCallback(self, res):
200                 res = res and res.strip().replace(" ", "_").capitalize()
201                 if res and len(res) and res != self.thistag[1]:
202                         thistag = self.thistag[1]
203                         self.foreachTimerTags(lambda t, tg: (thistag in tg) and self.setTimerTags(t, self.listReplace(tg, thistag, res)))
204                         self.foreachMovieTags(lambda r, tg: (thistag in tg) and self.setMovieTags(r, self.listReplace(tg, thistag, res)))
205                         self.listReplace(self.tags, thistag, res)
206                         self.listReplace(self.ghosttags, thistag, res)
207                         self.updateMenuList(self.tags, self.thistag[3] and [res] or [])
208
209         def removeTag(self):
210                 self.thistag = self["list"].list[self["list"].getSelectedIndex()][0]
211                 self.session.openWithCallback(
212                         self.removeTagCallback,
213                         MessageBox,
214                         _("Do you really want to delete tag \"%s\" everywhere?\n(Note that 'Cancel' will not undo this!)") % (self.thistag[1])
215                 )
216
217         def removeTagCallback(self, res):
218                 if res:
219                         thistag = self.thistag[1]
220                         self.foreachTimerTags(lambda t, tg: (thistag in tg) and self.setTimerTags(t, self.listReplace(tg, thistag)))
221                         self.foreachMovieTags(lambda r, tg: (thistag in tg) and self.setMovieTags(r, self.listReplace(tg, thistag)))
222                         self.listReplace(self.tags, thistag)
223                         self.listReplace(self.ghosttags, thistag)
224                         self.updateMenuList(self.tags)
225
226         def removeAll(self):
227                 self.session.openWithCallback(
228                         self.removeAllCallback,
229                         MessageBox,
230                         _("Do you really want to delete all tags everywhere?\n(Note that 'Cancel' will not undo this!)")
231                 )
232
233         def removeAllCallback(self, res):
234                 if res:
235                         self.foreachTimerTags(lambda t, tg: tg and self.setTimerTags(t, []))
236                         self.foreachMovieTags(lambda r, tg: tg and self.setMovieTags(r, []))
237                         self.tags = []
238                         self.ghosttags = []
239                         self.updateMenuList(self.tags)
240
241         def showMenu(self):
242                 menu = []
243                 menu.append((_("Add new tag"), self.addCustom))
244                 menu.append((_("Rename this tag"), self.renameTag))
245                 menu.append((_("Delete this tag"), self.removeTag))
246                 menu.append((_("Delete unused tags"), self.removeUnused))
247                 menu.append((_("Delete all tags"), self.removeAll))
248                 self.session.openWithCallback(self.menuCallback, ChoiceBox, title="", list=menu)
249
250         def menuCallback(self, choice):
251                 if choice is None:
252                         return
253                 choice[1]()
254
255         def cancel(self):
256                 if not self.origtags == self.ghosttags:
257                         self.saveTagsFile(self.ghosttags)
258                 self.close(None)
259
260         def accept(self):
261                 list = [x[1] for x in self["list"].getSelectionsList()]
262                 if not self.origtags == self.tags:
263                         self.saveTagsFile(self.tags)
264                 self.close(list)
265
266 class MovieTagEditor(TagEditor):
267         def __init__(self, session, service, args = 0):
268                 self.service = service
269                 serviceHandler = eServiceCenter.getInstance()
270                 info = serviceHandler.info(self.service)
271                 self.path = self.service.getPath()
272                 if self.path.endswith(".ts") is True:
273                         self.path = self.path[:-3]
274                 tags = info.getInfoString(self.service, iServiceInformation.sTags)
275                 if tags:
276                         self.tags = tags.split(' ')
277                 else:
278                         self.tags = []
279                 TagEditor.__init__(self, session, self.tags, args)
280 #               self.session.openWithCallback(self.tagsConfirmed, TagEditor, self.tags)
281
282         def saveTags(self, file, tags):
283                 if os.path.exists(file + ".ts.meta"):
284                         metafile = open(file + ".ts.meta", "r")
285                         sid = metafile.readline()
286                         title = metafile.readline()
287                         descr = metafile.readline()
288                         time = metafile.readline()
289                         oldtags = metafile.readline().rstrip()
290                         metafile.close()
291                         tags = " ".join(tags)
292                         if tags != oldtags:
293                                 metafile = open(file + ".ts.meta", "w")
294                                 metafile.write("%s%s%s%s%s" %(sid, title, descr, time, tags))
295                                 metafile.close()
296
297         def cancel(self):
298                 if not self.origtags == self.ghosttags:
299                         self.saveTagsFile(self.ghosttags)
300                         self.exitDialog()
301                 else:
302                         self.close()
303
304         def accept(self):
305                 list = [x[1] for x in self["list"].getSelectionsList()]
306                 if not self.origtags == self.tags:
307                         self.saveTagsFile(self.tags)
308                 self.saveTags(self.path, list)
309                 self.exitDialog()
310
311         def exitDialog(self):
312                 self.close()
313                 # This will try to get back to an updated movie list.
314                 # A proper way to do this should be provided in enigma2.
315                 try:
316                         self.session.current_dialog.csel.reloadList()
317                         self.session.current_dialog.close()
318                 except AttributeError:
319                         pass