Fix faulty track attribute collection

This commit is contained in:
RatzzFatzz
2025-12-11 17:56:56 +01:00
parent 37cedecea7
commit 363492be43
5 changed files with 76 additions and 42 deletions

View File

@@ -67,7 +67,7 @@ public abstract class AttributeUpdaterKernel {
executor.awaitTermination(1, TimeUnit.DAYS); executor.awaitTermination(1, TimeUnit.DAYS);
} }
writeLastExecutionDate(); // writeLastExecutionDate();
statistic.stopTimer(); statistic.stopTimer();
statistic.printResult(); statistic.printResult();
@@ -88,7 +88,7 @@ public abstract class AttributeUpdaterKernel {
return; return;
} }
attributeProcessor.findDefaultMatchAndApplyChanges(fileInfo); attributeProcessor.findDefaultMatchAndApplyChanges(fileInfo, config.getAttributeConfig());
attributeProcessor.findForcedTracksAndApplyChanges(fileInfo, config.isOverwriteForced()); attributeProcessor.findForcedTracksAndApplyChanges(fileInfo, config.isOverwriteForced());
attributeProcessor.findCommentaryTracksAndApplyChanges(fileInfo); attributeProcessor.findCommentaryTracksAndApplyChanges(fileInfo);
attributeProcessor.findHearingImpairedTracksAndApplyChanges(fileInfo); attributeProcessor.findHearingImpairedTracksAndApplyChanges(fileInfo);
@@ -134,25 +134,25 @@ public abstract class AttributeUpdaterKernel {
} }
// should this be here? // should this be here?
protected void writeLastExecutionDate() { // protected void writeLastExecutionDate() {
if (config.isSafeMode()) { // if (config.isSafeMode()) {
return; // return;
} // }
//
try { // try {
String filePath = AppDirsFactory.getInstance().getUserConfigDir(ProjectUtil.getProjectName(), null, null); // String filePath = AppDirsFactory.getInstance().getUserConfigDir(ProjectUtil.getProjectName(), null, null);
//
File configDir = Path.of(filePath).toFile(); // File configDir = Path.of(filePath).toFile();
if (!configDir.exists()) configDir.mkdirs(); // if (!configDir.exists()) configDir.mkdirs();
//
File lastExecutionFile = Path.of(filePath + "/last-execution.yml").toFile(); // File lastExecutionFile = Path.of(filePath + "/last-execution.yml").toFile();
if (!lastExecutionFile.exists()) lastExecutionFile.createNewFile(); // if (!lastExecutionFile.exists()) lastExecutionFile.createNewFile();
//
YAML yaml = new YAML(lastExecutionFile); // YAML yaml = new YAML(lastExecutionFile);
yaml.set(config.getNormalizedLibraryPath(), DateUtils.convert(new Date())); // yaml.set(config.getNormalizedLibraryPath(), DateUtils.convert(new Date()));
yaml.save(lastExecutionFile); // yaml.save(lastExecutionFile);
} catch (IOException | YamlInvalidContentException e) { // } catch (IOException | YamlInvalidContentException e) {
log.error("last-execution.yml could not be created or read.", e); // log.error("last-execution.yml could not be created or read.", e);
} // }
} // }
} }

View File

