SeriesPlugin: Added show log from setup via blue button
[enigma2-plugins.git] / seriesplugin / src / SeriesPlugin.py
1 # by betonme @2012
2
3 import re
4 import os, sys, traceback
5 from time import localtime, strftime
6 from datetime import datetime
7
8 # Localization
9 from . import _
10
11 from datetime import datetime
12
13 from Components.config import config
14
15 from enigma import eServiceReference, iServiceInformation, eServiceCenter, ePythonMessagePump
16 from ServiceReference import ServiceReference
17
18 # Plugin framework
19 from Modules import Modules
20
21 # Tools
22 from Tools.BoundFunction import boundFunction
23 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
24 from Tools.Notifications import AddPopup
25 from Screens.MessageBox import MessageBox
26
27 # Plugin internal
28 from IdentifierBase import IdentifierBase
29 from ManagerBase import ManagerBase
30 from GuideBase import GuideBase
31 from Channels import ChannelsBase, removeEpisodeInfo, lookupServiceAlternatives
32 from Logger import splog
33 from CancelableThread import QueueWithTimeOut, CancelableThread, synchronized, myLock
34
35 from ThreadQueue import ThreadQueue
36 from threading import Thread
37 from enigma import ePythonMessagePump
38
39
40 try:
41         if(config.plugins.autotimer.timeout.value == 1):
42                 config.plugins.autotimer.timeout.value = 5
43                 config.plugins.autotimer.save()
44 except Exception as e:
45         pass
46
47
48 # Constants
49 AUTOTIMER_PATH  = os.path.join( resolveFilename(SCOPE_PLUGINS), "Extensions/AutoTimer/" )
50 SERIESPLUGIN_PATH  = os.path.join( resolveFilename(SCOPE_PLUGINS), "Extensions/SeriesPlugin/" )
51
52
53 # Globals
54 instance = None
55
56 CompiledRegexpNonDecimal = re.compile(r'[^\d]+')
57
58 def dump(obj):
59         for attr in dir(obj):
60                 splog( "config.plugins.seriesplugin.%s = %s" % (attr, getattr(obj, attr)) )
61
62
63 def getInstance():
64         global instance
65         
66         if instance is None:
67                 
68                 from plugin import VERSION
69                 
70                 splog("SERIESPLUGIN NEW INSTANCE " + VERSION)
71                 
72                 try:
73
74
75                         from Tools.HardwareInfo import HardwareInfo
76                         splog( "DeviceName " + HardwareInfo().get_device_name().strip() )
77                 except:
78                         pass
79                 
80                 try:
81                         from Components.About import about
82                         splog( "EnigmaVersion " + about.getEnigmaVersionString().strip() )
83                         splog( "ImageVersion " + about.getVersionString().strip() )
84                 except:
85                         pass
86                 
87                 try:
88                         #http://stackoverflow.com/questions/1904394/python-selecting-to-read-the-first-line-only
89                         splog( "dreamboxmodel " + open("/proc/stb/info/model").readline().strip() )
90                         splog( "imageversion " + open("/etc/image-version").readline().strip() )
91                         splog( "imageissue " + open("/etc/issue.net").readline().strip() )
92                 except:
93                         pass
94                 
95                 try:
96                         for key, value in config.plugins.seriesplugin.dict().iteritems():
97                                 splog( "config.plugins.seriesplugin.%s = %s" % (key, str(value.value)) )
98                 except Exception as e:
99                         pass
100                 
101                 #try:
102                 #       if os.path.exists(SERIESPLUGIN_PATH):
103                 #               dirList = os.listdir(SERIESPLUGIN_PATH)
104                 #               for fname in dirList:
105                 #                       splog( fname, datetime.fromtimestamp( int( os.path.getctime( os.path.join(SERIESPLUGIN_PATH,fname) ) ) ).strftime('%Y-%m-%d %H:%M:%S') )
106                 #except Exception as e:
107                 #       pass
108                 #try:
109                 #       if os.path.exists(AUTOTIMER_PATH):
110                 #               dirList = os.listdir(AUTOTIMER_PATH)
111                 #               for fname in dirList:
112                 #                       splog( fname, datetime.fromtimestamp( int( os.path.getctime( os.path.join(AUTOTIMER_PATH,fname) ) ) ).strftime('%Y-%m-%d %H:%M:%S') )
113                 #except Exception as e:
114                 #       pass
115                 
116                 instance = SeriesPlugin()
117                 #instance[os.getpid()] = SeriesPlugin()
118                 splog( strftime("%a, %d %b %Y %H:%M:%S", localtime()) )
119         
120         return instance
121
122 def resetInstance():
123         #Rename to closeInstance
124         global instance
125         if instance is not None:
126                 splog("SERIESPLUGIN INSTANCE STOP")
127                 instance.stop()
128                 instance = None
129         from Cacher import cache
130         global cache
131         cache = {}
132
133
134 def refactorTitle(org, data):
135         if data:
136                 season, episode, title, series = data
137                 if config.plugins.seriesplugin.pattern_title.value and not config.plugins.seriesplugin.pattern_title.value == "Off":
138                         #if season == 0 and episode == 0:
139                         #       return config.plugins.seriesplugin.pattern_title.value.strip().format( **{'org': org, 'title': title, 'series': series} )
140                         #else:
141                         return config.plugins.seriesplugin.pattern_title.value.strip().format( **{'org': org, 'season': season, 'episode': episode, 'title': title, 'series': series} )
142                 else:
143                         return org
144         else:
145                 return org
146
147 def refactorDescription(org, data):
148         if data:
149                 season, episode, title, series = data
150                 if config.plugins.seriesplugin.pattern_description.value and not config.plugins.seriesplugin.pattern_description.value == "Off":
151                         #if season == 0 and episode == 0:
152                         #       description = config.plugins.seriesplugin.pattern_description.value.strip().format( **{'org': org, 'title': title, 'series': series} )
153                         #else:
154                         description = config.plugins.seriesplugin.pattern_description.value.strip().format( **{'org': org, 'season': season, 'episode': episode, 'title': title, 'series': series} )
155                         description = description.replace("\n", " ")
156                         return description
157                 else:
158                         return org
159         else:
160                 return org
161                 
162 class ThreadItem:
163         def __init__(self, identifier = None, callback = None, name = None, begin = None, end = None, service = None, channels = None):
164                 self.identifier = identifier
165                 self.callback = callback
166                 self.name = name
167                 self.begin = begin
168                 self.end = end
169                 self.service = service
170                 self.channels = channels
171
172 class SeriesPluginWorker(Thread):
173
174         def __init__(self):
175                 Thread.__init__(self)
176                 self.__running = False
177                 self.__messages = ThreadQueue()
178                 self.__messagePump = ePythonMessagePump()
179                 self.__beginn = None
180                 self.__end = None
181
182         def __getMessagePump(self):
183                 return self.__messagePump
184         MessagePump = property(__getMessagePump)
185
186         def __getMessageQueue(self):
187                 return self.__messages
188         Message = property(__getMessageQueue)
189
190         def __getRunning(self):
191                 return self.__running
192         isRunning = property(__getRunning)
193
194         def Start(self, item):
195                 if not self.__running:
196                         self.__running = True
197                         splog("SeriesPluginRenamer")
198                         #self.__item = item
199                         self.__list = [item]
200                         self.start() # Start blocking code in Thread
201                 else:
202                         self.__list.append(item)
203         
204         def run(self):
205         
206                 while self.__list:
207                         item = self.__list.pop(0)
208                         splog('SeriesPluginWorker is processing: ', item.identifier)
209                         # do processing stuff here
210                         result = None
211                         
212                         try:
213                                 result = item.identifier.getEpisode(
214                                         item.name, item.begin, item.end, item.service, item.channels
215                                 )
216                         except Exception, e:
217                                 splog("SeriesPluginWorker Identifier Exception:", str(e))
218                                 
219                                 # Exception finish job with error
220                                 result = str(e)
221                         
222                         try:
223                                 splog("SeriesPluginWorker result")
224                                 if result and len(result) == 4:
225                                         season, episode, title, series = result
226                                         season = int(CompiledRegexpNonDecimal.sub('', season))
227                                         episode = int(CompiledRegexpNonDecimal.sub('', episode))
228                                         title = title.strip()
229                                         splog("SeriesPluginWorker result callback")
230                                         self.__messages.push((2, item.callback, season, episode, title, series,))
231                                         self.__messagePump.send(0)
232                                 else:
233                                         splog("SeriesPluginWorker result failed")
234                                         self.__messages.push((1, item.callback, result))
235                                         self.__messagePump.send(0)
236                         except Exception, e:
237                                 splog("SeriesPluginWorker Callback Exception:", str(e))
238                         
239                         config.plugins.seriesplugin.lookup_counter.value += 1
240                         config.plugins.seriesplugin.lookup_counter.save()
241                         
242                 self.__messages.push((0, result,))
243                 self.__messagePump.send(0)
244                 self.__running = False
245                 Thread.__init__(self)
246                 splog('SeriesPluginWorker] list is emty, done')
247
248 #TBD Because of E2 Update 05.2013
249 #                       if (config.plugins.seriesplugin.lookup_counter.value == 10) \
250 #                               or (config.plugins.seriesplugin.lookup_counter.value == 100) \
251 #                               or (config.plugins.seriesplugin.lookup_counter.value % 1000 == 0):
252 #                               from plugin import ABOUT
253 #                               about = ABOUT.format( **{'lookups': config.plugins.seriesplugin.lookup_counter.value} )
254 #                               AddPopup(
255 #                                       about,
256 #                                       MessageBox.TYPE_INFO,
257 #                                       0,
258 #                                       'SP_PopUp_ID_About'
259 #                               )
260
261 seriespluginworker = SeriesPluginWorker()
262
263 class SeriesPlugin(Modules, ChannelsBase):
264
265         def __init__(self):
266                 splog("SeriesPlugin")
267                 Modules.__init__(self)
268                 ChannelsBase.__init__(self)
269                 
270                 self.serviceHandler = eServiceCenter.getInstance()
271                 self.__pump_recv_msg_conn = seriespluginworker.MessagePump.recv_msg.connect(self.gotThreadMsg_seriespluginworker)
272                 #http://bugs.python.org/issue7980
273                 datetime.strptime('2012-01-01', '%Y-%m-%d')
274                         
275                 self.identifier_elapsed = self.instantiateModuleWithName( config.plugins.seriesplugin.identifier_elapsed.value )
276                 splog(self.identifier_elapsed)
277                 
278                 self.identifier_today = self.instantiateModuleWithName( config.plugins.seriesplugin.identifier_today.value )
279                 splog(self.identifier_today)
280                 
281                 self.identifier_future = self.instantiateModuleWithName( config.plugins.seriesplugin.identifier_future.value )
282                 splog(self.identifier_future)
283
284
285         def stop(self):
286                 splog("SeriesPlugin stop")
287                 if config.plugins.seriesplugin.lookup_counter.isChanged():
288                         config.plugins.seriesplugin.lookup_counter.save()
289                 self.saveXML()
290         
291         ################################################
292         # Identifier functions
293         def getIdentifier(self, future=False, today=False, elapsed=False):
294                 if elapsed:
295                         return self.identifier_elapsed and self.identifier_elapsed.getName()
296                 elif today:
297                         return self.identifier_today and self.identifier_today.getName()
298                 elif future:
299                         return self.identifier_future and self.identifier_future.getName()
300                 else:
301                         return None
302         
303         def getEpisode(self, callback, name, begin, end=None, service=None, future=False, today=False, elapsed=False):
304                 #available = False
305                 
306                 if config.plugins.seriesplugin.skip_during_records.value:
307                         try:
308                                 import NavigationInstance
309                                 if NavigationInstance.instance.RecordTimer.isRecording():
310                                         print("[SeriesPlugin]: Skip check during running records")
311                                         return
312                         except:
313                                 pass
314                 
315                 name = removeEpisodeInfo(name)
316                 name = removeEpisodeInfo(name)
317                 if name.startswith("The ") or name.startswith("Der ") or name.startswith("Die ")or name.startswith("Das "):
318                         name = name[4:]
319                 
320                 begin = datetime.fromtimestamp(begin)
321                 end = datetime.fromtimestamp(end)
322                 
323                 if elapsed:
324                         identifier = self.identifier_elapsed
325                 elif today:
326                         identifier = self.identifier_today
327                 elif future:
328                         identifier = self.identifier_future
329                 else:
330                         identifier = None
331                 
332                 # Reset title search depth
333                 identifier.search_depth = 0;
334                 
335                 if identifier:
336                         channels = lookupServiceAlternatives(service)
337
338                         seriespluginworker.Start(ThreadItem(identifier = identifier, callback = callback, name = name, begin = begin, end = end, service = service, channels = channels))
339                         '''
340                         try:
341                                 
342                                 
343                                 result = identifier.getEpisode(
344                                                                 name, begin, end, service, channels
345                                                         )
346                                 
347                                 if result and len(result) == 4:
348                                 
349                                         season, episode, title, series = result
350                                         season = int(CompiledRegexpNonDecimal.sub('', season))
351                                         episode = int(CompiledRegexpNonDecimal.sub('', episode))
352                                         #title = CompiledRegexpNonAlphanum.sub(' ', title)
353                                         title = title.strip()
354                                         splog("SeriesPlugin result callback")
355                                         callback( (season, episode, title, series) )
356                                         
357                                         config.plugins.seriesplugin.lookup_counter.value += 1
358                                         config.plugins.seriesplugin.lookup_counter.save()
359                                         
360                                 else:
361                                         splog("SeriesPlugin result failed")
362                                         callback( result )
363                                         
364                         except Exception as e:
365                                 splog("SeriesPlugin Callback Exception:", str(e))
366                                 callback( str(e) )
367                         '''
368                         
369 #TBD Because of E2 Update 05.2013
370                 #from threading import currentThread
371                 #if currentThread().getName() == 'MainThread':
372 #                       if (config.plugins.seriesplugin.lookup_counter.value == 10) \
373 #                               or (config.plugins.seriesplugin.lookup_counter.value == 100) \
374 #                               or (config.plugins.seriesplugin.lookup_counter.value % 1000 == 0):
375 #                               from plugin import ABOUT
376 #                               about = ABOUT.format( **{'lookups': config.plugins.seriesplugin.lookup_counter.value} )
377 #                               AddPopup(
378 #                                       about,
379 #                                       MessageBox.TYPE_INFO,
380 #                                       0,
381 #                                       'SP_PopUp_ID_About'
382 #                               )
383                         
384                         return identifier.getName()
385                         
386                 #if not available:
387                 else:
388                         callback( "No identifier available" )
389                         
390         def gotThreadMsg_seriespluginworker(self, msg):
391                 msg = seriespluginworker.Message.pop()
392                 print ('gotThreadMsg_seriespluginrenameservice]msg: %s' %str(msg))
393                 if msg[0] == 2:
394                         callback = msg[1]
395                         if callable(callback):
396                                 callback((msg[2],msg[3],msg[4],msg[5]))
397                 elif msg[0] == 1:
398                         callback = msg[1]
399                         if callable(callback):
400                                 callback(msg[2])
401                 self.renamerCallback()
402
403
404         def renamerCallback(self):
405                 if (config.plugins.seriesplugin.lookup_counter.value == 10) \
406                         or (config.plugins.seriesplugin.lookup_counter.value == 100) \
407                         or (config.plugins.seriesplugin.lookup_counter.value % 1000 == 0):
408                         from plugin import ABOUT
409                         about = ABOUT.format( **{'lookups': config.plugins.seriesplugin.lookup_counter.value} )
410                         AddPopup(
411                                 about,
412                                 MessageBox.TYPE_INFO,
413                                 0,
414                                 'SP_PopUp_ID_About'
415                         )
416
417         def cancel(self):
418                 self.__pump_recv_msg_conn = None
419                 self.stop()