Пример #1
0
    def modify(self, entry, config, validate=False, errors=True):
        """This can be called from a plugin to add set values to an entry"""

        # Create a new dict so we don't overwrite the set config with string replaced values.
        conf = copy(config)

        # Do jinja2 rendering/string replacement
        for field, value in conf.items():
            if isinstance(value, basestring):
                logger = log.error if errors else log.debug
                result = replace_from_entry(value, entry, field, logger, default=None)
                if result is None:
                    # If the replacement failed, remove this key from the update dict
                    del conf[field]
                else:
                    conf[field] = result

        if validate:
            from flexget import validator
            v = validator.factory('dict')
            for key in self.keys:
                v.accept(self.keys[key], key=key)

            if not v.validate(config):
                log.info('set parameters are invalid, error follows')
                log.info(v.errors.messages)
                return

        # If there are valid items in the config, apply to entry.
        if conf:
            log.debug('adding set: info to entry:\'%s\' %s' % (entry['title'], conf))
            entry.update(conf)
Пример #2
0
    def _make_torrent_options_dict(self, feed, entry):

        opt_dic = {}
        config = self.get_config(feed)

        for opt_key in ('path', 'addpaused', 'honourlimits', 'bandwidthpriority',
                        'maxconnections', 'maxupspeed', 'maxdownspeed', 'ratio'):
            if opt_key in entry:
                opt_dic[opt_key] = entry[opt_key]
            elif opt_key in config:
                opt_dic[opt_key] = config[opt_key]

        options = {'add': {}, 'change': {}}

        if 'path' in opt_dic:
            opt_dic['path'] = replace_from_entry(opt_dic['path'], entry, 'path', log.error)
            if opt_dic['path']:
                options['add']['download_dir'] = os.path.expanduser(opt_dic['path'])
        if 'addpaused' in opt_dic and opt_dic['addpaused']:
            options['add']['paused'] = True
        if 'bandwidthpriority' in opt_dic:
            options['add']['bandwidthPriority'] = opt_dic['bandwidthpriority']
        if 'maxconnections' in opt_dic:
            options['add']['peer_limit'] = opt_dic['maxconnections']

        if 'honourlimits' in opt_dic and not opt_dic['honourlimits']:
            options['change']['honorsSessionLimits'] = False
        if 'maxupspeed' in opt_dic:
            options['change']['uploadLimit'] = opt_dic['maxupspeed']
            options['change']['uploadLimited'] = True
        if 'maxdownspeed' in opt_dic:
            options['change']['downloadLimit'] = opt_dic['maxdownspeed']
            options['change']['downloadLimited'] = True

        if 'ratio' in opt_dic:
            options['change']['seedRatioLimit'] = opt_dic['ratio']
            if opt_dic['ratio'] == -1:
                # seedRatioMode:
                # 0 follow the global settings
                # 1 override the global settings, seeding until a certain ratio
                # 2 override the global settings, seeding regardless of ratio
                options['change']['seedRatioMode'] = 2
            else:
                options['change']['seedRatioMode'] = 1

        return options
Пример #3
0
    def execute(self, feed, phase_name):
        config = self.get_config(feed)
        if not phase_name in config:
            log.debug('phase %s not configured' % phase_name)
            return

        name_map = {'for_entries': feed.entries, 'for_accepted': feed.accepted,
                    'for_rejected': feed.rejected, 'for_failed': feed.failed}

        allow_background = config.get('allow_background')
        for operation, entries in name_map.iteritems():
            if not operation in config[phase_name]:
                continue

            log.debug('running phase_name: %s operation: %s entries: %s' % (phase_name, operation, len(entries)))

            for entry in entries:
                cmd = config[phase_name][operation]
                entrydict = EscapingDict(entry) if config.get('auto_escape') else entry
                # Do string replacement from entry, but make sure quotes get escaped
                cmd = replace_from_entry(cmd, entrydict, 'exec command', log.error, default=None)
                if cmd is None:
                    # fail the entry if configured to do so
                    if config.get('fail_entries'):
                        feed.fail(entry, 'Entry `%s` does not have required fields for string replacement.' %
                                         entry['title'])
                    continue

                log.debug('phase_name: %s operation: %s cmd: %s' % (phase_name, operation, cmd))
                if feed.manager.options.test:
                    log.info('Would execute: %s' % cmd)
                else:
                    # Run the command, fail entries with non-zero return code if configured to
                    if self.execute_cmd(cmd, allow_background) != 0 and config.get('fail_entries'):
                        feed.fail(entry, "exec return code was non-zero")

        # phase keyword in this
        if 'phase' in config[phase_name]:
            cmd = config[phase_name]['phase']
            log.debug('phase cmd: %s' % cmd)
            if feed.manager.options.test:
                log.info('Would execute: %s' % cmd)
            else:
                self.execute_cmd(cmd, allow_background)
