From 55a8b445ad59eda57596ab2166ab7535502f310d Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Sat, 11 Apr 2015 14:00:26 -0400 Subject: [PATCH] Add BitTorrent module (deluge-web). --- actions/bittorrent | 129 ++++++++++++++++++ data/etc/plinth/modules-enabled/bittorrent | 1 + plinth/modules/bittorrent/__init__.py | 34 +++++ plinth/modules/bittorrent/forms.py | 30 ++++ .../bittorrent/templates/bittorrent.html | 71 ++++++++++ plinth/modules/bittorrent/urls.py | 30 ++++ plinth/modules/bittorrent/views.py | 101 ++++++++++++++ 7 files changed, 396 insertions(+) create mode 100755 actions/bittorrent create mode 100644 data/etc/plinth/modules-enabled/bittorrent create mode 100644 plinth/modules/bittorrent/__init__.py create mode 100644 plinth/modules/bittorrent/forms.py create mode 100644 plinth/modules/bittorrent/templates/bittorrent.html create mode 100644 plinth/modules/bittorrent/urls.py create mode 100644 plinth/modules/bittorrent/views.py diff --git a/actions/bittorrent b/actions/bittorrent new file mode 100755 index 000000000..77f4ae4b1 --- /dev/null +++ b/actions/bittorrent @@ -0,0 +1,129 @@ +#!/usr/bin/python3 +# -*- mode: python -*- +# +# This file is part of Plinth. +# +# 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. +# +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +Configuration helper for BitTorrent web client +""" + +import argparse +import os +import subprocess + + +SITE_CONF = '/etc/apache2/conf-available/deluge-web.conf' +SITE_ENABLED = '/etc/apache2/conf-enabled/deluge-web.conf' + + +def parse_arguments(): + """Return parsed command line arguments as dictionary.""" + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') + + # Get whether deluge-web site is enabled + subparsers.add_parser('get-enabled', + help='Get whether deluge-web site is enabled') + + # Enable deluge-web site and start deluge-web + subparsers.add_parser('enable', help='Enable deluge-web site') + + # Disable deluge-web site and stop deluge-web + subparsers.add_parser('disable', help='Disable deluge-web site') + + # Get whether deluge-web is running + subparsers.add_parser('is-running', + help='Get whether deluge-web is running') + + # Start deluge-web + subparsers.add_parser('start', help='Start deluge-web') + + # Stop deluge-web + subparsers.add_parser('stop', help='Stop deluge-web') + + return parser.parse_args() + + +def subcommand_get_enabled(_): + """Get whether deluge-web site is enabled.""" + if os.path.isfile(SITE_ENABLED): + print('yes') + else: + print('no') + + +def subcommand_enable(_): + """Enable deluge-web site and start deluge-web.""" + if not os.path.isfile(SITE_CONF): + setup() + subprocess.check_call(['a2enconf', 'deluge-web']) + subprocess.check_call(['service', 'apache2', 'reload']) + + subcommand_start(_) + + +def subcommand_disable(_): + """Disable deluge-web site and stop deluge-web.""" + subprocess.check_call(['a2disconf', 'deluge-web']) + subprocess.check_call(['service', 'apache2', 'reload']) + + subcommand_stop(_) + + +def subcommand_is_running(_): + """Get whether deluge-web is running.""" + if subprocess.call(['pgrep', 'deluge-web']) == 0: + print('yes') + else: + print('no') + + +def subcommand_start(_): + """Start deluge-web.""" + subprocess.check_call(['start-stop-daemon', '--start', '--background', + '--name', 'deluge-web', + '--exec', '/usr/bin/deluge-web', + '--chuid', 'debian-deluged', + '--', '--base=bittorrent']) + + +def subcommand_stop(_): + """Stop deluge-web.""" + subprocess.call(['killall', 'deluge-web']) + + +def setup(): + """Perform initial setup for deluge-web site.""" + with open(SITE_CONF, 'w') as conffile: + conffile.writelines([ + '\n', + ' ProxyPass http://localhost:8112\n', + '\n', + ]) + + +def main(): + """Parse arguments and perform all duties.""" + arguments = parse_arguments() + + subcommand = arguments.subcommand.replace('-', '_') + subcommand_method = globals()['subcommand_' + subcommand] + subcommand_method(arguments) + + +if __name__ == '__main__': + main() diff --git a/data/etc/plinth/modules-enabled/bittorrent b/data/etc/plinth/modules-enabled/bittorrent new file mode 100644 index 000000000..cb3cdd997 --- /dev/null +++ b/data/etc/plinth/modules-enabled/bittorrent @@ -0,0 +1 @@ +plinth.modules.bittorrent diff --git a/plinth/modules/bittorrent/__init__.py b/plinth/modules/bittorrent/__init__.py new file mode 100644 index 000000000..6e9171f65 --- /dev/null +++ b/plinth/modules/bittorrent/__init__.py @@ -0,0 +1,34 @@ +# +# This file is part of Plinth. +# +# 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. +# +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +Plinth module to configure a BitTorrent web client (deluge-web) +""" + +from gettext import gettext as _ + +from plinth import cfg + + +depends = ['plinth.modules.apps'] + + +def init(): + """Initialize the BitTorrent module.""" + menu = cfg.main_menu.get('apps:index') + menu.add_urlname(_('BitTorrent (Deluge)'), 'glyphicon-magnet', + 'bittorrent:index', 60) diff --git a/plinth/modules/bittorrent/forms.py b/plinth/modules/bittorrent/forms.py new file mode 100644 index 000000000..1cb12ece4 --- /dev/null +++ b/plinth/modules/bittorrent/forms.py @@ -0,0 +1,30 @@ +# +# This file is part of Plinth. +# +# 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. +# +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +Forms for configuring BitTorrent web client +""" + +from django import forms +from gettext import gettext as _ + + +class BitTorrentForm(forms.Form): + """BitTorrent configuration form.""" + enabled = forms.BooleanField( + label=_('Enable BitTorrent web client'), + required=False) diff --git a/plinth/modules/bittorrent/templates/bittorrent.html b/plinth/modules/bittorrent/templates/bittorrent.html new file mode 100644 index 000000000..4e0df584e --- /dev/null +++ b/plinth/modules/bittorrent/templates/bittorrent.html @@ -0,0 +1,71 @@ +{% extends "base.html" %} +{% comment %} +# +# This file is part of Plinth. +# +# 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. +# +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +{% endcomment %} + +{% load bootstrap %} + +{% block content %} + +

