summaryrefslogtreecommitdiffstats
path: root/pyload.py
diff options
context:
space:
mode:
Diffstat (limited to 'pyload.py')
-rwxr-xr-xpyload.py623
1 files changed, 22 insertions, 601 deletions
diff --git a/pyload.py b/pyload.py
index b1b76d6af..19d51ed38 100755
--- a/pyload.py
+++ b/pyload.py
@@ -1,607 +1,29 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-"""
- Copyright(c) 2008-2013 pyLoad Team
- http://www.pyload.org
- 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.
+###############################################################################
+# Copyright(c) 2008-2013 pyLoad Team
+# http://www.pyload.org
+#
+# 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.
+#
+# Subjected to the terms and conditions in LICENSE
+#
+# @author: spoob
+# @author: sebnapi
+# @author: RaNaN
+# @author: mkaay
+# @version: v0.5.0
+###############################################################################
- Subjected to the terms and conditions in LICENSE
-
- @author: spoob
- @author: sebnapi
- @author: RaNaN
- @author: mkaay
- @version: v0.4.9
-"""
-CURRENT_VERSION = '0.4.9.9-dev'
-
-import __builtin__
-
-from getopt import getopt, GetoptError
-import logging
-import logging.handlers
import os
-from os import _exit, execl, getcwd, remove, walk, chdir, close
-import signal
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.AccountManager import AccountManager
-from module.config.ConfigParser import ConfigParser
-from module.config.ConfigManager import ConfigManager
-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.remote.RemoteManager import RemoteManager
-from module.utils.JsEngine import JsEngine
-
-import module.utils.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
-
-# 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.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
-
- if len(argv) > 1:
- try:
- options, args = getopt(argv[1:], 'vchdusqp:',
- ["version", "clear", "clean", "help", "debug", "user",
- "setup", "configdir=", "changedir", "daemon",
- "quit", "status", "no-remote","pidfile="])
-
- for option, argument in options:
- if option in ("-v", "--version"):
- print "pyLoad", CURRENT_VERSION
- exit()
- elif option in ("-p", "--pidfile"):
- self.pidfile = argument
- elif option == "--daemon":
- self.daemon = True
- elif option in ("-c", "--clear"):
- self.deleteLinks = True
- elif option in ("-h", "--help"):
- self.print_help()
- exit()
- elif option in ("-d", "--debug"):
- self.doDebug = True
- elif option in ("-u", "--user"):
- from module.Setup import Setup
-
- self.config = ConfigParser()
- s = Setup(pypath, self.config)
- s.set_user()
- exit()
- elif option in ("-s", "--setup"):
- from module.Setup import Setup
-
- self.config = ConfigParser()
- s = Setup(pypath, self.config)
- s.start()
- exit()
- elif option == "--changedir":
- from module.Setup import Setup
-
- self.config = ConfigParser()
- s = Setup(pypath, self.config)
- s.conf_path(True)
- exit()
- elif option in ("-q", "--quit"):
- self.quitInstance()
- exit()
- elif option == "--status":
- pid = self.isAlreadyRunning()
- if self.isAlreadyRunning():
- print pid
- exit(0)
- else:
- print "false"
- exit(1)
- elif option == "--clean":
- self.cleanTree()
- exit()
- elif option == "--no-remote":
- self.remote = False
-
- except GetoptError:
- print 'Unknown Argument(s) "%s"' % " ".join(argv[1:])
- self.print_help()
- exit()
-
- def print_help(self):
- print ""
- print "pyLoad v%s 2008-2013 the pyLoad Team" % CURRENT_VERSION
- print ""
- if sys.argv[0].endswith(".py"):
- print "Usage: python pyload.py [options]"
- else:
- print "Usage: pyload [options]"
- print ""
- print "<Options>"
- print " -v, --version", " " * 10, "Print version to terminal"
- print " -c, --clear", " " * 12, "Delete all saved packages/links"
- #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 assistant"
- print " --configdir=<dir>", " " * 6, "Run with <dir> as configuration directory"
- print " -p, --pidfile=<file>", " " * 3, "Set pidfile to <file>"
- print " --changedir", " " * 12, "Change configuration directory permanently"
- print " --daemon", " " * 15, "Daemonize after startup"
- print " --no-remote", " " * 12, "Disable remote access"
- print " --status", " " * 15, "Display pid if running or False"
- print " --clean", " " * 16, "Remove .pyc/.pyo files"
- print " -q, --quit", " " * 13, "Quit a running pyLoad instance"
- print " -h, --help", " " * 13, "Display this help screen"
- print ""
-
-
- def quit(self, a, b):
- self.shutdown()
- self.log.info(_("Received Quit signal"))
- _exit(1)
-
- def writePidFile(self):
- self.deletePidFile()
- pid = os.getpid()
- f = open(self.pidfile, "wb")
- f.write(str(pid))
- f.close()
- chmod(self.pidfile, 0660)
-
- def deletePidFile(self):
- if self.checkPidFile():
- self.log.debug("Deleting old pidfile %s" % self.pidfile)
- os.remove(self.pidfile)
-
- def checkPidFile(self):
- """ return pid as int or 0"""
- if os.path.isfile(self.pidfile):
- f = open(self.pidfile, "rb")
- pid = f.read().strip()
- f.close()
- if pid:
- pid = int(pid)
- return pid
-
- return 0
-
- def isAlreadyRunning(self):
- pid = self.checkPidFile()
- if not pid or os.name == "nt": return False
- try:
- os.kill(pid, 0) # 0 - default signal (does nothing)
- except:
- return 0
-
- return pid
-
- def quitInstance(self):
- if os.name == "nt":
- print "Not supported on windows."
- return
-
- pid = self.isAlreadyRunning()
- if not pid:
- print "No pyLoad running."
- return
-
- try:
- os.kill(pid, 3) #SIGUIT
-
- t = time()
- print "waiting for pyLoad to quit"
-
- while exists(self.pidfile) and t + 10 > time():
- sleep(0.25)
-
- if not exists(self.pidfile):
- print "pyLoad successfully stopped"
- else:
- os.kill(pid, 9) #SIGKILL
- print "pyLoad did not respond"
- print "Kill signal was send to process with id %s" % pid
-
- except:
- print "Error quitting pyLoad"
-
-
- def cleanTree(self):
- for path, dirs, files in walk(self.path("")):
- for f in files:
- if not f.endswith(".pyo") and not f.endswith(".pyc"):
- continue
-
- if "_25" in f or "_26" in f or "_27" in f:
- continue
-
- print join(path, f)
- remove(join(path, f))
-
- def start(self, rpc=True, web=True, tests=False):
- """ starts the fun :D """
-
- self.version = CURRENT_VERSION
-
- # TODO: Re-enable when its working again
- # TODO: Don't forget it
- if False and not exists("pyload.conf") and not tests:
- from module.Setup import Setup
-
- print "This is your first start, running configuration assistant now."
- self.config = ConfigParser()
- s = Setup(pypath, self.config)
- res = False
- try:
- res = s.start()
- except SystemExit:
- pass
- except KeyboardInterrupt:
- print "\nSetup interrupted"
- except:
- res = False
- print_exc()
- print "Setup failed"
- if not res:
- remove("pyload.conf")
-
- exit()
-
- try: signal.signal(signal.SIGQUIT, self.quit)
- except: pass
-
- self.config = ConfigParser()
-
- gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None])
- translation = gettext.translation("pyLoad", self.path("locale"),
- 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()
- # don't exit when in test runner
- if pid and not tests:
- print _("pyLoad already running with pid %s") % pid
- exit()
-
- if os.name != "nt" and self.config["general"]["renice"]:
- os.system("renice %d %d" % (self.config["general"]["renice"], os.getpid()))
-
- if self.config["permission"]["change_group"]:
- if os.name != "nt":
- try:
- from grp import getgrnam
-
- group = getgrnam(self.config["permission"]["group"])
- os.setgid(group[2])
- except Exception, e:
- print _("Failed changing group: %s") % e
-
- if self.config["permission"]["change_user"]:
- if os.name != "nt":
- try:
- from pwd import getpwnam
-
- user = getpwnam(self.config["permission"]["user"])
- os.setuid(user[2])
- except Exception, e:
- print _("Failed changing user: %s") % e
-
- if self.debug:
- self.init_logger(logging.DEBUG) # logging level
- else:
- self.init_logger(logging.INFO) # logging level
-
- self.do_kill = False
- self.do_restart = False
- self.shuttedDown = False
-
- self.log.info(_("Starting") + " pyLoad %s" % CURRENT_VERSION)
- self.log.info(_("Using home directory: %s") % getcwd())
-
- if not tests:
- self.writePidFile()
+from os import _exit
- self.captcha = True # checks seems to fail, although tesseract is available
-
- self.eventManager = self.evm = EventManager(self)
- self.setupDB()
-
- # Upgrade to configManager
- self.config = ConfigManager(self, self.config)
-
- if self.deleteLinks:
- self.log.info(_("All links removed"))
- self.db.purgeLinks()
-
- self.requestFactory = RequestFactory(self)
- __builtin__.pyreq = self.requestFactory
-
- # deferred import, could improve start-up time
- from module.Api import Api
- from module.AddonManager import AddonManager
- from module.interaction.InteractionManager import InteractionManager
- from module.threads.ThreadManager import ThreadManager
-
- Api.initComponents()
- self.api = Api(self)
-
- self.scheduler = Scheduler(self)
-
- #hell yeah, so many important managers :D
- self.pluginManager = PluginManager(self)
- self.interactionManager = self.im = InteractionManager(self)
- self.accountManager = AccountManager(self)
- self.threadManager = ThreadManager(self)
- self.addonManager = AddonManager(self)
- self.remoteManager = RemoteManager(self)
-
- self.js = JsEngine()
-
- # enough initialization for test cases
- if tests: return
-
- self.log.info(_("Download time: %s") % self.api.isTimeDownload())
-
- if rpc:
- self.remoteManager.startBackends()
-
- if web:
- self.init_webserver()
-
- 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))
-
- self.config.save() #save so config files gets filled
-
- link_file = join(pypath, "links.txt")
-
- if exists(link_file):
- f = open(link_file, "rb")
- if f.read().strip():
- self.api.addPackage("links.txt", [link_file], 1)
- f.close()
-
- link_file = "links.txt"
- if exists(link_file):
- f = open(link_file, "rb")
- if f.read().strip():
- self.api.addPackage("links.txt", [link_file], 1)
- f.close()
-
- #self.scheduler.addJob(0, self.accountManager.getAccountInfos)
- self.log.info(_("Activating Accounts..."))
- 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.addonManager.activateAddons()
-
- self.log.info(_("pyLoad is up and running"))
- self.eventManager.dispatchEvent("core:ready")
-
- #test api
-# from module.common.APIExerciser import startApiExerciser
-# startApiExerciser(self, 3)
-
- #some memory stats
-# from guppy import hpy
-# hp=hpy()
-# print hp.heap()
-# import objgraph
-# 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(1.5)
- if self.do_restart:
- self.log.info(_("restarting pyLoad"))
- self.restart()
- if self.do_kill:
- self.shutdown()
- self.log.info(_("pyLoad quits"))
- self.removeLogger()
- _exit(0)
- # TODO check exits codes, clean exit is still blocked
-
- 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 = FileManager(self)
- self.db.manager = self.files #ugly?
-
- def init_webserver(self):
- if self.config['webinterface']['activated']:
- self.webserver = WebServer(self)
- self.webserver.start()
-
- def init_logger(self, level):
- console = logging.StreamHandler(sys.stdout)
-
- # try to get a time formatting depending on system locale
- tfrm = None
- try: # change current locale to default if it is not set
- current_locale = locale.getlocale()
- if current_locale == (None, None):
- current_locale = locale.setlocale(locale.LC_ALL, '')
-
- # We use timeformat provided by locale when available
- if current_locale != (None, None):
- tfrm = locale.nl_langinfo(locale.D_FMT) + " " + locale.nl_langinfo(locale.T_FMT)
- except: # something did go wrong, locale is heavily platform dependant
- pass
-
- # default formatting when no one was obtained
- if not tfrm:
- tfrm = "%d.%m.%Y %H:%M:%S"
-
- frm = logging.Formatter("%(asctime)s %(levelname)-8s %(message)s", tfrm)
- console.setFormatter(frm)
- 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']:
- file_handler = logging.handlers.RotatingFileHandler(join(self.config['log']['log_folder'], 'log.txt'),
- maxBytes=self.config['log']['log_size'] * 1024,
- backupCount=int(self.config['log']['log_count']),
- encoding="utf8")
- else:
- file_handler = logging.FileHandler(join(self.config['log']['log_folder'], 'log.txt'), encoding="utf8")
-
- file_handler.setFormatter(frm)
- self.log.addHandler(file_handler)
-
- self.log.addHandler(console) #if console logging
- self.log.setLevel(level)
-
- def removeLogger(self):
- for h in list(self.log.handlers):
- self.log.removeHandler(h)
- h.close()
-
-
- def restart(self):
- self.shutdown()
- chdir(owd)
- # close some open fds
- for i in range(3,50):
- try:
- close(i)
- except :
- pass
-
- execl(executable, executable, *sys.argv)
- _exit(0)
-
- def shutdown(self):
- self.log.info(_("shutting down..."))
- self.eventManager.dispatchEvent("coreShutdown")
- try:
- if self.config['webinterface']['activated'] and hasattr(self, "webserver"):
- pass # TODO: quit webserver?
-# self.webserver.quit()
-
- for thread in self.threadManager.threads:
- thread.put("quit")
-
- self.api.stopAllDownloads()
- self.addonManager.deactivateAddons()
-
- except:
- self.print_exc()
- self.log.info(_("error while shutting down"))
-
- finally:
- self.files.syncSave()
- self.db.shutdown()
- self.shuttedDown = True
-
- 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)
+from pyload.Core import Core
def deamon():
try:
@@ -612,7 +34,7 @@ def deamon():
print >> sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror)
sys.exit(1)
- # decouple from parent environment
+ # decouple from parent environment
os.setsid()
os.umask(0)
@@ -658,7 +80,6 @@ def main():
pyload_core.removeLogger()
_exit(1)
-# And so it begins...
+# And so it begins...
if __name__ == "__main__":
- main()
-
+ main() \ No newline at end of file