Пример #4
0
    def output(self, feed, entry):
        """Moves temp-file into final destination

        Raises:
            PluginError if operation fails
        """

        config = self.get_config(feed)

        if 'file' not in entry and not feed.manager.options.test:
            log.debug('file missing, entry: %s' % entry)
            raise PluginError('Entry `%s` has no temp file associated with' % entry['title'])

        try:
            # use path from entry if has one, otherwise use from download definition parameter
            path = entry.get('path', config.get('path'))
            if not isinstance(path, basestring):
                raise PluginError('Invalid `path` in entry `%s`' % entry['title'])

            # override path from command line parameter
            if feed.manager.options.dl_path:
                path = feed.manager.options.dl_path

            # expand variables in path
            path = replace_from_entry(path, entry, 'path', log.error)
            if not path:
                feed.fail(entry, 'Could not set path. Does not contain all fields for string replacement.')
                return
            path = os.path.expanduser(path)

            # If we are in test mode, report and return
            if feed.manager.options.test:
                log.info('Would write `%s` to `%s`' % (entry['title'], path))
                # Set a fake location, so the exec plugin can do string replacement during --test #1015
                entry['output'] = os.path.join(path, 'TEST_MODE_NO_OUTPUT')
                return

            # make path
            if not os.path.isdir(path):
                log.info('Creating directory %s' % path)
                try:
                    os.makedirs(path)
                except:
                    raise PluginError('Cannot create path %s' % path, log)

            # check that temp file is present
            if not os.path.exists(entry['file']):
                tmp_path = os.path.join(feed.manager.config_base, 'temp')
                log.debug('entry: %s' % entry)
                log.debug('temp: %s' % ', '.join(os.listdir(tmp_path)))
                raise PluginWarning('Downloaded temp file `%s` doesn\'t exist!?' % entry['file'])

            # if we still don't have a filename, try making one from title (last resort)
            if not entry.get('filename'):
                entry['filename'] = entry['title']
                log.debug('set filename from title %s' % entry['filename'])
                if not 'mime-type' in entry:
                    log.warning('Unable to figure proper filename for %s. Using title.' % entry['title'])
                else:
                    guess = mimetypes.guess_extension(entry['mime-type'])
                    if not guess:
                        log.warning('Unable to guess extension with mime-type %s' % guess)
                    else:
                        self.filename_ext_from_mime(entry)

            # combine to full path + filename, replace / from filename (replaces bc tickets #208, #325, #353)
            name = entry.get('filename', entry['title'])
            for char in '/:<>^*?~"':
                name = name.replace(char, ' ')
            # remove duplicate spaces
            name = ' '.join(name.split())
            destfile = os.path.join(path, name)
            log.debug('destfile: %s' % destfile)

            if os.path.exists(destfile):
                import filecmp
                if filecmp.cmp(entry['file'], destfile):
                    log.debug("Identical destination file '%s' already exists", destfile)
                    return
                elif config.get('overwrite'):
                    log.debug("Overwriting already existing file %s" % destfile)
                else:
                    log.info('File `%s` already exists and is not identical, download failed.' % destfile)
                    feed.fail(entry, 'File `%s` already exists and is not identical.' % destfile)
                    return

            # move temp file
            log.debug('moving %s to %s' % (entry['file'], destfile))

            try:
                shutil.move(entry['file'], destfile)
            except OSError, err:
                # ignore permission errors, see ticket #555
                import errno
                if not os.path.exists(destfile):
                    raise PluginError('Unable to write %s' % destfile)
                if err.errno != errno.EPERM:
                    raise

            # store final destination as output key
            entry['output'] = destfile
