From 2e2851dc13d73352530dd4495c7e05603b2e520d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 23:38:38 +0200 Subject: Adding upstream version 2.1.2~dev0+20240219. Signed-off-by: Daniel Baumann --- deluge/plugins/Extractor/deluge_extractor/core.py | 186 ++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 deluge/plugins/Extractor/deluge_extractor/core.py (limited to 'deluge/plugins/Extractor/deluge_extractor/core.py') diff --git a/deluge/plugins/Extractor/deluge_extractor/core.py b/deluge/plugins/Extractor/deluge_extractor/core.py new file mode 100644 index 0000000..23b2a00 --- /dev/null +++ b/deluge/plugins/Extractor/deluge_extractor/core.py @@ -0,0 +1,186 @@ +# +# Copyright (C) 2009 Andrew Resch +# +# Basic plugin template created by: +# Copyright (C) 2008 Martijn Voncken +# Copyright (C) 2007-2009 Andrew Resch +# +# 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. +# + +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.common import windows_check +from deluge.core.rpcserver import export +from deluge.plugins.pluginbase import CorePluginBase + +log = logging.getLogger(__name__) + +DEFAULT_PREFS = {'extract_path': '', 'use_name_folder': True} + +if windows_check(): + win_7z_exes = [ + '7z.exe', + 'C:\\Program Files\\7-Zip\\7z.exe', + 'C:\\Program Files (x86)\\7-Zip\\7z.exe', + ] + + import winreg + + try: + hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Software\\7-Zip') + except OSError: + pass + else: + win_7z_path = os.path.join(winreg.QueryValueEx(hkey, 'Path')[0], '7z.exe') + winreg.CloseKey(hkey) + win_7z_exes.insert(1, win_7z_path) + + 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( + 'extractor.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']) + + files = tid.get_files() + for f in files: + 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.debug('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.debug('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']) + ) + dest = os.path.normpath(self.config['extract_path']) + if self.config['use_name_folder']: + dest = os.path.join(dest, tid_status['name']) + + try: + os.makedirs(dest) + except OSError as ex: + if not (ex.errno == errno.EEXIST and os.path.isdir(dest)): + log.error('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.debug( + '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) + + @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 -- cgit v1.2.3