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