Пример #5
0
        def on_get_session_state(torrent_ids):
            """Gets called with a list of torrent_ids loaded in the deluge session.
            Adds new torrents and modifies the settings for ones already in the session."""
            dlist = []
            # add the torrents
            for entry in feed.accepted:

                def add_entry(entry, opts):
                    """Adds an entry to the deluge session"""
                    magnet, filedump = None, None
                    if entry.get('url', '').startswith('magnet:'):
                        magnet = entry['url']
                    else:
                        if not os.path.exists(entry['file']):
                            feed.fail(entry, 'Downloaded temp file \'%s\' doesn\'t exist!' % entry['file'])
                            del(entry['file'])
                            return
                        try:
                            f = open(entry['file'], 'rb')
                            filedump = base64.encodestring(f.read())
                        finally:
                            f.close()

                    log.debug('Adding %s to deluge.' % entry['title'])
                    if magnet:
                        return client.core.add_torrent_magnet(magnet, opts)
                    else:
                        return client.core.add_torrent_file(entry['title'], filedump, opts)

                # Generate deluge options dict for torrent add
                path = replace_from_entry(entry.get('path', config['path']), entry, 'path', log.error)
                add_opts = {}
                if path:
                    add_opts['download_location'] = make_valid_path(os.path.expanduser(path))
                for fopt, dopt in self.options.iteritems():
                    value = entry.get(fopt, config.get(fopt))
                    if value is not None:
                        add_opts[dopt] = value
                        if fopt == 'ratio':
                            add_opts['stop_at_ratio'] = True
                # Make another set of options, that get set after the torrent has been added
                content_filename = entry.get('content_filename', config.get('content_filename', ''))
                movedone = replace_from_entry(entry.get('movedone', config['movedone']), entry, 'movedone', log.error)
                modify_opts = {'movedone': make_valid_path(os.path.expanduser(movedone)),
                        'label': format_label(entry.get('label', config['label'])),
                        'queuetotop': entry.get('queuetotop', config.get('queuetotop', None)),
                        'content_filename': replace_from_entry(content_filename, entry, 'content_filename', log.error),
                        'main_file_only': entry.get('main_file_only', config.get('main_file_only', False))}

                torrent_id = entry.get('deluge_id') or entry.get('torrent_info_hash')
                torrent_id = torrent_id and torrent_id.lower()
                if torrent_id in torrent_ids:
                    log.info('%s is already loaded in deluge, setting options' % entry['title'])
                    # Entry has a deluge id, verify the torrent is still in the deluge session and apply options
                    # Since this is already loaded in deluge, we may also need to change the path
                    modify_opts['path'] = add_opts.pop('download_location', None)
                    dlist.extend([set_torrent_options(torrent_id, entry, modify_opts),
                                  client.core.set_torrent_options([torrent_id], add_opts)])
                else:
                    dlist.append(add_entry(entry, add_opts).addCallbacks(set_torrent_options, on_fail,
                            callbackArgs=(entry, modify_opts), errbackArgs=(feed, entry)))
            return defer.DeferredList(dlist)
Пример #6
0
    def add_to_deluge11(self, feed, config):
        """Add torrents to deluge using deluge 1.1.x api."""
        try:
            from deluge.ui.client import sclient
        except:
            raise PluginError('Deluge module required', log)

        sclient.set_core_uri()
        for entry in feed.accepted:
            try:
                before = sclient.get_session_state()
            except Exception, (errno, msg):
                raise PluginError('Could not communicate with deluge core. %s' % msg, log)
            if feed.manager.options.test:
                return
            opts = {}
            path = entry.get('path', config['path'])
            if path:
                opts['download_location'] = os.path.expanduser(replace_from_entry(path, entry, 'path', logger=log.error))
            for fopt, dopt in self.options.iteritems():
                value = entry.get(fopt, config.get(fopt))
                if value is not None:
                    opts[dopt] = value
                    if fopt == 'ratio':
                        opts['stop_at_ratio'] = True

            # check that file is downloaded
            if not 'file' in entry:
                feed.fail(entry, 'file missing?')
                continue

            # see that temp file is present
            if not os.path.exists(entry['file']):
                tmp_path = os.path.join(feed.manager.config_base, 'temp')
                log.debug('entry: %s' % entry)
                log.debug('temp: %s' % ', '.join(os.listdir(tmp_path)))
                feed.fail(entry, 'Downloaded temp file \'%s\' doesn\'t exist!?' % entry['file'])
                continue

            sclient.add_torrent_file([entry['file']], [opts])
            log.info('%s torrent added to deluge with options %s' % (entry['title'], opts))

            movedone = entry.get('movedone', config['movedone'])
            label = entry.get('label', config['label']).lower()
            queuetotop = entry.get('queuetotop', config.get('queuetotop'))

            # Sometimes deluge takes a moment to add the torrent, wait a second.
            time.sleep(2)
            after = sclient.get_session_state()
            for item in after:
                # find torrentid of just added torrent
                if not item in before:
                    movedone = replace_from_entry(movedone, entry, 'movedone', log.error)
                    movedone = os.path.expanduser(movedone)
                    if movedone:
                        if not os.path.isdir(movedone):
                            log.debug('movedone path %s doesn\'t exist, creating' % movedone)
                            os.makedirs(movedone)
                        log.debug('%s move on complete set to %s' % (entry['title'], movedone))
                        sclient.set_torrent_move_on_completed(item, True)
                        sclient.set_torrent_move_on_completed_path(item, movedone)
                    if label:
                        if not 'label' in sclient.get_enabled_plugins():
                            sclient.enable_plugin('label')
                        if not label in sclient.label_get_labels():
                            sclient.label_add(label)
                        log.debug('%s label set to \'%s\'' % (entry['title'], label))
                        sclient.label_set_torrent(item, label)
                    if queuetotop:
                        log.debug('%s moved to top of queue' % entry['title'])
                        sclient.queue_top([item])
                    break
            else:
                log.info('%s is already loaded in deluge. Cannot change label, movedone, or queuetotop' % entry['title'])