1 # -*- coding: utf-8 -*-
5 import os, sys, traceback
6 from time import localtime, strftime
7 from datetime import datetime
12 from datetime import datetime
14 from Components.config import config
16 from enigma import eServiceReference, iServiceInformation, eServiceCenter, ePythonMessagePump
17 from ServiceReference import ServiceReference
20 from Modules import Modules
23 from Tools.BoundFunction import boundFunction
24 from Tools.Directories import resolveFilename, SCOPE_PLUGINS
25 from Tools.Notifications import AddPopup
26 from Screens.MessageBox import MessageBox
29 from IdentifierBase import IdentifierBase
30 from ManagerBase import ManagerBase
31 from GuideBase import GuideBase
32 from Channels import ChannelsBase, removeEpisodeInfo, lookupServiceAlternatives
33 from Logger import splog
34 from CancelableThread import QueueWithTimeOut, CancelableThread, synchronized, myLock
36 from ThreadQueue import ThreadQueue
37 from threading import Thread
38 from enigma import ePythonMessagePump
42 if(config.plugins.autotimer.timeout.value == 1):
43 config.plugins.autotimer.timeout.value = 5
44 config.plugins.autotimer.save()
45 except Exception as e:
50 AUTOTIMER_PATH = os.path.join( resolveFilename(SCOPE_PLUGINS), "Extensions/AutoTimer/" )
51 SERIESPLUGIN_PATH = os.path.join( resolveFilename(SCOPE_PLUGINS), "Extensions/SeriesPlugin/" )
57 CompiledRegexpNonDecimal = re.compile(r'[^\d]+')
58 CompiledRegexpNonAlphanum = re.compile(r'[^-_\.()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789äÄüÜöÖ ]+')
62 splog( "SP: %s = %s" % (attr, getattr(obj, attr)) )
70 from plugin import VERSION
72 splog("SP: SERIESPLUGIN NEW INSTANCE " + VERSION)
77 from Tools.HardwareInfo import HardwareInfo
78 splog( "SP: DeviceName " + HardwareInfo().get_device_name().strip() )
83 from Components.About import about
84 splog( "SP: EnigmaVersion " + about.getEnigmaVersionString().strip() )
85 splog( "SP: ImageVersion " + about.getVersionString().strip() )
90 #http://stackoverflow.com/questions/1904394/python-selecting-to-read-the-first-line-only
91 splog( "SP: dreamboxmodel " + open("/proc/stb/info/model").readline().strip() )
92 splog( "SP: imageversion " + open("/etc/image-version").readline().strip() )
93 splog( "SP: imageissue " + open("/etc/issue.net").readline().strip() )
98 for key, value in config.plugins.seriesplugin.dict().iteritems():
99 splog( "SP: config..%s = %s" % (key, str(value.value)) )
100 except Exception as e:
104 # if os.path.exists(SERIESPLUGIN_PATH):
105 # dirList = os.listdir(SERIESPLUGIN_PATH)
106 # for fname in dirList:
107 # splog( "SP: ", fname, datetime.fromtimestamp( int( os.path.getctime( os.path.join(SERIESPLUGIN_PATH,fname) ) ) ).strftime('%Y-%m-%d %H:%M:%S') )
108 #except Exception as e:
111 # if os.path.exists(AUTOTIMER_PATH):
112 # dirList = os.listdir(AUTOTIMER_PATH)
113 # for fname in dirList:
114 # splog( "SP: ", fname, datetime.fromtimestamp( int( os.path.getctime( os.path.join(AUTOTIMER_PATH,fname) ) ) ).strftime('%Y-%m-%d %H:%M:%S') )
115 #except Exception as e:
118 instance = SeriesPlugin()
119 #instance[os.getpid()] = SeriesPlugin()
120 splog( "SP: ", strftime("%a, %d %b %Y %H:%M:%S", localtime()) )
125 #Rename to closeInstance
127 if instance is not None:
128 splog("SP: SERIESPLUGIN INSTANCE STOP")
131 from Cacher import cache
136 def refactorTitle(org, data):
138 season, episode, title, series = data
139 if config.plugins.seriesplugin.pattern_title.value and not config.plugins.seriesplugin.pattern_title.value == "Off":
140 splog("SP: refactor org", org)
141 org = CompiledRegexpNonAlphanum.sub('', org)
142 splog("SP: refactor org", org)
143 splog("SP: refactor title", title)
144 title = CompiledRegexpNonAlphanum.sub('', title)
145 splog("SP: refactor title", title)
146 splog("SP: refactor series", series)
147 series = CompiledRegexpNonAlphanum.sub('', series)
148 splog("SP: refactor series", series)
149 return config.plugins.seriesplugin.pattern_title.value.strip().format( **{'org': org, 'season': season, 'episode': episode, 'title': title, 'series': series} )
155 def refactorDescription(org, data):
157 season, episode, title, series = data
158 if config.plugins.seriesplugin.pattern_description.value and not config.plugins.seriesplugin.pattern_description.value == "Off":
159 #if season == 0 and episode == 0:
160 # description = config.plugins.seriesplugin.pattern_description.value.strip().format( **{'org': org, 'title': title, 'series': series} )
162 description = config.plugins.seriesplugin.pattern_description.value.strip().format( **{'org': org, 'season': season, 'episode': episode, 'title': title, 'series': series} )
163 description = description.replace("\n", " ")
171 def __init__(self, identifier = None, callback = None, name = None, begin = None, end = None, service = None, channels = None):
172 self.identifier = identifier
173 self.callback = callback
177 self.service = service
178 self.channels = channels
180 class SeriesPluginWorker(Thread):
183 Thread.__init__(self)
184 self.__running = False
185 self.__messages = ThreadQueue()
186 self.__messagePump = ePythonMessagePump()
189 def __getMessagePump(self):
190 return self.__messagePump
191 MessagePump = property(__getMessagePump)
193 def __getMessageQueue(self):
194 return self.__messages
195 Message = property(__getMessageQueue)
197 def __getRunning(self):
198 return self.__running
199 isRunning = property(__getRunning)
201 def isListEmpty(self):
202 return self.__list and True
204 def Start(self, item):
205 if not self.__running:
206 self.__running = True
207 splog("SP: Worker: Start")
210 self.start() # Start blocking code in Thread
212 self.__list.append(item)
217 item = self.__list.pop(0)
218 splog('SP: Worker is processing: ', item.identifier)
219 # do processing stuff here
223 result = item.identifier.getEpisode(
224 item.name, item.begin, item.end, item.service, item.channels
227 splog("SP: Worker: Exception:", str(e))
229 # Exception finish job with error
233 splog("SP: Worker: result")
234 if result and len(result) == 4:
235 season, episode, title, series = result
236 season = int(CompiledRegexpNonDecimal.sub('', season))
237 episode = int(CompiledRegexpNonDecimal.sub('', episode))
238 title = title.strip()
239 splog("SP: Worker: result callback")
240 self.__messages.push((2, item.callback, season, episode, title, series,))
241 self.__messagePump.send(0)
243 splog("SP: Worker: result failed")
244 self.__messages.push((1, item.callback, result))
245 self.__messagePump.send(0)
247 splog("SP: Worker: Callback Exception:", str(e))
249 config.plugins.seriesplugin.lookup_counter.value += 1
250 config.plugins.seriesplugin.lookup_counter.save()
252 self.__messages.push((0, result,))
253 self.__messagePump.send(0)
254 self.__running = False
255 Thread.__init__(self)
256 splog('SP: Worker: list is emty, done')
258 seriespluginworker = SeriesPluginWorker()
260 class SeriesPlugin(Modules, ChannelsBase):
263 splog("SP: Main: Init")
264 Modules.__init__(self)
265 ChannelsBase.__init__(self)
267 self.serviceHandler = eServiceCenter.getInstance()
268 self.__pump_recv_msg_conn = seriespluginworker.MessagePump.recv_msg.connect(self.gotThreadMsg_seriespluginworker)
270 #http://bugs.python.org/issue7980
271 datetime.strptime('2012-01-01', '%Y-%m-%d')
273 self.identifier_elapsed = self.instantiateModuleWithName( config.plugins.seriesplugin.identifier_elapsed.value )
274 #splog(self.identifier_elapsed)
276 self.identifier_today = self.instantiateModuleWithName( config.plugins.seriesplugin.identifier_today.value )
277 #splog(self.identifier_today)
279 self.identifier_future = self.instantiateModuleWithName( config.plugins.seriesplugin.identifier_future.value )
280 #splog(self.identifier_future)
283 splog("SP: Main: stop")
284 if config.plugins.seriesplugin.lookup_counter.isChanged():
285 config.plugins.seriesplugin.lookup_counter.save()
288 ################################################
289 # Identifier functions
290 def getIdentifier(self, future=False, today=False, elapsed=False):
292 return self.identifier_elapsed and self.identifier_elapsed.getName()
294 return self.identifier_today and self.identifier_today.getName()
296 return self.identifier_future and self.identifier_future.getName()
300 def getEpisode(self, callback, name, begin, end=None, service=None, future=False, today=False, elapsed=False):
303 if config.plugins.seriesplugin.skip_during_records.value:
305 import NavigationInstance
306 if NavigationInstance.instance.RecordTimer.isRecording():
307 splog("SP: Main: Skip check during running records")
312 name = removeEpisodeInfo(name)
313 #name = removeEpisodeInfo(name)
314 if name.startswith("The ") or name.startswith("Der ") or name.startswith("Die ")or name.startswith("Das "):
317 begin = datetime.fromtimestamp(begin)
318 splog("SP: Main: begin:", begin.strftime('%Y-%m-%d %H:%M:%S'))
319 end = datetime.fromtimestamp(end)
320 splog("SP: Main: end:", end.strftime('%Y-%m-%d %H:%M:%S'))
323 identifier = self.identifier_elapsed
325 identifier = self.identifier_today
327 identifier = self.identifier_future
333 # Reset title search depth on every new request
334 identifier.search_depth = 0;
336 # Reset the knownids on every new request
337 identifier.knownids = []
339 channels = lookupServiceAlternatives(service)
341 seriespluginworker.Start(ThreadItem(identifier = identifier, callback = callback, name = name, begin = begin, end = end, service = service, channels = channels))
345 result = identifier.getEpisode(
346 name, begin, end, service, channels
349 if result and len(result) == 4:
351 season, episode, title, series = result
352 season = int(CompiledRegexpNonDecimal.sub('', season))
353 episode = int(CompiledRegexpNonDecimal.sub('', episode))
354 #title = CompiledRegexpNonAlphanum.sub(' ', title)
355 title = title.strip()
356 splog("SeriesPlugin result callback")
357 callback( (season, episode, title, series) )
359 config.plugins.seriesplugin.lookup_counter.value += 1
360 config.plugins.seriesplugin.lookup_counter.save()
363 splog("SeriesPlugin result failed")
366 except Exception as e:
367 splog("SeriesPlugin Callback Exception:", str(e))
371 return identifier.getName()
375 callback( "No identifier available" )
377 def gotThreadMsg_seriespluginworker(self, msg):
378 msg = seriespluginworker.Message.pop()
379 splog("SP: Main: gotThreadMsg:", msg)
382 if callable(callback):
383 callback((msg[2],msg[3],msg[4],msg[5]))
386 if callable(callback):
389 if (config.plugins.seriesplugin.lookup_counter.value == 10) \
390 or (config.plugins.seriesplugin.lookup_counter.value == 100) \
391 or (config.plugins.seriesplugin.lookup_counter.value % 1000 == 0):
392 from plugin import ABOUT
393 about = ABOUT.format( **{'lookups': config.plugins.seriesplugin.lookup_counter.value} )
396 MessageBox.TYPE_INFO,
402 splog("SP: Main: cancel")
403 self.__pump_recv_msg_conn = None