Cleanup and Fixes and Inplace - Oh My

Add option to extract all archives to torrent root, destination folder, or true in-place.
Add sonarr/radarr support (marks torrent "incomplete" until extraction is done).
Bundle 7z.exe for windows users.
Fix GTK UI not working...at all.
Improve web and GTK UI so they're uniform, have radio buttons, etc.
This commit is contained in:
d8ahazard
2019-12-27 16:05:53 -06:00
parent a9140d33a1
commit 3d4740b95b
14 changed files with 537 additions and 438 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
from __future__ import unicode_literals
from deluge.plugins.init import PluginInitBase
class CorePlugin(PluginInitBase):
def __init__(self, plugin_name):
from .core import Core as _pluginCls
self._plugin_cls = _pluginCls
super(CorePlugin, self).__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from .gtkui import GtkUI as _pluginCls
self._plugin_cls = _pluginCls
super(GtkUIPlugin, self).__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from .webui import WebUI as _pluginCls
self._plugin_cls = _pluginCls
super(WebUIPlugin, self).__init__(plugin_name)

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# 2007-2009 Andrew Resch <andrewresch@gmail.com>
# 2009 Damien Churchill <damoxc@gmail.com>
# 2010 Pedro Algarvio <pedro@algarvio.me>
# 2017 Calum Lind <calumlind+deluge@gmail.com>
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
from __future__ import unicode_literals
import os.path
from pkg_resources import resource_filename
def get_resource(filename):
return resource_filename(__package__, os.path.join('data', filename))

View File

