diff options
Diffstat (limited to 'pyLoadCore.py')
-rwxr-xr-x | pyLoadCore.py | 241 |
1 files changed, 124 insertions, 117 deletions
diff --git a/pyLoadCore.py b/pyLoadCore.py index 35cac4682..99c01dbf7 100755 --- a/pyLoadCore.py +++ b/pyLoadCore.py @@ -1,18 +1,15 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """ - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. + Copyright(c) 2008-2012 pyLoad Team + http://www.pyload.org - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. + Subjected to the terms and conditions in LICENSE @author: spoob @author: sebnapi @@ -20,59 +17,80 @@ @author: mkaay @version: v0.4.9 """ -CURRENT_VERSION = '0.4.9' +CURRENT_VERSION = '0.4.9.9-dev' import __builtin__ from getopt import getopt, GetoptError -import module.common.pylgettext as gettext from imp import find_module import logging import logging.handlers import os -from os import _exit, execl, getcwd, makedirs, remove, sep, walk, chdir, close -from os.path import exists, join +from os import _exit, execl, getcwd, remove, walk, chdir, close import signal -import subprocess import sys from sys import argv, executable, exit from time import time, sleep from traceback import print_exc +import locale +locale.locale_alias = locale.windows_locale = {} #save ~100kb ram, no known sideeffects for now + +import subprocess +subprocess.__doc__ = None # the module with the largest doc we are using + from module import InitHomeDir -from module.plugins.AccountManager import AccountManager -from module.CaptchaManager import CaptchaManager -from module.ConfigParser import ConfigParser -from module.plugins.PluginManager import PluginManager -from module.PullEvents import PullManager +from module.AccountManager import AccountManager +from module.config.ConfigParser import ConfigParser +from module.PluginManager import PluginManager +from module.interaction.EventManager import EventManager from module.network.RequestFactory import RequestFactory from module.web.ServerThread import WebServer from module.Scheduler import Scheduler from module.common.JsEngine import JsEngine from module import remote from module.remote.RemoteManager import RemoteManager -from module.database import DatabaseBackend, FileHandler -from module.utils import freeSpace, formatSize, get_console_encoding +import module.common.pylgettext as gettext +from module.utils import formatSize, get_console_encoding +from module.utils.fs import free_space, exists, makedirs, join, chmod from codecs import getwriter -enc = get_console_encoding(sys.stdout.encoding) +# test runner overwrites sys.stdout +if hasattr(sys.stdout, "encoding"): enc = get_console_encoding(sys.stdout.encoding) +else: enc = "utf8" + +sys._stdout = sys.stdout sys.stdout = getwriter(enc)(sys.stdout, errors="replace") # TODO List # - configurable auth system ldap/mysql # - cron job like sheduler +# - plugin stack / multi decrypter +# - media plugin type +# - general progress info +# - content attribute for files / sync status +# - sync with disk content / file manager / nested packages +# - sync between pyload cores +# - new attributes (date|sync status) +# - embedded packages +# - would require new/modified link collector concept +# - pausable links/packages +# - toggable accounts +# - interaction manager +# - improve external scripts +# - make pyload undestructable to fail plugins -> see ConfigParser first class Core(object): """pyLoad Core, one tool to rule them all... (the filehosters) :D""" def __init__(self): self.doDebug = False - self.startedInGui = False self.running = False self.daemon = False self.remote = True + self.pdb = None self.arg_links = [] self.pidfile = "pyload.pid" self.deleteLinks = False # will delete links on startup @@ -129,7 +147,7 @@ class Core(object): print pid exit(0) else: - print "false" + print "false" exit(1) elif option == "--clean": self.cleanTree() @@ -144,7 +162,7 @@ class Core(object): def print_help(self): print "" - print "pyLoad v%s 2008-2011 the pyLoad Team" % CURRENT_VERSION + print "pyLoad v%s 2008-2012 the pyLoad Team" % CURRENT_VERSION print "" if sys.argv[0].endswith(".py"): print "Usage: python pyLoadCore.py [options]" @@ -157,15 +175,15 @@ class Core(object): #print " -a, --add=<link/list>", " " * 2, "Add the specified links" print " -u, --user", " " * 13, "Manages users" print " -d, --debug", " " * 12, "Enable debug mode" - print " -s, --setup", " " * 12, "Run Setup Assistent" - print " --configdir=<dir>", " " * 6, "Run with <dir> as config directory" + print " -s, --setup", " " * 12, "Run setup assistant" + print " --configdir=<dir>", " " * 6, "Run with <dir> as configuration directory" print " -p, --pidfile=<file>", " " * 3, "Set pidfile to <file>" - print " --changedir", " " * 12, "Change config dir permanently" - print " --daemon", " " * 15, "Daemonmize after start" + print " --changedir", " " * 12, "Change configuration directory permanently" + print " --daemon", " " * 15, "Daemonize after startup" print " --no-remote", " " * 12, "Disable remote access (saves RAM)" print " --status", " " * 15, "Display pid if running or False" print " --clean", " " * 16, "Remove .pyc/.pyo files" - print " -q, --quit", " " * 13, "Quit running pyLoad instance" + print " -q, --quit", " " * 13, "Quit a running pyLoad instance" print " -h, --help", " " * 13, "Display this help screen" print "" @@ -188,6 +206,7 @@ class Core(object): f = open(self.pidfile, "wb") f.write(str(pid)) f.close() + chmod(self.pidfile, 0660) def deletePidFile(self): if self.checkPidFile(): @@ -258,15 +277,15 @@ class Core(object): print join(path, f) remove(join(path, f)) - def start(self, rpc=True, web=True): + def start(self, rpc=True, web=True, tests=False): """ starts the fun :D """ self.version = CURRENT_VERSION - if not exists("pyload.conf"): + if not exists("pyload.conf") and not tests: from module.setup import Setup - print "This is your first start, running configuration assistent now." + print "This is your first start, running configuration assistant now." self.config = ConfigParser() s = Setup(pypath, self.config) res = False @@ -295,11 +314,15 @@ class Core(object): languages=[self.config['general']['language'],"en"],fallback=True) translation.install(True) + # load again so translations are propagated + self.config.loadDefault() + self.debug = self.doDebug or self.config['general']['debug_mode'] self.remote &= self.config['remote']['activated'] pid = self.isAlreadyRunning() - if pid: + # don't exit when in test runner + if pid and not tests: print _("pyLoad already running with pid %s") % pid exit() @@ -326,8 +349,6 @@ class Core(object): except Exception, e: print _("Failed changing user: %s") % e - self.check_file(self.config['log']['log_folder'], _("folder for logs"), True) - if self.debug: self.init_logger(logging.DEBUG) # logging level else: @@ -339,8 +360,9 @@ class Core(object): self.log.info(_("Starting") + " pyLoad %s" % CURRENT_VERSION) self.log.info(_("Using home directory: %s") % getcwd()) - - self.writePidFile() + + if not tests: + self.writePidFile() #@TODO refractor @@ -348,24 +370,15 @@ class Core(object): self.log.debug("Remote activated: %s" % self.remote) self.check_install("Crypto", _("pycrypto to decode container files")) - #img = self.check_install("Image", _("Python Image Libary (PIL) for captcha reading")) - #self.check_install("pycurl", _("pycurl to download any files"), True, True) - self.check_file("tmp", _("folder for temporary files"), True) - #tesser = self.check_install("tesseract", _("tesseract for captcha reading"), False) if os.name != "nt" else True - self.captcha = True # checks seems to fail, althoug tesseract is available - - self.check_file(self.config['general']['download_folder'], _("folder for downloads"), True) + self.captcha = True # checks seems to fail, although tesseract is available if self.config['ssl']['activated']: self.check_install("OpenSSL", _("OpenSSL for secure connection")) - self.setupDB() - if self.config.oldRemoteData: - self.log.info(_("Moving old user config to DB")) - self.db.addUser(self.config.oldRemoteData["username"], self.config.oldRemoteData["password"]) - self.log.info(_("Please check your logindata with ./pyLoadCore.py -u")) + self.eventManager = EventManager(self) + self.setupDB() if self.deleteLinks: self.log.info(_("All links removed")) @@ -374,12 +387,11 @@ class Core(object): self.requestFactory = RequestFactory(self) __builtin__.pyreq = self.requestFactory - self.lastClientConnected = 0 - # later imported because they would trigger api import, and remote value not set correctly from module import Api - from module.HookManager import HookManager - from module.ThreadManager import ThreadManager + from module.AddonManager import AddonManager + from module.interaction.InteractionManager import InteractionManager + from module.threads.ThreadManager import ThreadManager if Api.activated != self.remote: self.log.warning("Import error: API remote status not correct.") @@ -390,16 +402,18 @@ class Core(object): #hell yeah, so many important managers :D self.pluginManager = PluginManager(self) - self.pullManager = PullManager(self) + self.interactionManager = InteractionManager(self) self.accountManager = AccountManager(self) self.threadManager = ThreadManager(self) - self.captchaManager = CaptchaManager(self) - self.hookManager = HookManager(self) + self.addonManager = AddonManager(self) self.remoteManager = RemoteManager(self) self.js = JsEngine() - self.log.info(_("Downloadtime: %s") % self.api.isTimeDownload()) + # enough initialization for test cases + if tests: return + + self.log.info(_("Download time: %s") % self.api.isTimeDownload()) if rpc: self.remoteManager.startBackends() @@ -407,7 +421,12 @@ class Core(object): if web: self.init_webserver() - spaceLeft = freeSpace(self.config["general"]["download_folder"]) + dl_folder = self.config["general"]["download_folder"] + + if not exists(dl_folder): + makedirs(dl_folder) + + spaceLeft = free_space(dl_folder) self.log.info(_("Free space: %s") % formatSize(spaceLeft)) @@ -430,15 +449,20 @@ class Core(object): #self.scheduler.addJob(0, self.accountManager.getAccountInfos) self.log.info(_("Activating Accounts...")) - self.accountManager.getAccountInfos() + self.accountManager.refreshAllAccounts() + #restart failed + if self.config["download"]["restart_failed"]: + self.log.info(_("Restarting failed downloads...")) + self.api.restartFailed() + self.threadManager.pause = False self.running = True - self.log.info(_("Activating Plugins...")) - self.hookManager.coreReady() + self.addonManager.activateAddons() self.log.info(_("pyLoad is up and running")) + self.eventManager.dispatchEvent("coreReady") #test api # from module.common.APIExerciser import startApiExerciser @@ -447,15 +471,18 @@ class Core(object): #some memory stats # from guppy import hpy # hp=hpy() +# print hp.heap() # import objgraph -# objgraph.show_most_common_types(limit=20) +# objgraph.show_most_common_types(limit=30) # import memdebug # memdebug.start(8002) +# from meliae import scanner +# scanner.dump_all_objects(self.path('objs.json')) locals().clear() while True: - sleep(2) + sleep(1.5) if self.do_restart: self.log.info(_("restarting pyLoad")) self.restart() @@ -466,13 +493,17 @@ class Core(object): _exit(0) #@TODO thrift blocks shutdown self.threadManager.work() + self.interactionManager.work() self.scheduler.work() def setupDB(self): + from module.database import DatabaseBackend + from module.FileManager import FileManager + self.db = DatabaseBackend(self) # the backend self.db.setup() - self.files = FileHandler(self) + self.files = FileManager(self) self.db.manager = self.files #ugly? def init_webserver(self): @@ -484,7 +515,10 @@ class Core(object): console = logging.StreamHandler(sys.stdout) frm = logging.Formatter("%(asctime)s %(levelname)-8s %(message)s", "%d.%m.%Y %H:%M:%S") console.setFormatter(frm) - self.log = logging.getLogger("log") # settable in config + self.log = logging.getLogger("log") # setable in config + + if not exists(self.config['log']['log_folder']): + makedirs(self.config['log']['log_folder'], 0700) if self.config['log']['file_log']: if self.config['log']['log_rotate']: @@ -507,7 +541,7 @@ class Core(object): h.close() def check_install(self, check_name, legend, python=True, essential=False): - """check wether needed tools are installed""" + """check whether needed tools are installed""" try: if python: find_module(check_name) @@ -523,46 +557,6 @@ class Core(object): return False - def check_file(self, check_names, description="", folder=False, empty=True, essential=False, quiet=False): - """check wether needed files exists""" - tmp_names = [] - if not type(check_names) == list: - tmp_names.append(check_names) - else: - tmp_names.extend(check_names) - file_created = True - file_exists = True - for tmp_name in tmp_names: - if not exists(tmp_name): - file_exists = False - if empty: - try: - if folder: - tmp_name = tmp_name.replace("/", sep) - makedirs(tmp_name) - else: - open(tmp_name, "w") - except: - file_created = False - else: - file_created = False - - if not file_exists and not quiet: - if file_created: - #self.log.info( _("%s created") % description ) - pass - else: - if not empty: - self.log.warning( - _("could not find %(desc)s: %(name)s") % {"desc": description, "name": tmp_name}) - else: - print _("could not create %(desc)s: %(name)s") % {"desc": description, "name": tmp_name} - if essential: - exit() - - def isClientConnected(self): - return (self.lastClientConnected + 30) > time() - def restart(self): self.shutdown() chdir(owd) @@ -578,22 +572,19 @@ class Core(object): def shutdown(self): self.log.info(_("shutting down...")) + self.eventManager.dispatchEvent("coreShutdown") try: if self.config['webinterface']['activated'] and hasattr(self, "webserver"): self.webserver.quit() for thread in self.threadManager.threads: thread.put("quit") - pyfiles = self.files.cache.values() - for pyfile in pyfiles: - pyfile.abortDownload() - - self.hookManager.coreExiting() + self.api.stopAllDownloads() + self.addonManager.deactivateAddons() except: - if self.debug: - print_exc() + self.print_exc() self.log.info(_("error while shutting down")) finally: @@ -602,11 +593,27 @@ class Core(object): self.deletePidFile() + def shell(self): + """ stop and open an ipython shell inplace""" + if self.debug: + from IPython import embed + sys.stdout = sys._stdout + embed() + + def breakpoint(self): + if self.debug: + from IPython.core.debugger import Pdb + sys.stdout = sys._stdout + if not self.pdb: self.pdb = Pdb() + self.pdb.set_trace() + + def print_exc(self): + if self.debug: + print_exc() def path(self, *args): return join(pypath, *args) - def deamon(): try: pid = os.fork() @@ -658,7 +665,7 @@ def main(): pyload_core.start() except KeyboardInterrupt: pyload_core.shutdown() - pyload_core.log.info(_("killed pyLoad from Terminal")) + pyload_core.log.info(_("killed pyLoad from terminal")) pyload_core.removeLogger() _exit(1) |