flexget.plugins.plugin_adv_exec
Covered: 35 lines
Missed: 63 lines
Skipped 37 lines
Percent: 35 %
  1
import subprocess
  2
import logging
  3
import shlex
  4
import pipes
  5
from flexget.plugin import *
  7
log = logging.getLogger('adv_exec')
 10
class PluginAdvExec(object):
 11
    """
 12
    Execute commands
 14
    Example:
 16
    adv_exec:
 17
      on_start:
 18
        event: echo "Started"
 19
      on_input:
 20
        for_entries: echo 'got %(title)s'
 21
      on_output:
 22
        for_accepted: echo 'accepted %(title)s - %(url)s > file
 24
    You can use all (available) entry fields in the command.
 25
    """
 27
    NAME = 'adv_exec'
 29
    def validator(self):
 30
        from flexget import validator
 31
        root = validator.factory('dict')
 33
        def add(name):
 34
            event = root.accept('dict', key=name)
 35
            event.accept('text', key='event')
 36
            event.accept('text', key='for_entries')
 37
            event.accept('text', key='for_accepted')
 38
            event.accept('text', key='for_rejected')
 39
            event.accept('text', key='for_failed')
 41
        for name in ['on_start', 'on_input', 'on_filter', 'on_output', 'on_exit']:
 42
            add(name)
 44
        root.accept('boolean', key='fail_entries')
 46
        return root
 48
    def on_feed_start(self, feed):
 49
        self.execute(feed, 'on_start')
 52
    @priority(100)
 53
    def on_feed_input(self, feed):
 54
        self.execute(feed, 'on_input')
 57
    @priority(100)
 58
    def on_feed_filter(self, feed):
 59
        self.execute(feed, 'on_filter')
 62
    @priority(100)
 63
    def on_feed_output(self, feed):
 64
        self.execute(feed, 'on_output')
 66
    def on_feed_exit(self, feed):
 67
        self.execute(feed, 'on_exit')
 69
    def execute_cmd(self, cmd):
 70
        p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, \
 71
            stderr=subprocess.STDOUT, close_fds=False)
 72
        (r, w) = (p.stdout, p.stdin)
 73
        response = r.read()
 74
        r.close()
 75
        w.close()
 76
        if response:
 77
            log.info('Stdout: %s' % response)
 78
        return p.wait() 
 80
    def execute(self, feed, event_name):
 81
        if not event_name in feed.config[self.NAME]:
 82
            log.debug('event %s not configured' % event_name)
 83
            return
 85
        name_map = {'for_entries': feed.entries, 'for_accepted': feed.accepted, \
 86
            'for_rejected': feed.rejected, 'for_failed': feed.failed}
 88
        for operation, entries in name_map.iteritems():
 89
            if not operation in feed.config[self.NAME][event_name]:
 90
                continue
 92
            log.debug('running event_name: %s operation: %s entries: %s' % (event_name, operation, len(entries)))
 94
            for entry in entries:
 95
                try:
 96
                    cmd = feed.config[self.NAME][event_name][operation]
 98
                    args = []
100
                    for arg in shlex.split(cmd.encode('utf-8'), comments=True):
101
                        arg = unicode(arg, 'utf-8')
102
                        formatted = arg % entry
103
                        if formatted != arg:
104
                            arg = pipes.quote(formatted)
105
                        args.append(arg)
106
                    cmd = ' '.join(args)
108
                except KeyError, e:
109
                    msg = 'Entry %s does not have required field %s' % (entry['title'], e.message)
110
                    log.error(msg)
112
                    if feed.config[self.NAME].get('fail_entries'):
113
                        feed.fail(entry, msg)
114
                    continue
116
                log.debug('event_name: %s operation: %s cmd: %s' % (event_name, operation, cmd))
117
                if feed.manager.options.test:
118
                    log.info('Would execute: %s' % cmd)
119
                else:
121
                    if self.execute_cmd(cmd) != 0 and feed.config[self.NAME].get('fail_entries'):
122
                        feed.fail(entry, "adv_exec return code was non-zero")
125
        if 'event' in feed.config[self.NAME][event_name]:
126
            cmd = feed.config[self.NAME][event_name]['event']
127
            log.debug('event cmd: %s' % cmd)
128
            if feed.manager.options.test:
129
                log.info('Would execute: %s' % cmd)
130
            else:
131
                self.execute_cmd(cmd)
134
register_plugin(PluginAdvExec, 'adv_exec')