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