4 import os, sys, traceback
5 from time import localtime, strftime
6 from datetime import datetime
11 from datetime import datetime
13 from Components.config import config
15 from enigma import eServiceReference, iServiceInformation, eServiceCenter, ePythonMessagePump
16 from ServiceReference import ServiceReference
19 from Modules import Modules
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
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
35 from ThreadQueue import ThreadQueue
36 from threading import Thread
37 from enigma import ePythonMessagePump
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:
49 AUTOTIMER_PATH = os.path.join( resolveFilename(SCOPE_PLUGINS), "Extensions/AutoTimer/" )
50 SERIESPLUGIN_PATH = os.path.join( resolveFilename(SCOPE_PLUGINS), "Extensions/SeriesPlugin/" )
56 CompiledRegexpNonDecimal = re.compile(r'[^\d]+')
57 CompiledRegexpNonAlphanum = re.compile(r'[^a-zA-Z0-9_ ]+')
61 splog( "SP: %s = %s" % (attr, getattr(obj, attr)) )
69 from plugin import VERSION
71 splog("SP: SERIESPLUGIN NEW INSTANCE " + VERSION)
76 from Tools.HardwareInfo import HardwareInfo
77 splog( "SP: DeviceName " + HardwareInfo().get_device_name().strip() )
82 from Components.About import about
83 splog( "SP: EnigmaVersion " + about.getEnigmaVersionString().strip() )
84 splog( "SP: ImageVersion " + about.getVersionString().strip() )
89 #http://stackoverflow.com/questions/1904394/python-selecting-to-read-the-first-line-only
90 splog( "SP: dreamboxmodel " + open("/proc/stb/info/model").readline().strip() )
91 splog( "SP: imageversion " + open("/etc/image-version").readline().strip() )
92 splog( "SP: imageissue " + open("/etc/issue.net").readline().strip() )
97 for key, value in config.plugins.seriesplugin.dict().iteritems():
98 splog( "SP: config..%s = %s" % (key, str(value.value)) )
99 except Exception as e:
103 # if os.path.exists(SERIESPLUGIN_PATH):
104 # dirList = os.listdir(SERIESPLUGIN_PATH)
105 # for fname in dirList:
106 # splog( "SP: ", fname, datetime.fromtimestamp( int( os.path.getctime( os.path.join(SERIESPLUGIN_PATH,fname) ) ) ).strftime('%Y-%m-%d %H:%M:%S') )
107 #except Exception as e:
110 # if os.path.exists(AUTOTIMER_PATH):
111 # dirList = os.listdir(AUTOTIMER_PATH)
112 # for fname in dirList:
113 # splog( "SP: ", fname, datetime.fromtimestamp( int( os.path.getctime( os.path.join(AUTOTIMER_PATH,fname) ) ) ).strftime('%Y-%m-%d %H:%M:%S') )
114 #except Exception as e:
117 instance = SeriesPlugin()
118 #instance[os.getpid()] = SeriesPlugin()
119 splog( "SP: ", strftime("%a, %d %b %Y %H:%M:%S", localtime()) )
124 #Rename to closeInstance
126 if instance is not None:
127 splog("SP: SERIESPLUGIN INSTANCE STOP")
130 from Cacher import cache
135 def refactorTitle(org, data):
137 season, episode, title, series = data
138 if config.plugins.seriesplugin.pattern_title.value and not config.plugins.seriesplugin.pattern_title.value == "Off":
139 splog("SP: refactor org", org)
140 org = CompiledRegexpNonAlphanum.sub('', org)
141 splog("SP: refactor org", org)
142 splog("SP: refactor title", title)
143 title = CompiledRegexpNonAlphanum.sub('', title)
144 splog("SP: refactor title", title)
145 splog("SP: refactor series", series)
146 series = CompiledRegexpNonAlphanum.sub('', series)
147 splog("SP: refactor series", series)
148 return config.plugins.seriesplugin.pattern_title.value.strip().format( **{'org': org, 'season': season, 'episode': episode, 'title': title, 'series': series} )
154 def refactorDescription(org, data):
156 season, episode, title, series = data
157 if config.plugins.seriesplugin.pattern_description.value and not config.plugins.seriesplugin.pattern_description.value == "Off":
158 #if season == 0 and episode == 0:
159 # description = config.plugins.seriesplugin.pattern_description.value.strip().format( **{'org': org, 'title': title, 'series': series} )
161 description = config.plugins.seriesplugin.pattern_description.value.strip().format( **{'org': org, 'season': season, 'episode': episode, 'title': title, 'series': series} )
162 description = description.replace("\n", " ")
170 def __init__(self, identifier = None, callback = None, name = None, begin = None, end = None, service = None, channels = None):
171 self.identifier = identifier
172 self.callback = callback
176 self.service = service
177 self.channels = channels
179 class SeriesPluginWorker(Thread):
182 Thread.__init__(self)
183 self.__running = False
184 self.__messages = ThreadQueue()
185 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 Start(self, item):
202 if not self.__running:
203 self.__running = True
204 splog("SP: Worker: Start")
207 self.start() # Start blocking code in Thread
209 self.__list.append(item)
214 item = self.__list.pop(0)
215 splog('SP: Worker is processing: ', item.identifier)
216 # do processing stuff here
220 result = item.identifier.getEpisode(
221 item.name, item.begin, item.end, item.service, item.channels
224 splog("SP: Worker: Exception:", str(e))
226 # Exception finish job with error
230 splog("SP: Worker: result")
231 if result and len(result) == 4:
232 season, episode, title, series = result
233 season = int(CompiledRegexpNonDecimal.sub('', season))
234 episode = int(CompiledRegexpNonDecimal.sub('', episode))
235 title = title.strip()
236 splog("SP: Worker: result callback")
237 self.__messages.push((2, item.callback, season, episode, title, series,))
238 self.__messagePump.send(0)
240 splog("SP: Worker: result failed")
241 self.__messages.push((1, item.callback, result))
242 self.__messagePump.send(0)
244 splog("SP: Worker: Callback Exception:", str(e))
246 config.plugins.seriesplugin.lookup_counter.value += 1
247 config.plugins.seriesplugin.lookup_counter.save()
249 self.__messages.push((0, result,))
250 self.__messagePump.send(0)
251 self.__running = False
252 Thread.__init__(self)
253 splog('SP: Worker: list is emty, done')
255 seriespluginworker = SeriesPluginWorker()
257 class SeriesPlugin(Modules, ChannelsBase):
260 splog("SP: Main: Init")
261 Modules.__init__(self)
262 ChannelsBase.__init__(self)
264 self.serviceHandler = eServiceCenter.getInstance()
265 self.__pump_recv_msg_conn = seriespluginworker.MessagePump.recv_msg.connect(self.gotThreadMsg_seriespluginworker)
267 #http://bugs.python.org/issue7980
268 datetime.strptime('2012-01-01', '%Y-%m-%d')
270 self.identifier_elapsed = self.instantiateModuleWithName( config.plugins.seriesplugin.identifier_elapsed.value )
271 #splog(self.identifier_elapsed)
273 self.identifier_today = self.instantiateModuleWithName( config.plugins.seriesplugin.identifier_today.value )
274 #splog(self.identifier_today)
276 self.identifier_future = self.instantiateModuleWithName( config.plugins.seriesplugin.identifier_future.value )
277 #splog(self.identifier_future)
280 splog("SP: Main: stop")
281 if config.plugins.seriesplugin.lookup_counter.isChanged():
282 config.plugins.seriesplugin.lookup_counter.save()
285 ################################################
286 # Identifier functions
287 def getIdentifier(self, future=False, today=False, elapsed=False):
289 return self.identifier_elapsed and self.identifier_elapsed.getName()
291 return self.identifier_today and self.identifier_today.getName()
293 return self.identifier_future and self.identifier_future.getName()
297 def getEpisode(self, callback, name, begin, end=None, service=None, future=False, today=False, elapsed=False):
300 if config.plugins.seriesplugin.skip_during_records.value:
302 import NavigationInstance
303 if NavigationInstance.instance.RecordTimer.isRecording():
304 splog("SP: Main: Skip check during running records")
309 name = removeEpisodeInfo(name)
310 #name = removeEpisodeInfo(name)
311 if name.startswith("The ") or name.startswith("Der ") or name.startswith("Die ")or name.startswith("Das "):
314 begin = datetime.fromtimestamp(begin)
315 end = datetime.fromtimestamp(end)
318 identifier = self.identifier_elapsed
320 identifier = self.identifier_today
322 identifier = self.identifier_future
328 # Reset title search depth on every new request
329 identifier.search_depth = 0;
331 # Reset the knownids on every new request
332 identifier.knownids = []
334 channels = lookupServiceAlternatives(service)
336 seriespluginworker.Start(ThreadItem(identifier = identifier, callback = callback, name = name, begin = begin, end = end, service = service, channels = channels))
340 result = identifier.getEpisode(
341 name, begin, end, service, channels
344 if result and len(result) == 4:
346 season, episode, title, series = result
347 season = int(CompiledRegexpNonDecimal.sub('', season))
348 episode = int(CompiledRegexpNonDecimal.sub('', episode))
349 #title = CompiledRegexpNonAlphanum.sub(' ', title)
350 title = title.strip()
351 splog("SeriesPlugin result callback")
352 callback( (season, episode, title, series) )
354 config.plugins.seriesplugin.lookup_counter.value += 1
355 config.plugins.seriesplugin.lookup_counter.save()
358 splog("SeriesPlugin result failed")
361 except Exception as e:
362 splog("SeriesPlugin Callback Exception:", str(e))
366 return identifier.getName()
370 callback( "No identifier available" )
372 def gotThreadMsg_seriespluginworker(self, msg):
373 msg = seriespluginworker.Message.pop()
374 splog("SP: Main: gotThreadMsg:", msg)
377 if callable(callback):
378 callback((msg[2],msg[3],msg[4],msg[5]))
381 if callable(callback):
384 if (config.plugins.seriesplugin.lookup_counter.value == 10) \
385 or (config.plugins.seriesplugin.lookup_counter.value == 100) \
386 or (config.plugins.seriesplugin.lookup_counter.value % 1000 == 0):
387 from plugin import ABOUT
388 about = ABOUT.format( **{'lookups': config.plugins.seriesplugin.lookup_counter.value} )
391 MessageBox.TYPE_INFO,
397 splog("SP: Main: cancel")
398 self.__pump_recv_msg_conn = None