<commit_before>import base64
import os
import re
import sys
import time

from loguru import logger

from flexget import plugin
from flexget.entry import Entry
from flexget.event import event
from flexget.utils.pathscrub import pathscrub
from flexget.utils.template import RenderError

logger = logger.bind(name='deluge')


class DelugePlugin:
    """Base class for deluge plugins, contains settings and methods for connecting to a deluge daemon."""

    def on_task_start(self, task, config):
        """Fail early if we can't import/configure the deluge client."""
        self.setup_client(config)

    def setup_client(self, config):
        try:
            from deluge_client import DelugeRPCClient
        except ImportError as e:
            logger.debug('Error importing deluge-client: {}', e)
            raise plugin.DependencyError(
                'deluge',
                'deluge-client',
                'deluge-client >=1.5 is required. `pip install deluge-client` to install.',
                logger,
            )
        config = self.prepare_config(config)

        if config['host'] in ['localhost', '127.0.0.1'] and not config.get('username'):
            # If an username is not specified, we have to do a lookup for the localclient username/password
            auth = self.get_localhost_auth(config.get('config_path'))
            if auth and auth[0]:
                config['username'], config['password'] = auth
        if not config.get('username') or not config.get('password'):
            raise plugin.PluginError(
                'Unable to get authentication info for Deluge. You may need to '
                'specify an username and password from your Deluge auth file.'
            )

        return DelugeRPCClient(
            config['host'],
            config['port'],
            config['username'],
            config['password'],
            decode_utf8=True,
        )

    def prepare_config(self, config):
        config.setdefault('host', 'localhost')
        config.setdefault('port', 58846)
        return config

    @staticmethod
    def get_localhost_auth(config_path=None):
        if config_path is None:
            if sys.platform.startswith('win'):
                auth_file = os.path.join(os.getenv('APPDATA'), 'deluge', 'auth')
            else:
                auth_file = os.path.expanduser('~/.config/deluge/auth')
        else:
            auth_file = os.path.join(config_path, 'auth')
        if not os.path.isfile(auth_file):
            return None

        with open(auth_file) as auth:
            for line in auth:
                line = line.strip()
                if line.startswith('#') or not line:
                    # This is a comment or blank line
                    continue

                lsplit = line.split(':')
                if lsplit[0] == 'localclient':
                    username, password = lsplit[:2]
                    return username, password


