Merge pull request #40 from RatzzFatzz/dev

Coherent Feature
This commit is contained in:
Michael
2023-03-12 12:46:02 +01:00
committed by GitHub
23 changed files with 555 additions and 193 deletions

18
pom.xml
View File

@@ -136,22 +136,6 @@
<artifactId>log4j-slf4j-impl</artifactId> <artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.1</version> <version>2.17.1</version>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.28</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.28</version>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId> <groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId> <artifactId>jackson-dataformat-yaml</artifactId>
@@ -175,7 +159,7 @@
<dependency> <dependency>
<groupId>me.tongfei</groupId> <groupId>me.tongfei</groupId>
<artifactId>progressbar</artifactId> <artifactId>progressbar</artifactId>
<version>0.9.3</version> <version>0.9.5</version>
</dependency> </dependency>
<!-- endregion --> <!-- endregion -->
<!-- region unit-tests --> <!-- region unit-tests -->

View File

@@ -1,92 +0,0 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileCollector;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileProcessor;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import me.tongfei.progressbar.ProgressBar;
import me.tongfei.progressbar.ProgressBarBuilder;
import me.tongfei.progressbar.ProgressBarStyle;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Log4j2
public class AttributeUpdaterKernel {
private final ExecutorService executor = Executors.newFixedThreadPool(Config.getInstance().getThreads());
private final FileCollector collector;
private final FileProcessor processor;
private final ResultStatistic statistic = new ResultStatistic();
public AttributeUpdaterKernel(FileCollector collector, FileProcessor processor) {
this.collector = collector;
this.processor = processor;
}
@SneakyThrows
public void execute() {
statistic.startTimer();
try (ProgressBar progressBar = pbBuilder().build()) {
List<File> excludedFiles = Config.getInstance().getExcludedDirectories().stream()
.map(collector::loadFiles)
.flatMap(Collection::stream)
.collect(Collectors.toList());
List<File> files = collector.loadFiles(Config.getInstance().getLibraryPath().getAbsolutePath()).stream()
.filter(file -> !excludedFiles.contains(file))
.collect(Collectors.toList());
progressBar.maxHint(files.size());
files.forEach(file -> executor.submit(() -> process(file, progressBar)));
executor.shutdown();
executor.awaitTermination(1, TimeUnit.DAYS);
}
statistic.stopTimer();
System.out.println(statistic);
log.info(statistic);
}
private void process(File file, ProgressBar progressBar) {
List<FileAttribute> attributes = processor.loadAttributes(file);
FileInfoDto fileInfo = processor.filterAttributes(attributes);
statistic.total();
if (fileInfo.isChangeNecessary()) {
statistic.shouldChange();
if (!Config.getInstance().isSafeMode()) {
try {
processor.update(file, fileInfo);
statistic.success();
log.info("Updated {}", file.getAbsolutePath());
} catch (IOException | RuntimeException e) {
statistic.failedChanging();
log.warn("File couldn't be updated: '{}', Error: {}", file.getAbsoluteFile(), e.getMessage().replaceAll("\\r|\\n", " "));
}
}
} else if (fileInfo.isUnableToApplyConfig()) {
statistic.noSuitableConfigFound();
} else if (fileInfo.isAlreadySuitable()){
statistic.alreadyFits();
} else {
statistic.failure();
}
progressBar.step();
}
private static ProgressBarBuilder pbBuilder() {
return new ProgressBarBuilder()
.setStyle(ProgressBarStyle.ASCII)
.setUpdateIntervalMillis(250)
.setMaxRenderedLength(75);
}
}

View File

@@ -2,15 +2,20 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config; import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ConfigLoader; import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ConfigLoader;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel.AttributeUpdaterKernel;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel.CoherentAttributeUpdaterKernel;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel.DefaultAttributeUpdaterKernel;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.MkvFileCollector; import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.MkvFileCollector;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.MkvFileProcessor; import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.MkvFileProcessor;
import lombok.extern.log4j.Log4j2; import lombok.extern.slf4j.Slf4j;
@Log4j2 @Slf4j
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {
ConfigLoader.initConfig(args); ConfigLoader.initConfig(args);
AttributeUpdaterKernel kernel = new AttributeUpdaterKernel(new MkvFileCollector(), new MkvFileProcessor()); AttributeUpdaterKernel kernel = Config.getInstance().getCoherent() != null
? new CoherentAttributeUpdaterKernel(new MkvFileCollector(), new MkvFileProcessor())
: new DefaultAttributeUpdaterKernel(new MkvFileCollector(), new MkvFileProcessor());
kernel.execute(); kernel.execute();
} }
} }

View File

