diff options
Diffstat (limited to 'pyload/database')
| -rw-r--r-- | pyload/database/Backend.py | 43 | ||||
| -rw-r--r-- | pyload/database/File.py | 124 | ||||
| -rw-r--r-- | pyload/database/Storage.py | 6 | ||||
| -rw-r--r-- | pyload/database/User.py | 16 | 
4 files changed, 189 insertions, 0 deletions
| diff --git a/pyload/database/Backend.py b/pyload/database/Backend.py index 4b63dd284..48c1fcb0d 100644 --- a/pyload/database/Backend.py +++ b/pyload/database/Backend.py @@ -22,34 +22,49 @@ class style(object):      db = None      @classmethod + +      def setDB(cls, db):          cls.db = db      @classmethod + +      def inner(cls, f):          @staticmethod + +          def x(*args, **kwargs):              if cls.db:                  return f(cls.db, *args, **kwargs)          return x      @classmethod + +      def queue(cls, f):          @staticmethod + +          def x(*args, **kwargs):              if cls.db:                  return cls.db.queue(f, *args, **kwargs)          return x      @classmethod + +      def async(cls, f):          @staticmethod + +          def x(*args, **kwargs):              if cls.db:                  return cls.db.async(f, *args, **kwargs)          return x  class DatabaseJob(object): +      def __init__(self, f, *args, **kwargs):          self.done = Event() @@ -63,6 +78,7 @@ class DatabaseJob(object):  #        import inspect  #        self.frame = inspect.currentframe() +      def __repr__(self):          from os.path import basename          frame = self.frame.f_back @@ -75,6 +91,7 @@ class DatabaseJob(object):          return "DataBase Job %s:%s\n%sResult: %s" % (self.f.__name__, self.args[1:], output, self.result) +      def processJob(self):          try:              self.result = self.f(*self.args, **self.kwargs) @@ -89,11 +106,14 @@ class DatabaseJob(object):          finally:              self.done.set() +      def wait(self):          self.done.wait()  class DatabaseBackend(Thread):      subs = [] + +      def __init__(self, core):          Thread.__init__(self)          self.setDaemon(True) @@ -105,10 +125,12 @@ class DatabaseBackend(Thread):          style.setDB(self) +      def setup(self):          self.start()          self.setuplock.wait() +      def run(self):          """main loop, which executes commands"""          convert = self._checkVersion() #returns None or current version @@ -137,10 +159,13 @@ class DatabaseBackend(Thread):              j.processJob()      @style.queue + +      def shutdown(self):          self.conn.commit()          self.jobs.put("quit") +      def _checkVersion(self):          """ check db version and delete it if needed"""          if not exists("files.version"): @@ -165,6 +190,7 @@ class DatabaseBackend(Thread):              f.close()              return v +      def _convertDB(self, v):          try:              getattr(self, "_convertV%i" % v)() @@ -176,6 +202,7 @@ class DatabaseBackend(Thread):      #convert scripts start----------------------------------------------------- +      def _convertV2(self):          self.c.execute('CREATE TABLE IF NOT EXISTS "storage" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "identifier" TEXT NOT NULL, "key" TEXT NOT NULL, "value" TEXT DEFAULT "")')          try: @@ -184,6 +211,7 @@ class DatabaseBackend(Thread):              print "Database was converted from v2 to v3."          self._convertV3() +      def _convertV3(self):          self.c.execute('CREATE TABLE IF NOT EXISTS "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "name" TEXT NOT NULL, "email" TEXT DEFAULT "" NOT NULL, "password" TEXT NOT NULL, "role" INTEGER DEFAULT 0 NOT NULL, "permission" INTEGER DEFAULT 0 NOT NULL, "template" TEXT DEFAULT "default" NOT NULL)')          try: @@ -193,6 +221,7 @@ class DatabaseBackend(Thread):      #convert scripts end------------------------------------------------------- +      def _createTables(self):          """create tables for database""" @@ -249,26 +278,35 @@ class DatabaseBackend(Thread):              self.c.executemany("INSERT INTO users(name, password, email) VALUES (?, ?, ?)", users)              move("pyload.db", "pyload.old.db") +      def createCursor(self):          return self.conn.cursor()      @style.async + +      def commit(self):          self.conn.commit()      @style.queue + +      def syncSave(self):          self.conn.commit()      @style.async + +      def rollback(self):          self.conn.rollback() +      def async(self, f, *args, **kwargs):          args = (self,) + args          job = DatabaseJob(f, *args, **kwargs)          self.jobs.put(job) +      def queue(self, f, *args, **kwargs):          args = (self,) + args          job = DatabaseJob(f, *args, **kwargs) @@ -277,13 +315,18 @@ class DatabaseBackend(Thread):          return job.result      @classmethod + +      def registerSub(cls, klass):          cls.subs.append(klass)      @classmethod + +      def unregisterSub(cls, klass):          cls.subs.remove(klass) +      def __getattr__(self, attr):          for sub in DatabaseBackend.subs:              if hasattr(sub, attr): diff --git a/pyload/database/File.py b/pyload/database/File.py index 857da1ff9..0ff520623 100644 --- a/pyload/database/File.py +++ b/pyload/database/File.py @@ -42,7 +42,10 @@ class FileHandler(object):          self.db = self.core.db +      def change(func): + +          def new(*args):              args[0].unchanged = False              args[0].filecount = -1 @@ -51,12 +54,16 @@ class FileHandler(object):              return func(*args)          return new +      #-------------------------------------------------------------------------- +      def save(self):          """saves all data to backend"""          self.db.commit() +      #-------------------------------------------------------------------------- +      def syncSave(self):          """saves all data to backend and waits until all data are written"""          pyfiles = self.cache.values() @@ -70,6 +77,8 @@ class FileHandler(object):          self.db.syncSave()      @lock + +      def getCompleteData(self, queue=1):          """gets a complete data representation""" @@ -89,6 +98,8 @@ class FileHandler(object):          return packs      @lock + +      def getInfoData(self, queue=1):          """gets a data representation without links""" @@ -101,6 +112,8 @@ class FileHandler(object):      @lock      @change + +      def addLinks(self, urls, package):          """adds links""" @@ -114,7 +127,9 @@ class FileHandler(object):          #@TODO: change from reloadAll event to package update event          self.core.pullManager.addEvent(ReloadAllEvent("collector")) +      #-------------------------------------------------------------------------- +      @lock      @change      def addPackage(self, name, folder, queue=0): @@ -125,7 +140,9 @@ class FileHandler(object):          self.core.pullManager.addEvent(e)          return lastID +      #-------------------------------------------------------------------------- +      @lock      @change      def deletePackage(self, id): @@ -161,7 +178,9 @@ class FileHandler(object):                  pack.order -= 1                  pack.notifyChange() +      #-------------------------------------------------------------------------- +      @lock      @change      def deleteLink(self, id): @@ -197,18 +216,24 @@ class FileHandler(object):                  pyfile.notifyChange()      #-------------------------------------------------------------------------- + +      def releaseLink(self, id):          """removes pyfile from cache"""          if id in self.cache:              del self.cache[id] +      #-------------------------------------------------------------------------- +      def releasePackage(self, id):          """removes package from cache"""          if id in self.packageCache:              del self.packageCache[id] +      #-------------------------------------------------------------------------- +      def updateLink(self, pyfile):          """updates link"""          self.db.updateLink(pyfile) @@ -216,7 +241,9 @@ class FileHandler(object):          e = UpdateEvent("file", pyfile.id, "collector" if not pyfile.package().queue else "queue")          self.core.pullManager.addEvent(e) +      #-------------------------------------------------------------------------- +      def updatePackage(self, pypack):          """updates a package"""          self.db.updatePackage(pypack) @@ -224,7 +251,9 @@ class FileHandler(object):          e = UpdateEvent("pack", pypack.id, "collector" if not pypack.queue else "queue")          self.core.pullManager.addEvent(e) +      #-------------------------------------------------------------------------- +      def getPackage(self, id):          """return package instance""" @@ -233,7 +262,9 @@ class FileHandler(object):          else:              return self.db.getPackage(id) +      #-------------------------------------------------------------------------- +      def getPackageData(self, id):          """returns dict with package information"""          pack = self.getPackage(id) @@ -257,7 +288,9 @@ class FileHandler(object):          return pack +      #-------------------------------------------------------------------------- +      def getFileData(self, id):          """returns dict with file information"""          if id in self.cache: @@ -265,7 +298,9 @@ class FileHandler(object):          return self.db.getLinkData(id) +      #-------------------------------------------------------------------------- +      def getFile(self, id):          """returns pyfile instance"""          if id in self.cache: @@ -273,7 +308,9 @@ class FileHandler(object):          else:              return self.db.getFile(id) +      #-------------------------------------------------------------------------- +      @lock      def getJob(self, occ):          """get suitable job""" @@ -317,6 +354,8 @@ class FileHandler(object):          return pyfile      @lock + +      def getDecryptJob(self):          """return job for decrypting"""          if "decrypt" in self.jobCache: @@ -332,6 +371,7 @@ class FileHandler(object):              self.jobCache["decrypt"] = "empty"              return None +      def getFileCount(self):          """returns number of files""" @@ -340,6 +380,7 @@ class FileHandler(object):          return self.filecount +      def getQueueCount(self, force=False):          """number of files that have to be processed"""          if self.queuecount == -1 or force: @@ -347,6 +388,7 @@ class FileHandler(object):          return self.queuecount +      def checkAllLinksFinished(self):          """checks if all files are finished and dispatch event""" @@ -357,6 +399,7 @@ class FileHandler(object):          return False +      def checkAllLinksProcessed(self, fid):          """checks if all files was processed and pyload would idle now, needs fid which will be ignored when counting""" @@ -370,11 +413,14 @@ class FileHandler(object):          return False +      def resetCount(self):          self.queuecount = -1      @lock      @change + +      def restartPackage(self, id):          """restart package"""          pyfiles = self.cache.values() @@ -392,6 +438,8 @@ class FileHandler(object):      @lock      @change + +      def restartFile(self, id):          """ restart file"""          if id in self.cache: @@ -407,6 +455,8 @@ class FileHandler(object):      @lock      @change + +      def setPackageLocation(self, id, queue):          """push package to queue""" @@ -440,6 +490,8 @@ class FileHandler(object):      @lock      @change + +      def reorderPackage(self, id, position):          p = self.getPackage(id) @@ -467,6 +519,8 @@ class FileHandler(object):      @lock      @change + +      def reorderFile(self, id, position):          f = self.getFileData(id)          f = f[id] @@ -497,12 +551,15 @@ class FileHandler(object):          self.core.pullManager.addEvent(e)      @change + +      def updateFileInfo(self, data, pid):          """ updates file info (name, size, status, url)"""          ids = self.db.updateLinkInfo(data)          e = UpdateEvent("pack", pid, "collector" if not self.getPackage(pid).queue else "queue")          self.core.pullManager.addEvent(e) +      def checkPackageFinished(self, pyfile):          """ checks if package is finished and calls AddonManager """ @@ -513,6 +570,7 @@ class FileHandler(object):                  self.core.addonManager.packageFinished(pyfile.package())                  pyfile.package().setFinished = True +      def reCheckPackage(self, pid):          """ recheck links in package """          data = self.db.getPackageData(pid) @@ -527,6 +585,8 @@ class FileHandler(object):      @lock      @change + +      def deleteFinishedLinks(self):          """ deletes finished links and packages, return deleted packages """ @@ -549,6 +609,8 @@ class FileHandler(object):      @lock      @change + +      def restartFailed(self):          """ restart all failed links """          self.db.restartFailed() @@ -556,24 +618,32 @@ class FileHandler(object):  class FileMethods(object):      @style.queue + +      def filecount(self, queue):          """returns number of files in queue"""          self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=?", (queue,))          return self.c.fetchone()[0]      @style.queue + +      def queuecount(self, queue):          """ number of files in queue not finished yet"""          self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=? AND l.status NOT IN (0, 4)", (queue,))          return self.c.fetchone()[0]      @style.queue + +      def processcount(self, queue, fid):          """ number of files which have to be proccessed """          self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=? AND l.status IN (2, 3, 5, 7, 12) AND l.id != ?", (queue, str(fid)))          return self.c.fetchone()[0]      @style.inner + +      def _nextPackageOrder(self, queue=0):          self.c.execute('SELECT MAX(packageorder) FROM packages WHERE queue=?', (queue,))          max = self.c.fetchone()[0] @@ -583,6 +653,8 @@ class FileMethods(object):              return 0      @style.inner + +      def _nextFileOrder(self, package):          self.c.execute('SELECT MAX(linkorder) FROM links WHERE package=?', (package,))          max = self.c.fetchone()[0] @@ -592,12 +664,16 @@ class FileMethods(object):              return 0      @style.queue + +      def addLink(self, url, name, plugin, package):          order = self._nextFileOrder(package)          self.c.execute('INSERT INTO links(url, name, plugin, package, linkorder) VALUES(?,?,?,?,?)', (url, name, ".".join(plugintype, pluginname), package, order))          return self.c.lastrowid      @style.queue + +      def addLinks(self, links, package):          """ links is a list of tupels (url, plugin)"""          order = self._nextFileOrder(package) @@ -606,23 +682,31 @@ class FileMethods(object):          self.c.executemany('INSERT INTO links(url, name, plugin, package, linkorder) VALUES(?,?,?,?,?)', links)      @style.queue + +      def addPackage(self, name, folder, queue):          order = self._nextPackageOrder(queue)          self.c.execute('INSERT INTO packages(name, folder, queue, packageorder) VALUES(?,?,?,?)', (name, folder, queue, order))          return self.c.lastrowid      @style.queue + +      def deletePackage(self, p):          self.c.execute('DELETE FROM links WHERE package=?', (str(p.id),))          self.c.execute('DELETE FROM packages WHERE id=?', (str(p.id),))          self.c.execute('UPDATE packages SET packageorder=packageorder-1 WHERE packageorder > ? AND queue=?', (p.order, p.queue))      @style.queue + +      def deleteLink(self, f):          self.c.execute('DELETE FROM links WHERE id=?', (str(f.id),))          self.c.execute('UPDATE links SET linkorder=linkorder-1 WHERE linkorder > ? AND package=?', (f.order, str(f.packageid)))      @style.queue + +      def getAllLinks(self, q):          """return information about all links in queue q @@ -656,6 +740,8 @@ class FileMethods(object):          return data      @style.queue + +      def getAllPackages(self, q):          """return information about packages in queue q          (only useful in get all data) @@ -693,6 +779,8 @@ class FileMethods(object):          return data      @style.queue + +      def getLinkData(self, id):          """get link information as dict"""          self.c.execute('SELECT id, url, name, size, status, error, plugin, package, linkorder FROM links WHERE id=?', (str(id),)) @@ -717,6 +805,8 @@ class FileMethods(object):          return data      @style.queue + +      def getPackageData(self, id):          """get data about links for a package"""          self.c.execute('SELECT id, url, name, size, status, error, plugin, package, linkorder FROM links WHERE package=? ORDER BY linkorder', (str(id),)) @@ -740,14 +830,20 @@ class FileMethods(object):          return data      @style.async + +      def updateLink(self, f):          self.c.execute('UPDATE links SET url=?, name=?, size=?, status=?, error=?, package=? WHERE id=?', (f.url, f.name, f.size, f.status, str(f.error), str(f.packageid), str(f.id)))      @style.queue + +      def updatePackage(self, p):          self.c.execute('UPDATE packages SET name=?, folder=?, site=?, password=?, queue=? WHERE id=?', (p.name, p.folder, p.site, p.password, p.queue, str(p.id)))      @style.queue + +      def updateLinkInfo(self, data):          """ data is list of tupels (name, size, status, url) """          self.c.executemany('UPDATE links SET name=?, size=?, status=? WHERE url=? AND status IN (1, 2, 3, 14)', data) @@ -758,6 +854,8 @@ class FileMethods(object):          return ids      @style.queue + +      def reorderPackage(self, p, position, noMove=False):          if position == -1:              position = self._nextPackageOrder(p.queue) @@ -770,6 +868,8 @@ class FileMethods(object):          self.c.execute('UPDATE packages SET packageorder=? WHERE id=?', (position, str(p.id)))      @style.queue + +      def reorderLink(self, f, position):          """ reorder link with f as dict for pyfile  """          if f["order"] > position: @@ -780,19 +880,27 @@ class FileMethods(object):          self.c.execute('UPDATE links SET linkorder=? WHERE id=?', (position, f["id"]))      @style.queue + +      def clearPackageOrder(self, p):          self.c.execute('UPDATE packages SET packageorder=? WHERE id=?', (-1, str(p.id)))          self.c.execute('UPDATE packages SET packageorder=packageorder-1 WHERE packageorder > ? AND queue=? AND id != ?', (p.order, p.queue, str(p.id)))      @style.async + +      def restartFile(self, id):          self.c.execute('UPDATE links SET status=3, error="" WHERE id=?', (str(id),))      @style.async + +      def restartPackage(self, id):          self.c.execute('UPDATE links SET status=3 WHERE package=?', (str(id),))      @style.queue + +      def getPackage(self, id):          """return package instance from id"""          self.c.execute("SELECT name, folder, site, password, queue, packageorder FROM packages WHERE id=?", (str(id),)) @@ -800,7 +908,9 @@ class FileMethods(object):          if not r: return None          return PyPackage(self.manager, id, * r) +      #-------------------------------------------------------------------------- +      @style.queue      def getFile(self, id):          """return link instance from id""" @@ -812,6 +922,8 @@ class FileMethods(object):          return PyFile(self.manager, id, * r)      @style.queue + +      def getJob(self, occ):          """return pyfile ids, which are suitable for download and dont use a occupied plugin""" @@ -832,6 +944,8 @@ class FileMethods(object):          return [x[0] for x in self.c]      @style.queue + +      def getPluginJob(self, plugins):          """returns pyfile ids with suited plugins"""          cmd = "SELECT l.id FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE l.plugin IN %s AND l.status IN (2, 3, 14) ORDER BY p.packageorder ASC, l.linkorder ASC LIMIT 5" % plugins @@ -841,6 +955,8 @@ class FileMethods(object):          return [x[0] for x in self.c]      @style.queue + +      def getUnfinished(self, pid):          """return list of max length 3 ids with pyfiles in package not finished or processed""" @@ -848,21 +964,29 @@ class FileMethods(object):          return [r[0] for r in self.c]      @style.queue + +      def deleteFinished(self):          self.c.execute("DELETE FROM links WHERE status IN (0, 4)")          self.c.execute("DELETE FROM packages WHERE NOT EXISTS(SELECT 1 FROM links WHERE packages.id=links.package)")      @style.queue + +      def restartFailed(self):          self.c.execute("UPDATE links SET status=3, error='' WHERE status IN (6, 8, 9)")      @style.queue + +      def findDuplicates(self, id, folder, filename):          """ checks if filename exists with different id and same package """          self.c.execute("SELECT l.plugin FROM links as l INNER JOIN packages as p ON l.package=p.id AND p.folder=? WHERE l.id!=? AND l.status=0 AND l.name=?", (folder, id, filename))          return self.c.fetchone()      @style.queue + +      def purgeLinks(self):          self.c.execute("DELETE FROM links;")          self.c.execute("DELETE FROM packages;") diff --git a/pyload/database/Storage.py b/pyload/database/Storage.py index 75e166d39..bd7844d8d 100644 --- a/pyload/database/Storage.py +++ b/pyload/database/Storage.py @@ -6,6 +6,8 @@ from pyload.database import DatabaseBackend  class StorageMethods(object):      @style.queue + +      def setStorage(db, identifier, key, value):          db.c.execute("SELECT id FROM storage WHERE identifier=? AND key=?", (identifier, key))          if db.c.fetchone() is not None: @@ -14,6 +16,8 @@ class StorageMethods(object):              db.c.execute("INSERT INTO storage (identifier, key, value) VALUES (?, ?, ?)", (identifier, key, value))      @style.queue + +      def getStorage(db, identifier, key=None):          if key is not None:              db.c.execute("SELECT value FROM storage WHERE identifier=? AND key=?", (identifier, key)) @@ -28,6 +32,8 @@ class StorageMethods(object):              return d      @style.queue + +      def delStorage(db, identifier, key):          db.c.execute("DELETE FROM storage WHERE identifier=? AND key=?", (identifier, key)) diff --git a/pyload/database/User.py b/pyload/database/User.py index 54545e588..52b05749f 100644 --- a/pyload/database/User.py +++ b/pyload/database/User.py @@ -8,6 +8,8 @@ from pyload.database import DatabaseBackend, style  class UserMethods(object):      @style.queue + +      def checkAuth(db, user, password):          c = db.c          c.execute('SELECT id, name, password, role, permission, template, email FROM "users" WHERE name=?', (user,)) @@ -25,6 +27,8 @@ class UserMethods(object):              return {}      @style.queue + +      def addUser(db, user, password):          salt = reduce(lambda x, y: x + y, [str(random.randint(0, 9)) for i in range(0, 5)])          h = sha1(salt + password) @@ -39,6 +43,8 @@ class UserMethods(object):      @style.queue + +      def changePassword(db, user, oldpw, newpw):          db.c.execute('SELECT id, name, password FROM users WHERE name=?', (user,))          r = db.c.fetchone() @@ -60,15 +66,21 @@ class UserMethods(object):      @style.async + +      def setPermission(db, user, perms):          db.c.execute("UPDATE users SET permission=? WHERE name=?", (perms, user))      @style.async + +      def setRole(db, user, role):          db.c.execute("UPDATE users SET role=? WHERE name=?", (role, user))      @style.queue + +      def listUsers(db):          db.c.execute('SELECT name FROM users')          users = [] @@ -77,6 +89,8 @@ class UserMethods(object):          return users      @style.queue + +      def getAllUserData(db):          db.c.execute("SELECT name, permission, role, template, email FROM users")          user = {} @@ -86,6 +100,8 @@ class UserMethods(object):          return user      @style.queue + +      def removeUser(db, user):          db.c.execute('DELETE FROM users WHERE name=?', (user,)) | 