@@ -0,0 +1,265 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
from __future__ import unicode_literals
import errno
import logging
import os
from twisted.internet.utils import getProcessOutputAndValue
from twisted.python.procutils import which
import deluge.component as component
import deluge.configmanager
from deluge.configmanager import ConfigManager
from deluge.common import windows_check
from deluge.core.rpcserver import export
from deluge.plugins.pluginbase import CorePluginBase
log = logging.getLogger(__name__)
DEFAULT_PREFS = {'extract_path': '',
'extract_in_place': False,
'extract_selected_folder': False,
'extract_torrent_root': True,
'label_filter': ''}
if windows_check():
win_7z_exes = [
'7z.exe',
'C:\\Program Files\\7-Zip\\7z.exe',
'C:\\Program Files (x86)\\7-Zip\\7z.exe',
]
switch_7z = 'x -y'
# Future suport:
# 7-zip cannot extract tar.* with single command.
# ".tar.gz", ".tgz",
# ".tar.bz2", ".tbz",
# ".tar.lzma", ".tlz",
# ".tar.xz", ".txz",
exts_7z = ['.rar', '.zip', '.tar', '.7z', '.xz', '.lzma']
for win_7z_exe in win_7z_exes:
if which(win_7z_exe):
EXTRACT_COMMANDS = dict.fromkeys(exts_7z, [win_7z_exe, switch_7z])
break
else:
required_cmds = ['unrar', 'unzip', 'tar', 'unxz', 'unlzma', '7zr', 'bunzip2']
# Possible future suport:
# gunzip: gz (cmd will delete original archive)
# the following do not extract to dest dir
# ".xz": ["xz", "-d --keep"],
# ".lzma": ["xz", "-d --format=lzma --keep"],
# ".bz2": ["bzip2", "-d --keep"],
EXTRACT_COMMANDS = {
'.rar': ['unrar', 'x -o+ -y'],
'.tar': ['tar', '-xf'],
'.zip': ['unzip', ''],
'.tar.gz': ['tar', '-xzf'],
'.tgz': ['tar', '-xzf'],
'.tar.bz2': ['tar', '-xjf'],
'.tbz': ['tar', '-xjf'],
'.tar.lzma': ['tar', '--lzma -xf'],
'.tlz': ['tar', '--lzma -xf'],
'.tar.xz': ['tar', '--xz -xf'],
'.txz': ['tar', '--xz -xf'],
'.7z': ['7zr', 'x'],
}
# Test command exists and if not, remove.
for command in required_cmds:
if not which(command):
for k, v in list(EXTRACT_COMMANDS.items()):
if command in v[0]:
log.warning('%s not found, disabling support for %s', command, k)
del EXTRACT_COMMANDS[k]
if not EXTRACT_COMMANDS:
raise Exception('No archive extracting programs found, plugin will be disabled')
class Core(CorePluginBase):
def enable(self):
self.config = deluge.configmanager.ConfigManager(
'simpleextractor.conf', DEFAULT_PREFS
)
if not self.config['extract_path']:
self.config['extract_path'] = deluge.configmanager.ConfigManager(
'core.conf'
)['download_location']
component.get('EventManager').register_event_handler(
'TorrentFinishedEvent', self._on_torrent_finished
)
def disable(self):
component.get('EventManager').deregister_event_handler(
'TorrentFinishedEvent', self._on_torrent_finished
)
def update(self):
pass
def _on_torrent_finished(self, torrent_id):
"""
This is called when a torrent finishes and checks if any files to extract.
"""
tid = component.get('TorrentManager').torrents[torrent_id]
tid_status = tid.get_status(['download_location', 'name'])
tstatus = tid.get_status([], False, False, True)
do_extract = False
tid = component.get("TorrentManager").torrents[torrent_id]
tid_status = tid.get_status(["save_path", "name"])
tid.is_finished = False
log.info("Processing completed torrent %s", tstatus)
# Fetch our torrent's label
labels = self.get_labels(torrent_id)
log.info("Schmancy label collector: %s", labels)
# If we've set a label filter, process it
if self.config['label_filter'] is not "":
log.info("We should filter by label(s): %s", self.config['extract_labels'])
# Make sure there's actually a label
if len(labels) > 0:
for label in labels:
log.info("Label for torrent is %s", label)
# Check if it's more than one, split
if "," in self.config['extract_labels']:
log.info("And we have a list")
label_list = self.config['extract_labels'].split(",")
# And loop
for label in label_list:
if label.strip() == label:
log.info("This matches")
do_extract = True
break
# Otherwise, just check the whole string
else:
log.info("Single label string detected.")
if self.config['extract_labels'].strip() == label:
log.info("This matches")
do_extract = True
# We don't need to do this, but it adds sanity
else:
log.info("We have a label filter and no label, doing nothing")
do_extract = False
# Otherwise, we just extract everything
else:
log.info("No label, extracting.")
do_extract = True
# Now, extract if filter match or no filter set
extract_in_place = self.config["extract_in_place"]
extract_torrent_root = self.config["extract_torrent_root"]
if do_extract:
files = tid.get_files()
for f in files:
log.info("Handling file %s", f['path'])
file_root, file_ext = os.path.splitext(f['path'])
file_ext_sec = os.path.splitext(file_root)[1]
if file_ext_sec and file_ext_sec + file_ext in EXTRACT_COMMANDS:
file_ext = file_ext_sec + file_ext
elif file_ext not in EXTRACT_COMMANDS or file_ext_sec == '.tar':
log.info('Cannot extract file with unknown file type: %s', f['path'])
continue
elif file_ext == '.rar' and 'part' in file_ext_sec:
part_num = file_ext_sec.split('part')[1]
if part_num.isdigit() and int(part_num) != 1:
log.info('Skipping remaining multi-part rar files: %s', f['path'])
continue
cmd = EXTRACT_COMMANDS[file_ext]
fpath = os.path.join(
tid_status['download_location'], os.path.normpath(f['path'])
)
# Get the destination path
dest = os.path.normpath(self.config["extract_path"])
name_dest = os.path.join(dest, tid_status["name"])
# Override destination if in_place_extraction is set
if extract_torrent_root:
dest = tid_status["save_path"]
name_dest = os.path.join(dest, tid_status["name"])
if extract_in_place and ((not os.path.exists(name_dest)) or os.path.isdir(name_dest)):
dest = name_dest
try:
os.makedirs(dest)
except OSError as ex:
if not (ex.errno == errno.EEXIST and os.path.isdir(dest)):
log.error("EXTRACTOR: Error creating destination folder: %s", ex)
break
def on_extract(result, torrent_id, fpath):
# Check command exit code.
if not result[2]:
log.info('Extract successful: %s (%s)', fpath, torrent_id)
else:
log.error(
'Extract failed: %s (%s) %s', fpath, torrent_id, result[1]
)
# Run the command and add callback.
log.info(
'Extracting %s from %s with %s %s to %s',
fpath,
torrent_id,
cmd[0],
cmd[1],
dest,
)
d = getProcessOutputAndValue(
cmd[0], cmd[1].split() + [str(fpath)], os.environ, str(dest)
)
d.addCallback(on_extract, torrent_id, fpath)
tid.is_finished = True
def get_labels(self, torrent_id):
"""
Asking the system about the labels isn't very cool, so try this instead
"""
labels = []
label_config = ConfigManager('label.conf', defaults=False)
if label_config is not False:
log.info("We have a Label config")
if 'torrent_labels' in label_config:
if torrent_id in label_config['torrent_labels']:
log.info("Data from Label plugin: %s", label_config['torrent_labels'][torrent_id])
labels.append(label_config['torrent_labels'][torrent_id])
label_plus_config = ConfigManager('labelplus.conf', defaults=False)
if label_plus_config is not False:
log.info("We have a label plus config")
if 'mappings' in label_plus_config:
if torrent_id in label_plus_config['mappings']:
mapping = label_plus_config['mappings'][torrent_id]
log.info("We have a label plus mapping: %s", mapping)
labels.append(label_plus_config['labels'][mapping]['name'])
return labels
@export
def set_config(self, config):
"""Sets the config dictionary."""
for key in config:
self.config[key] = config[key]
self.config.save()
@export
def get_config(self):
"""Returns the config dictionary."""
return self.config.config

