summaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
Diffstat (limited to 'module')
-rw-r--r--module/FileManager.py26
-rw-r--r--module/PyFile.py40
-rw-r--r--module/PyPackage.py42
-rw-r--r--module/database/DatabaseBackend.py48
-rw-r--r--module/database/FileDatabase.py184
-rw-r--r--module/lib/hg_tool.py133
-rw-r--r--module/remote/socketbackend/ttypes.py16
-rw-r--r--module/remote/thriftbackend/pyload.thrift48
-rw-r--r--module/remote/thriftbackend/thriftgen/pyload/Pyload.py2
-rw-r--r--module/remote/thriftbackend/thriftgen/pyload/ttypes.py52
-rw-r--r--module/web/pyload_app.py6
-rw-r--r--module/web/utils.py2
12 files changed, 395 insertions, 204 deletions
diff --git a/module/FileManager.py b/module/FileManager.py
index cb3b2ae96..5a0d2b958 100644
--- a/module/FileManager.py
+++ b/module/FileManager.py
@@ -1,6 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+###############################################################################
+# Copyright(c) 2008-2012 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: RaNaN
+###############################################################################
+
from time import time
from threading import RLock
from module.utils import lock
@@ -19,6 +33,8 @@ def invalidate(func):
return new
+# TODO: needs to be replaced later
+OWNER = 0
class FileManager:
"""Handles all request made to obtain information,
@@ -78,7 +94,7 @@ class FileManager:
@invalidate
def addLinks(self, data, package):
"""Add links, data = (plugin, url) tuple. Internal method should use API."""
- self.db.addLinks(data, package)
+ self.db.addLinks(data, package, OWNER)
self.evm.dispatchEvent("packageUpdated", package)
@@ -86,7 +102,7 @@ class FileManager:
def addPackage(self, name, folder, root, password, site, comment, paused):
"""Adds a package to database"""
pid = self.db.addPackage(name, folder, root, password, site, comment,
- PackageStatus.Paused if paused else PackageStatus.Ok)
+ PackageStatus.Paused if paused else PackageStatus.Ok, OWNER)
p = self.db.getPackageInfo(pid)
self.evm.dispatchEvent("packageInserted", pid, p.root, p.packageorder)
@@ -97,7 +113,7 @@ class FileManager:
def getPackage(self, pid):
"""return package instance"""
if pid == self.ROOT_PACKAGE:
- return RootPackage(self)
+ return RootPackage(self, OWNER)
elif pid in self.packages:
pack = self.packages[pid]
pack.timestamp = time()
@@ -115,7 +131,7 @@ class FileManager:
def getPackageInfo(self, pid):
"""returns dict with package information"""
if pid == self.ROOT_PACKAGE:
- pack = RootPackage(self).toInfoData()
+ pack = RootPackage(self, OWNER).toInfoData()
elif pid in self.packages:
pack = self.packages[pid].toInfoData()
pack.stats = self.db.getStatsForPackage(pid)
@@ -183,7 +199,7 @@ class FileManager:
# root package is not in database, create an instance
if pid == self.ROOT_PACKAGE:
- view.root = RootPackage(self).toInfoData()
+ view.root = RootPackage(self, OWNER).toInfoData()
packs[self.ROOT_PACKAGE] = view.root
elif pid in packs:
view.root = packs[pid]
diff --git a/module/PyFile.py b/module/PyFile.py
index 5e6a3fae3..4cd0488a0 100644
--- a/module/PyFile.py
+++ b/module/PyFile.py
@@ -1,20 +1,19 @@
#!/usr/bin/env python
-"""
- 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.
-
- 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.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# -*- coding: utf-8 -*-
+
+###############################################################################
+# Copyright(c) 2008-2012 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: RaNaN
+###############################################################################
from time import sleep, time
from threading import RLock
@@ -48,14 +47,14 @@ class PyFile(object):
Represents a file object at runtime
"""
__slots__ = ("m", "fid", "_name", "_size", "filestatus", "media", "added", "fileorder",
- "url", "pluginname", "hash", "status", "error", "packageid",
+ "url", "pluginname", "hash", "status", "error", "packageid", "ownerid",
"lock", "plugin", "waitUntil", "active", "abort", "statusname",
"reconnected", "progress", "maxprogress", "pluginclass")
@staticmethod
def fromInfoData(m, info):
f = PyFile(m, info.fid, info.name, info.size, info.status, info.media, info.added, info.fileorder,
- "", "", "", DownloadStatus.NA, "", info.package)
+ "", "", "", DownloadStatus.NA, "", info.package, info.owner)
if info.download:
f.url = info.download.url
f.pluginname = info.download.plugin
@@ -66,7 +65,7 @@ class PyFile(object):
return f
def __init__(self, manager, fid, name, size, filestatus, media, added, fileorder,
- url, pluginname, hash, status, error, package):
+ url, pluginname, hash, status, error, package, owner):
self.m = manager
@@ -82,6 +81,7 @@ class PyFile(object):
self.hash = hash
self.status = status
self.error = error
+ self.ownerid = owner
self.packageid = package #should not be used, use package() instead
# database information ends here
@@ -183,7 +183,7 @@ class PyFile(object):
def toInfoData(self):
- return FileInfo(self.fid, self.getName(), self.packageid, self.getSize(), self.filestatus,
+ return FileInfo(self.fid, self.getName(), self.packageid, self.ownerid, self.getSize(), self.filestatus,
self.media, self.added, self.fileorder, DownloadInfo(
self.url, self.pluginname, self.hash, self.status, self.getStatusName(), self.error
)
diff --git a/module/PyPackage.py b/module/PyPackage.py
index d0739124f..1dc2754ef 100644
--- a/module/PyPackage.py
+++ b/module/PyPackage.py
@@ -1,20 +1,19 @@
#!/usr/bin/env python
-"""
- 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.
-
- 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.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+# -*- coding: utf-8 -*-
+
+###############################################################################
+# Copyright(c) 2008-2012 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: RaNaN
+###############################################################################
from time import time
@@ -29,16 +28,17 @@ class PyPackage:
@staticmethod
def fromInfoData(m, info):
- return PyPackage(m, info.pid, info.name, info.folder, info.root,
+ return PyPackage(m, info.pid, info.name, info.folder, info.root, info.owner,
info.site, info.comment, info.password, info.added, info.status, info.packageorder)
- def __init__(self, manager, pid, name, folder, root, site, comment, password, added, status, packageorder):
+ def __init__(self, manager, pid, name, folder, root, owner, site, comment, password, added, status, packageorder):
self.m = manager
self.pid = pid
self.name = name
self.folder = folder
self.root = root
+ self.ownerid = owner
self.site = site
self.comment = comment
self.password = password
@@ -56,7 +56,7 @@ class PyPackage:
return self.timestamp + 30 * 60 > time()
def toInfoData(self):
- return PackageInfo(self.pid, self.name, self.folder, self.root, self.site,
+ return PackageInfo(self.pid, self.name, self.folder, self.root, self.ownerid, self.site,
self.comment, self.password, self.added, self.status, self.packageorder
)
@@ -92,8 +92,8 @@ class PyPackage:
class RootPackage(PyPackage):
- def __init__(self, m):
- PyPackage.__init__(self, m, -1, "root", "", -2, "", "", "", 0, PackageStatus.Ok, 0)
+ def __init__(self, m, owner):
+ PyPackage.__init__(self, m, -1, "root", "", owner, -2, "", "", "", 0, PackageStatus.Ok, 0)
def getPath(self, name=""):
return join(self.m.core.config["general"]["download_folder"], name)
diff --git a/module/database/DatabaseBackend.py b/module/database/DatabaseBackend.py
index 516aa981f..ec39e3fd9 100644
--- a/module/database/DatabaseBackend.py
+++ b/module/database/DatabaseBackend.py
@@ -1,21 +1,21 @@
#!/usr/bin/env python
-"""
- 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.
-
- 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.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
- @author: mkaay
-"""
+# -*- coding: utf-8 -*-
+
+###############################################################################
+# Copyright(c) 2008-2012 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: RaNaN
+# @author: mkaay
+###############################################################################
+
from threading import Thread, Event
from shutil import move
@@ -245,8 +245,10 @@ class DatabaseBackend(Thread):
'"added" INTEGER DEFAULT 0 NOT NULL,' # set by trigger
'"status" INTEGER DEFAULT 0 NOT NULL,'
'"packageorder" INTEGER DEFAULT -1 NOT NULL,' #incremented by trigger
- '"root" INTEGER DEFAULT -1 NOT NULL,'
- 'CHECK (root != pid) '
+ '"root" INTEGER DEFAULT -1 NOT NULL, '
+ '"owner" INTEGER NOT NULL, '
+ 'FOREIGN KEY(owner) REFERENCES users(uid), '
+ 'CHECK (root != pid)'
')'
)
@@ -267,7 +269,8 @@ class DatabaseBackend(Thread):
'END'
)
- self.c.execute('CREATE INDEX IF NOT EXISTS "root_index" ON packages(root)')
+ self.c.execute('CREATE INDEX IF NOT EXISTS "package_index" ON packages(root, owner)')
+ self.c.execute('CREATE INDEX IF NOT EXISTS "package_owner" ON packages(owner)')
self.c.execute(
'CREATE TABLE IF NOT EXISTS "files" ('
@@ -284,11 +287,14 @@ class DatabaseBackend(Thread):
'"dlstatus" INTEGER DEFAULT 0 NOT NULL, '
'"error" TEXT DEFAULT "" NOT NULL, '
'"package" INTEGER NOT NULL, '
+ '"owner" INTEGER NOT NULL, '
+ 'FOREIGN KEY(owner) REFERENCES users(uid), '
'FOREIGN KEY(package) REFERENCES packages(id)'
')'
)
- self.c.execute('CREATE INDEX IF NOT EXISTS "package_index" ON files(package)')
+ self.c.execute('CREATE INDEX IF NOT EXISTS "file_index" ON files(package, owner)')
+ self.c.execute('CREATE INDEX IF NOT EXISTS "file_owner" ON files(owner)')
self.c.execute(
'CREATE TRIGGER IF NOT EXISTS "insert_file" AFTER INSERT ON "files"'
diff --git a/module/database/FileDatabase.py b/module/database/FileDatabase.py
index b783d15d9..80da775c7 100644
--- a/module/database/FileDatabase.py
+++ b/module/database/FileDatabase.py
@@ -1,88 +1,97 @@
#!/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.
-
- 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.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
+###############################################################################
+# Copyright(c) 2008-2012 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: RaNaN
+###############################################################################
+
from new_collections import OrderedDict
-from module.Api import DownloadInfo, LinkStatus, FileInfo, PackageInfo, PackageStats
+from module.Api import DownloadInfo, FileInfo, PackageInfo, PackageStats
from module.database import DatabaseMethods, queue, async, inner
-default = PackageStats(0, 0, 0, 0)
+zero_stats = PackageStats(0, 0, 0, 0)
class FileMethods(DatabaseMethods):
@queue
- def filecount(self):
+ def filecount(self, user=None):
"""returns number of files"""
self.c.execute("SELECT COUNT(*) FROM files")
return self.c.fetchone()[0]
@queue
- def queuecount(self):
+ def queuecount(self, user=None):
""" number of files in queue not finished yet"""
# status not in NA, finished, skipped
self.c.execute("SELECT COUNT(*) FROM files WHERE dlstatus NOT IN (0,5,6)")
return self.c.fetchone()[0]
@queue
- def processcount(self, fid):
+ def processcount(self, fid, user=None):
""" number of files which have to be processed """
# status in online, queued, starting, waiting, downloading
self.c.execute("SELECT COUNT(*) FROM files as WHERE dlstatus IN (2,3,8,9,10) AND fid != ?", (str(fid), ))
return self.c.fetchone()[0]
+ # TODO: think about multiuser side effects on *count methods
+
@queue
- def addLink(self, url, name, plugin, package):
+ def addLink(self, url, name, plugin, package, owner):
# mark file status initially as missing, dlstatus - queued
- self.c.execute('INSERT INTO files(url, name, plugin, status, dlstatus, package) VALUES(?,?,?,1,3,?)',
- (url, name, plugin, package))
+ self.c.execute('INSERT INTO files(url, name, plugin, status, dlstatus, package, owner) VALUES(?,?,?,1,3,?,?)',
+ (url, name, plugin, package, owner))
return self.c.lastrowid
@async
- def addLinks(self, links, package):
+ def addLinks(self, links, package, owner):
""" links is a list of tuples (url, plugin)"""
- links = [(x[0], x[0], x[1], package) for x in links]
- self.c.executemany('INSERT INTO files(url, name, plugin, status, dlstatus, package) VALUES(?,?,?,1,3,?)', links)
+ links = [(x[0], x[0], x[1], package, owner) for x in links]
+ self.c.executemany('INSERT INTO files(url, name, plugin, status, dlstatus, package, owner) VALUES(?,?,?,1,3,?,?)',
+ links)
@queue
- def addFile(self, name, size, media, package):
+ def addFile(self, name, size, media, package, owner):
# file status - ok, dl status NA
- self.c.execute('INSERT INTO files(name, size, media, package) VALUES(?,?,?,?)',
- (name, size, media, package))
+ self.c.execute('INSERT INTO files(name, size, media, package, owner) VALUES(?,?,?,?,?)',
+ (name, size, media, package, owner))
return self.c.lastrowid
@queue
- def addPackage(self, name, folder, root, password, site, comment, status):
- self.c.execute('INSERT INTO packages(name, folder, root, password, site, comment, status) VALUES(?,?,?,?,?,?,?)'
- ,
- (name, folder, root, password, site, comment, status))
+ def addPackage(self, name, folder, root, password, site, comment, status, owner):
+ self.c.execute(
+ 'INSERT INTO packages(name, folder, root, password, site, comment, status, owner) VALUES(?,?,?,?,?,?,?,?)'
+ , (name, folder, root, password, site, comment, status, owner))
return self.c.lastrowid
@async
- def deletePackage(self, pid):
- # order updated by trigger
- self.c.execute('DELETE FROM packages WHERE pid=?', (pid,))
+ def deletePackage(self, pid, owner=None):
+ # order updated by trigge
+ if owner is None:
+ self.c.execute('DELETE FROM packages WHERE pid=?', (pid,))
+ else:
+ self.c.execute('DELETE FROM packages WHERE pid=? AND owner=?', (pid, owner))
@async
- def deleteFile(self, fid, order, package):
+ def deleteFile(self, fid, order, package, owner=None):
""" To delete a file order and package of it is needed """
- self.c.execute('DELETE FROM files WHERE fid=?', (fid,))
- self.c.execute('UPDATE files SET fileorder=fileorder-1 WHERE fileorder > ? AND package=?',
- (order, package))
+ if owner is None:
+ self.c.execute('DELETE FROM files WHERE fid=?', (fid,))
+ self.c.execute('UPDATE files SET fileorder=fileorder-1 WHERE fileorder > ? AND package=?',
+ (order, package))
+ else:
+ self.c.execute('DELETE FROM files WHERE fid=? AND owner=?', (fid, owner))
+ self.c.execute('UPDATE files SET fileorder=fileorder-1 WHERE fileorder > ? AND package=? AND owner=?',
+ (order, package, owner))
@async
def saveCollector(self, owner, data):
@@ -92,8 +101,10 @@ class FileMethods(DatabaseMethods):
@queue
def retrieveCollector(self, owner):
""" retrieve the saved string """
- self.c.execute('SELECT data FROM collector owner=?', (owner,))
- return self.c.fetchone()[0]
+ self.c.execute('SELECT data FROM collector WHERE owner=?', (owner,))
+ r = self.c.fetchone()
+ if not r: return None
+ return r[0]
@async
def deleteCollector(self, owner):
@@ -101,66 +112,81 @@ class FileMethods(DatabaseMethods):
self.c.execute('DELETE FROM collector WHERE owner=?', (owner,))
@queue
- def getAllFiles(self, package=None, search=None, unfinished=False):
+ def getAllFiles(self, package=None, search=None, unfinished=False, owner=None):
""" Return dict with file information
:param package: optional package to filter out
:param search: or search string for file name
:param unfinished: filter by dlstatus not finished
+ :param owner: only specific owner
"""
- qry = ('SELECT fid, name, size, status, media, added, fileorder, '
- 'url, plugin, hash, dlstatus, error, package FROM files')
+ qry = ('SELECT fid, name, owner, size, status, media, added, fileorder, '
+ 'url, plugin, hash, dlstatus, error, package FROM files WHERE ')
+
+ arg = []
if unfinished:
- qry += ' WHERE dlstatus NOT IN (0, 5, 6)'
+ qry += 'dlstatus NOT IN (0, 5, 6) AND '
+ if owner is not None:
+ qry += 'owner=? AND '
+ arg.append(owner)
if package is not None:
- qry += ' AND' if unfinished else ' WHERE'
- self.c.execute(qry + ' package=? ORDER BY package, fileorder', (package,))
- elif search is not None:
- qry += ' AND' if unfinished else ' WHERE'
+ arg.append(package)
+ qry += 'package=? AND '
+ if search is not None:
search = "%%%s%%" % search.strip("%")
- self.c.execute(qry + ' name LIKE ? ORDER BY package, fileorder', (search,))
+ arg.append(search)
+ qry += "name LIKE ? "
- else:
- self.c.execute(qry)
+ # make qry valid
+ if qry.endswith("WHERE "): qry = qry[:-6]
+ if qry.endswith("AND "): qry = qry[:-4]
+
+ self.c.execute(qry + "ORDER BY package, fileorder", arg)
data = OrderedDict()
for r in self.c:
- f = FileInfo(r[0], r[1], r[12], r[2], r[3], r[4], r[5], r[6])
- if r[10] > 0: # dl status != NA
- f.download = DownloadInfo(r[7], r[8], r[9], r[10], self.manager.statusMsg[r[10]], r[11])
+ f = FileInfo(r[0], r[1], r[13], r[2], r[3], r[4], r[5], r[6], r[7])
+ if r[11] > 0: # dl status != NA
+ f.download = DownloadInfo(r[8], r[9], r[10], r[11], self.manager.statusMsg[r[11]], r[12])
data[r[0]] = f
return data
@queue
- def getAllPackages(self, root=None):
+ def getAllPackages(self, root=None, owner=None):
""" Return dict with package information
:param root: optional root to filter
"""
- qry = ('SELECT pid, name, folder, root, site, comment, password, added, status, packageorder '
+ qry = ('SELECT pid, name, folder, root, owner, site, comment, password, added, status, packageorder '
'FROM packages%s ORDER BY root, packageorder')
if root is None:
- stats = self.getPackageStats()
- self.c.execute(qry % "")
+ stats = self.getPackageStats(owner=owner)
+ if owner is None:
+ self.c.execute(qry % "")
+ else:
+ self.c.execute(qry % " WHERE owner=?", (owner,))
else:
- stats = self.getPackageStats(root=root)
- self.c.execute(qry % ' WHERE root=? OR pid=?', (root, root))
+ stats = self.getPackageStats(root=root, owner=owner)
+ if owner is None:
+ self.c.execute(qry % ' WHERE root=? OR pid=?', (root, root))
+ else:
+ self.c.execute(qry % ' WHERE (root=? OR pid=?) AND owner=?', (root, root, owner))
data = OrderedDict()
for r in self.c:
data[r[0]] = PackageInfo(
- r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], stats.get(r[0], default)
+ r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], stats.get(r[0], zero_stats)
)
return data
@inner
- def getPackageStats(self, pid=None, root=None):
+ def getPackageStats(self, pid=None, root=None, owner=None):
qry = ("SELECT p.pid, SUM(f.size) AS sizetotal, COUNT(f.fid) AS linkstotal, sizedone, linksdone "
"FROM packages p JOIN files f ON p.pid = f.package AND f.dlstatus > 0 %(sub)s LEFT OUTER JOIN "
"(SELECT p.pid AS pid, SUM(f.size) AS sizedone, COUNT(f.fid) AS linksdone "
@@ -173,6 +199,8 @@ class FileMethods(DatabaseMethods):
self.c.execute(qry % {"sub": "AND (p.root=:root OR p.pid=:root)"}, locals())
elif pid is not None:
self.c.execute(qry % {"sub": "AND p.pid=:pid"}, locals())
+ elif owner is not None:
+ self.c.execute(qry % {"sub": "AND p.owner=:owner"}, locals())
else:
self.c.execute(qry % {"sub": ""})
@@ -193,17 +221,17 @@ class FileMethods(DatabaseMethods):
@queue
def getFileInfo(self, fid, force=False):
- """get data for specific file"""
- self.c.execute('SELECT fid, name, size, status, media, added, fileorder, '
+ """get data for specific file, when force is true download info will be appended"""
+ self.c.execute('SELECT fid, name, owner, size, status, media, added, fileorder, '
'url, plugin, hash, dlstatus, error, package FROM files '
'WHERE fid=?', (fid,))
r = self.c.fetchone()
if not r:
return None
else:
- f = FileInfo(r[0], r[1], r[12], r[2], r[3], r[4], r[5], r[6])
- if r[10] > 0 or force:
- f.download = DownloadInfo(r[7], r[8], r[9], r[10], self.manager.statusMsg[r[10]], r[11])
+ f = FileInfo(r[0], r[1], r[13], r[2], r[3], r[4], r[5], r[6], r[7])
+ if r[11] > 0 or force:
+ f.download = DownloadInfo(r[8], r[9], r[10], r[11], self.manager.statusMsg[r[11]], r[12])
return f
@@ -213,7 +241,7 @@ class FileMethods(DatabaseMethods):
if stats:
stats = self.getPackageStats(pid=pid)
- self.c.execute('SELECT pid, name, folder, root, site, comment, password, added, status, packageorder '
+ self.c.execute('SELECT pid, name, folder, root, owner, site, comment, password, added, status, packageorder '
'FROM packages WHERE pid=?', (pid,))
r = self.c.fetchone()
@@ -221,11 +249,11 @@ class FileMethods(DatabaseMethods):
return None
else:
return PackageInfo(
- r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], stats.get(r[0], default) if stats else None
+ r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], stats.get(r[0], zero_stats) if stats else None
)
@async
- def updateLinkInfo(self, data):
+ def updateLinkInfo(self, data, owner):
""" data is list of tuples (name, size, status,[ hash,] url)"""
if data and len(data[0]) == 4:
self.c.executemany('UPDATE files SET name=?, size=?, dlstatus=? WHERE url=? AND dlstatus IN (0,1,2,3,14)',
@@ -246,6 +274,7 @@ class FileMethods(DatabaseMethods):
self.c.execute('UPDATE packages SET name=?, folder=?, site=?, comment=?, password=?, status=? WHERE pid=?',
(p.name, p.folder, p.site, p.comment, p.password, p.status, p.pid))
+ # TODO: most modifying methods needs owner argument to avoid checking beforehand
@async
def orderPackage(self, pid, root, oldorder, order):
if oldorder > order: # package moved upwards
@@ -293,11 +322,6 @@ class FileMethods(DatabaseMethods):
self.c.execute('SELECT max(packageorder) FROM packages WHERE root=?', (dpid,))
r = self.c.fetchone()
max = (r[0] if r[0] else 0) + 1
- print max
-
- self.c.execute('SELECT pid, packageorder FROM packages WHERE root=?', (dpid,))
- for r in self.c:
- print r
self.c.execute('UPDATE packages SET packageorder=packageorder-1 WHERE packageorder > ? AND root=?',
(order, root))
@@ -314,6 +338,8 @@ class FileMethods(DatabaseMethods):
# status -> queued
self.c.execute('UPDATE files SET status=3 WHERE package=?', (pid,))
+
+ # TODO: multi user approach
@queue
def getJob(self, occ):
"""return pyfile ids, which are suitable for download and dont use a occupied plugin"""
@@ -338,7 +364,7 @@ class FileMethods(DatabaseMethods):
return [r[0] for r in self.c]
@queue
- def restartFailed(self):
+ def restartFailed(self, owner):
# status=queued, where status in failed, aborted, temp offline
self.c.execute("UPDATE files SET dlstatus=3, error='' WHERE dlstatus IN (7, 11, 12)")
diff --git a/module/lib/hg_tool.py b/module/lib/hg_tool.py
new file mode 100644
index 000000000..cd97833df
--- /dev/null
+++ b/module/lib/hg_tool.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import re
+from subprocess import Popen, PIPE
+from time import time, gmtime, strftime
+
+aliases = {"zoidber": "zoidberg", "zoidberg10": "zoidberg", "webmaster": "dhmh", "mast3rranan": "ranan",
+ "ranan2": "ranan"}
+exclude = ["locale/*", "module/lib/*"]
+date_format = "%Y-%m-%d"
+line_re = re.compile(r" (\d+) \**", re.I)
+
+def add_exclude_flags(args):
+ for dir in exclude:
+ args.extend(["-X", dir])
+
+# remove small percentages
+def wipe(data, perc=1):
+ s = (sum(data.values()) * perc) / 100
+ for k, v in data.items():
+ if v < s: del data[k]
+
+ return data
+
+# remove aliases
+def de_alias(data):
+ for k, v in aliases.iteritems():
+ if k not in data: continue
+ alias = aliases[k]
+
+ if alias in data: data[alias] += data[k]
+ else: data[alias] = data[k]
+
+ del data[k]
+
+ return data
+
+
+def output(data):
+ s = float(sum(data.values()))
+ print "Total Lines: %d" % s
+ for k, v in data.iteritems():
+ print "%15s: %.1f%% | %d" % (k, (v * 100) / s, v)
+ print
+
+
+def file_list():
+ args = ["hg", "status", "-A"]
+ add_exclude_flags(args)
+ p = Popen(args, stdout=PIPE)
+ out, err = p.communicate()
+ return [x.split()[1] for x in out.splitlines() if x.split()[0] in "CMA"]
+
+
+def hg_annotate(path):
+ args = ["hg", "annotate", "-u", path]
+ p = Popen(args, stdout=PIPE)
+ out, err = p.communicate()
+
+ data = {}
+
+ for line in out.splitlines():
+ author, non, line = line.partition(":")
+
+ # probably binary file
+ if author == path: return {}
+
+ author = author.strip().lower()
+ if not line.strip(): continue # don't count blank lines
+
+ if author in data: data[author] += 1
+ else: data[author] = 1
+
+ return de_alias(data)
+
+
+def hg_churn(days=None):
+ args = ["hg", "churn"]
+ if days:
+ args.append("-d")
+ t = time() - 60 * 60 * 24 * days
+ args.append("%s to %s" % (strftime(date_format, gmtime(t)), strftime(date_format)))
+
+ add_exclude_flags(args)
+ p = Popen(args, stdout=PIPE)
+ out, err = p.communicate()
+
+ data = {}
+
+ for line in out.splitlines():
+ m = line_re.search(line)
+ author = line.split()[0]
+ lines = int(m.group(1))
+
+ if "@" in author:
+ author, n, email = author.partition("@")
+
+ author = author.strip().lower()
+
+ if author in data: data[author] += lines
+ else: data[author] = lines
+
+ return de_alias(data)
+
+
+def complete_annotate():
+ files = file_list()
+ data = {}
+ for f in files:
+ tmp = hg_annotate(f)
+ for k, v in tmp.iteritems():
+ if k in data: data[k] += v
+ else: data[k] = v
+
+ return data
+
+
+if __name__ == "__main__":
+ for d in (30, 90, 180):
+ c = wipe(hg_churn(d))
+ print "Changes in %d days:" % d
+ output(c)
+
+ c = wipe(hg_churn())
+ print "Total changes:"
+ output(c)
+
+ print "Current source code version:"
+ data = wipe(complete_annotate())
+ output(data)
+
+
diff --git a/module/remote/socketbackend/ttypes.py b/module/remote/socketbackend/ttypes.py
index 36f2b01ef..127098ec3 100644
--- a/module/remote/socketbackend/ttypes.py
+++ b/module/remote/socketbackend/ttypes.py
@@ -159,12 +159,13 @@ class FileDoesNotExists(Exception):
self.fid = fid
class FileInfo(BaseObject):
- __slots__ = ['fid', 'name', 'package', 'size', 'status', 'media', 'added', 'fileorder', 'download']
+ __slots__ = ['fid', 'name', 'package', 'owner', 'size', 'status', 'media', 'added', 'fileorder', 'download']
- def __init__(self, fid=None, name=None, package=None, size=None, status=None, media=None, added=None, fileorder=None, download=None):
+ def __init__(self, fid=None, name=None, package=None, owner=None, size=None, status=None, media=None, added=None, fileorder=None, download=None):
self.fid = fid
self.name = name
self.package = package
+ self.owner = owner
self.size = size
self.status = status
self.media = media
@@ -210,13 +211,14 @@ class PackageDoesNotExists(Exception):
self.pid = pid
class PackageInfo(BaseObject):
- __slots__ = ['pid', 'name', 'folder', 'root', 'site', 'comment', 'password', 'added', 'status', 'packageorder', 'stats', 'fids', 'pids']
+ __slots__ = ['pid', 'name', 'folder', 'root', 'owner', 'site', 'comment', 'password', 'added', 'status', 'packageorder', 'stats', 'fids', 'pids']
- def __init__(self, pid=None, name=None, folder=None, root=None, site=None, comment=None, password=None, added=None, status=None, packageorder=None, stats=None, fids=None, pids=None):
+ def __init__(self, pid=None, name=None, folder=None, root=None, owner=None, site=None, comment=None, password=None, added=None, status=None, packageorder=None, stats=None, fids=None, pids=None):
self.pid = pid
self.name = name
self.folder = folder
self.root = root
+ self.owner = owner
self.site = site
self.comment = comment
self.password = password
@@ -291,9 +293,9 @@ class ServiceException(Exception):
self.msg = msg
class UserData(BaseObject):
- __slots__ = ['uid', 'name', 'email', 'role', 'permission', 'folder', 'traffic', 'limit', 'user', 'templateName']
+ __slots__ = ['uid', 'name', 'email', 'role', 'permission', 'folder', 'traffic', 'dllimit', 'user', 'templateName']
- def __init__(self, uid=None, name=None, email=None, role=None, permission=None, folder=None, traffic=None, limit=None, user=None, templateName=None):
+ def __init__(self, uid=None, name=None, email=None, role=None, permission=None, folder=None, traffic=None, dllimit=None, user=None, templateName=None):
self.uid = uid
self.name = name
self.email = email
@@ -301,7 +303,7 @@ class UserData(BaseObject):
self.permission = permission
self.folder = folder
self.traffic = traffic
- self.limit = limit
+ self.dllimit = dllimit
self.user = user
self.templateName = templateName
diff --git a/module/remote/thriftbackend/pyload.thrift b/module/remote/thriftbackend/pyload.thrift
index 254f41068..181ca4204 100644
--- a/module/remote/thriftbackend/pyload.thrift
+++ b/module/remote/thriftbackend/pyload.thrift
@@ -84,13 +84,12 @@ enum Permission {
All = 0, // requires no permission, but login
Add = 1, // can add packages
Delete = 2, // can delete packages
- Status = 4, // see and change server status
- List = 16, // see listed downloads
- Modify = 32, // modify some attribute of downloads
- Download = 64, // can download from webinterface
- Accounts = 128, // can access accounts
- Interaction = 256, // can interact with plugins
- Addons = 512 // user can activate addons
+ Modify = 4, // modify some attribute of downloads
+ Status = 8, // see and change server status
+ Download = 16, // can download from webinterface
+ Accounts = 32, // can access accounts
+ Interaction = 64, // can interact with plugins
+ Addons = 128 // user can activate addons
}
enum Role {
@@ -141,12 +140,13 @@ struct FileInfo {
1: FileID fid,
2: string name,
3: PackageID package,
- 4: ByteCount size,
- 5: FileStatus status,
- 6: MediaType media,
- 7: UTCDate added,
- 8: i16 fileorder,
- 9: optional DownloadInfo download,
+ 4: UserID owner,
+ 5: ByteCount size,
+ 6: FileStatus status,
+ 7: MediaType media,
+ 8: UTCDate added,
+ 9: i16 fileorder,
+ 10: optional DownloadInfo download,
}
struct PackageStats {
@@ -161,15 +161,16 @@ struct PackageInfo {
2: string name,
3: string folder,
4: PackageID root,
- 5: string site,
- 6: string comment,
- 7: string password,
- 8: UTCDate added,
- 9: PackageStatus status,
- 10: i16 packageorder,
- 11: PackageStats stats,
- 12: list<FileID> fids,
- 13: list<PackageID> pids,
+ 5: UserID owner,
+ 6: string site,
+ 7: string comment,
+ 8: string password,
+ 9: UTCDate added,
+ 10: PackageStatus status,
+ 11: i16 packageorder,
+ 12: PackageStats stats,
+ 13: list<FileID> fids,
+ 14: list<PackageID> pids,
}
// thrift does not allow recursive datatypes, so all data is accumulated and mapped with id
@@ -266,10 +267,9 @@ struct AddonService {
struct OnlineCheck {
1: ResultID rid, // -1 -> nothing more to get
- 2: map<string, LinkStatus> data, //url to result
+ 2: map<string, LinkStatus> data, // url to result
}
-
// exceptions
exception PackageDoesNotExists {
diff --git a/module/remote/thriftbackend/thriftgen/pyload/Pyload.py b/module/remote/thriftbackend/thriftgen/pyload/Pyload.py
index c45663d25..dd446cfc3 100644
--- a/module/remote/thriftbackend/thriftgen/pyload/Pyload.py
+++ b/module/remote/thriftbackend/thriftgen/pyload/Pyload.py
@@ -9,7 +9,7 @@
from thrift.Thrift import TType, TMessageType, TException
from ttypes import *
from thrift.Thrift import TProcessor
-from thrift.protocol.TBase import TBase, TExceptionBase, TApplicationException
+from thrift.protocol.TBase import TBase, TExceptionBase
class Iface(object):
diff --git a/module/remote/thriftbackend/thriftgen/pyload/ttypes.py b/module/remote/thriftbackend/thriftgen/pyload/ttypes.py
index c177a9dd2..d170f4688 100644
--- a/module/remote/thriftbackend/thriftgen/pyload/ttypes.py
+++ b/module/remote/thriftbackend/thriftgen/pyload/ttypes.py
@@ -411,6 +411,7 @@ class FileInfo(TBase):
- fid
- name
- package
+ - owner
- size
- status
- media
@@ -423,6 +424,7 @@ class FileInfo(TBase):
'fid',
'name',
'package',
+ 'owner',
'size',
'status',
'media',
@@ -436,18 +438,20 @@ class FileInfo(TBase):
(1, TType.I32, 'fid', None, None, ), # 1
(2, TType.STRING, 'name', None, None, ), # 2
(3, TType.I32, 'package', None, None, ), # 3
- (4, TType.I64, 'size', None, None, ), # 4
- (5, TType.I32, 'status', None, None, ), # 5
- (6, TType.I32, 'media', None, None, ), # 6
- (7, TType.I64, 'added', None, None, ), # 7
- (8, TType.I16, 'fileorder', None, None, ), # 8
- (9, TType.STRUCT, 'download', (DownloadInfo, DownloadInfo.thrift_spec), None, ), # 9
+ (4, TType.I32, 'owner', None, None, ), # 4
+ (5, TType.I64, 'size', None, None, ), # 5
+ (6, TType.I32, 'status', None, None, ), # 6
+ (7, TType.I32, 'media', None, None, ), # 7
+ (8, TType.I64, 'added', None, None, ), # 8
+ (9, TType.I16, 'fileorder', None, None, ), # 9
+ (10, TType.STRUCT, 'download', (DownloadInfo, DownloadInfo.thrift_spec), None, ), # 10
)
- def __init__(self, fid=None, name=None, package=None, size=None, status=None, media=None, added=None, fileorder=None, download=None,):
+ def __init__(self, fid=None, name=None, package=None, owner=None, size=None, status=None, media=None, added=None, fileorder=None, download=None,):
self.fid = fid
self.name = name
self.package = package
+ self.owner = owner
self.size = size
self.status = status
self.media = media
@@ -494,6 +498,7 @@ class PackageInfo(TBase):
- name
- folder
- root
+ - owner
- site
- comment
- password
@@ -510,6 +515,7 @@ class PackageInfo(TBase):
'name',
'folder',
'root',
+ 'owner',
'site',
'comment',
'password',
@@ -527,22 +533,24 @@ class PackageInfo(TBase):
(2, TType.STRING, 'name', None, None, ), # 2
(3, TType.STRING, 'folder', None, None, ), # 3
(4, TType.I32, 'root', None, None, ), # 4
- (5, TType.STRING, 'site', None, None, ), # 5
- (6, TType.STRING, 'comment', None, None, ), # 6
- (7, TType.STRING, 'password', None, None, ), # 7
- (8, TType.I64, 'added', None, None, ), # 8
- (9, TType.I32, 'status', None, None, ), # 9
- (10, TType.I16, 'packageorder', None, None, ), # 10
- (11, TType.STRUCT, 'stats', (PackageStats, PackageStats.thrift_spec), None, ), # 11
- (12, TType.LIST, 'fids', (TType.I32,None), None, ), # 12
- (13, TType.LIST, 'pids', (TType.I32,None), None, ), # 13
+ (5, TType.I32, 'owner', None, None, ), # 5
+ (6, TType.STRING, 'site', None, None, ), # 6
+ (7, TType.STRING, 'comment', None, None, ), # 7
+ (8, TType.STRING, 'password', None, None, ), # 8
+ (9, TType.I64, 'added', None, None, ), # 9
+ (10, TType.I32, 'status', None, None, ), # 10
+ (11, TType.I16, 'packageorder', None, None, ), # 11
+ (12, TType.STRUCT, 'stats', (PackageStats, PackageStats.thrift_spec), None, ), # 12
+ (13, TType.LIST, 'fids', (TType.I32,None), None, ), # 13
+ (14, TType.LIST, 'pids', (TType.I32,None), None, ), # 14
)
- def __init__(self, pid=None, name=None, folder=None, root=None, site=None, comment=None, password=None, added=None, status=None, packageorder=None, stats=None, fids=None, pids=None,):
+ def __init__(self, pid=None, name=None, folder=None, root=None, owner=None, site=None, comment=None, password=None, added=None, status=None, packageorder=None, stats=None, fids=None, pids=None,):
self.pid = pid
self.name = name
self.folder = folder
self.root = root
+ self.owner = owner
self.site = site
self.comment = comment
self.password = password
@@ -809,7 +817,7 @@ class UserData(TBase):
- permission
- folder
- traffic
- - limit
+ - dllimit
- user
- templateName
"""
@@ -822,7 +830,7 @@ class UserData(TBase):
'permission',
'folder',
'traffic',
- 'limit',
+ 'dllimit',
'user',
'templateName',
]
@@ -836,12 +844,12 @@ class UserData(TBase):
(5, TType.I16, 'permission', None, None, ), # 5
(6, TType.STRING, 'folder', None, None, ), # 6
(7, TType.I64, 'traffic', None, None, ), # 7
- (8, TType.I16, 'limit', None, None, ), # 8
+ (8, TType.I16, 'dllimit', None, None, ), # 8
(9, TType.I32, 'user', None, None, ), # 9
(10, TType.STRING, 'templateName', None, None, ), # 10
)
- def __init__(self, uid=None, name=None, email=None, role=None, permission=None, folder=None, traffic=None, limit=None, user=None, templateName=None,):
+ def __init__(self, uid=None, name=None, email=None, role=None, permission=None, folder=None, traffic=None, dllimit=None, user=None, templateName=None,):
self.uid = uid
self.name = name
self.email = email
@@ -849,7 +857,7 @@ class UserData(TBase):
self.permission = permission
self.folder = folder
self.traffic = traffic
- self.limit = limit
+ self.dllimit = dllimit
self.user = user
self.templateName = templateName
diff --git a/module/web/pyload_app.py b/module/web/pyload_app.py
index 4c448d2cd..2234b76c6 100644
--- a/module/web/pyload_app.py
+++ b/module/web/pyload_app.py
@@ -36,7 +36,7 @@ from utils import render_to_response, parse_permissions, parse_userdata, \
from filters import relpath, unquotepath
-from module.Api import Output
+from module.Api import Output, Permission
from module.utils import format_size
from module.utils.fs import save_join, fs_encode, fs_decode, listdir
@@ -150,7 +150,7 @@ def logout():
@route("/")
@route("/home")
-@login_required("LIST")
+@login_required("List")
def home():
try:
res = [toDict(x) for x in PYLOAD.getProgressInfo()]
@@ -168,7 +168,7 @@ def home():
@route("/queue")
-@login_required("LIST")
+@login_required("List")
def queue():
queue = PYLOAD.getQueue()
diff --git a/module/web/utils.py b/module/web/utils.py
index ac4bdd4f8..1641fdbba 100644
--- a/module/web/utils.py
+++ b/module/web/utils.py
@@ -50,7 +50,7 @@ def parse_permissions(session):
def permlist():
- return [x for x in dir(Permission) if not x.startswith("_") and x != "ALL"]
+ return [x for x in dir(Permission) if not x.startswith("_") and x != "All"]
def get_permission(perms, p):