Added `-exec` option that works similar to `find -exec` and the `--def exec` option.
e.g. ``` filebot -rename $OPTS -exec echo {f} filebot -rename $OPTS -exec echo {f} + ```
This commit is contained in:
parent
789c472876
commit
aa10510e87
|
@ -27,6 +27,7 @@ import org.kohsuke.args4j.CmdLineParser;
|
||||||
import org.kohsuke.args4j.Option;
|
import org.kohsuke.args4j.Option;
|
||||||
import org.kohsuke.args4j.ParserProperties;
|
import org.kohsuke.args4j.ParserProperties;
|
||||||
import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler;
|
import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler;
|
||||||
|
import org.kohsuke.args4j.spi.RestOfArgumentsHandler;
|
||||||
|
|
||||||
import net.filebot.ApplicationFolder;
|
import net.filebot.ApplicationFolder;
|
||||||
import net.filebot.Language;
|
import net.filebot.Language;
|
||||||
|
@ -140,6 +141,9 @@ public class ArgumentBean {
|
||||||
@Option(name = "--def", usage = "Define script variables", handler = BindingsHandler.class)
|
@Option(name = "--def", usage = "Define script variables", handler = BindingsHandler.class)
|
||||||
public Map<String, String> defines = new LinkedHashMap<String, String>();
|
public Map<String, String> defines = new LinkedHashMap<String, String>();
|
||||||
|
|
||||||
|
@Option(name = "-exec", usage = "Execute command", handler = RestOfArgumentsHandler.class)
|
||||||
|
public List<String> exec = new ArrayList<String>();
|
||||||
|
|
||||||
@Argument
|
@Argument
|
||||||
public List<String> arguments = new ArrayList<String>();
|
public List<String> arguments = new ArrayList<String>();
|
||||||
|
|
||||||
|
@ -307,6 +311,14 @@ public class ArgumentBean {
|
||||||
return Level.parse(log.toUpperCase());
|
return Level.parse(log.toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExecCommand getExecCommand() {
|
||||||
|
try {
|
||||||
|
return exec == null || exec.isEmpty() ? null : ExecCommand.parse(exec, getOutputPath());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CmdlineException("Illegal exec expression: " + exec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public PanelBuilder[] getPanelBuilders() {
|
public PanelBuilder[] getPanelBuilders() {
|
||||||
// default multi panel mode
|
// default multi panel mode
|
||||||
if (mode == null) {
|
if (mode == null) {
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class ArgumentProcessor {
|
||||||
|
|
||||||
// rename files in linear order
|
// rename files in linear order
|
||||||
if (args.list && args.rename) {
|
if (args.list && args.rename) {
|
||||||
return cli.rename(args.getEpisodeListProvider(), args.getSearchQuery(), args.getExpressionFileFormat(), args.getExpressionFilter(), args.getSortOrder(), args.getLanguage().getLocale(), args.isStrict(), args.getFiles(true), args.getRenameAction(), args.getConflictAction(), args.getOutputPath()).isEmpty() ? 1 : 0;
|
return cli.rename(args.getEpisodeListProvider(), args.getSearchQuery(), args.getExpressionFileFormat(), args.getExpressionFilter(), args.getSortOrder(), args.getLanguage().getLocale(), args.isStrict(), args.getFiles(true), args.getRenameAction(), args.getConflictAction(), args.getOutputPath(), args.getExecCommand()).isEmpty() ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// print episode info
|
// print episode info
|
||||||
|
@ -85,7 +85,7 @@ public class ArgumentProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.rename) {
|
if (args.rename) {
|
||||||
cli.rename(files, args.getRenameAction(), args.getConflictAction(), args.getAbsoluteOutputFolder(), args.getExpressionFileFormat(), args.getDatasource(), args.getSearchQuery(), args.getSortOrder(), args.getExpressionFilter(), args.getLanguage().getLocale(), args.isStrict());
|
cli.rename(files, args.getRenameAction(), args.getConflictAction(), args.getAbsoluteOutputFolder(), args.getExpressionFileFormat(), args.getDatasource(), args.getSearchQuery(), args.getSortOrder(), args.getExpressionFilter(), args.getLanguage().getLocale(), args.isStrict(), args.getExecCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.check) {
|
if (args.check) {
|
||||||
|
|
|
@ -23,9 +23,9 @@ import net.filebot.web.SortOrder;
|
||||||
|
|
||||||
public interface CmdlineInterface {
|
public interface CmdlineInterface {
|
||||||
|
|
||||||
List<File> rename(Collection<File> files, RenameAction action, ConflictAction conflict, File output, ExpressionFileFormat format, Datasource db, String query, SortOrder order, ExpressionFilter filter, Locale locale, boolean strict) throws Exception;
|
List<File> rename(Collection<File> files, RenameAction action, ConflictAction conflict, File output, ExpressionFileFormat format, Datasource db, String query, SortOrder order, ExpressionFilter filter, Locale locale, boolean strict, ExecCommand exec) throws Exception;
|
||||||
|
|
||||||
List<File> rename(EpisodeListProvider db, String query, ExpressionFileFormat format, ExpressionFilter filter, SortOrder order, Locale locale, boolean strict, List<File> files, RenameAction action, ConflictAction conflict, File output) throws Exception;
|
List<File> rename(EpisodeListProvider db, String query, ExpressionFileFormat format, ExpressionFilter filter, SortOrder order, Locale locale, boolean strict, List<File> files, RenameAction action, ConflictAction conflict, File output, ExecCommand exec) throws Exception;
|
||||||
|
|
||||||
List<File> rename(Map<File, File> rename, RenameAction action, ConflictAction conflict) throws Exception;
|
List<File> rename(Map<File, File> rename, RenameAction action, ConflictAction conflict) throws Exception;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package net.filebot.cli;
|
package net.filebot.cli;
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.*;
|
import static java.nio.charset.StandardCharsets.*;
|
||||||
|
import static java.util.Arrays.*;
|
||||||
import static java.util.Collections.*;
|
import static java.util.Collections.*;
|
||||||
import static java.util.stream.Collectors.*;
|
import static java.util.stream.Collectors.*;
|
||||||
import static net.filebot.Logging.*;
|
import static net.filebot.Logging.*;
|
||||||
|
@ -84,25 +85,25 @@ import net.filebot.web.VideoHashSubtitleService;
|
||||||
public class CmdlineOperations implements CmdlineInterface {
|
public class CmdlineOperations implements CmdlineInterface {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<File> rename(Collection<File> files, RenameAction action, ConflictAction conflict, File output, ExpressionFileFormat format, Datasource db, String query, SortOrder order, ExpressionFilter filter, Locale locale, boolean strict) throws Exception {
|
public List<File> rename(Collection<File> files, RenameAction action, ConflictAction conflict, File output, ExpressionFileFormat format, Datasource db, String query, SortOrder order, ExpressionFilter filter, Locale locale, boolean strict, ExecCommand exec) throws Exception {
|
||||||
// movie mode
|
// movie mode
|
||||||
if (db instanceof MovieIdentificationService) {
|
if (db instanceof MovieIdentificationService) {
|
||||||
return renameMovie(files, action, conflict, output, format, (MovieIdentificationService) db, query, filter, locale, strict);
|
return renameMovie(files, action, conflict, output, format, (MovieIdentificationService) db, query, filter, locale, strict, exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
// series mode
|
// series mode
|
||||||
if (db instanceof EpisodeListProvider) {
|
if (db instanceof EpisodeListProvider) {
|
||||||
return renameSeries(files, action, conflict, output, format, (EpisodeListProvider) db, query, order, filter, locale, strict);
|
return renameSeries(files, action, conflict, output, format, (EpisodeListProvider) db, query, order, filter, locale, strict, exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
// music mode
|
// music mode
|
||||||
if (db instanceof MusicIdentificationService) {
|
if (db instanceof MusicIdentificationService) {
|
||||||
return renameMusic(files, action, conflict, output, format, (MusicIdentificationService) db);
|
return renameMusic(files, action, conflict, output, format, singletonList((MusicIdentificationService) db), exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
// generic file / xattr mode
|
// generic file / xattr mode
|
||||||
if (db instanceof XattrMetaInfoProvider) {
|
if (db instanceof XattrMetaInfoProvider) {
|
||||||
return renameFiles(files, action, conflict, output, format, (XattrMetaInfoProvider) db, filter, strict);
|
return renameFiles(files, action, conflict, output, format, (XattrMetaInfoProvider) db, filter, strict, exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
// auto-detect mode for each fileset
|
// auto-detect mode for each fileset
|
||||||
|
@ -114,16 +115,16 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
for (Type key : it.getKey().types()) {
|
for (Type key : it.getKey().types()) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case Movie:
|
case Movie:
|
||||||
results.addAll(renameMovie(it.getValue(), action, conflict, output, format, TheMovieDB, query, filter, locale, strict));
|
results.addAll(renameMovie(it.getValue(), action, conflict, output, format, TheMovieDB, query, filter, locale, strict, exec));
|
||||||
break;
|
break;
|
||||||
case Series:
|
case Series:
|
||||||
results.addAll(renameSeries(it.getValue(), action, conflict, output, format, TheTVDB, query, order, filter, locale, strict));
|
results.addAll(renameSeries(it.getValue(), action, conflict, output, format, TheTVDB, query, order, filter, locale, strict, exec));
|
||||||
break;
|
break;
|
||||||
case Anime:
|
case Anime:
|
||||||
results.addAll(renameSeries(it.getValue(), action, conflict, output, format, AniDB, query, order, filter, locale, strict));
|
results.addAll(renameSeries(it.getValue(), action, conflict, output, format, AniDB, query, order, filter, locale, strict, exec));
|
||||||
break;
|
break;
|
||||||
case Music:
|
case Music:
|
||||||
results.addAll(renameMusic(it.getValue(), action, conflict, output, format, MediaInfoID3, AcoustID));
|
results.addAll(renameMusic(it.getValue(), action, conflict, output, format, asList(MediaInfoID3, AcoustID), exec)); // prefer existing ID3 tags and use acoustid only when necessary
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +141,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<File> rename(EpisodeListProvider db, String query, ExpressionFileFormat format, ExpressionFilter filter, SortOrder order, Locale locale, boolean strict, List<File> files, RenameAction action, ConflictAction conflict, File outputDir) throws Exception {
|
public List<File> rename(EpisodeListProvider db, String query, ExpressionFileFormat format, ExpressionFilter filter, SortOrder order, Locale locale, boolean strict, List<File> files, RenameAction action, ConflictAction conflict, File outputDir, ExecCommand exec) throws Exception {
|
||||||
// match files and episodes in linear order
|
// match files and episodes in linear order
|
||||||
List<Episode> episodes = fetchEpisodeList(db, query, filter, order, locale, strict);
|
List<Episode> episodes = fetchEpisodeList(db, query, filter, order, locale, strict);
|
||||||
|
|
||||||
|
@ -150,16 +151,16 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// rename episodes
|
// rename episodes
|
||||||
return renameAll(formatMatches(matches, format, outputDir), action, conflict, matches);
|
return renameAll(formatMatches(matches, format, outputDir), action, conflict, matches, exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<File> rename(Map<File, File> renameMap, RenameAction renameAction, ConflictAction conflict) throws Exception {
|
public List<File> rename(Map<File, File> renameMap, RenameAction renameAction, ConflictAction conflict) throws Exception {
|
||||||
// generic rename function that can be passed any set of files
|
// generic rename function that can be passed any set of files
|
||||||
return renameAll(renameMap, renameAction, conflict, null);
|
return renameAll(renameMap, renameAction, conflict, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<File> renameSeries(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFileFormat format, EpisodeListProvider db, String query, SortOrder sortOrder, ExpressionFilter filter, Locale locale, boolean strict) throws Exception {
|
public List<File> renameSeries(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFileFormat format, EpisodeListProvider db, String query, SortOrder sortOrder, ExpressionFilter filter, Locale locale, boolean strict, ExecCommand exec) throws Exception {
|
||||||
log.config(format("Rename episodes using [%s]", db.getName()));
|
log.config(format("Rename episodes using [%s]", db.getName()));
|
||||||
|
|
||||||
// ignore sample files
|
// ignore sample files
|
||||||
|
@ -242,7 +243,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
matches.addAll(derivateMatches);
|
matches.addAll(derivateMatches);
|
||||||
|
|
||||||
// rename episodes
|
// rename episodes
|
||||||
return renameAll(formatMatches(matches, format, outputDir), renameAction, conflictAction, matches);
|
return renameAll(formatMatches(matches, format, outputDir), renameAction, conflictAction, matches, exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Match<File, Object>> matchEpisodes(Collection<File> files, Collection<Episode> episodes, boolean strict) throws Exception {
|
private List<Match<File, Object>> matchEpisodes(Collection<File> files, Collection<Episode> episodes, boolean strict) throws Exception {
|
||||||
|
@ -303,7 +304,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
return new ArrayList<Episode>(episodes);
|
return new ArrayList<Episode>(episodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<File> renameMovie(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFileFormat format, MovieIdentificationService service, String query, ExpressionFilter filter, Locale locale, boolean strict) throws Exception {
|
public List<File> renameMovie(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFileFormat format, MovieIdentificationService service, String query, ExpressionFilter filter, Locale locale, boolean strict, ExecCommand exec) throws Exception {
|
||||||
log.config(format("Rename movies using [%s]", service.getName()));
|
log.config(format("Rename movies using [%s]", service.getName()));
|
||||||
|
|
||||||
// ignore sample files
|
// ignore sample files
|
||||||
|
@ -480,10 +481,10 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
});
|
});
|
||||||
|
|
||||||
// rename movies
|
// rename movies
|
||||||
return renameAll(formatMatches(matches, format, outputDir), renameAction, conflictAction, matches);
|
return renameAll(formatMatches(matches, format, outputDir), renameAction, conflictAction, matches, exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<File> renameMusic(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFileFormat format, MusicIdentificationService... services) throws Exception {
|
public List<File> renameMusic(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFileFormat format, List<MusicIdentificationService> services, ExecCommand exec) throws Exception {
|
||||||
List<File> audioFiles = sortByUniquePath(filter(files, AUDIO_FILES, VIDEO_FILES));
|
List<File> audioFiles = sortByUniquePath(filter(files, AUDIO_FILES, VIDEO_FILES));
|
||||||
|
|
||||||
// check audio files against all services if necessary
|
// check audio files against all services if necessary
|
||||||
|
@ -491,24 +492,26 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
LinkedHashSet<File> remaining = new LinkedHashSet<File>(audioFiles);
|
LinkedHashSet<File> remaining = new LinkedHashSet<File>(audioFiles);
|
||||||
|
|
||||||
// check audio files against all services
|
// check audio files against all services
|
||||||
for (int i = 0; i < services.length && remaining.size() > 0; i++) {
|
for (MusicIdentificationService service : services) {
|
||||||
log.config(format("Rename music using %s", services[i].getIdentifier()));
|
if (remaining.size() > 0) {
|
||||||
services[i].lookup(remaining).forEach((file, music) -> {
|
log.config(format("Rename music using %s", service.getIdentifier()));
|
||||||
if (music != null) {
|
service.lookup(remaining).forEach((file, music) -> {
|
||||||
matches.add(new Match<File, AudioTrack>(file, music.clone()));
|
if (music != null) {
|
||||||
remaining.remove(file);
|
matches.add(new Match<File, AudioTrack>(file, music.clone()));
|
||||||
}
|
remaining.remove(file);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// error logging
|
// error logging
|
||||||
remaining.forEach(f -> log.warning(format("Failed to process music file: %s", f)));
|
remaining.forEach(f -> log.warning(format("Failed to process music file: %s", f)));
|
||||||
|
|
||||||
// rename movies
|
// rename movies
|
||||||
return renameAll(formatMatches(matches, format, outputDir), renameAction, conflictAction, null);
|
return renameAll(formatMatches(matches, format, outputDir), renameAction, conflictAction, null, exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<File> renameFiles(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFileFormat format, XattrMetaInfoProvider service, ExpressionFilter filter, boolean strict) throws Exception {
|
public List<File> renameFiles(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFileFormat format, XattrMetaInfoProvider service, ExpressionFilter filter, boolean strict, ExecCommand exec) throws Exception {
|
||||||
log.config(format("Rename files using [%s]", service.getName()));
|
log.config(format("Rename files using [%s]", service.getName()));
|
||||||
|
|
||||||
Map<File, File> renameMap = new LinkedHashMap<File, File>();
|
Map<File, File> renameMap = new LinkedHashMap<File, File>();
|
||||||
|
@ -525,7 +528,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return renameAll(renameMap, renameAction, conflictAction, null);
|
return renameAll(renameMap, renameAction, conflictAction, null, exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<File, Object> getContext(List<Match<File, ?>> matches) {
|
private Map<File, Object> getContext(List<Match<File, ?>> matches) {
|
||||||
|
@ -533,7 +536,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Entry<File, Object>> entrySet() {
|
public Set<Entry<File, Object>> entrySet() {
|
||||||
return matches.stream().collect(toMap(it -> it.getValue(), it -> (Object) it.getCandidate())).entrySet();
|
return matches.stream().collect(toMap(it -> it.getValue(), it -> (Object) it.getCandidate(), (a, b) -> a, LinkedHashMap::new)).entrySet();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -570,7 +573,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
return renameMap;
|
return renameMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<File> renameAll(Map<File, File> renameMap, RenameAction renameAction, ConflictAction conflictAction, List<Match<File, ?>> matches) throws Exception {
|
protected List<File> renameAll(Map<File, File> renameMap, RenameAction renameAction, ConflictAction conflictAction, List<Match<File, ?>> matches, ExecCommand exec) throws Exception {
|
||||||
if (renameMap.isEmpty()) {
|
if (renameMap.isEmpty()) {
|
||||||
throw new CmdlineException("Failed to identify or process any files");
|
throw new CmdlineException("Failed to identify or process any files");
|
||||||
}
|
}
|
||||||
|
@ -596,7 +599,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not allow abuse of online databases by repeatedly processing the same files
|
// do not allow abuse of online databases by repeatedly processing the same files
|
||||||
if (matches != null && equalsFileContent(source, destination)) {
|
if (matches != null && renameAction.canRevert() && source.length() > 0 && equalsFileContent(source, destination)) {
|
||||||
throw new CmdlineException(String.format("Failed to process [%s] because [%s] is an exact copy and already exists", source, destination));
|
throw new CmdlineException(String.format("Failed to process [%s] because [%s] is an exact copy and already exists", source, destination));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,33 +639,39 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// update history and xattr metadata
|
// update history and xattr metadata
|
||||||
writeHistory(renameAction, renameLog, matches);
|
if (renameLog.size() > 0) {
|
||||||
|
writeHistory(renameAction, renameLog, matches);
|
||||||
|
}
|
||||||
|
|
||||||
// print number of processed files
|
// print number of processed files
|
||||||
log.fine(format("Processed %d files", renameLog.size()));
|
log.fine(format("Processed %d files", renameLog.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// new file names
|
// execute command
|
||||||
|
if (exec != null) {
|
||||||
|
Map<File, Object> context = renameLog.values().stream().filter(Objects::nonNull).collect(toMap(f -> f, f -> xattr.getMetaInfo(f), (a, b) -> a, LinkedHashMap::new));
|
||||||
|
if (context.size() > 0) {
|
||||||
|
exec.execute(context.entrySet().stream().map(m -> new MediaBindingBean(m.getValue(), m.getKey(), context)).toArray(MediaBindingBean[]::new));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// destination files may include null values
|
||||||
return new ArrayList<File>(renameLog.values());
|
return new ArrayList<File>(renameLog.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeHistory(RenameAction action, Map<File, File> log, List<Match<File, ?>> matches) {
|
protected void writeHistory(RenameAction action, Map<File, File> log, List<Match<File, ?>> matches) {
|
||||||
if (log.isEmpty() || !action.canRevert()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write rename history
|
// write rename history
|
||||||
HistorySpooler.getInstance().append(log.entrySet());
|
if (action.canRevert()) {
|
||||||
|
HistorySpooler.getInstance().append(log.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
// write xattr metadata
|
// write xattr metadata
|
||||||
if (matches != null) {
|
if (matches != null) {
|
||||||
for (Match<File, ?> match : matches) {
|
for (Match<File, ?> match : matches) {
|
||||||
File source = match.getValue();
|
if (match.getCandidate() != null) {
|
||||||
Object infoObject = match.getCandidate();
|
File destination = log.get(match.getValue());
|
||||||
if (infoObject != null) {
|
|
||||||
File destination = log.get(source);
|
|
||||||
if (destination != null && destination.isFile()) {
|
if (destination != null && destination.isFile()) {
|
||||||
xattr.setMetaInfo(destination, infoObject, source.getName());
|
xattr.setMetaInfo(destination, match.getCandidate(), match.getValue().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,10 +69,10 @@ public class CmdlineOperationsTextUI extends CmdlineOperations {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<File> renameAll(Map<File, File> renameMap, RenameAction renameAction, ConflictAction conflictAction, List<Match<File, ?>> matches) throws Exception {
|
public List<File> renameAll(Map<File, File> renameMap, RenameAction renameAction, ConflictAction conflictAction, List<Match<File, ?>> matches, ExecCommand exec) throws Exception {
|
||||||
// default behavior if rename map is empty
|
// default behavior if rename map is empty
|
||||||
if (renameMap.isEmpty()) {
|
if (renameMap.isEmpty()) {
|
||||||
return super.renameAll(renameMap, renameAction, conflictAction, matches);
|
return super.renameAll(renameMap, renameAction, conflictAction, matches, exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
// manually confirm each file mapping
|
// manually confirm each file mapping
|
||||||
|
@ -91,7 +91,7 @@ public class CmdlineOperationsTextUI extends CmdlineOperations {
|
||||||
return emptyList();
|
return emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.renameAll(selection.stream().collect(toMap(Entry::getKey, Entry::getValue, (a, b) -> a, LinkedHashMap::new)), renameAction, conflictAction, matches);
|
return super.renameAll(selection.stream().collect(toMap(Entry::getKey, Entry::getValue, (a, b) -> a, LinkedHashMap::new)), renameAction, conflictAction, matches, exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
package net.filebot.cli;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.*;
|
||||||
|
import static net.filebot.Logging.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
|
import net.filebot.format.ExpressionFormat;
|
||||||
|
import net.filebot.format.MediaBindingBean;
|
||||||
|
|
||||||
|
public class ExecCommand {
|
||||||
|
|
||||||
|
private List<ExpressionFormat> template;
|
||||||
|
private boolean parallel;
|
||||||
|
|
||||||
|
private File directory;
|
||||||
|
|
||||||
|
public ExecCommand(List<ExpressionFormat> template, boolean parallel, File directory) {
|
||||||
|
this.template = template;
|
||||||
|
this.parallel = parallel;
|
||||||
|
this.directory = directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(MediaBindingBean... group) throws IOException, InterruptedException {
|
||||||
|
if (parallel) {
|
||||||
|
executeParallel(group);
|
||||||
|
} else {
|
||||||
|
executeSequence(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeSequence(MediaBindingBean... group) throws IOException, InterruptedException {
|
||||||
|
// collect unique commands
|
||||||
|
List<List<String>> commands = Stream.of(group).map(v -> {
|
||||||
|
return template.stream().map(t -> getArgumentValue(t, v)).filter(Objects::nonNull).collect(toList());
|
||||||
|
}).distinct().collect(toList());
|
||||||
|
|
||||||
|
// execute unique commands
|
||||||
|
for (List<String> command : commands) {
|
||||||
|
execute(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeParallel(MediaBindingBean... group) throws IOException, InterruptedException {
|
||||||
|
// collect single command
|
||||||
|
List<String> command = template.stream().flatMap(t -> {
|
||||||
|
return Stream.of(group).map(v -> getArgumentValue(t, v)).filter(Objects::nonNull).distinct();
|
||||||
|
}).collect(toList());
|
||||||
|
|
||||||
|
// execute single command
|
||||||
|
execute(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getArgumentValue(ExpressionFormat template, MediaBindingBean variables) {
|
||||||
|
try {
|
||||||
|
return template.format(variables);
|
||||||
|
} catch (Exception e) {
|
||||||
|
debug.warning(cause(template.getExpression(), e));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void execute(List<String> command) throws IOException, InterruptedException {
|
||||||
|
ProcessBuilder process = new ProcessBuilder(command);
|
||||||
|
process.directory(directory);
|
||||||
|
process.inheritIO();
|
||||||
|
|
||||||
|
debug.finest(message("Execute", command));
|
||||||
|
|
||||||
|
int exitCode = process.start().waitFor();
|
||||||
|
if (exitCode != 0) {
|
||||||
|
throw new IOException(String.format("%s failed with exit code %d", command, exitCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExecCommand parse(List<String> args, File directory) throws ScriptException {
|
||||||
|
// execute one command per file or one command with many file arguments
|
||||||
|
boolean parallel = args.lastIndexOf("+") == args.size() - 1;
|
||||||
|
|
||||||
|
if (parallel) {
|
||||||
|
args = args.subList(0, args.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ExpressionFormat> template = new ArrayList<ExpressionFormat>();
|
||||||
|
for (String argument : args) {
|
||||||
|
template.add(new ExpressionFormat(argument));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExecCommand(template, parallel, directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -339,7 +339,7 @@ public abstract class ScriptShellBaseClass extends Script {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (files.size() > 0) {
|
if (files.size() > 0) {
|
||||||
return getCLI().rename(files, action, args.getConflictAction(), args.getAbsoluteOutputFolder(), args.getExpressionFileFormat(), args.getDatasource(), args.getSearchQuery(), args.getSortOrder(), args.getExpressionFilter(), args.getLanguage().getLocale(), args.isStrict());
|
return getCLI().rename(files, action, args.getConflictAction(), args.getAbsoluteOutputFolder(), args.getExpressionFileFormat(), args.getDatasource(), args.getSearchQuery(), args.getSortOrder(), args.getExpressionFilter(), args.getLanguage().getLocale(), args.isStrict(), args.getExecCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (map.size() > 0) {
|
if (map.size() > 0) {
|
||||||
|
|
Loading…
Reference in New Issue