View File

@@ -0,0 +1,178 @@
/*!
* simpleextractor.js
*
* Copyright (c) Damien Churchill 2010 <damoxc@gmail.com>
* Copyright (C) Digitalhigh 2019 <donate.to.digitalhigh@gmail.com>
*
*
*/
Ext.ns('Deluge.ux.preferences');
/**
* @class Deluge.ux.preferences.SimpleExtractorPage
* @extends Ext.Panel
*/
Deluge.ux.preferences.SimpleExtractorPage = Ext.extend(Ext.Panel, {
title: _('SimpleExtractor'),
layout: 'fit',
border: false,
initComponent: function() {
Deluge.ux.preferences.SimpleExtractorPage.superclass.initComponent.call(this);
this.form = this.add({
xtype: 'form',
layout: 'form',
border: false,
autoHeight: true
});
this.behaviorSet = this.form.add({
xtype: 'fieldset',
border: false,
title: '',
autoHeight: true,
labelAlign: 'top',
labelWidth: 80,
defaultType: 'textfield'
});
this.behaviorSet.add({
xtype: 'label',
fieldLabel: _('<b>Extract Behavior:</b></br>'),
labelSeparator : '',
name: '',
width: '97%'
});
// Behavior Label
// Add radio group for extract behavior
this.extractBehavior = this.behaviorSet.add({
xtype: 'radiogroup',
columns: 1,
colspan: 2,
style: 'margin-left: 10px',
items: [
{
boxLabel: _('Selected Folder'),
name: 'extract_behavior',
inputValue: "extract_selected_folder"
},
{
boxLabel: _('Torrent Root'),
name: 'extract_behavior',
inputValue: "extract_torrent_root"
},
{
boxLabel: _('In-Place'),
name: 'extract_behavior',
inputValue: "extract_in_place"
}
],
});
this.destinationSet = this.form.add({
xtype: 'fieldset',
border: false,
title: '',
autoHeight: true,
labelAlign: 'top',
labelWidth: 80,
defaultType: 'textfield'
});
// Destination label
this.extractPath = this.destinationSet.add({
fieldLabel: _('<b>Destination:</b></br>'),
name: 'extract_path',
labelSeparator : '',
width: '97%'
});
this.labelSet = this.form.add({
xtype: 'fieldset',
border: false,
title: '',
autoHeight: true,
labelAlign: 'top',
labelWidth: 80,
defaultType: 'textfield'
});
// Label Filter Label
this.labelFilter = this.labelSet.add({
fieldLabel: _('<b>Label Filtering:</b></br>'),
name: 'label_filter',
labelSeparator : '',
width: '97%'
});
this.labelSet.add({
xtype: 'label',
fieldLabel: _('</br>Comma-separated, leave blank for none.'),
labelSeparator : '',
name: '',
width: '97%'
});
this.on('show', this.updateConfig, this);
},
onApply: function() {
// build settings object
var config = {};
config['extract_path'] = this.extractPath.getValue();
var eBehavior = this.extractBehavior.getValue();
config['extract_in_place'] = false;
config['extract_torrent_root'] = false;
config['extract_selected_folder'] = false;
config[eBehavior] = true;
config['label_filter'] = this.labelFilter.getValue();
deluge.client.simpleextractor.set_config(config);
},
onOk: function() {
this.onApply();
},
updateConfig: function() {
deluge.client.simpleextractor.get_config({
success: function(config) {
this.extractPath.setValue(config['extract_path']);
var behavior = "extract_selected_folder";
if (config['extract_in_place']) {
behavior = 'extract_in_place';
}
if (config['extract_torrent_root']) {
behavior = 'extract_torrent_root';
}
this.extractBehavior.setValue(behavior);
this.labelFilter.setValue(config['label_filter']);
},
scope: this
});
}
});
Deluge.plugins.SimpleExtractorPlugin = Ext.extend(Deluge.Plugin, {
name: 'SimpleExtractor',
onDisable: function() {
deluge.preferences.removePage(this.prefsPage);
},
onEnable: function() {
this.prefsPage = deluge.preferences.addPage(new Deluge.ux.preferences.SimpleExtractorPage());
}
});
Deluge.registerPlugin('SimpleExtractor', Deluge.plugins.SimpleExtractorPlugin);