@@ -6,7 +6,7 @@ import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import lombok.extern.log4j.Log4j2; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.HelpFormatter;
@@ -15,7 +15,7 @@ import java.io.File;
import java.util.*; import java.util.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@Log4j2 @Slf4j
@Getter @Getter
@Setter @Setter
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
@@ -36,7 +36,10 @@ public class Config {
private Pattern includePattern; private Pattern includePattern;
private boolean safeMode; private boolean safeMode;
private Set<String> forcedKeywords = new HashSet<>(Arrays.asList("forced", "signs")); private Integer coherent;
private boolean forceCoherent;
private Set<String> forcedKeywords = new HashSet<>(Arrays.asList("forced", "signs", "songs"));
private Set<String> commentaryKeywords = new HashSet<>(Arrays.asList("commentary", "director")); private Set<String> commentaryKeywords = new HashSet<>(Arrays.asList("commentary", "director"));
private Set<String> excludedDirectories = new HashSet<>(); private Set<String> excludedDirectories = new HashSet<>();

View File

@@ -27,8 +27,10 @@ public class ConfigLoader {
new PatternValidator(INCLUDE_PATTERN, false, Pattern.compile(".*")), new PatternValidator(INCLUDE_PATTERN, false, Pattern.compile(".*")),
new SetValidator(FORCED_KEYWORDS, false, true), new SetValidator(FORCED_KEYWORDS, false, true),
new SetValidator(COMMENTARY_KEYWORDS, false, true), new SetValidator(COMMENTARY_KEYWORDS, false, true),
new SetValidator(EXCLUDE_DIRECTORY, false, true), new SetValidator(EXCLUDED_DIRECTORY, false, true),
new AttributeConfigValidator() new AttributeConfigValidator(),
new CoherentConfigValidator(COHERENT, false),
new BooleanValidator(FORCE_COHERENT, false)
); );
public static void initConfig(String[] args) { public static void initConfig(String[] args) {
@@ -55,7 +57,7 @@ public class ConfigLoader {
} }
} }
if (results.contains(ValidationResult.INVALID)) System.exit(1); if (results.contains(ValidationResult.INVALID) || results.contains(ValidationResult.MISSING)) System.exit(1);
System.out.println(); System.out.println();
} }

View File

@@ -0,0 +1,22 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
import org.apache.commons.lang3.math.NumberUtils;
public class CoherentConfigValidator extends ConfigValidator<Integer> {
private static final Integer DISABLED = -1;
public CoherentConfigValidator(ConfigProperty property, boolean required) {
super(property, required, null);
}
@Override
Integer parse(String value) {
return NumberUtils.isParsable(value) ? Integer.parseInt(value) : DISABLED;
}
@Override
boolean isValid(Integer result) {
return result >= 0;
}
}

View File

