def getUserScript(self, script_route, **kwargs): klass = self class UserscriptHandler(RequestHandler): def get(self, random, route): bookmarklet_host = Env.setting('bookmarklet_host') loc = bookmarklet_host if bookmarklet_host else "{0}://{1}".format(self.request.protocol, self.request.headers.get('X-Forwarded-Host') or self.request.headers.get('host')) params = { 'includes': fireEvent('userscript.get_includes', merge = True), 'excludes': fireEvent('userscript.get_excludes', merge = True), 'version': klass.getVersion(), 'api': '%suserscript/' % Env.get('api_base'), 'host': loc, } script = klass.renderTemplate(__file__, 'template.js_tmpl', **params) klass.createFile(os.path.join(Env.get('cache_dir'), 'whatpotato.user.js'), script) self.redirect(Env.get('api_base') + 'file.cache/whatpotato.user.js') Env.get('app').add_handlers(".*$", [('%s%s' % (Env.get('api_base'), script_route), UserscriptHandler)])
def addMovies(self): movies = fireEvent('automation.get_movies', merge = True) movie_ids = [] for imdb_id in movies: if self.shuttingDown(): break prop_name = 'automation.added.%s' % imdb_id added = Env.prop(prop_name, default = False) if not added: added_movie = fireEvent('movie.add', params = {'identifier': imdb_id}, force_readd = False, search_after = False, update_after = True, single = True) if added_movie: movie_ids.append(added_movie['_id']) Env.prop(prop_name, True) for movie_id in movie_ids: if self.shuttingDown(): break movie_dict = fireEvent('media.get', movie_id, single = True) if movie_dict: fireEvent('movie.searcher.single', movie_dict) return True
def getDomain(self, url = ''): forced_domain = self.conf('domain') if forced_domain: return cleanHost(forced_domain).rstrip('/') + url if not self.proxy_domain: for proxy in self.proxy_list: prop_name = 'proxy.%s' % proxy last_check = float(Env.prop(prop_name, default = 0)) if last_check > time.time() - 86400: continue data = '' try: data = self.urlopen(proxy, timeout = 3, show_error = False) except: log.debug('Failed %s proxy %s: %s', (self.getName(), proxy, traceback.format_exc())) if self.correctProxy(data): log.debug('Using proxy for %s: %s', (self.getName(), proxy)) self.proxy_domain = proxy break Env.prop(prop_name, time.time()) if not self.proxy_domain: log.error('No %s proxies left, please add one in settings, or let us know which one to add on the forum.', self.getName()) return None return cleanHost(self.proxy_domain).rstrip('/') + url
def getCache(self, cache_key, url = None, **kwargs): use_cache = not len(kwargs.get('data', {})) > 0 and not kwargs.get('files') if use_cache: cache_key_md5 = md5(cache_key) cache = Env.get('cache').get(cache_key_md5) if cache: if not Env.get('dev'): log.debug('Getting cache %s', cache_key) return cache if url: try: cache_timeout = 300 if 'cache_timeout' in kwargs: cache_timeout = kwargs.get('cache_timeout') del kwargs['cache_timeout'] data = self.urlopen(url, **kwargs) if data and cache_timeout > 0 and use_cache: self.setCache(cache_key, data, timeout = cache_timeout) return data except: if not kwargs.get('show_error', True): raise log.debug('Failed getting cache: %s', (traceback.format_exc(0))) return ''
def __init__(self): if Env.get('desktop'): self.updater = DesktopUpdater() elif os.path.isdir(os.path.join(Env.get('app_dir'), '.git')): git_default = 'git' git_command = self.conf('git_command', default = git_default) git_command = git_command if git_command != git_default and (os.path.isfile(git_command) or re.match('^[a-zA-Z0-9_/\.\-]+$', git_command)) else git_default self.updater = GitUpdater(git_command) else: self.updater = SourceUpdater() addEvent('app.load', self.logVersion, priority = 10000) addEvent('app.load', self.setCrons) addEvent('updater.info', self.info) addApiView('updater.info', self.info, docs = { 'desc': 'Get updater information', 'return': { 'type': 'object', 'example': """{ 'last_check': "last checked for update", 'update_version': "available update version or empty", 'version': current_cp_version }"""} }) addApiView('updater.update', self.doUpdateView) addApiView('updater.check', self.checkView, docs = { 'desc': 'Check for available update', 'return': {'type': 'see updater.info'} }) addEvent('setting.save.updater.enabled.after', self.setCrons)
def createBaseUrl(self): host = Env.setting('host') if host == '0.0.0.0' or host == '': host = 'localhost' port = Env.setting('port') ssl = Env.setting('ssl_cert') and Env.setting('ssl_key') return '%s:%d%s' % (cleanHost(host, ssl = ssl).rstrip('/'), int(port), Env.get('web_base'))
def getCredentials(self, **kwargs): try: oauth_token = kwargs.get('oauth') except: return 'redirect', Env.get('web_base') + 'settings/downloaders/' log.debug('oauth_token is: %s', oauth_token) self.conf('oauth_token', value = oauth_token); return 'redirect', Env.get('web_base') + 'settings/downloaders/'
def get_current_user(self): username = Env.setting('username') password = Env.setting('password') if username and password: return self.get_secure_cookie('user') else: # Login when no username or password are set return True
def suggestView(self, limit = 6, **kwargs): if self.isDisabled(): return { 'success': True, 'movies': [] } movies = splitString(kwargs.get('movies', '')) ignored = splitString(kwargs.get('ignored', '')) seen = splitString(kwargs.get('seen', '')) cached_suggestion = self.getCache('suggestion_cached') if cached_suggestion: suggestions = cached_suggestion else: if not movies or len(movies) == 0: active_movies = fireEvent('media.with_status', ['active', 'done'], types = 'movie', single = True) movies = [getIdentifier(x) for x in active_movies] if not ignored or len(ignored) == 0: ignored = splitString(Env.prop('suggest_ignore', default = '')) if not seen or len(seen) == 0: movies.extend(splitString(Env.prop('suggest_seen', default = ''))) suggestions = fireEvent('movie.suggest', movies = movies, ignore = ignored, single = True) self.setCache('suggestion_cached', suggestions, timeout = 6048000) # Cache for 10 weeks medias = [] for suggestion in suggestions[:int(limit)]: # Cache poster posters = suggestion.get('images', {}).get('poster', []) poster = [x for x in posters if 'tmdb' in x] posters = poster if len(poster) > 0 else posters cached_poster = fireEvent('file.download', url = posters[0], single = True) if len(posters) > 0 else False files = {'image_poster': [cached_poster] } if cached_poster else {} medias.append({ 'status': 'suggested', 'title': getTitle(suggestion), 'type': 'movie', 'info': suggestion, 'files': files, 'identifiers': { 'imdb': suggestion.get('imdb') } }) return { 'success': True, 'movies': medias }
def page_not_found(rh): index_url = Env.get('web_base') url = rh.request.uri[len(index_url):] if url[:3] != 'api': r = index_url + '#' + url.lstrip('/') rh.redirect(r) else: if not Env.get('dev'): time.sleep(0.1) rh.set_status(404) rh.write('Wrong API key used')
def calculate(self, nzb, movie): """ Calculate the score of a NZB, used for sorting later """ # Merge global and category preferred_words = splitString(Env.setting('preferred_words', section = 'searcher').lower()) try: preferred_words = removeDuplicate(preferred_words + splitString(movie['category']['preferred'].lower())) except: pass score = nameScore(toUnicode(nzb['name']), movie['info']['year'], preferred_words) for movie_title in movie['info']['titles']: score += nameRatioScore(toUnicode(nzb['name']), toUnicode(movie_title)) score += namePositionScore(toUnicode(nzb['name']), toUnicode(movie_title)) score += sizeScore(nzb['size']) # Torrents only if nzb.get('seeders'): try: score += nzb.get('seeders') * 100 / 15 score += nzb.get('leechers') * 100 / 30 except: pass # Provider score score += providerScore(nzb['provider']) # Duplicates in name score += duplicateScore(nzb['name'], getTitle(movie)) # Merge global and category ignored_words = splitString(Env.setting('ignored_words', section = 'searcher').lower()) try: ignored_words = removeDuplicate(ignored_words + splitString(movie['category']['ignored'].lower())) except: pass # Partial ignored words score += partialIgnoredScore(nzb['name'], getTitle(movie), ignored_words) # Ignore single downloads from multipart score += halfMultipartScore(nzb['name']) # Extra provider specific check extra_score = nzb.get('extra_score') if extra_score: score += extra_score(nzb) # Scene / Nuke scoring score += sceneScore(nzb['name']) return score
def post(self, *args, **kwargs): api_key = None username = Env.setting('username') password = Env.setting('password') if (self.get_argument('username') == username or not username) and (md5(self.get_argument('password')) == password or not password): api_key = Env.setting('api_key') if api_key: remember_me = tryInt(self.get_argument('remember_me', default = 0)) self.set_secure_cookie('user', api_key, expires_days = 30 if remember_me > 0 else None) self.redirect(Env.get('web_base'))
def download(self, url = '', nzb_id = ''): try: return self.urlopen(url, headers = {'User-Agent': Env.getIdentifier()}, show_error = False) except: log.error('Failed getting release from %s: %s', (self.getName(), traceback.format_exc())) return 'try_next'
def getRequestHeaders(self): return { 'X-CP-Version': fireEvent('app.version', single = True), 'X-CP-API': self.api_version, 'X-CP-Time': time.time(), 'X-CP-Identifier': '+%s' % Env.setting('api_key', 'core')[:10], # Use first 10 as identifier, so we don't need to use IP address in api stats }
def check(self): if self.update_version: return True log.info('Checking for new version on github for %s', self.repo_name) if not Env.get('dev'): self.repo.fetch() current_branch = self.repo.getCurrentBranch().name for branch in self.repo.getRemoteByName('origin').getBranches(): if current_branch == branch.name: local = self.repo.getHead() remote = branch.getHead() log.debug('Versions, local:%s, remote:%s', (local.hash[:8], remote.hash[:8])) if local.getDate() < remote.getDate(): self.update_version = { 'hash': remote.hash[:8], 'date': remote.getDate(), } return True self.last_check = time.time() return False
def get(self, nr = 0, **kwargs): nr = tryInt(nr) current_path = None total = 1 for x in range(0, 50): path = '%s%s' % (Env.get('log_path'), '.%s' % x if x > 0 else '') # Check see if the log exists if not os.path.isfile(path): total = x - 1 break # Set current path if x is nr: current_path = path log_content = '' if current_path: f = open(current_path, 'r') log_content = f.read() logs = self.toList(log_content) return { 'success': True, 'log': logs, 'total': total, }
def __init__(self): fireEvent('scheduler.interval', identifier = 'manage.update_library', handle = self.updateLibrary, hours = 2) addEvent('manage.update', self.updateLibrary) addEvent('manage.diskspace', self.getDiskSpace) # Add files after renaming def after_rename(message = None, group = None): if not group: group = {} return self.scanFilesToLibrary(folder = group['destination_dir'], files = group['renamed_files'], release_download = group['release_download']) addEvent('renamer.after', after_rename, priority = 110) addApiView('manage.update', self.updateLibraryView, docs = { 'desc': 'Update the library by scanning for new movies', 'params': { 'full': {'desc': 'Do a full update or just recently changed/added movies.'}, } }) addApiView('manage.progress', self.getProgress, docs = { 'desc': 'Get the progress of current manage update', 'return': {'type': 'object', 'example': """{ 'progress': False || object, total & to_go, }"""}, }) if not Env.get('dev') and self.conf('startup_scan'): addEvent('app.load', self.updateLibraryQuick) addEvent('app.load', self.setCrons) # Enable / disable interval addEvent('setting.save.manage.library_refresh_interval.after', self.setCrons)
def createStructure(self): custom_dir = os.path.join(Env.get('data_dir'), 'custom_plugins') if not os.path.isdir(custom_dir): self.makeDir(custom_dir) self.createFile(os.path.join(custom_dir, '__init__.py'), '# Don\'t remove this file')
def download(self, url = '', nzb_id = ''): host = urlparse(url).hostname if self.limits_reached.get(host): # Try again in 3 hours if self.limits_reached[host] > time.time() - 10800: return 'try_next' try: data = self.urlopen(url, show_error = False, headers = {'User-Agent': Env.getIdentifier()}) self.limits_reached[host] = False return data except HTTPError as e: sc = e.response.status_code if sc in [503, 429]: response = e.read().lower() if sc == 429 or 'maximum api' in response or 'download limit' in response: if not self.limits_reached.get(host): log.error('Limit reached / to many requests for newznab provider: %s', host) self.limits_reached[host] = time.time() return 'try_next' log.error('Failed download from %s: %s', (host, traceback.format_exc())) return 'try_next'
def _createType(self, meta_name, root, movie_info, group, file_type, i): # Get file path camelcase_method = underscoreToCamel(file_type.capitalize()) name = getattr(self, "get" + camelcase_method + "Name")(meta_name, root, i) if name and (self.conf("meta_" + file_type) or self.conf("meta_" + file_type) is None): # Get file content content = getattr(self, "get" + camelcase_method)(movie_info=movie_info, data=group, i=i) if content: log.debug("Creating %s file: %s", (file_type, name)) if os.path.isfile(content): content = sp(content) name = sp(name) if not os.path.exists(os.path.dirname(name)): os.makedirs(os.path.dirname(name)) shutil.copy2(content, name) shutil.copyfile(content, name) # Try and copy stats seperately try: shutil.copystat(content, name) except: pass else: self.createFile(name, content) group["renamed_files"].append(name) try: os.chmod(sp(name), Env.getPermission("file")) except: log.debug("Failed setting permissions for %s: %s", (name, traceback.format_exc()))
def search(self, name, year = None, imdb_only = False): prop_name = 'automation.cached.%s.%s' % (name, year) cached_imdb = Env.prop(prop_name, default = False) if cached_imdb and imdb_only: return cached_imdb result = fireEvent('movie.search', q = '%s %s' % (name, year if year else ''), limit = 1, merge = True) if len(result) > 0: if imdb_only and result[0].get('imdb'): Env.prop(prop_name, result[0].get('imdb')) return result[0].get('imdb') if imdb_only else result[0] else: return None
def register(self): if self.registered: return try: hostname = self.conf("hostname") password = self.conf("password") port = self.conf("port") self.growl = notifier.GrowlNotifier( applicationName=Env.get("appname"), notifications=["Updates"], defaultNotifications=["Updates"], applicationIcon=self.getNotificationImage("medium"), hostname=hostname if hostname else "localhost", password=password if password else None, port=port if port else 23053, ) self.growl.register() self.registered = True except Exception as e: if "timed out" in str(e): self.registered = True else: log.error("Failed register of growl: %s", traceback.format_exc())
def __init__(self): desktop = Env.get('desktop') desktop.setSettings({ 'base_url': fireEvent('app.base_url', single = True), 'api_url': fireEvent('app.api_url', single = True), 'api': Env.setting('api'), }) # Events from desktop desktop.addEvents({ 'onClose': self.onClose, }) # Events to desktop addEvent('app.after_shutdown', desktop.afterShutdown) addEvent('app.load', desktop.onAppLoad, priority = 110)
def signalHandler(self): if Env.get('daemonized'): return def signal_handler(*args, **kwargs): fireEvent('app.shutdown', single = True) signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler)
def providerScore(provider): try: score = tryInt(Env.setting('extra_score', section = provider.lower(), default = 0)) except: score = 0 return score
def get(self, *args, **kwargs): api_key = None try: username = Env.setting('username') password = Env.setting('password') if (self.get_argument('u') == md5(username) or not username) and (self.get_argument('p') == password or not password): api_key = Env.setting('api_key') self.write({ 'success': api_key is not None, 'api_key': api_key }) except: log.error('Failed doing key request: %s', (traceback.format_exc())) self.write({'success': False, 'error': 'Failed returning results'})
def __init__(self): addEvent('app.load', self.setCrons) if not Env.get('dev'): addEvent('app.load', self.addMovies) addEvent('setting.save.automation.hour.after', self.setCrons)
def replaceWith(self, path): path = sp(path) app_dir = Env.get('app_dir') data_dir = Env.get('data_dir') # Get list of files we want to overwrite removePyc(app_dir) existing_files = [] for root, subfiles, filenames in os.walk(app_dir): for filename in filenames: existing_files.append(os.path.join(root, filename)) for root, subfiles, filenames in os.walk(path): for filename in filenames: fromfile = os.path.join(root, filename) tofile = os.path.join(app_dir, fromfile.replace(path + os.path.sep, '')) if not Env.get('dev'): try: if os.path.isfile(tofile): os.remove(tofile) dirname = os.path.dirname(tofile) if not os.path.isdir(dirname): self.makeDir(dirname) shutil.move(fromfile, tofile) try: existing_files.remove(tofile) except ValueError: pass except: log.error('Failed overwriting file "%s": %s', (tofile, traceback.format_exc())) return False for still_exists in existing_files: if data_dir in still_exists: continue try: os.remove(still_exists) except: log.error('Failed removing non-used file: %s', traceback.format_exc()) return True
def checkMessages(self): prop_name = 'messages.last_check' last_check = tryInt(Env.prop(prop_name, default = 0)) messages = fireEvent('cp.messages', last_check = last_check, single = True) or [] for message in messages: if message.get('time') > last_check: message['sticky'] = True # Always sticky core messages message_type = 'core.message.important' if message.get('important') else 'core.message' fireEvent(message_type, message = message.get('message'), data = message) if last_check < message.get('time'): last_check = message.get('time') Env.prop(prop_name, value = last_check)
def fillResult(self, result): defaults = { 'id': 0, 'protocol': self.provider.protocol, 'type': self.provider.type, 'provider': self.provider.getName(), 'download': self.provider.loginDownload if self.provider.urls.get('login') else self.provider.download, 'seed_ratio': Env.setting('seed_ratio', section = self.provider.getName().lower(), default = ''), 'seed_time': Env.setting('seed_time', section = self.provider.getName().lower(), default = ''), 'url': '', 'name': '', 'age': 0, 'size': 0, 'description': '', 'score': 0 } return mergeDicts(defaults, result)