SeriesPlugin: 1.5.8 Fixed encoding, popups, channel list integration
[enigma2-plugins.git] / seriesplugin / src / SeriesPluginRenamer.py
1 # -*- coding: utf-8 -*-
2 #######################################################################
3 #
4 #    Series Plugin for Enigma-2
5 #    Coded by betonme (c) 2012 <glaserfrank(at)gmail.com>
6 #    Support: http://www.i-have-a-dreambox.com/wbb2/thread.php?threadid=TBD
7 #
8 #    This program is free software; you can redistribute it and/or
9 #    modify it under the terms of the GNU General Public License
10 #    as published by the Free Software Foundation; either version 2
11 #    of the License, or (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #######################################################################
19
20 import os
21 import re
22 from glob import glob #Py3 ,escape
23
24 # for localized messages
25 from . import _
26
27 # Config
28 from Components.config import config
29
30 from Screens.MessageBox import MessageBox
31 from Tools.Notifications import AddPopup
32
33 from Tools.BoundFunction import boundFunction
34 from Tools.ASCIItranslit import ASCIItranslit
35
36 from enigma import eServiceCenter, iServiceInformation, eServiceReference
37 from ServiceReference import ServiceReference
38
39 # Plugin internal
40 from SeriesPlugin import getInstance, refactorTitle, refactorDescription   #, refactorRecord
41 from Logger import splog
42
43 CompiledRegexpGlobEscape = re.compile('([\[\]\?*])')  # "[\\1]"
44
45
46 # By Bin4ry
47 def newLegacyEncode(string):
48         string2 = ""
49         for z, char in enumerate(string.decode("utf-8")):
50                 i = ord(char)
51                 if i < 33:
52                         string2 += " "
53                 elif i in ASCIItranslit:
54                         # There is a bug in the E2 ASCIItranslit some (not all) german-umlaut(a) -> AE
55                         if char.islower():
56                                 string2 += ASCIItranslit[i].lower()
57                         else:
58                                 string2 += ASCIItranslit[i]
59                                 
60                 else:
61                         try:
62                                 string2 += char.encode('ascii', 'strict')
63                         except:
64                                 string2 += " "
65         return string2
66
67
68 def rename(service, name, short, data):
69         # Episode data available
70         splog("SPR: rename:", data)
71         
72         #MAYBE Check if it is already renamed?
73         try:
74                 # Before renaming change content
75                 renameMeta(service, data)
76                 if config.plugins.seriesplugin.pattern_title.value and not config.plugins.seriesplugin.pattern_title.value == "Off":
77
78                         if config.plugins.seriesplugin.rename_file.value == True:
79                                 renameFile(service, name, data)
80                 #if config.plugins.seriesplugin.pattern_record.value and not config.plugins.seriesplugin.pattern_record.value == "Off":
81                 #       renameFile(service, name, data)
82                 return True
83         except:
84                 #pass
85                 raise
86         return False
87
88 # Adapted from MovieRetitle setTitleDescr
89 def renameMeta(service, data):
90         try:
91                 #TODO Use MetaSupport EitSupport classes from EMC ?
92                 if service.getPath().endswith(".ts"):
93                         meta_file = service.getPath() + ".meta"
94                 else:
95                         meta_file = service.getPath() + ".ts.meta"
96                 
97                 # Create new meta for ts files
98                 if not os.path.exists(meta_file):
99                         if os.path.isfile(service.getPath()):
100                                 _title = os.path.basename(os.path.splitext(service.getPath())[0])
101                         else:
102                                 _title = service.getName()
103                         _sid = ""
104                         _descr = ""
105                         _time = ""
106                         _tags = ""
107                         metafile = open(meta_file, "w")
108                         metafile.write("%s\n%s\n%s\n%s\n%s" % (_sid, _title, _descr, _time, _tags))
109                         metafile.close()
110                 
111                 if os.path.exists(meta_file):
112                         metafile = open(meta_file, "r")
113                         sid = metafile.readline()
114                         oldtitle = metafile.readline().rstrip()
115                         olddescr = metafile.readline().rstrip()
116                         rest = metafile.read()
117                         metafile.close()
118                         
119                         if config.plugins.seriesplugin.pattern_title.value and not config.plugins.seriesplugin.pattern_title.value == "Off":
120                                 title = refactorTitle(oldtitle, data)
121                         else:
122                                 title = oldtitle
123                         splog("SPR: title",title)
124                         if config.plugins.seriesplugin.pattern_description.value and not config.plugins.seriesplugin.pattern_description.value == "Off":
125                                 descr = refactorDescription(olddescr, data)
126                         else:
127                                 descr = olddescr
128                         splog("SPR: descr",descr)
129                         
130                         metafile = open(meta_file, "w")
131                         metafile.write("%s%s\n%s\n%s" % (sid, title, descr, rest))
132                         metafile.close()
133         except Exception as e:
134                 splog("SPR: renameMeta:", e)
135
136 def renameFile(service, name, data, tidy=False):
137         try:
138                 servicepath = service.getPath()
139                 splog("SPR: servicepath", servicepath)
140                 
141                 path = os.path.dirname(servicepath)
142                 file_name = os.path.basename(os.path.splitext(servicepath)[0])
143                 splog("SPR: file_name", file_name)
144                 
145                 splog("SPR: name     ", name)
146                 # Refactor title
147                 if config.plugins.seriesplugin.rename_tidy.value or tidy:
148                         name = refactorTitle(name, data)
149                 else:
150                         name = refactorTitle(file_name, data)
151                 splog("SPR: name     ", name)
152                 #if config.recording.ascii_filenames.value:
153                 #       filename = ASCIItranslit.legacyEncode(filename)
154                 if config.plugins.seriesplugin.rename_legacy.value:
155                         name = newLegacyEncode(name)
156                         splog("SPR: name     ", name)
157                 
158                 src = os.path.join(path, file_name)
159                 splog("SPR: servicepathSrc", src)
160                 dst = os.path.join(path, name)
161                 splog("SPR: servicepathDst", dst)
162
163                 #Py3 for f in glob( escape(src) + "*" ):
164                 glob_src = CompiledRegexpGlobEscape.sub("[\\1]", src)
165                 splog("SPR: glob_src      ", glob_src)
166                 for f in glob( glob_src + "*" ):
167                         splog("SPR: servicepathRnm", f)
168                         to = f.replace(src, dst)
169                         splog("SPR: servicepathTo ", to)
170                         if not os.path.exists(to):
171                                 os.rename(f, to)
172                         elif config.plugins.seriesplugin.rename_existing_files.value:
173                                 splog("SPR: Destination file alreadey exists", to, " - Append _")
174                                 renameFile(service, name + "_", data, True)
175                                 break
176                         else:
177                                 splog("SPR: Destination file alreadey exists", to, " - Skip rename")
178         except Exception as e:
179                 splog("SPR: renameFile:", e)
180
181
182 from ThreadQueue import ThreadQueue
183 from threading import Thread
184 from enigma import ePythonMessagePump
185
186 class SeriesPluginRenameService(Thread):
187         
188         def __init__(self):
189                 Thread.__init__(self)
190                 self.__running = False
191                 self.__messages = ThreadQueue()
192                 self.__messagePump = ePythonMessagePump()
193
194         def __getMessagePump(self):
195                 return self.__messagePump
196         MessagePump = property(__getMessagePump)
197
198         def __getMessageQueue(self):
199                 return self.__messages
200         Message = property(__getMessageQueue)
201
202         def __getRunning(self):
203                 return self.__running
204         isRunning = property(__getRunning)
205
206         def Start(self, services):
207         
208                 if not self.__running:
209                         self.__running = True
210                         self.__services = services
211                         self.start() # Start blocking code in Thread 
212                         
213         def run(self):
214                 
215                 for service in self.__services:
216                         
217                         splog("SPR: run")
218                         seriesPlugin = getInstance()
219                         self.serviceHandler = eServiceCenter.getInstance()
220                         
221                         if isinstance(service, eServiceReference):
222                                 service = service
223                         elif isinstance(service, ServiceReference):
224                                 service = service.ref
225                         else:
226                                 splog("SPR: Wrong instance")
227                                 self.__messages.push(service)
228                                 self.__messagePump.send(0)
229                                 self.__running = False
230                                 Thread.__init__(self)
231                                 return
232                         
233                         if not os.path.exists( service.getPath() ):
234                                 splog("SPR: File not exists: " + service.getPath())
235                                 self.__messages.push(service)
236                                 self.__messagePump.send(0)
237                                 self.__running = False
238                                 Thread.__init__(self)
239                                 return
240                         
241                         info = self.serviceHandler.info(service)
242                         if not info:
243                                 splog("SPR: No info available: " + service.getPath())
244                                 self.__messages.push(service)
245                                 self.__messagePump.send(0)
246                                 self.__running = False
247                                 Thread.__init__(self)
248                                 return 
249                         
250                         name = service.getName() or info.getName(service) or ""
251                         #splog("SPR: name", name)
252                         
253                         short = ""
254                         begin = None
255                         end = None
256                         duration = 0
257                         
258                         event = info.getEvent(service)
259                         if event:
260                                 short = event.getShortDescription()
261                                 begin = event.getBeginTime()
262                                 duration = event.getDuration() or 0
263                                 end = begin + duration or 0
264                                 # We got the exact start times, no need for margin handling
265                         
266                         if not begin:
267                                 begin = info.getInfo(service, iServiceInformation.sTimeCreate) or -1
268                                 if begin != -1:
269                                         end = begin + (info.getLength(service) or 0)
270                                 else:
271                                         end = os.path.getmtime(service.getPath())
272                                         begin = end - (info.getLength(service) or 0)
273                                 #MAYBE we could also try to parse the filename
274                                 # We don't know the exact margins, we will assume the E2 default margins
275                                 begin + (int(config.recording.margin_before.value) * 60)
276                                 end - (int(config.recording.margin_after.value) * 60)
277                         
278                         rec_ref_str = info.getInfoString(service, iServiceInformation.sServiceref)
279                         #channel = ServiceReference(rec_ref_str).getServiceName()
280                         
281                         splog("SPR: getEpisode:", name, begin, end)
282                         seriesPlugin.getEpisode(
283                                         boundFunction(self.serviceCallback, service, name, short),
284                                         name, begin, end, rec_ref_str, elapsed=True
285                                 )
286
287         def serviceCallback(self, service, name, short, data=None):
288                 splog("SPR: serviceCallback", name, data)
289                 
290                 result = None
291                 
292                 if data and len(data) == 4:
293                         if rename(service, name, short, data):
294                                 # Rename was successfully
295                                 result = None
296                 elif data:
297                         result = service.getPath() + " : " + str( data )
298                 else:
299                         result = service.getPath()
300                 
301                 self.__messages.push(result)
302                 self.__messagePump.send(0)
303
304                 self.__running = False
305                 Thread.__init__(self)
306                 splog("SPR: Service done")
307
308                 
309 seriespluginrenameservice = SeriesPluginRenameService()
310
311 #######################################################
312 # Rename movies
313 class SeriesPluginRenamer(object):
314         def __init__(self, session, services, *args, **kwargs):
315                 
316                 splog("SPR: SeriesPluginRenamer")
317                 
318                 if services and not isinstance(services, list):
319                         services = [services]   
320                 
321                 splog("SPR: len()", len(services))
322                 
323                 self.services = services
324                 
325                 self.data = []
326                 self.counter = 0
327                 self.__pump_recv_msg_conn = None
328                 
329                 session.openWithCallback(
330                         self.confirm,
331                         MessageBox,
332                         _("Do You want to start renaming?"),
333                         MessageBox.TYPE_YESNO,
334                         timeout = 15,
335                         default = True
336                 )
337
338         def confirm(self, confirmed):
339                 if confirmed and self.services:
340                         try:
341                                 self.__pump_recv_msg_conn = seriespluginrenameservice.MessagePump.recv_msg.connect(self.gotThreadMsg_seriespluginrenameservice) # interconnect to thread start
342                         except:
343                                 seriespluginrenameservice.MessagePump.recv_msg.get().append(self.gotThreadMsg_seriespluginrenameservice) # interconnect to thread start
344                         seriespluginrenameservice.Start(self.services)
345
346         def gotThreadMsg_seriespluginrenameservice(self, msg):
347                 msg = seriespluginrenameservice.Message.pop()
348                 splog("SPR: gotThreadMsg", msg)
349                 self.renamerCallback(msg)
350                 self.__pump_recv_msg_conn = None
351                 try:
352                         seriespluginrenameservice.MessagePump.recv_msg.get().remove(self.gotThreadMsg_seriespluginrenameservice) # interconnect to thread stop
353                 except:
354                         pass
355
356         def renamerCallback(self, result=None):
357                 splog("SPR: renamerCallback", result)
358                 
359                 if result and isinstance(result, basestring):
360                         self.data.append( result )
361                 
362                 if config.plugins.seriesplugin.rename_popups.value or config.plugins.seriesplugin.rename_popups_success.value:
363                         
364                         self.counter = self.counter +1
365                         
366                         if self.data or config.plugins.seriesplugin.rename_popups_success.value:
367                         
368                                 # Maybe there is a better way to avoid multiple Popups
369                                 from SeriesPlugin import seriespluginworker
370                                 
371                                 splog("SPR: renamerCallback getListLength", not seriespluginworker or seriespluginworker.getListLength(), not seriespluginworker or seriespluginworker.isListEmpty() )
372                                 
373                                 if not seriespluginworker or seriespluginworker.isListEmpty():
374                                         if self.data:
375                                                 AddPopup(
376                                                         "SeriesPlugin:\n" + _("Record rename has been finished with %d errors:\n") % (len(self.data)) +"\n" +"\n".join(self.data),
377                                                         MessageBox.TYPE_ERROR,
378                                                         int(config.plugins.seriesplugin.rename_popups_timeout.value),
379                                                         'SP_PopUp_ID_RenameFinished'
380                                                 )
381                                         else:
382                                                 AddPopup(
383                                                         "SeriesPlugin:\n" + _("%d records renamed successfully") % (self.counter),
384                                                         MessageBox.TYPE_INFO,
385                                                         int(config.plugins.seriesplugin.rename_popups_timeout.value),
386                                                         'SP_PopUp_ID_RenameFinished'
387                                                 )
388                                         self.data = []
389                                         self.counter = 0