class InputDeluge(DelugePlugin):
    """Create entries for torrents in the deluge session."""

    # Fields we provide outside of the deluge_ prefixed namespace
    settings_map = {
        'name': 'title',
        'hash': 'torrent_info_hash',
        'num_peers': 'torrent_peers',
        'num_seeds': 'torrent_seeds',
        'total_size': ('content_size', lambda size: size / 1024 / 1024),
        'files': ('content_files', lambda file_dicts: [f['path'] for f in file_dicts]),
    }

    schema = {
        'anyOf': [
            {'type': 'boolean'},
            {
                'type': 'object',
                'properties': {
                    'host': {'type': 'string'},
                    'port': {'type': 'integer'},
                    'username': {'type': 'string'},
                    'password': {'type': 'string'},
                    'config_path': {'type': 'string', 'format': 'path'},
                    'filter': {
                        'type': 'object',
                        'properties': {
                            'label': {'type': 'string'},
                            'state': {
                                'type': 'string',
                                'enum': ['active', 'downloading', 'seeding', 'queued', 'paused'],
                            },
                        },
                        'additionalProperties': False,
                    },
                },
                'additionalProperties': False,
            },
        ]
    }

    def on_task_start(self, task, config):
        config = self.prepare_config(config)
        super().on_task_start(task, config)

    def prepare_config(self, config):
        if isinstance(config, bool):
            config = {}
        if 'filter' in config:
            filter = config['filter']
            if 'label' in filter:
                filter['label'] = filter['label'].lower()
            if 'state' in filter:
                filter['state'] = filter['state'].capitalize()
        super().prepare_config(config)
        return config

    def on_task_input(self, task, config):
        """Generates and returns a list of entries from the deluge daemon."""
        config = self.prepare_config(config)
        # Reset the entries list
        client = self.setup_client(config)

        try:
            client.connect()
        except ConnectionError as exc:
            raise plugin.PluginError(
                f'Error connecting to deluge daemon: {exc}', logger=logger
            ) from exc

        entries = self.generate_entries(client, config)
        client.disconnect()
        return entries

    def generate_entries(self, client, config):
        entries = []
        filter = config.get('filter', {})
        torrents = client.call('core.get_torrents_status', filter or {}, [])
        for hash, torrent_dict in torrents.items():
            # Make sure it has a url so no plugins crash
            entry = Entry(deluge_id=hash, url='')
            config_path = os.path.expanduser(config.get('config_path', ''))
            if config_path:
                torrent_path = os.path.join(config_path, 'state', hash + '.torrent')
                if os.path.isfile(torrent_path):
                    entry['location'] = torrent_path
                    if not torrent_path.startswith('/'):
                        torrent_path = '/' + torrent_path
                    entry['url'] = 'file://' + torrent_path
                else:
                    logger.warning('Did not find torrent file at {}', torrent_path)
            for key, value in torrent_dict.items():
                # All fields provided by deluge get placed under the deluge_ namespace
                entry['deluge_' + key] = value
                # Some fields also get special handling
                if key in self.settings_map:
                    flexget_key = self.settings_map[key]
                    if isinstance(flexget_key, tuple):
                        flexget_key, format_func = flexget_key
                        value = format_func(value)
                    entry[flexget_key] = value
            entries.append(entry)
<commit_msg>[change] from_deluge: Stop populating deluge_pieces field to declutter --dump output<commit_after>import base64
import os
import re
import sys
import time

from loguru import logger

from flexget import plugin
from flexget.entry import Entry
from flexget.event import event
from flexget.utils.pathscrub import pathscrub
from flexget.utils.template import RenderError

logger = logger.bind(name='deluge')


class DelugePlugin:
    """Base class for deluge plugins, contains settings and methods for connecting to a deluge daemon."""

    def on_task_start(self, task, config):
        """Fail early if we can't import/configure the deluge client."""
        self.setup_client(config)

    def setup_client(self, config):
        try:
            from deluge_client import DelugeRPCClient
        except ImportError as e:
            logger.debug('Error importing deluge-client: {}', e)
            raise plugin.DependencyError(
                'deluge',
                'deluge-client',
                'deluge-client >=1.5 is required. `pip install deluge-client` to install.',
                logger,
            )
        config = self.prepare_config(config)

        if config['host'] in ['localhost', '127.0.0.1'] and not config.get('username'):
            # If an username is not specified, we have to do a lookup for the localclient username/password
            auth = self.get_localhost_auth(config.get('config_path'))
            if auth and auth[0]:
                config['username'], config['password'] = auth
        if not config.get('username') or not config.get('password'):
            raise plugin.PluginError(
                'Unable to get authentication info for Deluge. You may need to '
                'specify an username and password from your Deluge auth file.'
            )

        return DelugeRPCClient(
            config['host'],
            config['port'],
            config['username'],
            config['password'],
            decode_utf8=True,
        )

    def prepare_config(self, config):
        config.setdefault('host', 'localhost')
        config.setdefault('port', 58846)
        return config

    @staticmethod
    def get_localhost_auth(config_path=None):
        if config_path is None:
            if sys.platform.startswith('win'):
                auth_file = os.path.join(os.getenv('APPDATA'), 'deluge', 'auth')
            else:
                auth_file = os.path.expanduser('~/.config/deluge/auth')
        else:
            auth_file = os.path.join(config_path, 'auth')
        if not os.path.isfile(auth_file):
            return None

        with open(auth_file) as auth:
            for line in auth:
                line = line.strip()
                if line.startswith('#') or not line:
                    # This is a comment or blank line
                    continue

                lsplit = line.split(':')
                if lsplit[0] == 'localclient':
                    username, password = lsplit[:2]
                    return username, password


