diff options
Diffstat (limited to 'module/web/pyload')
-rw-r--r-- | module/web/pyload/__init__.py | 0 | ||||
-rw-r--r-- | module/web/pyload/admin.py | 15 | ||||
-rw-r--r-- | module/web/pyload/models.py | 31 | ||||
-rw-r--r-- | module/web/pyload/templatetags/__init__.py | 0 | ||||
-rw-r--r-- | module/web/pyload/templatetags/contains.py | 14 | ||||
-rw-r--r-- | module/web/pyload/templatetags/token.py | 17 | ||||
-rw-r--r-- | module/web/pyload/tests.py | 23 | ||||
-rw-r--r-- | module/web/pyload/urls.py | 24 | ||||
-rw-r--r-- | module/web/pyload/views.py | 373 |
9 files changed, 497 insertions, 0 deletions
diff --git a/module/web/pyload/__init__.py b/module/web/pyload/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/module/web/pyload/__init__.py diff --git a/module/web/pyload/admin.py b/module/web/pyload/admin.py new file mode 100644 index 000000000..99cb28836 --- /dev/null +++ b/module/web/pyload/admin.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from django.contrib import admin +from models import Prefs +from django.contrib.auth.models import User +from django.contrib.auth.admin import UserAdmin as RealUserAdmin + + +class UserProfileInline(admin.StackedInline): + model = Prefs + +class UserAdmin(RealUserAdmin): + inlines = [ UserProfileInline ] + +admin.site.unregister(User) +admin.site.register(User, UserAdmin)
\ No newline at end of file diff --git a/module/web/pyload/models.py b/module/web/pyload/models.py new file mode 100644 index 000000000..86962f23c --- /dev/null +++ b/module/web/pyload/models.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from django.db import models +from django.contrib.auth.models import User +# Create your models here. + +class Prefs(models.Model): + """ Permissions setting """ + + user = models.ForeignKey(User, unique=True) + template = models.CharField(max_length=30, default='default', null=False, blank=False) #@TODO: currently unused + + class Meta: + permissions = ( + ('can_see_dl', 'User can see Downloads'), + ('can_change_status', 'User can change Status'), + ('can_download', 'User can download'), + ('can_add', 'User can add Links'), + ('can_delete', 'User can delete Links'), + ('can_see_logs', 'User can see Logs'), + ) + verbose_name = "Preferences" + verbose_name_plural = "Preferences" + + def __unicode__(self): + return "Preferences for %s" % self.user + + +def user_post_save(sender, instance, **kwargs): + profile, new = Prefs.objects.get_or_create(user=instance) + +models.signals.post_save.connect(user_post_save, User)
\ No newline at end of file diff --git a/module/web/pyload/templatetags/__init__.py b/module/web/pyload/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/module/web/pyload/templatetags/__init__.py diff --git a/module/web/pyload/templatetags/contains.py b/module/web/pyload/templatetags/contains.py new file mode 100644 index 000000000..ed6225a95 --- /dev/null +++ b/module/web/pyload/templatetags/contains.py @@ -0,0 +1,14 @@ +from django import template +register = template.Library() + +@register.filter() +def contains(value, arg): + """ + Usage: + {% if text|contains:" http://" %} + This is a link. + {% else %} + Not a link. + {% endif %} + """ + return arg in value diff --git a/module/web/pyload/templatetags/token.py b/module/web/pyload/templatetags/token.py new file mode 100644 index 000000000..e6117b839 --- /dev/null +++ b/module/web/pyload/templatetags/token.py @@ -0,0 +1,17 @@ + +from django import VERSION +from django import template +register = template.Library() + +if VERSION[:3] < (1,1,2): + + class TokenNode(template.Node): + def render(self, content): + return "" + + @register.tag() + def csrf_token(parser, token): + """ + Return nothing, since csrf is deactivated in django 1.1 + """ + return TokenNode() diff --git a/module/web/pyload/tests.py b/module/web/pyload/tests.py new file mode 100644 index 000000000..2247054b3 --- /dev/null +++ b/module/web/pyload/tests.py @@ -0,0 +1,23 @@ +""" +This file demonstrates two different styles of tests (one doctest and one +unittest). These will both pass when you run "manage.py test". + +Replace these with more appropriate tests for your application. +""" + +from django.test import TestCase + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.failUnlessEqual(1 + 1, 2) + +__test__ = {"doctest": """ +Another way to test that 1 + 1 is equal to 2. + +>>> 1 + 1 == 2 +True +"""} + diff --git a/module/web/pyload/urls.py b/module/web/pyload/urls.py new file mode 100644 index 000000000..66ea68e39 --- /dev/null +++ b/module/web/pyload/urls.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from os.path import join + +from django.conf import settings +from django.conf.urls.defaults import * + + +urlpatterns = patterns('pyload', + (r'^home/$', 'views.home'), + (r'^downloads/$', 'views.downloads',{},'downloads'), + (r'^download/(?P<path>[a-zA-z\.0-9\-/_% "\\]+)$', 'views.download',{},'download'), + (r'^queue/$', 'views.queue',{}, 'queue'), + (r'^collector/$', 'views.collector',{}, 'collector'), + (r'^settings/$', 'views.config',{}, 'config'), + (r'^logs/$', 'views.logs',{}, 'logs'), + (r'^logs/(?P<item>\d+)$', 'views.logs',{}, 'logs'), + (r'^$', 'views.home',{}, 'home'), + ) + +urlpatterns += patterns('django.contrib.auth', + (r'^login/$', 'views.login', {'template_name': join(settings.TEMPLATE, 'login.html')}), + (r'^logout/$', 'views.logout', {'template_name': join(settings.TEMPLATE, 'logout.html')}, 'logout'), +)
\ No newline at end of file diff --git a/module/web/pyload/views.py b/module/web/pyload/views.py new file mode 100644 index 000000000..615840428 --- /dev/null +++ b/module/web/pyload/views.py @@ -0,0 +1,373 @@ +# -*- coding: utf-8 -*- + +# Create your views here. +import mimetypes +from os import listdir +from os import stat +from os.path import isdir +from os.path import isfile +from os.path import join +from urllib import unquote +from itertools import chain +from datetime import datetime + +from django.conf import settings +from django.contrib.auth.decorators import login_required +from django.http import HttpResponse +from django.http import HttpResponseNotFound +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.utils.translation import ugettext as _ + + +def get_sort_key(item): + return item[1]["order"] + +def check_server(function): + def _dec(view_func): + def _view(request, * args, ** kwargs): + try: + version = settings.PYLOAD.get_server_version() + except Exception, e: + return base(request, messages=[_('Can\'t connect to pyLoad. Please check your configuration and make sure pyLoad is running.'), str(e)]) + return view_func(request, * args, ** kwargs) + + _view.__name__ = view_func.__name__ + _view.__dict__ = view_func.__dict__ + _view.__doc__ = view_func.__doc__ + + return _view + + if function is None: + return _dec + else: + return _dec(function) + + +def permission(perm): + def _dec(view_func): + def _view(request, * args, ** kwargs): + if request.user.has_perm(perm) and request.user.is_authenticated(): + return view_func(request, * args, ** kwargs) + else: + return base(request, messages=[_('You don\'t have permission to view this page.')]) + + _view.__name__ = view_func.__name__ + _view.__dict__ = view_func.__dict__ + _view.__doc__ = view_func.__doc__ + + return _view + + return _dec + + + +def status_proc(request): + return {'status': settings.PYLOAD.status_server(), 'captcha': settings.PYLOAD.is_captcha_waiting()} + + +def base(request, messages): + return render_to_response(join(settings.TEMPLATE, 'base.html'), {'messages': messages}, RequestContext(request)) + +@login_required +@permission('pyload.can_see_dl') +@check_server +def home(request): + res = settings.PYLOAD.status_downloads() + + for link in res: + if link["status"] == 12: + link["information"] = "%s kB @ %s kB/s" % (link["size"] - link["kbleft"], link["speed"]) + + return render_to_response(join(settings.TEMPLATE, 'home.html'), RequestContext(request, {'content': res}, [status_proc])) + + +@login_required +@permission('pyload.can_see_dl') +@check_server +def queue(request): + queue = settings.PYLOAD.get_queue() + for package in queue.itervalues(): + for pyfile in package["links"].itervalues(): + if pyfile["status"] == 0: + pyfile["icon"] = "status_finished.png" + elif pyfile["status"] in (2,3): + pyfile["icon"] = "status_queue.png" + elif pyfile["status"] in (9,1): + pyfile["icon"] = "status_offline.png" + elif pyfile["status"] == 5: + pyfile["icon"] = "status_waiting.png" + elif pyfile["status"] == 8: + pyfile["icon"] = "status_failed.png" + elif pyfile["status"] in (11,13): + pyfile["icon"] = "status_proc.png" + else: + pyfile["icon"] = "status_downloading.png" + + data = zip(queue.keys(), queue.values()) + data.sort(key=get_sort_key) + + for id, value in data: + tmp = zip(value["links"].keys(), value["links"].values()) + tmp.sort(key=get_sort_key) + value["links"] = tmp + + return render_to_response(join(settings.TEMPLATE, 'queue.html'), RequestContext(request, {'content': data}, [status_proc])) + + +@login_required +@permission('pyload.can_download') +@check_server +def downloads(request): + + root = settings.PYLOAD.get_conf_val("general", "download_folder") + + if not isdir(root): + return base(request, [_('Download directory not found.')]) + data = { + 'folder': [], + 'files': [] + } + + for item in listdir(root): + if isdir(join(root, item)): + folder = { + 'name': item, + 'path': item, + 'files': [] + } + for file in listdir(join(root, item)): + if isfile(join(root, item, file)): + folder['files'].append(file) + + data['folder'].append(folder) + elif isfile(join(root, item)): + data['files'].append(item) + + + return render_to_response(join(settings.TEMPLATE, 'downloads.html'), RequestContext(request, {'files': data}, [status_proc])) + +@login_required +@permission('pyload.can_download') +@check_server +def download(request, path): + path = unquote(path) + path = path.split("/") + + root = settings.PYLOAD.get_conf_val("general", "download_folder") + + dir = join(root, path[1].replace('..', '')) + if isdir(dir) or isfile(dir): + if isdir(dir): filepath = join(dir, path[2]) + elif isfile(dir): filepath = dir + + if isfile(filepath): + try: + type, encoding = mimetypes.guess_type(filepath) + if type is None: + type = 'application/octet-stream' + + response = HttpResponse(mimetype=type) + response['Content-Length'] = str(stat(filepath).st_size) + + if encoding is not None: + response['Content-Encoding'] = encoding + + response.write(file(filepath, "rb").read()) + return response + + except Exception, e: + return HttpResponseNotFound("File not Found. %s" % str(e)) + + return HttpResponseNotFound("File not Found.") + +@login_required +@permission('pyload.can_see_logs') +@check_server +def logs(request, item=-1): + + perpage = request.session.get('perpage', 34); + reversed = request.session.get('reversed', False); + + warning = "" + conf = settings.PYLOAD.get_config() + if not conf['log']['file_log']['value']: + warning = "Warning: File log is disabled, see settings page." + + perpage_p = ((20,20), (34, 34), (40, 40), (100, 100), (0,'all')) + fro = None; + + if request.method == 'POST': + try: + fro = datetime.strptime(request.POST['from'], '%d.%m.%Y %H:%M:%S') + except: + pass + try: + perpage = int(request.POST['perpage']) + request.session['perpage'] = perpage + + reversed = bool(request.POST.get('reversed', False)) + request.session['reversed'] = reversed + except: + pass + + try: + item = int(item) + except: + pass + + log = settings.PYLOAD.get_log() + if perpage == 0: + item = 0 + + if item < 1 or type(item) is not int: + item = 1 if len(log) - perpage + 1 < 1 else len(log) - perpage + 1 + + if type(fro) is datetime: # we will search for datetime + item = -1 + + data = [] + counter = 0 + perpagecheck = 0 + for l in log: + counter = counter+1; + + if counter >= item: + try: + date,time,level,message = l.split(" ", 3) + dtime = datetime.strptime(date+' '+time, '%d.%m.%Y %H:%M:%S') + except: + dtime = None + date = '?' + time = ' ' + level = '?' + message = l; + if item == -1 and dtime != None and fro <= dtime: + item = counter #found our datetime + if item >= 0: + data.append({'line': counter, 'date': date+" "+time, 'level':level, 'message': message}) + perpagecheck = perpagecheck +1; + if fro == None and dtime != None: #if fro not set set it to first showed line + fro = dtime; + if perpagecheck >= perpage and perpage > 0: + break + + if fro == None: #still not set, empty log? + fro = datetime.now() + if reversed: + data.reverse() + return render_to_response(join(settings.TEMPLATE, 'logs.html'), RequestContext(request, {'warning': warning, 'log': data, 'from': fro.strftime('%d.%m.%Y %H:%M:%S'), 'reversed': reversed, 'perpage':perpage, 'perpage_p':sorted(perpage_p), 'iprev': 1 if item - perpage < 1 else item - perpage, 'inext': (item + perpage) if item+perpage < len(log) else item}, [status_proc])) + +@login_required +@permission('pyload.can_add_dl') +@check_server +def collector(request): + queue = settings.PYLOAD.get_collector() + for package in queue.itervalues(): + for pyfile in package["links"].itervalues(): + if pyfile["status"] == 0: + pyfile["icon"] = "status_finished.png" + elif pyfile["status"] in (2,3): + pyfile["icon"] = "status_queue.png" + elif pyfile["status"] in (9,1): + pyfile["icon"] = "status_offline.png" + elif pyfile["status"] == 5: + pyfile["icon"] = "status_waiting.png" + elif pyfile["status"] == 8: + pyfile["icon"] = "status_failed.png" + elif pyfile["status"] in (11,13): + pyfile["icon"] = "status_proc.png" + else: + pyfile["icon"] = "status_downloading.png" + + data = zip(queue.keys(), queue.values()) + data.sort(key=get_sort_key) + + for id, value in data: + tmp = zip(value["links"].keys(), value["links"].values()) + tmp.sort(key=get_sort_key) + value["links"] = tmp + + return render_to_response(join(settings.TEMPLATE, 'collector.html'), RequestContext(request, {'content': data}, [status_proc])) + + +@login_required +@permission('pyload.can_change_status') +@check_server +def config(request): + conf = settings.PYLOAD.get_config() + plugin = settings.PYLOAD.get_plugin_config() + accs = settings.PYLOAD.get_accounts() + messages = [] + + for section in chain(conf.itervalues(), plugin.itervalues()): + for key, option in section.iteritems(): + if key == "desc": continue + + if ";" in option["type"]: + option["list"] = option["type"].split(";") + + if request.META.get('REQUEST_METHOD', "GET") == "POST": + + errors = [] + + for key, value in request.POST.iteritems(): + if not "|" in key: continue + sec, skey, okey = key.split("|")[:] + + if sec == "General": + + if conf.has_key(skey): + if conf[skey].has_key(okey): + try: + if str(conf[skey][okey]['value']) != value: + settings.PYLOAD.set_conf_val(skey, okey, value) + except Exception, e: + errors.append("%s | %s : %s" % (skey, okey, e)) + else: + continue + else: + continue + + elif sec == "Plugin": + if plugin.has_key(skey): + if plugin[skey].has_key(okey): + try: + if str(plugin[skey][okey]['value']) != value: + settings.PYLOAD.set_conf_val(skey, okey, value, "plugin") + except Exception, e: + errors.append("%s | %s : %s" % (skey, okey, e)) + else: + continue + else: + continue + elif sec == "Accounts": + if ";" in okey: + action, name = okey.split(";") + + if action == "delete": + settings.PYLOAD.remove_account(skey, name) + elif action == "password": + + for acc in accs[skey]: + if acc["login"] == name and value.strip(): + settings.PYLOAD.update_account(skey, name, value) + + elif okey == "newacc" and value: + # add account + + pw = request.POST.get("Accounts|%s|newpw" % skey) + + settings.PYLOAD.update_account(skey, value, pw) + + + if errors: + messages.append(_("Error occured when setting the following options:")) + messages.append("") + messages += errors + else: + messages.append(_("All options were set correctly.")) + + accs = settings.PYLOAD.get_accounts() + + return render_to_response(join(settings.TEMPLATE, 'settings.html'), RequestContext(request, {'conf': {'Plugin':plugin, 'General':conf, 'Accounts': accs}, 'errors': messages}, [status_proc])) |