BitTorrent Web Client (Deluge)

+ +

Deluge is a BitTorrent client that features a Web UI.

+ +

When enabled, the Deluge web client will be available from + /bittorrent path on the web server. The default + password is 'deluge', but you should log in and change it immediately after + enabling this service.

+ +

Configuration

+ +
+ {% csrf_token %} + + {{ form|bootstrap }} + + +
+ +{% if status.enabled %} +

Status

+ +

If your {{ cfg.box_name }} is restarted, you will need to start deluge-web + manually. In the future, this will be handled automatically.

+ +

+ {% if status.is_running %} + deluge-web is running +

+ {% csrf_token %} + +
+ + {% else %} + deluge-web is not running +
+ {% csrf_token %} + +
+ + {% endif %} +

+{% endif %} + +{% endblock %} diff --git a/plinth/modules/bittorrent/urls.py b/plinth/modules/bittorrent/urls.py new file mode 100644 index 000000000..4b6ff949d --- /dev/null +++ b/plinth/modules/bittorrent/urls.py @@ -0,0 +1,30 @@ +# +# This file is part of Plinth. +# +# 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. +# +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +URLs for the BitTorrent module +""" + +from django.conf.urls import patterns, url + + +urlpatterns = patterns( + 'plinth.modules.bittorrent.views', + url(r'^apps/bittorrent/$', 'index', name='index'), + url(r'^apps/bittorrent/start/$', 'start', name='start'), + url(r'^apps/bittorrent/stop/$', 'stop', name='stop'), + ) diff --git a/plinth/modules/bittorrent/views.py b/plinth/modules/bittorrent/views.py new file mode 100644 index 000000000..c0400286d --- /dev/null +++ b/plinth/modules/bittorrent/views.py @@ -0,0 +1,101 @@ +# +# This file is part of Plinth. +# +# 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. +# +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +Plinth module to configure a BitTorrent web client (deluge-web) +""" + +from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.core.urlresolvers import reverse_lazy +from django.shortcuts import redirect +from django.template.response import TemplateResponse +from django.views.decorators.http import require_POST +from gettext import gettext as _ + +from .forms import BitTorrentForm +from plinth import actions +from plinth import package + + +@login_required +@package.required(['deluged', 'deluge-web']) +def index(request): + """Serve configuration page.""" + status = get_status() + + form = None + + if request.method == 'POST': + form = BitTorrentForm(request.POST, prefix='bittorrent') + # pylint: disable=E1101 + if form.is_valid(): + _apply_changes(request, status, form.cleaned_data) + status = get_status() + form = BitTorrentForm(initial=status, prefix='bittorrent') + else: + form = BitTorrentForm(initial=status, prefix='bittorrent') + + return TemplateResponse(request, 'bittorrent.html', + {'title': _('BitTorrent (Deluge)'), + 'status': status, + 'form': form}) + + +@login_required +@require_POST +def start(request): + """Start deluge-web.""" + actions.run('bittorrent', ['start']) + return redirect(reverse_lazy('bittorrent:index')) + + +@login_required +@require_POST +def stop(request): + """Stop deluge-web.""" + actions.run('bittorrent', ['stop']) + return redirect(reverse_lazy('bittorrent:index')) + + +def get_status(): + """Get the current settings.""" + output = actions.run('bittorrent', ['get-enabled']) + enabled = (output.strip() == 'yes') + + output = actions.run('bittorrent', ['is-running']) + is_running = ('yes' in output.strip()) + + status = {'enabled': enabled, + 'is_running': is_running} + + return status + + +def _apply_changes(request, old_status, new_status): + """Apply the changes.""" + modified = False + + if old_status['enabled'] != new_status['enabled']: + sub_command = 'enable' if new_status['enabled'] else 'disable' + actions.superuser_run('bittorrent', [sub_command]) + modified = True + + if modified: + messages.success(request, _('Configuration updated')) + else: + messages.info(request, _('Setting unchanged'))