001package net.filebot.cli;
002
003import static java.util.stream.Collectors.*;
004import static net.filebot.Execute.*;
005import static net.filebot.Logging.*;
006
007import java.io.File;
008import java.util.ArrayList;
009import java.util.List;
010import java.util.Objects;
011import java.util.stream.IntStream;
012import java.util.stream.Stream;
013
014import javax.script.ScriptException;
015
016import net.filebot.ExecuteException;
017import net.filebot.ExitCode;
018import net.filebot.format.ExpressionFormat;
019import net.filebot.format.MediaBindingBean;
020
021public class ExecCommand {
022
023        private List<ExpressionFormat> template;
024        private boolean parallel;
025
026        private File directory;
027
028        public ExecCommand(List<ExpressionFormat> template, boolean parallel, File directory) {
029                this.template = template;
030                this.parallel = parallel;
031                this.directory = directory;
032        }
033
034        public IntStream execute(Stream<MediaBindingBean> group) {
035                if (parallel) {
036                        return executeParallel(group);
037                } else {
038                        return executeSequence(group);
039                }
040        }
041
042        private IntStream executeSequence(Stream<MediaBindingBean> group) {
043                return group.map(v -> {
044                        return template.stream().map(t -> getArgumentValue(t, v)).filter(Objects::nonNull).collect(toList());
045                }).distinct().mapToInt(this::execute);
046        }
047
048        private IntStream executeParallel(Stream<MediaBindingBean> group) {
049                // collect all bindings and combine them into a single command
050                List<MediaBindingBean> bindings = group.collect(toList());
051
052                if (bindings.isEmpty()) {
053                        return IntStream.empty();
054                }
055
056                // collect single command
057                List<String> command = template.stream().flatMap(t -> {
058                        return bindings.stream().map(v -> getArgumentValue(t, v)).filter(Objects::nonNull).distinct();
059                }).collect(toList());
060
061                // execute single command
062                return Stream.of(command).mapToInt(this::execute);
063        }
064
065        private int execute(List<String> command) {
066                try {
067                        system(command, directory);
068                        return ExitCode.SUCCESS;
069                } catch (ExecuteException e) {
070                        log.warning(e::getMessage);
071                        return e.getExitCode();
072                } catch (Exception e) {
073                        log.warning(e::getMessage);
074                        return ExitCode.ERROR;
075                }
076        }
077
078        private String getArgumentValue(ExpressionFormat template, MediaBindingBean variables) {
079                try {
080                        return template.format(variables);
081                } catch (Exception e) {
082                        debug.warning(cause(template.getExpression(), e));
083                }
084                return null;
085        }
086
087        public static ExecCommand parse(List<String> args, File directory) throws ScriptException {
088                // execute one command per file or one command with many file arguments
089                boolean parallel = args.lastIndexOf("+") == args.size() - 1;
090
091                if (parallel) {
092                        args = args.subList(0, args.size() - 1);
093                }
094
095                List<ExpressionFormat> template = new ArrayList<ExpressionFormat>();
096                for (String argument : args) {
097                        template.add(new ExpressionFormat(argument));
098                }
099
100                return new ExecCommand(template, parallel, directory);
101        }
102
103}