@@ -121,27 +121,27 @@ public abstract class ConfigValidator<FieldType> {
* @return false if method invocation failed * @return false if method invocation failed
*/ */
protected boolean setValue(FieldType result) { protected boolean setValue(FieldType result) {
List<Method> methods = Arrays.stream(Config.getInstance().getClass().getDeclaredMethods()) for (Method method: Config.getInstance().getClass().getDeclaredMethods()) {
.filter(containsSetterOf(property)) if(containsSetterOf(property).test(method)) {
.collect(Collectors.toList());
if (methods.size() != 1) {
return false;
}
try { try {
methods.get(0).invoke(Config.getInstance(), result); method.invoke(Config.getInstance(), result);
return true;
} catch (IllegalAccessException | InvocationTargetException e) { } catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return true; }
}
return false;
} }
protected Predicate<Method> containsSetterOf(ConfigProperty property) { protected Predicate<Method> containsSetterOf(ConfigProperty property) {
return method -> StringUtils.containsIgnoreCase(method.getName(), "set") return method -> StringUtils.startsWith(method.getName(), "set")
&& StringUtils.containsIgnoreCase(method.getName(), property.prop().replace("-", "")); && StringUtils.equalsIgnoreCase(method.getName().replace("set", ""), property.prop().replace("-", ""));
} }
protected Predicate<Method> containsGetterOf(ConfigProperty property) { protected Predicate<Method> containsGetterOf(ConfigProperty property) {
return method -> StringUtils.containsIgnoreCase(method.getName(), "get") return method -> StringUtils.startsWith(method.getName(), "get")
&& StringUtils.containsIgnoreCase(method.getName(), property.prop().replace("-", "")); && StringUtils.containsIgnoreCase(method.getName(), property.prop().replace("-", ""));
} }

View File

@@ -0,0 +1,13 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions;
public class MkvToolNixException extends RuntimeException{
public MkvToolNixException(String message) {
super(message);
}
@Override
public String getMessage() {
return super.getMessage().replaceAll("\\r|\\n", " ");
}
}

View File

@@ -10,4 +10,13 @@ public interface FileCollector {
* @return list of all files within the directory * @return list of all files within the directory
*/ */
List<File> loadFiles(String path); List<File> loadFiles(String path);
/**
* Load all directories from path, but only until depth is reached.
*
* @param path leads to a directory which will be loaded recursively until depth
* @param depth limit directory crawling
* @return list of directory until depth
*/
List<File> loadDirectories(String path, int depth);
} }

View File

@@ -1,13 +1,19 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl; package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config; import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.io.File; import java.io.File;
public class FileFilter { public class FileFilter {
static boolean accept(File pathName, String[] fileExtensions) { static boolean accept(File pathName, String[] fileExtensions) {
return StringUtils.endsWithAny(pathName.getAbsolutePath().toLowerCase(), fileExtensions) if (StringUtils.endsWithAny(pathName.getAbsolutePath().toLowerCase(), fileExtensions)
&& Config.getInstance().getIncludePattern().matcher(pathName.getName()).matches(); && Config.getInstance().getIncludePattern().matcher(pathName.getName()).matches()) {
return true;
}
ResultStatistic.getInstance().excluded();
return false;
} }
} }

View File

@@ -1,5 +1,7 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl; package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
@@ -10,12 +12,41 @@ import java.util.List;
public interface FileProcessor { public interface FileProcessor {
/** /**
* Load track information from file.
*
* @param file Takes the file from which the attributes will be returned * @param file Takes the file from which the attributes will be returned
* @return list of all important attributes * @return list of all important attributes
*/ */
List<FileAttribute> loadAttributes(File file); List<FileAttribute> loadAttributes(File file);
FileInfoDto filterAttributes(List<FileAttribute> attributes); /**
* Populate FileInfoDto with the currently set default tracks.
* @param info to be populated
* @param attributes Track information of FileInfoDto
* @param nonForcedTracks List of all not forced tracks
*/
void detectDefaultTracks(FileInfoDto info, List<FileAttribute> attributes, List<FileAttribute> nonForcedTracks);
void update(File file, FileInfoDto fileInfo) throws IOException; /**
* Populate FileInfoDto with the desired tracks, based on AttributeConfig.
* @param info to be populated
* @param nonForcedTracks List of all not forced tracks
* @param nonCommentaryTracks List of all not commentary tracks
* @param configs
*/
void detectDesiredTracks(FileInfoDto info, List<FileAttribute> nonForcedTracks, List<FileAttribute> nonCommentaryTracks,
AttributeConfig... configs);
List<FileAttribute> retrieveNonForcedTracks(List<FileAttribute> attributes);
List<FileAttribute> retrieveNonCommentaryTracks(List<FileAttribute> attributes);
/**
* Update the file.
* @param file to be updated
* @param fileInfo information to update file
* @throws IOException
* @throws MkvToolNixException when error occurs while sending query to mkvpropedit
*/
void update(File file, FileInfoDto fileInfo) throws IOException, MkvToolNixException;
} }

View File

@@ -1,6 +1,6 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl; package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
import lombok.extern.log4j.Log4j2; import lombok.extern.slf4j.Slf4j;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -12,10 +12,13 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@Log4j2 @Slf4j
public class MkvFileCollector implements FileCollector { public class MkvFileCollector implements FileCollector {
private static final String[] fileExtensions = new String[]{".mkv", ".mka", ".mks", ".mk3d"}; private static final String[] fileExtensions = new String[]{".mkv", ".mka", ".mks", ".mk3d"};
/**
* {@inheritDoc}
*/
@Override @Override
public List<File> loadFiles(String path) { public List<File> loadFiles(String path) {
try (Stream<Path> paths = Files.walk(Paths.get(path))) { try (Stream<Path> paths = Files.walk(Paths.get(path))) {
@@ -28,4 +31,19 @@ public class MkvFileCollector implements FileCollector {
return new ArrayList<>(); return new ArrayList<>();
} }
} }
/**
* {@inheritDoc}
*/
@Override
public List<File> loadDirectories(String path, int depth) {
try (Stream<Path> paths = Files.walk(Paths.get(path), depth)) {
return paths.map(Path::toFile)
.filter(File::isDirectory)
.collect(Collectors.toList());
} catch (IOException e) {
log.error("Couldn't find file or directory!", e);
return new ArrayList<>();
}
}
} }

View File

@@ -1,10 +1,11 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl; package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config; import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.*; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.*;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.SetUtils; import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.SetUtils;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.core.util.IOUtils; import org.apache.logging.log4j.core.util.IOUtils;
@@ -19,14 +20,13 @@ import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.LaneType.AUDIO;
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.LaneType.SUBTITLES; import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.LaneType.SUBTITLES;
import static java.lang.String.format; import static java.lang.String.format;
@Log4j2 @Slf4j
public class MkvFileProcessor implements FileProcessor { public class MkvFileProcessor implements FileProcessor {
private final ObjectMapper mapper = new ObjectMapper(); private final ObjectMapper mapper = new ObjectMapper();
private static final String DISABLE_DEFAULT_TRACK = "--edit track:%s --set flag-default=0 "; private static final String DISABLE_DEFAULT_TRACK = "--edit track:%s --set flag-default=0 ";
private static final String ENABLE_DEFAULT_TRACK = "--edit track:%s --set flag-default=1 "; private static final String ENABLE_DEFAULT_TRACK = "--edit track:%s --set flag-default=1 ";
private static final String ENABLE_FORCED_TRACK = "--edit track:%s --set flag-forced=1 "; private static final String ENABLE_FORCED_TRACK = "--edit track:%s --set flag-forced=1 ";
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public List<FileAttribute> loadAttributes(File file) { public List<FileAttribute> loadAttributes(File file) {
@@ -62,7 +62,7 @@ public class MkvFileProcessor implements FileProcessor {
} }
} }
log.debug(fileAttributes); log.debug(fileAttributes.toString());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
log.error("File could not be found or loaded!"); log.error("File could not be found or loaded!");
@@ -70,27 +70,11 @@ public class MkvFileProcessor implements FileProcessor {
return fileAttributes; return fileAttributes;
} }
/**
* {@inheritDoc}
*/
@Override @Override
public FileInfoDto filterAttributes(List<FileAttribute> attributes) { public void detectDefaultTracks(FileInfoDto info, List<FileAttribute> attributes, List<FileAttribute> nonForcedTracks) {
FileInfoDto info = new FileInfoDto();
List<FileAttribute> nonForcedTracks = attributes.stream()
.filter(elem -> !StringUtils.containsAnyIgnoreCase(elem.getTrackName(),
Config.getInstance().getForcedKeywords().toArray(new CharSequence[0])))
.filter(elem -> !elem.isForcedTrack())
.collect(Collectors.toList());
List<FileAttribute> nonCommentaryTracks = attributes.stream()
.filter(elem -> !StringUtils.containsAnyIgnoreCase(elem.getTrackName(),
Config.getInstance().getCommentaryKeywords().toArray(new CharSequence[0])))
.collect(Collectors.toList());
detectDefaultTracks(info, attributes, nonForcedTracks);
detectDesiredTracks(info, nonForcedTracks, nonCommentaryTracks);
log.debug(info);
return info;
}
protected void detectDefaultTracks(FileInfoDto info, List<FileAttribute> attributes, List<FileAttribute> nonForcedTracks) {
Set<FileAttribute> detectedForcedSubtitleLanes = new HashSet<>(); Set<FileAttribute> detectedForcedSubtitleLanes = new HashSet<>();
for (FileAttribute attribute : attributes) { for (FileAttribute attribute : attributes) {
if (attribute.isDefaultTrack() && AUDIO.equals(attribute.getType())) if (attribute.isDefaultTrack() && AUDIO.equals(attribute.getType()))
@@ -108,8 +92,13 @@ public class MkvFileProcessor implements FileProcessor {
); );
} }
protected void detectDesiredTracks(FileInfoDto info, List<FileAttribute> nonForcedTracks, List<FileAttribute> nonCommentaryTracks) { /**
for (AttributeConfig config : Config.getInstance().getAttributeConfig()) { * {@inheritDoc}
*/
@Override
public void detectDesiredTracks(FileInfoDto info, List<FileAttribute> nonForcedTracks, List<FileAttribute> nonCommentaryTracks,
AttributeConfig... configs) {
for (AttributeConfig config : configs) {
FileAttribute desiredAudio = null; FileAttribute desiredAudio = null;
FileAttribute desiredSubtitle = null; FileAttribute desiredSubtitle = null;
for (FileAttribute attribute : SetUtils.retainOf(nonForcedTracks, nonCommentaryTracks)) { for (FileAttribute attribute : SetUtils.retainOf(nonForcedTracks, nonCommentaryTracks)) {
@@ -127,10 +116,31 @@ public class MkvFileProcessor implements FileProcessor {
} }
@Override @Override
public void update(File file, FileInfoDto fileInfo) throws IOException, RuntimeException { public List<FileAttribute> retrieveNonForcedTracks(List<FileAttribute> attributes) {
return attributes.stream()
.filter(elem -> !StringUtils.containsAnyIgnoreCase(elem.getTrackName(),
Config.getInstance().getForcedKeywords().toArray(new CharSequence[0])))
.filter(elem -> !elem.isForcedTrack())
.collect(Collectors.toList());
}
@Override
public List<FileAttribute> retrieveNonCommentaryTracks(List<FileAttribute> attributes) {
return attributes.stream()
.filter(elem -> !StringUtils.containsAnyIgnoreCase(elem.getTrackName(),
Config.getInstance().getCommentaryKeywords().toArray(new CharSequence[0])))
.collect(Collectors.toList());
}
/**
* {@inheritDoc}
*/
@Override
public void update(File file, FileInfoDto fileInfo) throws IOException, MkvToolNixException {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(format("\"%s\" ", Config.getInstance().getPathFor(MkvToolNix.MKV_PROP_EDIT))); sb.append(format("\"%s\" ", Config.getInstance().getPathFor(MkvToolNix.MKV_PROP_EDIT)));
sb.append(format("\"%s\" ", file.getAbsolutePath())); sb.append(format("\"%s\" ", file.getAbsolutePath()));
if (fileInfo.isAudioDifferent()) { if (fileInfo.isAudioDifferent()) {
if (fileInfo.getDefaultAudioLanes() != null && !fileInfo.getDefaultSubtitleLanes().isEmpty()) { if (fileInfo.getDefaultAudioLanes() != null && !fileInfo.getDefaultSubtitleLanes().isEmpty()) {
for (FileAttribute track: fileInfo.getDefaultAudioLanes()) { for (FileAttribute track: fileInfo.getDefaultAudioLanes()) {
@@ -139,6 +149,7 @@ public class MkvFileProcessor implements FileProcessor {
} }
sb.append(format(ENABLE_DEFAULT_TRACK, fileInfo.getDesiredAudioLane().getId())); sb.append(format(ENABLE_DEFAULT_TRACK, fileInfo.getDesiredAudioLane().getId()));
} }
if (fileInfo.isSubtitleDifferent()) { if (fileInfo.isSubtitleDifferent()) {
if (fileInfo.getDefaultSubtitleLanes() != null && !fileInfo.getDefaultSubtitleLanes().isEmpty()) { if (fileInfo.getDefaultSubtitleLanes() != null && !fileInfo.getDefaultSubtitleLanes().isEmpty()) {
for (FileAttribute track: fileInfo.getDefaultSubtitleLanes()) { for (FileAttribute track: fileInfo.getDefaultSubtitleLanes()) {
@@ -147,6 +158,7 @@ public class MkvFileProcessor implements FileProcessor {
} }
sb.append(format(ENABLE_DEFAULT_TRACK, fileInfo.getDesiredSubtitleLane().getId())); sb.append(format(ENABLE_DEFAULT_TRACK, fileInfo.getDesiredSubtitleLane().getId()));
} }
if (fileInfo.areForcedTracksDifferent()) { if (fileInfo.areForcedTracksDifferent()) {
for (FileAttribute attribute : fileInfo.getDesiredForcedSubtitleLanes()) { for (FileAttribute attribute : fileInfo.getDesiredForcedSubtitleLanes()) {
sb.append(format(ENABLE_FORCED_TRACK, attribute.getId())); sb.append(format(ENABLE_FORCED_TRACK, attribute.getId()));
@@ -156,6 +168,6 @@ public class MkvFileProcessor implements FileProcessor {
InputStream inputstream = Runtime.getRuntime().exec(sb.toString()).getInputStream(); InputStream inputstream = Runtime.getRuntime().exec(sb.toString()).getInputStream();
String output = IOUtils.toString(new InputStreamReader(inputstream)); String output = IOUtils.toString(new InputStreamReader(inputstream));
log.debug(output); log.debug(output);
if (output.contains("Error")) throw new RuntimeException(output); if (output.contains("Error")) throw new MkvToolNixException(output);
} }
} }

View File

@@ -0,0 +1,140 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileCollector;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileProcessor;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.tongfei.progressbar.ProgressBar;
import me.tongfei.progressbar.ProgressBarBuilder;
import me.tongfei.progressbar.ProgressBarStyle;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@RequiredArgsConstructor
public abstract class AttributeUpdaterKernel {
protected final FileCollector collector;
protected final FileProcessor processor;
protected final ResultStatistic statistic = ResultStatistic.getInstance();
private final ExecutorService executor = Executors.newFixedThreadPool(Config.getInstance().getThreads());
protected ProgressBarBuilder pbBuilder() {
return new ProgressBarBuilder()
.setStyle(ProgressBarStyle.ASCII)
.setUpdateIntervalMillis(250)
.setMaxRenderedLength(75);
}
@SneakyThrows
public void execute() {
statistic.startTimer();
try (ProgressBar progressBar = pbBuilder().build()) {
List<File> files = loadFiles(Config.getInstance().getLibraryPath().getAbsolutePath());
progressBar.maxHint(files.size());
files.forEach(file -> executor.submit(() -> {
process(file);
progressBar.step();
}));
executor.shutdown();
executor.awaitTermination(1, TimeUnit.DAYS);
}
statistic.stopTimer();
statistic.printResult();
}
protected List<File> loadExcludedFiles() {
List<File> excludedFiles = Config.getInstance().getExcludedDirectories().stream()
.map(collector::loadFiles)
.flatMap(Collection::stream)
.collect(Collectors.toList());
statistic.increaseTotalBy(excludedFiles.size());
statistic.increaseExcludedBy(excludedFiles.size());
return excludedFiles;
}
/**
* Load files or directories to update.
* Remove excluded directories.
*
* @param path Path to library
* @return List of files to update.
*/
abstract List<File> loadFiles(String path);
/**
* Start of the file updating process.
* This method is called by the executor and its contents are executed in parallel.
*
* @param file file or directory to update
*/
void process(File file) {
FileInfoDto fileInfo = new FileInfoDto(file);
List<FileAttribute> attributes = processor.loadAttributes(file);
List<FileAttribute> nonForcedTracks = processor.retrieveNonForcedTracks(attributes);
List<FileAttribute> nonCommentaryTracks = processor.retrieveNonCommentaryTracks(attributes);
processor.detectDefaultTracks(fileInfo, attributes, nonForcedTracks);
processor.detectDesiredTracks(fileInfo, nonForcedTracks, nonCommentaryTracks);
updateFile(fileInfo);
}
/**
* Persist file changes.
*
* @param fileInfoDto contains information about file and desired configuration.
*/
protected void updateFile(FileInfoDto fileInfoDto) {
switch (fileInfoDto.getStatus()) {
case CHANGE_NECESSARY:
statistic.shouldChange();
commitChange(fileInfoDto);
break;
case UNABLE_TO_APPLY:
statistic.noSuitableConfigFound();
break;
case ALREADY_SUITED:
statistic.alreadyFits();
break;
case UNKNOWN:
default:
statistic.failure();
break;
}
}
private void commitChange(FileInfoDto fileInfo) {
if (Config.getInstance().isSafeMode()) {
return;
}
try {
statistic.total();
processor.update(fileInfo.getFile(), fileInfo);
statistic.success();
log.info("Updated {}", fileInfo.getFile().getAbsolutePath());
} catch (IOException | MkvToolNixException e) {
statistic.failedChanging();
log.warn("File couldn't be updated: '{}', Error: {}", fileInfo.getFile().getAbsoluteFile(), e.getMessage());
}
}
}

View File

@@ -0,0 +1,111 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileCollector;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileProcessor;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
import lombok.extern.slf4j.Slf4j;
import me.tongfei.progressbar.ProgressBarBuilder;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
public class CoherentAttributeUpdaterKernel extends AttributeUpdaterKernel {
public CoherentAttributeUpdaterKernel(FileCollector collector, FileProcessor processor) {
super(collector, processor);
}
@Override
protected ProgressBarBuilder pbBuilder() {
return super.pbBuilder()
.setUnit(" directories", 1);
}
/**
* {@inheritDoc}
*/
@Override
List<File> loadFiles(String path) {
List<File> excludedFiles = loadExcludedFiles();
List<File> directories = collector.loadDirectories(path, Config.getInstance().getCoherent())
.stream().filter(file -> !excludedFiles.contains(file))
.collect(Collectors.toList());
return directories.stream()
.filter(dir -> isParentDirectory(dir, directories))
.collect(Collectors.toList());
}
private boolean isParentDirectory(File directory, List<File> directories) {
String path = directory.getAbsolutePath();
return directories.stream()
.noneMatch(dir -> dir.getAbsolutePath().contains(path) && !StringUtils.equals(path, dir.getAbsolutePath()));
}
/**
* Update files in directory, if possible, with the same {@link AttributeConfig}.
* If {@link Config#isForceCoherent()} then there will be no changes to the file if they don't match the same config.
* Otherwise, the default behaviour is executed.
* This method is called by the executor and is run in parallel.
*
* @param file directory containing files
*/
@Override
void process(File file) {
// TODO: Implement level crawl if coherence is not possible on user entered depth
// IMPL idea: recursive method call, cache needs to be implemented
List<FileInfoDto> fileInfos = collector.loadFiles(file.getAbsolutePath())
.stream().map(FileInfoDto::new)
.collect(Collectors.toList());
Map<FileInfoDto, List<FileAttribute>> fileAttributeCache = new HashMap<>();
for (FileInfoDto fileInfo : fileInfos) {
if (!Config.getInstance().getIncludePattern().matcher(fileInfo.getFile().getAbsolutePath()).matches()) {
statistic.excluded();
continue;
}
fileAttributeCache.put(fileInfo, processor.loadAttributes(fileInfo.getFile()));
}
for (AttributeConfig config : Config.getInstance().getAttributeConfig()) {
for (FileInfoDto fileInfo : fileInfos) {
List<FileAttribute> attributes = fileAttributeCache.get(fileInfo);
List<FileAttribute> nonForcedTracks = processor.retrieveNonForcedTracks(attributes);
List<FileAttribute> nonCommentaryTracks = processor.retrieveNonCommentaryTracks(attributes);
processor.detectDefaultTracks(fileInfo, attributes, nonForcedTracks);
processor.detectDesiredTracks(fileInfo, nonForcedTracks, nonCommentaryTracks, config);
}
if (fileInfos.stream().allMatch(elem -> elem.getDesiredSubtitleLane() != null && elem.getDesiredAudioLane() != null)) {
log.info("Found {}/{} match for {}", config.getAudioLanguage(), config.getSubtitleLanguage(), file.getAbsolutePath());
statistic.increaseTotalBy(fileInfos.size());
fileInfos.forEach(this::updateFile);
return; // match found, end process here
}
fileInfos.forEach(f -> {
f.setDesiredAudioLane(null);
f.setDesiredSubtitleLane(null);
});
}
for (FileInfoDto fileInfo : fileInfos) {
statistic.total();
if (Config.getInstance().isForceCoherent()) {
super.process(fileInfo.getFile());
} else {
statistic.excluded();
}
}
}
}

View File

@@ -0,0 +1,36 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileCollector;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileProcessor;
import lombok.extern.slf4j.Slf4j;
import me.tongfei.progressbar.ProgressBarBuilder;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
public class DefaultAttributeUpdaterKernel extends AttributeUpdaterKernel {
public DefaultAttributeUpdaterKernel(FileCollector collector, FileProcessor processor) {
super(collector, processor);
}
@Override
protected ProgressBarBuilder pbBuilder() {
return super.pbBuilder()
.setUnit(" files", 1);
}
/**
* {@inheritDoc}
*/
@Override
List<File> loadFiles(String path) {
List<File> excludedFiles = loadExcludedFiles();
return collector.loadFiles(Config.getInstance().getLibraryPath().getAbsolutePath()).stream()
.filter(file -> !excludedFiles.contains(file))
.collect(Collectors.toList());
}
}

View File

@@ -2,11 +2,11 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.extern.log4j.Log4j2; import lombok.extern.slf4j.Slf4j;
import java.util.Objects; import java.util.Objects;
@Log4j2 @Slf4j
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public class AttributeConfig { public class AttributeConfig {

View File

@@ -10,16 +10,17 @@ import java.util.stream.Collectors;
@AllArgsConstructor @AllArgsConstructor
public enum ConfigProperty { public enum ConfigProperty {
LIBRARY("library", "Path to library", "l", 1), LIBRARY("library-path", "Path to library", "l", 1),
ATTRIBUTE_CONFIG("attribute-config", "Attribute config to decide which tracks to choose when", "a", Option.UNLIMITED_VALUES), ATTRIBUTE_CONFIG("attribute-config", "Attribute config to decide which tracks to choose when", "a", Option.UNLIMITED_VALUES),
CONFIG_PATH("config-path", "Path to config file", "p", 1), CONFIG_PATH("config-path", "Path to config file", "p", 1),
MKV_TOOL_NIX("mkvtoolnix", "Path to mkv tool nix installation", "m", 1), MKV_TOOL_NIX("mkvtoolnix", "Path to mkv tool nix installation", "m", 1),
SAFE_MODE("safe-mode", "Test run (no files will be changes)", "s", 0), SAFE_MODE("safe-mode", "Test run (no files will be changes)", "s", 0),
COHERENT("coherent", "Try to match whole series with same config", null, 0), COHERENT("coherent", "Try to match all files in dir of depth with the same config", "c", 1),
FORCE_COHERENT("force-coherent", "Force coherent and don't update anything if config fits not whole config (default: false)", "cf", 0),
WINDOWS("windows", "Is operating system windows", null, 0), WINDOWS("windows", "Is operating system windows", null, 0),
THREADS("threads", "Thread count (default: 2)", "t", 1), THREADS("threads", "Thread count (default: 2)", "t", 1),
INCLUDE_PATTERN("include-pattern", "Include files matching pattern (default: \".*\")", "i", 1), INCLUDE_PATTERN("include-pattern", "Include files matching pattern (default: \".*\")", "i", 1),
EXCLUDE_DIRECTORY("exclude-directories", "Directories to be excluded, combines with config file", "e", 1), EXCLUDED_DIRECTORY("excluded-directories", "Directories to be excluded, combines with config file", "e", Option.UNLIMITED_VALUES),
FORCED_KEYWORDS("forced-keywords", "Additional keywords to identify forced tracks", "fk", Option.UNLIMITED_VALUES), FORCED_KEYWORDS("forced-keywords", "Additional keywords to identify forced tracks", "fk", Option.UNLIMITED_VALUES),
COMMENTARY_KEYWORDS("commentary-keywords", "Additional keywords to identify commentary tracks", "ck", Option.UNLIMITED_VALUES), COMMENTARY_KEYWORDS("commentary-keywords", "Additional keywords to identify commentary tracks", "ck", Option.UNLIMITED_VALUES),
ARGUMENTS("arguments", "List of arguments", null, 0), ARGUMENTS("arguments", "List of arguments", null, 0),

View File

@@ -2,9 +2,9 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.extern.log4j.Log4j2; import lombok.extern.slf4j.Slf4j;
@Log4j2 @Slf4j
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public class FileAttribute { public class FileAttribute {

View File

@@ -1,32 +1,24 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model; package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
import java.io.File;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@Getter @Getter
@Setter @Setter
@RequiredArgsConstructor
public class FileInfoDto { public class FileInfoDto {
private final File file;
private Set<FileAttribute> defaultAudioLanes = new HashSet<>(); private Set<FileAttribute> defaultAudioLanes = new HashSet<>();
private Set<FileAttribute> defaultSubtitleLanes = new HashSet<>(); private Set<FileAttribute> defaultSubtitleLanes = new HashSet<>();
private Set<FileAttribute> desiredForcedSubtitleLanes; private Set<FileAttribute> desiredForcedSubtitleLanes;
private FileAttribute desiredAudioLane; private FileAttribute desiredAudioLane;
private FileAttribute desiredSubtitleLane; private FileAttribute desiredSubtitleLane;
public boolean isUnableToApplyConfig() {
return desiredAudioLane == null && desiredSubtitleLane == null;
}
public boolean isAlreadySuitable() {
return defaultAudioLanes.contains(desiredAudioLane) && defaultSubtitleLanes.contains(desiredSubtitleLane);
}
public boolean isChangeNecessary() {
return isAudioDifferent() || isSubtitleDifferent() || areForcedTracksDifferent();
}
public boolean isAudioDifferent() { public boolean isAudioDifferent() {
return desiredAudioLane != null && return desiredAudioLane != null &&
(defaultAudioLanes == null || !defaultAudioLanes.contains(desiredAudioLane)); (defaultAudioLanes == null || !defaultAudioLanes.contains(desiredAudioLane));
@@ -41,6 +33,25 @@ public class FileInfoDto {
return desiredForcedSubtitleLanes.size() > 0; return desiredForcedSubtitleLanes.size() > 0;
} }
public FileStatus getStatus() {
if (isChangeNecessary()) return FileStatus.CHANGE_NECESSARY;
if (isUnableToApplyConfig()) return FileStatus.UNABLE_TO_APPLY;
if (isAlreadySuitable()) return FileStatus.ALREADY_SUITED;
return FileStatus.UNKNOWN;
}
private boolean isUnableToApplyConfig() {
return desiredAudioLane == null && desiredSubtitleLane == null;
}
private boolean isAlreadySuitable() {
return defaultAudioLanes.contains(desiredAudioLane) && defaultSubtitleLanes.contains(desiredSubtitleLane);
}
private boolean isChangeNecessary() {
return isAudioDifferent() || isSubtitleDifferent() || areForcedTracksDifferent();
}
@Override @Override
public String toString() { public String toString() {
return "[" + "defaultAudioLanes=" + defaultAudioLanes + return "[" + "defaultAudioLanes=" + defaultAudioLanes +

View File

@@ -0,0 +1,8 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
public enum FileStatus {
CHANGE_NECESSARY,
UNABLE_TO_APPLY,
ALREADY_SUITED,
UNKNOWN;
}

View File

@@ -2,10 +2,13 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Getter @Getter
@Slf4j
public class ResultStatistic { public class ResultStatistic {
private static final String result = "Total files: %s%n" + private static final String result = "Total files: %s%n" +
"├─ Excluded: %s%n" +
"├─ Should change: %s%n" + "├─ Should change: %s%n" +
"│ ├─ Failed changing: %s%n" + "│ ├─ Failed changing: %s%n" +
"│ └─ Successfully changed: %s%n" + "│ └─ Successfully changed: %s%n" +
@@ -13,8 +16,9 @@ public class ResultStatistic {
"├─ Already fit config: %s%n" + "├─ Already fit config: %s%n" +
"└─ Failed: %s%n" + "└─ Failed: %s%n" +
"Runtime: %s"; "Runtime: %s";
private static ResultStatistic instance;
private int filesTotal = 0; private int filesTotal = 0;
private int excluded = 0;
private int shouldChange = 0; private int shouldChange = 0;
private int failedChanging = 0; private int failedChanging = 0;
@@ -28,10 +32,29 @@ public class ResultStatistic {
private long startTime = 0; private long startTime = 0;
private long runtime = 0; private long runtime = 0;
public static ResultStatistic getInstance() {
if (instance == null) {
instance = new ResultStatistic();
}
return instance;
}
public void increaseTotalBy(int amount) {
filesTotal += amount;
}
public synchronized void total() { public synchronized void total() {
filesTotal++; filesTotal++;
} }
public void increaseExcludedBy(int amount) {
excluded += amount;
}
public synchronized void excluded() {
excluded++;
}
public synchronized void shouldChange() { public synchronized void shouldChange() {
shouldChange++; shouldChange++;
} }
@@ -64,6 +87,11 @@ public class ResultStatistic {
runtime = System.currentTimeMillis() - startTime; runtime = System.currentTimeMillis() - startTime;
} }
public void printResult() {
System.out.println(prettyPrint());
log.info(this.toString());
}
private String formatTimer() { private String formatTimer() {
int seconds = (int) (runtime / 1000); int seconds = (int) (runtime / 1000);
int minutes = seconds / 60; int minutes = seconds / 60;
@@ -75,15 +103,29 @@ public class ResultStatistic {
} else if (hours >= 1) { } else if (hours >= 1) {
return String.format("%sh %sm %ss", hours, minutes % 60, seconds % 60); return String.format("%sh %sm %ss", hours, minutes % 60, seconds % 60);
} else if (minutes >= 1) { } else if (minutes >= 1) {
return String.format("%sm %ss", minutes , seconds % 60); return String.format("%sm %ss", minutes, seconds % 60);
} else { } else {
return String.format("%ss", seconds % 60); return String.format("%ss", seconds % 60);
} }
} }
@Override public String prettyPrint() {
public String toString() { return String.format(result, filesTotal, excluded, shouldChange, failedChanging, successfullyChanged,
return String.format(result, filesTotal, shouldChange, failedChanging, successfullyChanged,
noSuitableConfigFound, alreadyFits, failed, formatTimer()); noSuitableConfigFound, alreadyFits, failed, formatTimer());
} }
@Override
public String toString() {
String sb = "ResultStatistic[" + "filesTotal=" + filesTotal +
", excluded=" + excluded +
", shouldChange=" + shouldChange +
" (failedChanging=" + failedChanging +
", successfullyChanged=" + successfullyChanged +
"), noSuitableConfigFound=" + noSuitableConfigFound +
", alreadyFits=" + alreadyFits +
", failed=" + failed +
", runtime=" + formatTimer() +
']';
return sb;
}
} }

View File

@@ -39,7 +39,7 @@ class PathValidatorTest {
private static Stream<Arguments> provideTestCases() { private static Stream<Arguments> provideTestCases() {
return Stream.of( return Stream.of(
argumentsOf(LIBRARY, false, null, "library: " + TEST_DIR, new String[]{}, VALID), argumentsOf(LIBRARY, false, null, "library-path: " + TEST_DIR, new String[]{}, VALID),
argumentsOf(LIBRARY, true, null, "", new String[]{"-l", TEST_FILE}, VALID), argumentsOf(LIBRARY, true, null, "", new String[]{"-l", TEST_FILE}, VALID),
argumentsOf(LIBRARY, false, TEST_DIR, "", new String[]{}, DEFAULT), argumentsOf(LIBRARY, false, TEST_DIR, "", new String[]{}, DEFAULT),
@@ -48,7 +48,7 @@ class PathValidatorTest {
argumentsOf(LIBRARY, false, null, "", new String[]{}, NOT_PRESENT), argumentsOf(LIBRARY, false, null, "", new String[]{}, NOT_PRESENT),
argumentsOf(LIBRARY, true, null, "", new String[]{"-l", TEST_DIR + "/invalid"}, INVALID), argumentsOf(LIBRARY, true, null, "", new String[]{"-l", TEST_DIR + "/invalid"}, INVALID),
argumentsOf(LIBRARY, false, null, "library: " + TEST_DIR + "/invalid", new String[]{}, INVALID), argumentsOf(LIBRARY, false, null, "library-path: " + TEST_DIR + "/invalid", new String[]{}, INVALID),
argumentsOf(LIBRARY, true, TEST_DIR, "", new String[]{"-l", TEST_DIR + "/invalid"}, INVALID) argumentsOf(LIBRARY, true, TEST_DIR, "", new String[]{"-l", TEST_DIR + "/invalid"}, INVALID)
); );
} }