View File

@@ -0,0 +1,221 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.10 -->
<object class="GtkWindow" id="window1">
<property name="can_focus">False</property>
<child>
<object class="GtkBox" id="extractor_prefs_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="label_yalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="border_width">5</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkBox" id="extract_behavior">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkRadioButton" id="extract_selected_folder">
<property name="label" translatable="yes">Selected Folder</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">All torrents will be extracted to the selected folder below.</property>
<property name="xalign">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">extract_torrent_root</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="extract_torrent_root">
<property name="label" translatable="yes">Torrent Root</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Any found archives will be extracted to the root of the torrent directory.</property>
<property name="xalign">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="extract_in_place">
<property name="label" translatable="yes">In-Place</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Found archives will be extracted where they are found.</property>
<property name="xalign">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">extract_torrent_root</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Extract Behavior:&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkBox" id="destination_path">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<child>
<object class="GtkFileChooserButton" id="folderchooser_path">
<property name="can_focus">False</property>
<property name="action">select-folder</property>
<property name="title" translatable="yes">Select A Folder</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="extract_path">
<property name="can_focus">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Destination:&lt;/b&gt;
</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="frame3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkBox" id="hbox3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkEntry" id="label_filter">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Enter one or more labels to extract, separated by commas. Leave to extract any archive found.</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_labels">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Enter labels to extract (comma-separated, leave blank for all)</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;Label Filtering:&lt;/b&gt;
</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
# Copyright (C) 2019-2020 Digitalhigh <donate.to.digitalhigh@gmail.com>
#
#
from __future__ import unicode_literals
import logging
import gi # isort:skip (Required before Gtk import).
gi.require_version('Gtk', '3.0') # NOQA: E402
# isort:imports-thirdparty
from gi.repository import Gtk
# isort:imports-firstparty
import deluge.component as component
from deluge.plugins.pluginbase import Gtk3PluginBase
from deluge.ui.client import client
# isort:imports-localfolder
from .common import get_resource
log = logging.getLogger(__name__)
class GtkUI(Gtk3PluginBase):
def enable(self):
self.plugin = component.get('PluginManager')
self.builder = Gtk.Builder()
self.builder.add_from_file(get_resource('simpleextractor_prefs.ui'))
component.get('Preferences').add_page(
_('Simple Extractor'), self.builder.get_object('extractor_prefs_box')
)
self.plugin.register_hook('on_apply_prefs', self.on_apply_prefs)
self.plugin.register_hook('on_show_prefs', self.on_show_prefs)
self.on_show_prefs()
def disable(self):
component.get('Preferences').remove_page(_('Simple Extractor'))
self.plugin.deregister_hook(
'on_apply_prefs', self.on_apply_prefs
)
self.plugin.deregister_hook(
'on_show_prefs', self.on_show_prefs
)
del self.builder
def on_apply_prefs(self):
log.debug('applying prefs for Simple Extractor')
if client.is_localhost():
path = self.builder.get_object('folderchooser_path').get_filename()
else:
path = self.builder.get_object('extract_path').get_text()
config = {
'extract_path': path,
'extract_selected_folder': self.builder.get_object("extract_selected_folder").get_active(),
'extract_in_place': self.builder.get_object("extract_in_place").get_active(),
'extract_torrent_root': self.builder.get_object("extract_torrent_root").get_active(),
'label_filter': self.builder.get_object("label_filter").get_text()
}
client.simpleextractor.set_config(config)
def on_show_prefs(self):
def on_get_config(config):
if client.is_localhost():
self.builder.get_object('folderchooser_path').set_current_folder(config['extract_path'])
self.builder.get_object('folderchooser_path').show()
self.builder.get_object('extract_path').hide()
else:
self.builder.get_object('extract_path').set_text(config['extract_path'])
self.builder.get_object('folderchooser_path').hide()
self.builder.get_object('extract_path').show()
self.builder.get_object('extract_selected_folder').set_active(
config['extract_selected_folder']
)
self.builder.get_object('extract_torrent_root').set_active(
config['extract_torrent_root']
)
self.builder.get_object('extract_in_place').set_active(
config['extract_in_place']
)
self.builder.get_object('label_filter').set_text(config['label_filter'])
client.simpleextractor.get_config().addCallback(on_get_config)

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
from __future__ import unicode_literals
import logging
from deluge.plugins.pluginbase import WebPluginBase
from .common import get_resource
log = logging.getLogger(__name__)
class WebUI(WebPluginBase):
scripts = [get_resource('simpleextractor.js')]
debug_scripts = scripts