from couchpotato import addView from couchpotato.core.event import fireEvent, addEvent from couchpotato.core.helpers.encoding import tryUrlencode, simplifyString, ss from couchpotato.core.helpers.variable import getExt from couchpotato.core.logger import CPLog from couchpotato.environment import Env from flask.templating import render_template_string from multipartpost import MultipartPostHandler from urlparse import urlparse import cookielib import glob import math import os.path import re import time import traceback import urllib2 log = CPLog(__name__) class Plugin(object): enabled_option = 'enabled' auto_register_static = True _needs_shutdown = False http_last_use = {} http_time_between_calls = 0 http_failed_request = {} http_failed_disabled = {} def registerPlugin(self): addEvent('app.shutdown', self.doShutdown) addEvent('plugin.running', self.isRunning) def conf(self, attr, value = None, default = None): return Env.setting(attr, self.getName().lower(), value = value, default = default) def getName(self): return self.__class__.__name__ def renderTemplate(self, parent_file, template, **params): template = open(os.path.join(os.path.dirname(parent_file), template), 'r').read() return render_template_string(template, **params) def registerStatic(self, plugin_file, add_to_head = True): # Register plugin path self.plugin_path = os.path.dirname(plugin_file) # Get plugin_name from PluginName s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', self.__class__.__name__) class_name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() path = 'api/%s/static/%s/' % (Env.setting('api_key'), class_name) addView(path + '', self.showStatic, static = True) if add_to_head: for f in glob.glob(os.path.join(self.plugin_path, 'static', '*')): ext = getExt(f) if ext in ['js', 'css']: fireEvent('register_%s' % ('script' if ext in 'js' else 'style'), path + os.path.basename(f)) def showStatic(self, filename): d = os.path.join(self.plugin_path, 'static') from flask.helpers import send_from_directory return send_from_directory(d, filename) def createFile(self, path, content, binary = False): path = ss(path) self.makeDir(os.path.dirname(path)) try: f = open(path, 'w' if not binary else 'wb') f.write(content) f.close() os.chmod(path, Env.getPermission('file')) except Exception, e: log.error('Unable writing to file "%s": %s', (path, e)) def makeDir(self, path): path = ss(path) try: if not os.path.isdir(path): os.makedirs(path, Env.getPermission('folder')) return True except Exception, e: log.error('Unable to create folder "%s": %s', (path, e)) return False # http request def urlopen(self, url, timeout = 30, params = {}, headers = {}, multipart = False, show_error = True): # Fill in some headers if not headers.get('Referer'): headers['Referer'] = urlparse(url).hostname if not headers.get('User-Agent'): headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0.2) Gecko/20100101 Firefox/10.0.2' host = urlparse(url).hostname # Don't try for failed requests if self.http_failed_disabled.get(host, 0) > 0: if self.http_failed_disabled[host] > (time.time() - 900): log.info('Disabled calls to %s for 15 minutes because so many failed requests.', host) raise Exception else: del self.http_failed_request[host] del self.http_failed_disabled[host] self.wait(host) try: if multipart: log.info('Opening multipart url: %s, params: %s', (url, [x for x in params.iterkeys()])) request = urllib2.Request(url, params, headers) cookies = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies), MultipartPostHandler) data = opener.open(request, timeout = timeout).read() else: log.info('Opening url: %s, params: %s', (url, [x for x in params.iterkeys()])) data = tryUrlencode(params) if len(params) > 0 else None request = urllib2.Request(url, data, headers) data = urllib2.urlopen(request, timeout = timeout).read() self.http_failed_request[host] = 0 except IOError: if show_error: log.error('Failed opening url in %s: %s %s', (self.getName(), url, traceback.format_exc(1))) # Save failed requests by hosts try: if not self.http_failed_request.get(host): self.http_failed_request[host] = 1 else: self.http_failed_request[host] += 1 # Disable temporarily if self.http_failed_request[host] > 5: self.http_failed_disabled[host] = time.time() except: log.debug('Failed logging failed requests for %s: %s', (url, traceback.format_exc())) raise self.http_last_use[host] = time.time() return data def wait(self, host = ''): now = time.time() last_use = self.http_last_use.get(host, 0) wait = math.ceil(last_use - now + self.http_time_between_calls) if wait > 0: log.debug('Waiting for %s, %d seconds', (self.getName(), wait)) time.sleep(last_use - now + self.http_time_between_calls) def beforeCall(self, handler): self.isRunning('%s.%s' % (self.getName(), handler.__name__)) def afterCall(self, handler): self.isRunning('%s.%s' % (self.getName(), handler.__name__), False) def doShutdown(self): self.shuttingDown(True) def shuttingDown(self, value = None): if value is None: return self._needs_shutdown self._needs_shutdown = value def isRunning(self, value = None, boolean = True): if not hasattr(self, '_running'): self._running = [] if value is None: return self._running if boolean: self._running.append(value) else: try: self._running.remove(value) except: log.error("Something went wrong when finishing the plugin function. Could not find the 'is_running' key") def getCache(self, cache_key, url = None, **kwargs): cache_key = simplifyString(cache_key) cache = Env.get('cache').get(cache_key) if cache: if not Env.get('dev'): log.debug('Getting cache %s', cache_key) return cache if url: try: cache_timeout = 300 if kwargs.get('cache_timeout'): cache_timeout = kwargs.get('cache_timeout') del kwargs['cache_timeout'] opener = None if kwargs.get('opener'): opener = kwargs.get('opener') del kwargs['opener'] if opener: log.info('Opening url: %s', url) f = opener.open(url) data = f.read() f.close() else: data = self.urlopen(url, **kwargs) if data: self.setCache(cache_key, data, timeout = cache_timeout) return data except: pass def setCache(self, cache_key, value, timeout = 300): log.debug('Setting cache %s', cache_key) Env.get('cache').set(cache_key, value, timeout) return value def isDisabled(self): return not self.isEnabled() def isEnabled(self): return self.conf(self.enabled_option) or self.conf(self.enabled_option) == None