class InputDeluge(DelugePlugin):
    """Create entries for torrents in the deluge session."""

    # Fields we provide outside of the deluge_ prefixed namespace
    settings_map = {
        'name': 'title',
        'hash': 'torrent_info_hash',
        'num_peers': 'torrent_peers',
        'num_seeds': 'torrent_seeds',
        'total_size': ('content_size', lambda size: size / 1024 / 1024),
        'files': ('content_files', lambda file_dicts: [f['path'] for f in file_dicts]),
    }

    schema = {
        'anyOf': [
            {'type': 'boolean'},
            {
                'type': 'object',
                'properties': {
                    'host': {'type': 'string'},
                    'port': {'type': 'integer'},
                    'username': {'type': 'string'},
                    'password': {'type': 'string'},
                    'config_path': {'type': 'string', 'format': 'path'},
                    'filter': {
                        'type': 'object',
                        'properties': {
                            'label': {'type': 'string'},
                            'state': {
                                'type': 'string',
                                'enum': ['active', 'downloading', 'seeding', 'queued', 'paused'],
                            },
                        },
                        'additionalProperties': False,
                    },
                },
                'additionalProperties': False,
            },
        ]
    }

    def on_task_start(self, task, config):
        config = self.prepare_config(config)
        super().on_task_start(task, config)

    def prepare_config(self, config):
        if isinstance(config, bool):
            config = {}
        if 'filter' in config:
            filter = config['filter']
            if 'label' in filter:
                filter['label'] = filter['label'].lower()
            if 'state' in filter:
                filter['state'] = filter['state'].capitalize()
        super().prepare_config(config)
        return config

    def on_task_input(self, task, config):
        """Generates and returns a list of entries from the deluge daemon."""
        config = self.prepare_config(config)
        # Reset the entries list
        client = self.setup_client(config)

        try:
            client.connect()
        except ConnectionError as exc:
            raise plugin.PluginError(
                f'Error connecting to deluge daemon: {exc}', logger=logger
            ) from exc

        entries = self.generate_entries(client, config)
        client.disconnect()
        return entries

    def generate_entries(self, client, config):
        entries = []
        filter = config.get('filter', {})
        torrents = client.call('core.get_torrents_status', filter or {}, [])
        for hash, torrent_dict in torrents.items():
            # Make sure it has a url so no plugins crash
            entry = Entry(deluge_id=hash, url='')
            config_path = os.path.expanduser(config.get('config_path', ''))
            if config_path:
                torrent_path = os.path.join(config_path, 'state', hash + '.torrent')
                if os.path.isfile(torrent_path):
                    entry['location'] = torrent_path
                    if not torrent_path.startswith('/'):
                        torrent_path = '/' + torrent_path
                    entry['url'] = 'file://' + torrent_path
                else:
                    logger.warning('Did not find torrent file at {}', torrent_path)
            # Pieces is just a really long list, cluttering up the entry and --dump output
            blacklist_fields = ['pieces']
            for key, value in torrent_dict.items():
                # All fields (except a few) provided by deluge get placed under the deluge_ namespace
                if key in blacklist_fields:
                    continue
                entry['deluge_' + key] = value
                # Some fields also get special handling
                if key in self.settings_map:
                    flexget_key = self.settings_map[key]
                    if isinstance(flexget_key, tuple):
                        flexget_key, format_func = flexget_key
                        value = format_func(value)
                    entry[flexget_key] = value
            entries.append(entry)

{'fraction_non_alphanumeric': 0.10768061887000967, 'fraction_numerical': 0.0037297969332780773, 'mean_word_length': 1.69828550130451, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6180653', 'n_tokens_mistral': 3823, 'n_tokens_neox': 3617, 'n_words': 1045}