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) | 