@@ -91,7 +91,7 @@ public class MkvFileProcessor implements FileProcessor {
for (Map<String, Object> attribute : tracks) { for (Map<String, Object> attribute : tracks) {
if (!"video".equals(attribute.get("type"))) { if (!"video".equals(attribute.get("type"))) {
Map<String, Object> properties = (Map<String, Object>) attribute.get("properties"); Map<String, Object> properties = (Map<String, Object>) attribute.get("properties");
fileInfo.getTracks().add(new TrackAttributes( fileInfo.addTrack(new TrackAttributes(
(int) properties.get("number"), (int) properties.get("number"),
(String) properties.get("language"), (String) properties.get("language"),
(String) properties.get("track_name"), (String) properties.get("track_name"),

View File

@@ -1,5 +1,6 @@
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model; package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
@@ -7,6 +8,7 @@ import lombok.Setter;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
@Getter @Getter
@@ -14,9 +16,12 @@ import java.util.List;
public class FileInfo { public class FileInfo {
private final File file; private final File file;
@Getter(AccessLevel.NONE)
private final List<TrackAttributes> tracks = new ArrayList<>(); private final List<TrackAttributes> tracks = new ArrayList<>();
@Getter(AccessLevel.NONE)
private final List<TrackAttributes> audioTracks = new ArrayList<>(); private final List<TrackAttributes> audioTracks = new ArrayList<>();
@Getter(AccessLevel.NONE)
private final List<TrackAttributes> subtitleTracks = new ArrayList<>(); private final List<TrackAttributes> subtitleTracks = new ArrayList<>();
private final PlannedChange changes = new PlannedChange(); private final PlannedChange changes = new PlannedChange();
@@ -33,6 +38,18 @@ public class FileInfo {
for (TrackAttributes track : tracks) addTrack(track); for (TrackAttributes track : tracks) addTrack(track);
} }
public List<TrackAttributes> getTracks() {
return Collections.unmodifiableList(tracks);
}
public List<TrackAttributes> getAudioTracks() {
return Collections.unmodifiableList(audioTracks);
}
public List<TrackAttributes> getSubtitleTracks() {
return Collections.unmodifiableList(subtitleTracks);
}
public FileStatus getStatus() { public FileStatus getStatus() {
if (!changes.isEmpty()) return FileStatus.CHANGE_NECESSARY; if (!changes.isEmpty()) return FileStatus.CHANGE_NECESSARY;
if (matchedConfig == null) return FileStatus.NO_SUITABLE_CONFIG; if (matchedConfig == null) return FileStatus.NO_SUITABLE_CONFIG;

View File

@@ -3,7 +3,6 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.converter.AttributeConfigConverter; import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.converter.AttributeConfigConverter;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation.ValidFile; import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation.ValidFile;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation.ValidMkvToolNix; import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation.ValidMkvToolNix;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.FileUtils;
import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Min;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -31,7 +30,7 @@ public class InputConfig {
@Option(names = {"-a", "--attribute-config"}, required = true, arity = "1..*", converter = AttributeConfigConverter.class, @Option(names = {"-a", "--attribute-config"}, required = true, arity = "1..*", converter = AttributeConfigConverter.class,
description = "List of audio:subtitle pairs used to match in order and update files accordingly (e.g. jpn:eng jpn:ger)") description = "List of audio:subtitle pairs used to match in order and update files accordingly (e.g. jpn:eng jpn:ger)")
private List<AttributeConfig> attributeConfig; private AttributeConfig[] attributeConfig;
@ValidFile(message = "does not exist") @ValidFile(message = "does not exist")
@Option(names = {"-l", "--library"}, required = true, description = "path to library") @Option(names = {"-l", "--library"}, required = true, description = "path to library")
private File libraryPath; private File libraryPath;
@@ -53,7 +52,7 @@ public class InputConfig {
private boolean forceCoherent; private boolean forceCoherent;
@Option(names = {"-n", "--only-new-file"}, description = "sets filter-date to last successful execution (overwrites input of filter-date)") @Option(names = {"-n", "--only-new-file"}, description = "sets filter-date to last successful execution (overwrites input of filter-date)")
private boolean onlyNewFiles; private boolean onlyNewFiles; // TODO: implement usage
@Option(names = {"-d", "--filter-date"}, defaultValue = Option.NULL_VALUE, description = "only consider files created newer than entered date (format: \"dd.MM.yyyy-HH:mm:ss\")") @Option(names = {"-d", "--filter-date"}, defaultValue = Option.NULL_VALUE, description = "only consider files created newer than entered date (format: \"dd.MM.yyyy-HH:mm:ss\")")
private Date filterDate; private Date filterDate;
@Option(names = {"-i", "--include-pattern"}, defaultValue = ".*", description = "include files matching pattern (default: \".*\")") @Option(names = {"-i", "--include-pattern"}, defaultValue = ".*", description = "include files matching pattern (default: \".*\")")
@@ -71,7 +70,7 @@ public class InputConfig {
@Option(names = {"--commentary-keywords"}, arity = "1..*", defaultValue = "comment, commentary, director", split = ", ", @Option(names = {"--commentary-keywords"}, arity = "1..*", defaultValue = "comment, commentary, director", split = ", ",
description = "Keywords to identify commentary tracks (Defaults will be overwritten; Default: ${DEFAULT-VALUE})") description = "Keywords to identify commentary tracks (Defaults will be overwritten; Default: ${DEFAULT-VALUE})")
private Set<String> commentaryKeywords; private Set<String> commentaryKeywords;
@Option(names = {"--hearing-impaired"}, arity = "1..*", defaultValue = "SDH", split = ", ", @Option(names = {"--hearing-impaired"}, arity = "1..*", defaultValue = "SDH, CC", split = ", ",
description = "Keywords to identify hearing impaired tracks (Defaults will be overwritten; Default: ${DEFAULT-VALUE}") description = "Keywords to identify hearing impaired tracks (Defaults will be overwritten; Default: ${DEFAULT-VALUE}")
private Set<String> hearingImpaired; private Set<String> hearingImpaired;
@Option(names = {"--preferred-subtitles"}, arity = "1..*", defaultValue = "unstyled", split = ", ", @Option(names = {"--preferred-subtitles"}, arity = "1..*", defaultValue = "unstyled", split = ", ",
@@ -85,14 +84,6 @@ public class InputConfig {
System.setProperty("DEFAULT_MKV_TOOL_NIX", SystemUtils.IS_OS_WINDOWS ? "C:\\Program Files\\MKVToolNix" : "/usr/bin/"); System.setProperty("DEFAULT_MKV_TOOL_NIX", SystemUtils.IS_OS_WINDOWS ? "C:\\Program Files\\MKVToolNix" : "/usr/bin/");
} }
/**
* Get path to specific mkvtoolnix application.
* @return absolute path to desired application.
*/
public String getPathFor(MkvToolNix application) {
return FileUtils.getPathFor(mkvToolNix, application).getAbsolutePath();
}
public String getNormalizedLibraryPath() { public String getNormalizedLibraryPath() {
return this.getLibraryPath().getAbsolutePath().replace("\\", "/"); return this.getLibraryPath().getAbsolutePath().replace("\\", "/");
} }

View File

@@ -3,7 +3,6 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes;
import org.apache.logging.log4j.util.Strings;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
@@ -60,6 +59,31 @@ class AttributeProcessorTest {
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER, SUB_GER), List.of(AUDIO_ENG_DEFAULT, AUDIO_GER, SUB_GER),
arr(a("eng:eng"), a("eng:ger")), "eng:ger", arr(a("eng:eng"), a("eng:ger")), "eng:ger",
Map.ofEntries(on(SUB_GER)) Map.ofEntries(on(SUB_GER))
),
Arguments.of(
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER_COMMENTARY, SUB_GER_FORCED),
arr(a("ger:ger")), null,
Map.ofEntries()
),
Arguments.of(
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER_COMMENTARY, withName(SUB_GER, "forced")),
arr(a("ger:ger")), null,
Map.ofEntries()
),
Arguments.of(
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER_HEARING, SUB_GER),
arr(a("ger:ger")), null,
Map.ofEntries()
),
Arguments.of(
List.of(AUDIO_ENG_DEFAULT, withName(AUDIO_GER, "SDH"), SUB_GER),
arr(a("ger:ger")), null,
Map.ofEntries()
),
Arguments.of(
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER_COMMENTARY, AUDIO_GER_HEARING, AUDIO_GER, SUB_GER_FORCED, SUB_GER),
arr(a("ger:ger")), "ger:ger",
Map.ofEntries(off(AUDIO_ENG_DEFAULT), on(AUDIO_GER), on(SUB_GER))
) )
); );
} }
@@ -67,13 +91,12 @@ class AttributeProcessorTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("attributeConfigMatching") @MethodSource("attributeConfigMatching")
void findDefaultMatchAndApplyChanges(List<TrackAttributes> tracks, AttributeConfig[] config, String expectedConfig, Map<TrackAttributes, Boolean> changes) { void findDefaultMatchAndApplyChanges(List<TrackAttributes> tracks, AttributeConfig[] config, String expectedConfig, Map<TrackAttributes, Boolean> changes) {
AttributeProcessor attributeProcessor = new AttributeProcessor(new String[]{}, Set.of(), Set.of(), Set.of()); AttributeProcessor attributeProcessor = new AttributeProcessor(new String[]{}, Set.of("forced"), Set.of("commentary"), Set.of("SDH"));
FileInfo fileInfo = new FileInfo(null); FileInfo fileInfo = new FileInfo(null);
fileInfo.addTracks(tracks); fileInfo.addTracks(tracks);
attributeProcessor.findDefaultMatchAndApplyChanges(fileInfo, config); attributeProcessor.findDefaultMatchAndApplyChanges(fileInfo, config);
assertEquals(Strings.isBlank(expectedConfig), fileInfo.getMatchedConfig() == null); assertEquals(expectedConfig, fileInfo.getMatchedConfig() != null ? fileInfo.getMatchedConfig().toStringShort() : fileInfo.getMatchedConfig());
assertEquals(expectedConfig, fileInfo.getMatchedConfig().toStringShort());
assertEquals(changes.size(), fileInfo.getChanges().getDefaultTrack().size()); assertEquals(changes.size(), fileInfo.getChanges().getDefaultTrack().size());
changes.forEach((key, value) -> { changes.forEach((key, value) -> {
assertTrue(fileInfo.getChanges().getDefaultTrack().containsKey(key)); assertTrue(fileInfo.getChanges().getDefaultTrack().containsKey(key));
@@ -102,8 +125,11 @@ class AttributeProcessorTest {
return Stream.of( return Stream.of(
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)), Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "forced"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)), Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "forced"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "Forced"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "commentary"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)), Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "commentary"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "SHD"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)), Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "Commentary"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "SDH"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "sdh"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, SUB_GER, SUB_GER_FORCED, AUDIO_GER_COMMENTARY, AUDIO_GER_HEARING), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)) Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, SUB_GER, SUB_GER_FORCED, AUDIO_GER_COMMENTARY, AUDIO_GER_HEARING), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER))
); );
} }
@@ -111,7 +137,7 @@ class AttributeProcessorTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("filterForPossibleDefaults") @MethodSource("filterForPossibleDefaults")
void filterForPossibleDefaults(List<TrackAttributes> tracks, Set<TrackAttributes> expected) throws InvocationTargetException, IllegalAccessException { void filterForPossibleDefaults(List<TrackAttributes> tracks, Set<TrackAttributes> expected) throws InvocationTargetException, IllegalAccessException {
AttributeProcessor attributeProcessor = new AttributeProcessor(new String[]{}, Set.of("forced"), Set.of("commentary"), Set.of("SHD")); AttributeProcessor attributeProcessor = new AttributeProcessor(new String[]{}, Set.of("forced"), Set.of("commentary"), Set.of("SDH"));
Optional<Method> method = Arrays.stream(AttributeProcessor.class.getDeclaredMethods()) Optional<Method> method = Arrays.stream(AttributeProcessor.class.getDeclaredMethods())
.filter(m -> m.getName().equals("filterForPossibleDefaults")) .filter(m -> m.getName().equals("filterForPossibleDefaults"))
.findFirst(); .findFirst();