You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

224 lines
7.4 KiB

from couchpotato import addView
from couchpotato.core.event import fireEvent, addEvent
from couchpotato.core.helpers.encoding import tryUrlencode
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 = '%s/static/%s/' % (Env.setting('api_key'), class_name)
addView(path + '<path:filename>', 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):
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):
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 = 10, 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, timeout = 300):
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:
data = self.urlopen(url)
self.setCache(cache_key, data, timeout = 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