From f18fdbdda6cab255212a0041896433e1b1e24e30 Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Wed, 24 Dec 2025 16:22:17 +0100 Subject: [PATCH 01/15] Apply forced track if sub lang is OFF --- .../converter/AttributeConfigConverter.java | 8 +- .../processors/AttributeChangeProcessor.java | 119 ++++++++------ .../processors/CoherentAttributeUpdater.java | 9 +- .../SingleFileAttributeUpdater.java | 9 +- .../model/AttributeConfig.java | 14 +- .../AttributeChangeProcessorTest.java | 149 ++++++++++++++---- 6 files changed, 207 insertions(+), 101 deletions(-) diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/converter/AttributeConfigConverter.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/converter/AttributeConfigConverter.java index ba4e5e2..a8aae70 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/converter/AttributeConfigConverter.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/converter/AttributeConfigConverter.java @@ -37,10 +37,10 @@ public class AttributeConfigConverter implements CommandLine.ITypeConverter filterForPossibleDefaults(List tracks) { + /** + * Looks for default matches and applies them if found. + */ + public void findAndApplyDefaultMatch(FileInfo fileInfo, AttributeConfig... configs) { + Map> audiosByLanguage = new HashMap<>(fileInfo.getTracks().size()); + Map> subsByLanguage = new HashMap<>(fileInfo.getTracks().size()); + getPossibleDefaults(fileInfo.getTracks()).forEach(track -> { + if (TrackType.AUDIO.equals(track.type())) + audiosByLanguage.computeIfAbsent(track.language(), (k) -> new ArrayList<>()).add(track); + else if (TrackType.SUBTITLES.equals(track.type())) + subsByLanguage.computeIfAbsent(track.language(), (k) -> new ArrayList<>()).add(track); + }); + + for (AttributeConfig config : configs) { + if (("OFF".equals(config.getAudioLang()) || audiosByLanguage.containsKey(config.getAudioLang())) + && ("OFF".equals(config.getSubLang()) || subsByLanguage.containsKey(config.getSubLang()))) { + fileInfo.setMatchedConfig(config); + break; + } + } + + if (fileInfo.getMatchedConfig() == null) return; + + AttributeConfig match = fileInfo.getMatchedConfig(); + removeExistingDefaults(fileInfo); + if (!"OFF".equals(match.getAudioLang())) applyNewDefault(fileInfo, audiosByLanguage.get(fileInfo.getMatchedConfig().getAudioLang()).get(0)); + if (!"OFF".equals(match.getSubLang())) applyNewDefault(fileInfo, subsByLanguage.get(fileInfo.getMatchedConfig().getSubLang()).stream().max(subtitleTrackComparator).get()); + } + + /** + * If match with xxx:OFF was found forced track in audio language is applied as default. + * Forced track detection takes changes of {@link AttributeChangeProcessor#findAndApplyForcedTracks} into consideration. + */ + public void applyForcedAsDefault(FileInfo fileInfo) { + AttributeConfig c = fileInfo.getMatchedConfig(); + if (c == null) return; + if (!"OFF".equals(c.getAudioLang()) && "OFF".equals(c.getSubLang())) { + getForcedTracks(fileInfo) + .filter(track -> c.getAudioLang().equals(track.language())) + .findFirst() + .ifPresent(track -> applyNewDefault(fileInfo, track)); + } + } + + private Stream getPossibleDefaults(List tracks) { Stream attributes = tracks.stream(); return attributes @@ -34,55 +76,26 @@ public class AttributeChangeProcessor { .filter(attr -> { if (attr.trackName() == null) return true; return forcedKeywords.stream().noneMatch(keyword -> keyword.compareToIgnoreCase(attr.trackName()) == 0); - }) - .toList(); + }); } - public void findDefaultMatchAndApplyChanges(FileInfo fileInfo, AttributeConfig... configs) { - Map> audiosByLanguage = new HashMap<>(fileInfo.getTracks().size()); - Map> subsByLanguage = new HashMap<>(fileInfo.getTracks().size()); - filterForPossibleDefaults(fileInfo.getTracks()).forEach(track -> { - if (TrackType.AUDIO.equals(track.type())) - audiosByLanguage.computeIfAbsent(track.language(), (k) -> new ArrayList<>()).add(track); - else if (TrackType.SUBTITLES.equals(track.type())) - subsByLanguage.computeIfAbsent(track.language(), (k) -> new ArrayList<>()).add(track); - }); - - for (AttributeConfig config : configs) { - if (("OFF".equals(config.getAudioLanguage()) || audiosByLanguage.containsKey(config.getAudioLanguage())) - && ("OFF".equals(config.getSubtitleLanguage()) || subsByLanguage.containsKey(config.getSubtitleLanguage()))) { - fileInfo.setMatchedConfig(config); - break; - } - // TODO: forced if OFF - } - - if (fileInfo.getMatchedConfig() == null) return; - - applyDefaultChanges(fileInfo, FileInfo::getAudioTracks, fileInfo.getMatchedConfig().getAudioLanguage(), - () -> audiosByLanguage.get(fileInfo.getMatchedConfig().getAudioLanguage()).get(0)); - applyDefaultChanges(fileInfo, FileInfo::getSubtitleTracks, fileInfo.getMatchedConfig().getSubtitleLanguage(), - () -> subsByLanguage.get(fileInfo.getMatchedConfig().getSubtitleLanguage()).stream().max(subtitleTrackComparator).get()); - } - - private void applyDefaultChanges(FileInfo fileInfo, Function> tracks, String language, Supplier targetDefaultSupplier) { - tracks.apply(fileInfo).stream() + private void removeExistingDefaults(FileInfo fileInfo) { + fileInfo.getTracks().stream() .filter(TrackAttributes::defaultt) .forEach(attr -> fileInfo.getChanges().getDefaultTrack().put(attr, false)); - if (!"OFF".equals(language)) { - TrackAttributes targetDefault = targetDefaultSupplier.get(); - if (fileInfo.getChanges().getDefaultTrack().containsKey(targetDefault)) { - fileInfo.getChanges().getDefaultTrack().remove(targetDefault); - } else { - fileInfo.getChanges().getDefaultTrack().put(targetDefault, true); - } + } + + private void applyNewDefault(FileInfo fileInfo, TrackAttributes targetDefault) { + Map changes = fileInfo.getChanges().getDefaultTrack(); + if (changes.containsKey(targetDefault)) { + changes.remove(targetDefault); + } else { + changes.put(targetDefault, true); } } - public void findForcedTracksAndApplyChanges(FileInfo fileInfo, boolean overwrite) { - Stream forcedTracks = fileInfo.getTracks().stream() - .filter(track -> track.trackName() != null) - .filter(track -> forcedKeywords.stream().anyMatch(keyword -> track.trackName().toLowerCase().contains(keyword.toLowerCase(Locale.ROOT)))); + public void findAndApplyForcedTracks(FileInfo fileInfo, boolean overwrite) { + Stream forcedTracks = getForcedTracks(fileInfo); if (overwrite) { fileInfo.getTracks().stream().filter(TrackAttributes::forced).forEach(attr -> { @@ -97,7 +110,19 @@ public class AttributeChangeProcessor { }); } - public void findCommentaryTracksAndApplyChanges(FileInfo fileInfo) { + private Stream getForcedTracks(FileInfo fileInfo) { + return fileInfo.getTracks().stream() + .filter(track -> { + if (fileInfo.getChanges().getForcedTrack().containsKey(track)) return fileInfo.getChanges().getForcedTrack().get(track); + return matchesForcedKeywords(track) || track.forced(); + }); + } + + private boolean matchesForcedKeywords(TrackAttributes track) { + return track.trackName() != null && forcedKeywords.stream().anyMatch(keyword -> track.trackName().toLowerCase().contains(keyword.toLowerCase(Locale.ROOT))); + } + + public void findAndApplyCommentaryTracks(FileInfo fileInfo) { fileInfo.getTracks().stream() .filter(track -> !track.commentary()) .filter(track -> track.trackName() != null) @@ -107,7 +132,7 @@ public class AttributeChangeProcessor { }); } - public void findHearingImpairedTracksAndApplyChanges(FileInfo fileInfo) { + public void findAndApplyHearingImpairedTracks(FileInfo fileInfo) { fileInfo.getTracks().stream() .filter(track -> !track.hearingImpaired()) .filter(track -> track.trackName() != null) diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java index 24e356c..f156189 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java @@ -49,9 +49,10 @@ public class CoherentAttributeUpdater extends SingleFileAttributeUpdater { log.info("Found coherent match {} for {}", matchedConfig.toStringShort(), rootDir.getPath()); matchedFiles.forEach(fileInfo -> { - attributeChangeProcessor.findForcedTracksAndApplyChanges(fileInfo, this.config.isOverwriteForced()); - attributeChangeProcessor.findCommentaryTracksAndApplyChanges(fileInfo); - attributeChangeProcessor.findHearingImpairedTracksAndApplyChanges(fileInfo); + attributeChangeProcessor.findAndApplyForcedTracks(fileInfo, this.config.isOverwriteForced()); + attributeChangeProcessor.applyForcedAsDefault(fileInfo); + attributeChangeProcessor.findAndApplyCommentaryTracks(fileInfo); + attributeChangeProcessor.findAndApplyHearingImpairedTracks(fileInfo); checkStatusAndUpdate(fileInfo); }); @@ -89,7 +90,7 @@ public class CoherentAttributeUpdater extends SingleFileAttributeUpdater { break; } - attributeChangeProcessor.findDefaultMatchAndApplyChanges(fileInfo, config); + attributeChangeProcessor.findAndApplyDefaultMatch(fileInfo, config); if (matchedConfig == null) matchedConfig = fileInfo.getMatchedConfig(); if (matchedConfig == null || matchedConfig != fileInfo.getMatchedConfig()) { diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java index 0d28a08..a8c64ee 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java @@ -36,10 +36,11 @@ public class SingleFileAttributeUpdater extends AttributeUpdater { return; } - attributeChangeProcessor.findDefaultMatchAndApplyChanges(fileInfo, config.getAttributeConfig()); - attributeChangeProcessor.findForcedTracksAndApplyChanges(fileInfo, config.isOverwriteForced()); - attributeChangeProcessor.findCommentaryTracksAndApplyChanges(fileInfo); - attributeChangeProcessor.findHearingImpairedTracksAndApplyChanges(fileInfo); + attributeChangeProcessor.findAndApplyDefaultMatch(fileInfo, config.getAttributeConfig()); + attributeChangeProcessor.findAndApplyForcedTracks(fileInfo, config.isOverwriteForced()); + attributeChangeProcessor.applyForcedAsDefault(fileInfo); + attributeChangeProcessor.findAndApplyCommentaryTracks(fileInfo); + attributeChangeProcessor.findAndApplyHearingImpairedTracks(fileInfo); checkStatusAndUpdate(fileInfo); } diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/AttributeConfig.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/AttributeConfig.java index 45b278e..f9c30ab 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/AttributeConfig.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/AttributeConfig.java @@ -10,8 +10,8 @@ import java.util.Objects; @Getter @AllArgsConstructor public class AttributeConfig { - private final String audioLanguage; - private final String subtitleLanguage; + private final String audioLang; + private final String subLang; public static AttributeConfig of(String audioLanguage, String subtitleLanguage) { return new AttributeConfig(audioLanguage, subtitleLanguage); @@ -22,23 +22,23 @@ public class AttributeConfig { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AttributeConfig that = (AttributeConfig) o; - return Objects.equals(audioLanguage, that.audioLanguage) && Objects.equals(subtitleLanguage, that.subtitleLanguage); + return Objects.equals(audioLang, that.audioLang) && Objects.equals(subLang, that.subLang); } @Override public int hashCode() { - return Objects.hash(audioLanguage, subtitleLanguage); + return Objects.hash(audioLang, subLang); } public String toStringShort() { - return audioLanguage + ":" + subtitleLanguage; + return audioLang + ":" + subLang; } @Override public String toString() { return "AttributeConfig{" - + "audioLanguage='" + audioLanguage + '\'' - + ", subtitleLanguage='" + subtitleLanguage + '\'' + + + "audioLanguage='" + audioLang + '\'' + + ", subtitleLanguage='" + subLang + '\'' + '}'; } } diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeChangeProcessorTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeChangeProcessorTest.java index 67b9f6e..d12fa27 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeChangeProcessorTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeChangeProcessorTest.java @@ -18,7 +18,24 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class AttributeChangeProcessorTest { - private static Stream attributeConfigMatching() { + private static AttributeConfig[] arr(AttributeConfig... configs) { + return configs; + } + + private static AttributeConfig a(String config) { + String[] split = config.split(":"); + return new AttributeConfig(split[0], split[1]); + } + + private static Map.Entry on(TrackAttributes track) { + return Map.entry(track, true); + } + + private static Map.Entry off(TrackAttributes track) { + return Map.entry(track, false); + } + + private static Stream findAndApplyDefaultMatch() { return Stream.of( Arguments.of( List.of(withName(AUDIO_ENG, null), SUB_ENG), @@ -99,13 +116,14 @@ class AttributeChangeProcessorTest { } @ParameterizedTest - @MethodSource("attributeConfigMatching") - void findDefaultMatchAndApplyChanges(List tracks, AttributeConfig[] config, String expectedConfig, Map changes) { + @MethodSource("findAndApplyDefaultMatch") + void findAndApplyDefaultMatch(List tracks, AttributeConfig[] config, String expectedConfig, Map changes) { AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of("forced"), Set.of("commentary"), Set.of("SDH")); - FileInfo fileInfo = new FileInfo(null); fileInfo.addTracks(tracks); - attributeChangeProcessor.findDefaultMatchAndApplyChanges(fileInfo, config); + + attributeChangeProcessor.findAndApplyDefaultMatch(fileInfo, config); + assertEquals(expectedConfig, fileInfo.getMatchedConfig() != null ? fileInfo.getMatchedConfig().toStringShort() : fileInfo.getMatchedConfig()); assertEquals(changes.size(), fileInfo.getChanges().getDefaultTrack().size()); changes.forEach((key, value) -> { @@ -114,24 +132,54 @@ class AttributeChangeProcessorTest { }); } - private static AttributeConfig[] arr(AttributeConfig... configs) { - return configs; + private static Stream applyForcedAsDefault() { + return Stream.of( + Arguments.of( + List.of(AUDIO_GER, SUB_GER_FORCED), + a("ger:OFF"), + Map.ofEntries(on(SUB_GER_FORCED)) + ), + Arguments.of( + List.of(AUDIO_GER, SUB_GER_FORCED, SUB_GER), + a("ger:OFF"), + Map.ofEntries(on(SUB_GER_FORCED)) + ), + Arguments.of( + List.of(AUDIO_GER, withName(SUB_GER, "forced")), + a("ger:OFF"), + Map.ofEntries(on(withName(SUB_GER, "forced"))) + ), + Arguments.of( + List.of(AUDIO_GER, SUB_GER_FORCED, SUB_ENG), + a("ger:eng"), + Map.ofEntries() + ), + Arguments.of( + List.of(AUDIO_GER, SUB_GER_FORCED, SUB_ENG), + null, + Map.ofEntries() + ) + ); } - private static AttributeConfig a(String config) { - String[] split = config.split(":"); - return new AttributeConfig(split[0], split[1]); + @ParameterizedTest + @MethodSource("applyForcedAsDefault") + void applyForcedAsDefault(List tracks, AttributeConfig config, Map changes) { + AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of("forced"), Set.of(""), Set.of("")); + FileInfo fileInfo = new FileInfo(null); + fileInfo.addTracks(tracks); + fileInfo.setMatchedConfig(config); + + attributeChangeProcessor.applyForcedAsDefault(fileInfo); + + assertEquals(changes.size(), fileInfo.getChanges().getDefaultTrack().size()); + changes.forEach((key, value) -> { + assertTrue(fileInfo.getChanges().getDefaultTrack().containsKey(key)); + assertEquals(value, fileInfo.getChanges().getDefaultTrack().get(key)); + }); } - private static Map.Entry on(TrackAttributes track) { - return Map.entry(track, true); - } - - private static Map.Entry off(TrackAttributes track) { - return Map.entry(track, false); - } - - private static Stream filterForPossibleDefaults() { + private static Stream getPossibleDefaults() { 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, withName(AUDIO_GER, "forced"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)), @@ -145,24 +193,55 @@ class AttributeChangeProcessorTest { } @ParameterizedTest - @MethodSource("filterForPossibleDefaults") - void filterForPossibleDefaults(List tracks, Set expected) throws InvocationTargetException, IllegalAccessException { + @MethodSource("getPossibleDefaults") + void getPossibleDefaults(List tracks, Set expected) throws InvocationTargetException, IllegalAccessException { AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of("forced"), Set.of("commentary"), Set.of("SDH")); Optional method = Arrays.stream(AttributeChangeProcessor.class.getDeclaredMethods()) - .filter(m -> m.getName().equals("filterForPossibleDefaults")) + .filter(m -> m.getName().equals("getPossibleDefaults")) .findFirst(); assertTrue(method.isPresent()); Method underTest = method.get(); underTest.setAccessible(true); - List result = (List) underTest.invoke(attributeChangeProcessor, tracks); + List result = ((Stream) underTest.invoke(attributeChangeProcessor, tracks)).toList(); assertEquals(expected.size(), result.size()); for (TrackAttributes track : result) { assertTrue(expected.contains(track)); } } - private static Stream findForcedTracksAndApplyChanges() { + private static Stream getForcedTracks() { + return Stream.of( + Arguments.of(List.of(withName(SUB_GER, "forced")), Map.of(), Set.of(withName(SUB_GER, "forced"))), + Arguments.of(List.of(SUB_GER_FORCED), Map.of(), Set.of(SUB_GER_FORCED)), + Arguments.of(List.of(SUB_GER_FORCED, withName(SUB_GER, "forced")), Map.of(), Set.of(SUB_GER_FORCED, withName(SUB_GER, "forced"))), + Arguments.of(List.of(SUB_GER_FORCED, withName(SUB_GER, "forced")), Map.of(SUB_GER_FORCED, false), Set.of(withName(SUB_GER, "forced"))), + Arguments.of(List.of(SUB_GER, withName(SUB_GER, "forced")), Map.of(SUB_GER, true), Set.of(SUB_GER, withName(SUB_GER, "forced"))) + ); + } + + @ParameterizedTest + @MethodSource("getForcedTracks") + void getForcedTracks(List tracks, Map changes, Set expected) throws InvocationTargetException, IllegalAccessException { + AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of("forced"), Set.of(), Set.of()); + FileInfo fileInfo = new FileInfo(null); + fileInfo.addTracks(tracks); + changes.forEach((key, val) -> fileInfo.getChanges().getForcedTrack().put(key, val)); + Optional method = Arrays.stream(AttributeChangeProcessor.class.getDeclaredMethods()) + .filter(m -> m.getName().equals("getForcedTracks")) + .findFirst(); + + assertTrue(method.isPresent()); + Method underTest = method.get(); + underTest.setAccessible(true); + List result = ((Stream) underTest.invoke(attributeChangeProcessor, fileInfo)).toList(); + assertEquals(expected.size(), result.size()); + for (TrackAttributes track : result) { + assertTrue(expected.contains(track)); + } + } + + private static Stream findAndApplyForcedTracks() { return Stream.of( Arguments.of(List.of(), Set.of("song & signs"), false, @@ -196,13 +275,13 @@ class AttributeChangeProcessorTest { } @ParameterizedTest - @MethodSource("findForcedTracksAndApplyChanges") - void findForcedTracksAndApplyChanges(List tracks, Set keywords, boolean overwrite, Map changes) { + @MethodSource("findAndApplyForcedTracks") + void findAndApplyForcedTracks(List tracks, Set keywords, boolean overwrite, Map changes) { AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, keywords, Set.of(), Set.of()); FileInfo fileInfo = new FileInfo(null); fileInfo.addTracks(tracks); - attributeChangeProcessor.findForcedTracksAndApplyChanges(fileInfo, overwrite); + attributeChangeProcessor.findAndApplyForcedTracks(fileInfo, overwrite); assertEquals(changes.size(), fileInfo.getChanges().getForcedTrack().size()); changes.forEach((key, value) -> { @@ -211,7 +290,7 @@ class AttributeChangeProcessorTest { }); } - private static Stream findCommentaryTracksAndApplyChanges() { + private static Stream findAndApplyCommentaryTracks() { return Stream.of( Arguments.of(List.of(withName(SUB_GER, "commentary"), withName(SUB_GER, null)), Set.of("commentary"), @@ -233,13 +312,13 @@ class AttributeChangeProcessorTest { } @ParameterizedTest - @MethodSource("findCommentaryTracksAndApplyChanges") - void findCommentaryTracksAndApplyChanges(List tracks, Set keywords, Map changes) { + @MethodSource("findAndApplyCommentaryTracks") + void findAndApplyCommentaryTracks(List tracks, Set keywords, Map changes) { AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of(), keywords, Set.of()); FileInfo fileInfo = new FileInfo(null); fileInfo.addTracks(tracks); - attributeChangeProcessor.findCommentaryTracksAndApplyChanges(fileInfo); + attributeChangeProcessor.findAndApplyCommentaryTracks(fileInfo); assertEquals(changes.size(), fileInfo.getChanges().getCommentaryTrack().size()); changes.forEach((key, value) -> { @@ -248,7 +327,7 @@ class AttributeChangeProcessorTest { }); } - private static Stream findHearingImpairedTracksAndApplyChanges() { + private static Stream findAndApplyHearingImpairedTracks() { return Stream.of( Arguments.of(List.of(withName(SUB_GER, "SDH"), withName(SUB_GER, null)), Set.of("SDH"), @@ -270,13 +349,13 @@ class AttributeChangeProcessorTest { } @ParameterizedTest - @MethodSource("findHearingImpairedTracksAndApplyChanges") - void findHearingImpairedTracksAndApplyChanges(List tracks, Set keywords, Map changes) { + @MethodSource("findAndApplyHearingImpairedTracks") + void findAndApplyHearingImpairedTracks(List tracks, Set keywords, Map changes) { AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of(), Set.of(), keywords); FileInfo fileInfo = new FileInfo(null); fileInfo.addTracks(tracks); - attributeChangeProcessor.findHearingImpairedTracksAndApplyChanges(fileInfo); + attributeChangeProcessor.findAndApplyHearingImpairedTracks(fileInfo); assertEquals(changes.size(), fileInfo.getChanges().getHearingImpairedTrack().size()); changes.forEach((key, value) -> { From 94365651ff5c8442e599d008030b2ad3dce3973b Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Thu, 25 Dec 2025 14:28:18 +0100 Subject: [PATCH 02/15] Add tests for single file attribute updater --- .../processors/CoherentAttributeUpdater.java | 2 +- .../AttributeChangeProcessorTest.java | 19 +--- .../impl/processors/AttributeUpdaterTest.java | 2 +- .../CoherentAttributeUpdaterTest.java | 2 +- .../SingleFileAttributeUpdaterTest.java | 98 +++++++++++++++++++ ...oTestUtil.java => TrackAttributeUtil.java} | 23 ++++- 6 files changed, 124 insertions(+), 22 deletions(-) create mode 100644 src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdaterTest.java rename src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/{FileInfoTestUtil.java => TrackAttributeUtil.java} (81%) diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java index f156189..61459ad 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java @@ -59,7 +59,7 @@ public class CoherentAttributeUpdater extends SingleFileAttributeUpdater { return; // match was found and process must be stopped } - // Couldn't match any config at current level. Resetting changes and trying to one level deeper + // Couldn't match any config at current level. Resetting changes and trying one level deeper matchedFiles.forEach(fileInfo -> { fileInfo.resetChanges(); fileInfo.setMatchedConfig(null); diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeChangeProcessorTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeChangeProcessorTest.java index d12fa27..6f65d39 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeChangeProcessorTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeChangeProcessorTest.java @@ -12,29 +12,12 @@ import java.lang.reflect.Method; import java.util.*; import java.util.stream.Stream; -import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.FileInfoTestUtil.*; +import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TrackAttributeUtil.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class AttributeChangeProcessorTest { - private static AttributeConfig[] arr(AttributeConfig... configs) { - return configs; - } - - private static AttributeConfig a(String config) { - String[] split = config.split(":"); - return new AttributeConfig(split[0], split[1]); - } - - private static Map.Entry on(TrackAttributes track) { - return Map.entry(track, true); - } - - private static Map.Entry off(TrackAttributes track) { - return Map.entry(track, false); - } - private static Stream findAndApplyDefaultMatch() { return Stream.of( Arguments.of( diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdaterTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdaterTest.java index be5d7b2..d40c790 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdaterTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdaterTest.java @@ -11,7 +11,7 @@ import java.util.List; import java.util.function.Supplier; import java.util.stream.Stream; -import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.FileInfoTestUtil.AUDIO_GER; +import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TrackAttributeUtil.AUDIO_GER; import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_FILE; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java index afc05e8..7f808c9 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java @@ -21,7 +21,7 @@ import java.lang.reflect.Method; import java.util.*; import java.util.stream.Stream; -import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.FileInfoTestUtil.*; +import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TrackAttributeUtil.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdaterTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdaterTest.java new file mode 100644 index 0000000..3b19d02 --- /dev/null +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdaterTest.java @@ -0,0 +1,98 @@ +package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors; + +import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig; +import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo; +import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig; +import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_DIR; +import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_FILE; +import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TrackAttributeUtil.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class SingleFileAttributeUpdaterTest { + + private static Stream process() { + return Stream.of( + Arguments.of( + arr(a("ger:OFF")), a("ger:OFF"), + List.of(AUDIO_GER, SUB_GER_FORCED), + Map.ofEntries(on(AUDIO_GER), on(SUB_GER_FORCED)), + Map.ofEntries(), + Map.ofEntries(), + Map.ofEntries() + ), + Arguments.of( + arr(a("ger:ger")), a("ger:ger"), + List.of(AUDIO_GER, SUB_GER, withName(AUDIO_GER, "SDH"), withName(AUDIO_GER, "commentary"), withName(SUB_GER, "Forced")), + Map.ofEntries(on(AUDIO_GER), on(SUB_GER)), + Map.ofEntries(on(withName(SUB_GER, "Forced"))), + Map.ofEntries(on(withName(AUDIO_GER, "commentary"))), + Map.ofEntries(on(withName(AUDIO_GER, "SDH"))) + ), + Arguments.of( + arr(a("ger:OFF")), a("ger:OFF"), + List.of(AUDIO_GER, SUB_GER, withName(AUDIO_GER, "SDH"), withName(AUDIO_GER, "commentary"), withName(SUB_GER, "Forced")), + Map.ofEntries(on(AUDIO_GER), on(withName(SUB_GER, "Forced"))), + Map.ofEntries(on(withName(SUB_GER, "Forced"))), + Map.ofEntries(on(withName(AUDIO_GER, "commentary"))), + Map.ofEntries(on(withName(AUDIO_GER, "SDH"))) + ), + Arguments.of( + arr(a("ger:eng")), null, + List.of(AUDIO_GER, SUB_GER, withName(AUDIO_GER, "SDH"), withName(AUDIO_GER, "commentary"), withName(SUB_GER, "Forced")), + Map.ofEntries(), + Map.ofEntries(on(withName(SUB_GER, "Forced"))), + Map.ofEntries(on(withName(AUDIO_GER, "commentary"))), + Map.ofEntries(on(withName(AUDIO_GER, "SDH"))) + ) + ); + } + + @ParameterizedTest + @MethodSource("process") + void process(AttributeConfig[] attributeConfigs, AttributeConfig expectedMatch, + List tracks, + Map defaultExp, + Map forcedExp, + Map commentaryExp, + Map hearingImpairedExp) { + InputConfig config = new InputConfig(); + config.setThreads(1); + config.setSafeMode(true); + config.setAttributeConfig(attributeConfigs); + FileInfo fileInfo = new FileInfo(new File(TEST_DIR)); + fileInfo.addTracks(tracks); + FileProcessor fileProcessor = spy(FileProcessor.class); + doReturn(fileInfo).when(fileProcessor).readAttributes(any()); + AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{"pref"}, Set.of("forced"), Set.of("commentary"), Set.of("SDH")); + SingleFileAttributeUpdater underTest = new SingleFileAttributeUpdater(config, fileProcessor, attributeChangeProcessor); + + underTest.process(fileInfo.getFile()); + + assertEquals(expectedMatch, fileInfo.getMatchedConfig()); + assertEquals(fileInfo.getChanges().getDefaultTrack().size(), defaultExp.size()); + defaultExp.forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getDefaultTrack().get(key), "Default track flag")); + + assertEquals(fileInfo.getChanges().getForcedTrack().size(), forcedExp.size()); + forcedExp.forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getForcedTrack().get(key), "Forced track flag")); + + assertEquals(fileInfo.getChanges().getCommentaryTrack().size(), commentaryExp.size()); + commentaryExp.forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getCommentaryTrack().get(key), "Commentary track flag")); + + assertEquals(fileInfo.getChanges().getHearingImpairedTrack().size(), hearingImpairedExp.size()); + hearingImpairedExp.forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getHearingImpairedTrack().get(key), "Hearing Impaired track flag")); + } +} \ No newline at end of file diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/FileInfoTestUtil.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/TrackAttributeUtil.java similarity index 81% rename from src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/FileInfoTestUtil.java rename to src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/TrackAttributeUtil.java index 73d8102..d3d9bd2 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/FileInfoTestUtil.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/TrackAttributeUtil.java @@ -1,9 +1,12 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.util; +import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackType; -public class FileInfoTestUtil { +import java.util.Map; + +public class TrackAttributeUtil { public static final TrackAttributes AUDIO_GER = new TrackAttributes(0, "ger", "", false, false, false, false, TrackType.AUDIO); public static final TrackAttributes AUDIO_ENG = new TrackAttributes(1, "eng", "", false, false, false, false, TrackType.AUDIO); public static final TrackAttributes AUDIO_GER_DEFAULT = new TrackAttributes(0, "ger", "", true, false, false, false, TrackType.AUDIO); @@ -27,4 +30,22 @@ public class FileInfoTestUtil { public static TrackAttributes withName(TrackAttributes track, String trackName) { return new TrackAttributes(track.id(), track.language(), trackName, track.defaultt(), track.forced(), track.commentary(), track.hearingImpaired(), track.type()); } + + public static AttributeConfig[] arr(AttributeConfig... configs) { + return configs; + } + + public static AttributeConfig a(String config) { + String[] split = config.split(":"); + return new AttributeConfig(split[0], split[1]); + } + + public static Map.Entry on(TrackAttributes track) { + return Map.entry(track, true); + } + + public static Map.Entry off(TrackAttributes track) { + return Map.entry(track, false); + } + } From 31b155d8a37af88fc1b2f853f5118691d2c1ab9a Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Sat, 27 Dec 2025 20:55:41 +0100 Subject: [PATCH 03/15] Add tests for coherent file attribute updater --- .../CoherentAttributeUpdaterTest.java | 133 +++++++++++++++++- 1 file changed, 128 insertions(+), 5 deletions(-) diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java index 7f808c9..08ef0e5 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java @@ -21,19 +21,17 @@ import java.lang.reflect.Method; import java.util.*; import java.util.stream.Stream; +import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_DIR; import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TrackAttributeUtil.*; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class CoherentAttributeUpdaterTest { @Mock(lenient = true) FileProcessor fileProcessor; - @Test - void process() { - } - private static Stream findMatch() { return Stream.of( Arguments.of(AttributeConfig.of("ger", "ger"), @@ -91,4 +89,129 @@ class CoherentAttributeUpdaterTest { fileInfo.addTracks(List.of(tracks)); return Pair.of(file, fileInfo); } + + private static Stream process() { + return Stream.of( + Arguments.of( + arr(a("ger:ger")), a("ger:ger"), + List.of( + List.of(AUDIO_GER, SUB_GER), + List.of(AUDIO_GER, SUB_GER) + ), + List.of( + Map.ofEntries(on(AUDIO_GER), on(SUB_GER)), + Map.ofEntries(on(AUDIO_GER), on(SUB_GER)) + ), + List.of( + Map.ofEntries(), + Map.ofEntries() + ), + List.of( + Map.ofEntries(), + Map.ofEntries() + ), + List.of( + Map.ofEntries(), + Map.ofEntries() + ) + ), + Arguments.of( + arr(a("eng:eng"), a("ger:ger")), a("ger:ger"), + List.of( + List.of(SUB_ENG, AUDIO_GER, SUB_GER), + List.of(AUDIO_ENG, SUB_ENG, AUDIO_GER, SUB_GER) + ), + List.of( + Map.ofEntries(on(AUDIO_GER), on(SUB_GER)), + Map.ofEntries(on(AUDIO_GER), on(SUB_GER)) + ), + List.of( + Map.ofEntries(), + Map.ofEntries() + ), + List.of( + Map.ofEntries(), + Map.ofEntries() + ), + List.of( + Map.ofEntries(), + Map.ofEntries() + ) + ), + Arguments.of( + arr(a("eng:eng"), a("ger:ger")), a("eng:eng"), + List.of( + List.of(AUDIO_ENG, withName(SUB_ENG, "SDH"), AUDIO_GER, SUB_GER), + List.of(AUDIO_ENG, SUB_ENG, AUDIO_GER, SUB_GER) + ), + List.of( + Map.ofEntries(on(AUDIO_ENG), on(withName(SUB_ENG, "SDH"))), + Map.ofEntries(on(AUDIO_ENG), on(SUB_ENG)) + ), + List.of( + Map.ofEntries(), + Map.ofEntries() + ), + List.of( + Map.ofEntries(), + Map.ofEntries() + ), + List.of( + Map.ofEntries(on(withName(SUB_ENG, "SDH"))), + Map.ofEntries() + ) + ) + ); + } + + @ParameterizedTest + @MethodSource("process") + void process(AttributeConfig[] attributeConfigs, AttributeConfig expectedMatch, + List> tracks, + List> defaultExp, + List> forcedExp, + List> commentaryExp, + List> hearingImpairedExp) { + InputConfig config = new InputConfig(); + config.setThreads(1); + config.setSafeMode(true); + config.setAttributeConfig(attributeConfigs); + FileProcessor fileProcessor = spy(FileProcessor.class); + + List testMkvFiles = new ArrayList<>(); + List testFileInfo = new ArrayList<>(); + for (int i = 0; i < tracks.size(); i++) { + List tracks1 = tracks.get(i); + File file = new File(TEST_DIR + i); + FileInfo fileInfo = new FileInfo(file); + fileInfo.addTracks(tracks1); + doReturn(fileInfo).when(fileProcessor).readAttributes(file); + + testMkvFiles.add(file); + testFileInfo.add(fileInfo); + } + doReturn(testMkvFiles).when(fileProcessor).loadFiles(any()); + + AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{"pref"}, Set.of("forced"), Set.of("commentary"), Set.of("SDH")); + CoherentAttributeUpdater underTest = new CoherentAttributeUpdater(config, fileProcessor, attributeChangeProcessor); + + underTest.process(new File("")); + + for (int i = 0; i < testFileInfo.size(); i++) { + FileInfo fileInfo = testFileInfo.get(i); + assertEquals(expectedMatch, fileInfo.getMatchedConfig()); + assertEquals(fileInfo.getChanges().getDefaultTrack().size(), defaultExp.get(i).size()); + defaultExp.get(i).forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getDefaultTrack().get(key), "Default track flag")); + + assertEquals(fileInfo.getChanges().getForcedTrack().size(), forcedExp.get(i).size()); + forcedExp.get(i).forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getForcedTrack().get(key), "Forced track flag")); + + assertEquals(fileInfo.getChanges().getCommentaryTrack().size(), commentaryExp.get(i).size()); + commentaryExp.get(i).forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getCommentaryTrack().get(key), "Commentary track flag")); + + assertEquals(fileInfo.getChanges().getHearingImpairedTrack().size(), hearingImpairedExp.get(i).size()); + hearingImpairedExp.get(i).forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getHearingImpairedTrack().get(key), "Hearing Impaired track flag")); + } + + } } \ No newline at end of file From be004e6146d4a51936a241821e0e40f380aa62ca Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Sun, 28 Dec 2025 23:59:33 +0100 Subject: [PATCH 04/15] Add support for multiple library paths --- .../impl/processors/CoherentAttributeUpdater.java | 5 ++++- .../impl/processors/SingleFileAttributeUpdater.java | 5 ++++- .../impl/validation/ValidFileValidator.java | 7 ++++--- .../mkvaudiosubtitlechanger/model/InputConfig.java | 6 +++--- .../validation/ValidationExecutionStrategyTest.java | 10 +++++----- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java index 61459ad..5618ae1 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java @@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j; import me.tongfei.progressbar.ProgressBarBuilder; import java.io.File; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -25,7 +26,9 @@ public class CoherentAttributeUpdater extends SingleFileAttributeUpdater { } protected List getFiles() { - return fileProcessor.loadDirectory(config.getLibraryPath().getPath(), config.getCoherent()); + return Arrays.stream(config.getLibraryPaths()) + .flatMap(path -> fileProcessor.loadDirectory(path.getPath(), config.getCoherent()).stream()) + .toList(); } @Override diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java index a8c64ee..a75ee22 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java @@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j; import me.tongfei.progressbar.ProgressBarBuilder; import java.io.File; +import java.util.Arrays; import java.util.List; @Slf4j @@ -23,7 +24,9 @@ public class SingleFileAttributeUpdater extends AttributeUpdater { @Override protected List getFiles() { - return fileProcessor.loadFiles(config.getLibraryPath().getPath()); + return Arrays.stream(config.getLibraryPaths()) + .flatMap(path -> fileProcessor.loadFiles(path.getPath()).stream()) + .toList(); } @Override diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidFileValidator.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidFileValidator.java index b111865..0a7a278 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidFileValidator.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidFileValidator.java @@ -4,14 +4,15 @@ import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; import java.io.File; +import java.util.Arrays; -public class ValidFileValidator implements ConstraintValidator { +public class ValidFileValidator implements ConstraintValidator { @Override public void initialize(ValidFile constraintAnnotation) { } @Override - public boolean isValid(File file, ConstraintValidatorContext context) { - return file != null && file.exists(); + public boolean isValid(File[] files, ConstraintValidatorContext context) { + return files != null && files.length > 0 && Arrays.stream(files).allMatch(File::exists); } } diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java index bc9e69a..00a9418 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java @@ -29,8 +29,8 @@ public class InputConfig implements CommandLine.IVersionProvider { CommandLine.Model.CommandSpec spec; @ValidFile(message = "does not exist") - @CommandLine.Parameters(description = "path to library") - private File libraryPath; + @CommandLine.Parameters(description = "paths to library", arity = "1..*") + private File[] libraryPaths; @Option(names = {"-a", "--attribute-config"}, arity = "1..*", converter = AttributeConfigConverter.class, description = "List of audio:subtitle pairs for matching defaults in order (e.g. jpn:eng jpn:ger)") @@ -91,7 +91,7 @@ public class InputConfig implements CommandLine.IVersionProvider { return new StringJoiner(", ", InputConfig.class.getSimpleName() + "[", "]") .add("configPath=" + configPath) .add("spec=" + spec) - .add("libraryPath=" + libraryPath) + .add("libraryPath=" + libraryPaths) .add("attributeConfig=" + Arrays.toString(attributeConfig)) .add("mkvToolNix=" + mkvToolNix) .add("safeMode=" + safeMode) diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidationExecutionStrategyTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidationExecutionStrategyTest.java index 41a5d04..9ae7b45 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidationExecutionStrategyTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidationExecutionStrategyTest.java @@ -12,7 +12,6 @@ import java.io.StringWriter; import java.util.stream.Stream; import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.*; -import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.args; import static org.junit.jupiter.api.Assertions.*; class ValidationExecutionStrategyTest { @@ -24,16 +23,17 @@ class ValidationExecutionStrategyTest { .setExecutionStrategy(new ValidationExecutionStrategy()) .parseArgs("-a", "ger:ger", "-m", TEST_MKVTOOLNIX_DIR, TEST_FILE); - assertEquals(TEST_FILE, underTest.getConfig().getLibraryPath().getPath().replace("\\", "/")); + assertEquals(TEST_FILE, underTest.getConfig().getLibraryPaths()[0].getPath().replace("\\", "/")); assertEquals(TEST_MKVTOOLNIX_DIR, underTest.getConfig().getMkvToolNix().getPath().replace("\\", "/")); } private static Stream validateFailure() { return Stream.of( - Arguments.of(new String[]{"-a", "jpn:ger"}, "Error: Missing required argument(s): "), - Arguments.of(new String[]{"/arstarstarst"}, "libraryPath does not exist"), + Arguments.of(new String[]{"-a", "jpn:ger"}, "Error: Missing required argument(s): "), + Arguments.of(new String[]{"/arstarstarst"}, "libraryPaths does not exist"), + Arguments.of(new String[]{TEST_DIR + " /arstarstarst"}, "libraryPaths does not exist"), Arguments.of(new String[]{"/arstarstarst", "-a",}, "Missing required parameter for option '--attribute-config' at index 0 ()"), - Arguments.of(new String[]{"/arstarstarst", "-a", "jpn:ger"}, "libraryPath does not exist"), + Arguments.of(new String[]{"/arstarstarst", "-a", "jpn:ger"}, "libraryPaths does not exist"), Arguments.of(new String[]{"/arstarstarst", "-m"}, "Missing required parameter for option '--mkvtoolnix' ()"), Arguments.of(new String[]{"./", "-m", TEST_INVALID_DIR}, "mkvToolNix does not exist"), Arguments.of(new String[]{"./", "-t"}, "Missing required parameter for option '--threads' ()"), From 1165dd83802dfc16743b7feddf423000ca3f3348 Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Mon, 29 Dec 2025 00:12:28 +0100 Subject: [PATCH 05/15] Add support for multiple library paths --- .../impl/processors/CoherentAttributeUpdater.java | 2 +- .../impl/processors/SingleFileAttributeUpdater.java | 2 +- .../impl/validation/ValidFileValidator.java | 11 ++++++++++- .../mkvaudiosubtitlechanger/model/InputConfig.java | 4 ++-- .../validation/ValidationExecutionStrategyTest.java | 10 +++++----- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java index 5618ae1..595dd8b 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java @@ -26,7 +26,7 @@ public class CoherentAttributeUpdater extends SingleFileAttributeUpdater { } protected List getFiles() { - return Arrays.stream(config.getLibraryPaths()) + return Arrays.stream(config.getLibraryPath()) .flatMap(path -> fileProcessor.loadDirectory(path.getPath(), config.getCoherent()).stream()) .toList(); } diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java index a75ee22..7e6f97e 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java @@ -24,7 +24,7 @@ public class SingleFileAttributeUpdater extends AttributeUpdater { @Override protected List getFiles() { - return Arrays.stream(config.getLibraryPaths()) + return Arrays.stream(config.getLibraryPath()) .flatMap(path -> fileProcessor.loadFiles(path.getPath()).stream()) .toList(); } diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidFileValidator.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidFileValidator.java index 0a7a278..d3bec0c 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidFileValidator.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidFileValidator.java @@ -2,10 +2,12 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; +import lombok.extern.slf4j.Slf4j; import java.io.File; import java.util.Arrays; +@Slf4j public class ValidFileValidator implements ConstraintValidator { @Override public void initialize(ValidFile constraintAnnotation) { @@ -13,6 +15,13 @@ public class ValidFileValidator implements ConstraintValidator 0 && Arrays.stream(files).allMatch(File::exists); + if (files == null || files.length == 0) return false; + for (File file: files) { + if (!file.exists()) { + log.error("{} does not exist", file.getPath()); + return false; + } + } + return true; } } diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java index 00a9418..e610348 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java @@ -30,7 +30,7 @@ public class InputConfig implements CommandLine.IVersionProvider { @ValidFile(message = "does not exist") @CommandLine.Parameters(description = "paths to library", arity = "1..*") - private File[] libraryPaths; + private File[] libraryPath; @Option(names = {"-a", "--attribute-config"}, arity = "1..*", converter = AttributeConfigConverter.class, description = "List of audio:subtitle pairs for matching defaults in order (e.g. jpn:eng jpn:ger)") @@ -91,7 +91,7 @@ public class InputConfig implements CommandLine.IVersionProvider { return new StringJoiner(", ", InputConfig.class.getSimpleName() + "[", "]") .add("configPath=" + configPath) .add("spec=" + spec) - .add("libraryPath=" + libraryPaths) + .add("libraryPath=" + libraryPath) .add("attributeConfig=" + Arrays.toString(attributeConfig)) .add("mkvToolNix=" + mkvToolNix) .add("safeMode=" + safeMode) diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidationExecutionStrategyTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidationExecutionStrategyTest.java index 9ae7b45..a16a7c0 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidationExecutionStrategyTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/validation/ValidationExecutionStrategyTest.java @@ -23,17 +23,17 @@ class ValidationExecutionStrategyTest { .setExecutionStrategy(new ValidationExecutionStrategy()) .parseArgs("-a", "ger:ger", "-m", TEST_MKVTOOLNIX_DIR, TEST_FILE); - assertEquals(TEST_FILE, underTest.getConfig().getLibraryPaths()[0].getPath().replace("\\", "/")); + assertEquals(TEST_FILE, underTest.getConfig().getLibraryPath()[0].getPath().replace("\\", "/")); assertEquals(TEST_MKVTOOLNIX_DIR, underTest.getConfig().getMkvToolNix().getPath().replace("\\", "/")); } private static Stream validateFailure() { return Stream.of( - Arguments.of(new String[]{"-a", "jpn:ger"}, "Error: Missing required argument(s): "), - Arguments.of(new String[]{"/arstarstarst"}, "libraryPaths does not exist"), - Arguments.of(new String[]{TEST_DIR + " /arstarstarst"}, "libraryPaths does not exist"), + Arguments.of(new String[]{"-a", "jpn:ger"}, "Error: Missing required argument(s): "), + Arguments.of(new String[]{"/arstarstarst"}, "libraryPath does not exist"), + Arguments.of(new String[]{TEST_DIR, "/arstarstarst"}, "libraryPath does not exist"), Arguments.of(new String[]{"/arstarstarst", "-a",}, "Missing required parameter for option '--attribute-config' at index 0 ()"), - Arguments.of(new String[]{"/arstarstarst", "-a", "jpn:ger"}, "libraryPaths does not exist"), + Arguments.of(new String[]{"/arstarstarst", "-a", "jpn:ger"}, "libraryPath does not exist"), Arguments.of(new String[]{"/arstarstarst", "-m"}, "Missing required parameter for option '--mkvtoolnix' ()"), Arguments.of(new String[]{"./", "-m", TEST_INVALID_DIR}, "mkvToolNix does not exist"), Arguments.of(new String[]{"./", "-t"}, "Missing required parameter for option '--threads' ()"), From a5aae0acf4ba47a4b91144e791fa176223d05aec Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Thu, 8 Jan 2026 22:03:06 +0100 Subject: [PATCH 06/15] Add handler for only new files parameter --- .../impl/CommandRunner.java | 7 +- .../impl/FileFilter.java | 26 ++++--- .../impl/LastExecutionHandler.java | 61 ++++++++++++++++ .../impl/processors/AttributeUpdater.java | 8 ++- .../processors/CoherentAttributeUpdater.java | 5 +- .../SingleFileAttributeUpdater.java | 5 +- .../model/InputConfig.java | 4 +- .../impl/FileFilterTest.java | 2 +- .../impl/LastExecutionHandlerTest.java | 72 +++++++++++++++++++ .../impl/processors/AttributeUpdaterTest.java | 3 +- .../CoherentAttributeUpdaterTest.java | 8 ++- .../SingleFileAttributeUpdaterTest.java | 6 +- 12 files changed, 178 insertions(+), 29 deletions(-) create mode 100644 src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java create mode 100644 src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java index f818dad..4539d08 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java @@ -39,13 +39,14 @@ public class CommandRunner implements Runnable { System.out.println("Safemode active. No files will be changed!"); } - FileFilter fileFilter = new FileFilter(config.getExcluded(), config.getIncludePattern(), config.getFilterDate()); + LastExecutionHandler lastExecutionHandler = config.isOnlyNewFiles() ? new LastExecutionHandler("./last-executions.yml") : null; + FileFilter fileFilter = new FileFilter(config.getExcluded(), config.getIncludePattern(), config.getFilterDate(), lastExecutionHandler); FileProcessor fileProcessor = new CachedFileProcessor(new MkvFileProcessor(config.getMkvToolNix(), fileFilter)); AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(config.getPreferredSubtitles().toArray(new String[0]), config.getForcedKeywords(), config.getCommentaryKeywords(), config.getHearingImpaired()); AttributeUpdater kernel = config.getCoherent() != null - ? new CoherentAttributeUpdater(config, fileProcessor, attributeChangeProcessor) - : new SingleFileAttributeUpdater(config, fileProcessor, attributeChangeProcessor); + ? new CoherentAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler) + : new SingleFileAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler); kernel.execute(); } } diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java index 8f78dc6..b1045a0 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java @@ -9,9 +9,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.attribute.BasicFileAttributes; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -21,6 +19,7 @@ public class FileFilter { private final Set excluded; private final Pattern includePattern; private final Date filterDate; + private final LastExecutionHandler lastExecutionHandler; private final String EXTENSION_GROUP = "extension"; private final Pattern extensionPattern = Pattern.compile(String.format(".*(?<%s>\\..*)", EXTENSION_GROUP)); @@ -33,8 +32,9 @@ public class FileFilter { } if (!hasMatchingPattern(pathName) - || !isNewer(pathName) - || isExcluded(pathName, new HashSet<>(excluded))) { + || isExcluded(pathName, new HashSet<>(excluded)) + || lastExecutionHandler != null && !isNewOrChanged(pathName) + || !isNewer(pathName, filterDate)) { log.debug("Excluded {}", pathName); ResultStatistic.getInstance().excluded(); return false; @@ -52,19 +52,20 @@ public class FileFilter { return includePattern.matcher(pathName.getName()).matches(); } - private boolean isNewer(File pathName) { - if (filterDate == null) return true; + private boolean isNewer(File pathName, Date date) { + if (date == null) return true; try { BasicFileAttributes attributes = Files.readAttributes(pathName.toPath(), BasicFileAttributes.class); - return isNewer(DateUtils.convert(attributes.creationTime().toMillis())); + return isNewer(DateUtils.convert(attributes.creationTime().toMillis()), date) + || isNewer(DateUtils.convert(attributes.lastModifiedTime().toMillis()), date); } catch (IOException e) { log.warn("File attributes could not be read", e); } return true; } - private boolean isNewer(Date creationDate) { - return creationDate.toInstant().isAfter(filterDate.toInstant()); + private boolean isNewer(Date creationDate, Date compareDate) { + return creationDate.toInstant().isAfter(compareDate.toInstant()); } private boolean isExcluded(File pathName, Set excludedDirs) { @@ -86,4 +87,9 @@ public class FileFilter { return false; } + + private boolean isNewOrChanged(File pathname) { + Date lastExecutionDate = lastExecutionHandler.get(pathname.getAbsolutePath()); + return lastExecutionDate == null || isNewer(pathname, lastExecutionDate); + } } diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java new file mode 100644 index 0000000..596d41d --- /dev/null +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java @@ -0,0 +1,61 @@ +package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl; + +import at.pcgamingfreaks.yaml.YAML; +import at.pcgamingfreaks.yaml.YamlInvalidContentException; +import at.pcgamingfreaks.yaml.YamlKeyNotFoundException; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.IOException; +import java.time.Instant; +import java.util.Date; + +@Slf4j +public class LastExecutionHandler { + private final File file; + private YAML lastFileExecution; + + public LastExecutionHandler(String path) { + file = new File(path); + try { + lastFileExecution = loadLastFileExecution(file); + } catch (YamlInvalidContentException | IOException e) { + log.warn("Couldn't find or read {}", path, e); + } + } + + public YAML loadLastFileExecution(File file) throws YamlInvalidContentException, IOException { + if (file.exists() && file.isFile()) { + return new YAML(file); + } + return new YAML(""); + } + + public Date get(String path) { + if (!lastFileExecution.isSet(path)) return null; + try { + return Date.from(Instant.parse(lastFileExecution.getString(path))); + } catch (YamlKeyNotFoundException e) { + throw new RuntimeException(e); + } + } + + public void update(String path) { + update(path, Date.from(Instant.now())); + } + + public void update(String path, Date execution) { + if (lastFileExecution == null) return; + lastFileExecution.set(path, execution.toInstant()); + } + + public void persist() { + try { + lastFileExecution.save(file); + } catch (IOException e) { + log.warn("", e); + } + } +} + + diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdater.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdater.java index 228bcfc..2df82e5 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdater.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdater.java @@ -1,6 +1,7 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors; import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException; +import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic; @@ -25,14 +26,16 @@ public abstract class AttributeUpdater { protected final InputConfig config; protected final FileProcessor fileProcessor; protected final AttributeChangeProcessor attributeChangeProcessor; + protected final LastExecutionHandler lastExecutionHandler; protected final ResultStatistic statistic = ResultStatistic.getInstance(); private final ExecutorService executor; - public AttributeUpdater(InputConfig config, FileProcessor fileProcessor, AttributeChangeProcessor attributeChangeProcessor) { + public AttributeUpdater(InputConfig config, FileProcessor fileProcessor, AttributeChangeProcessor attributeChangeProcessor, LastExecutionHandler lastExecutionHandler) { this.config = config; this.fileProcessor = fileProcessor; this.attributeChangeProcessor = attributeChangeProcessor; + this.lastExecutionHandler = lastExecutionHandler; this.executor = Executors.newFixedThreadPool(config.getThreads()); } @@ -62,7 +65,7 @@ public abstract class AttributeUpdater { executor.awaitTermination(1, TimeUnit.DAYS); } -// writeLastExecutionDate(); + lastExecutionHandler.persist(); statistic.stopTimer(); statistic.print(); @@ -84,6 +87,7 @@ public abstract class AttributeUpdater { * @param fileInfo contains information about file and desired configuration. */ protected void checkStatusAndUpdate(FileInfo fileInfo) { + if (lastExecutionHandler != null) lastExecutionHandler.update(fileInfo.getFile().getAbsolutePath()); if (!fileInfo.getChanges().isEmpty()) { statistic.changePlanned(); diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java index 595dd8b..10cb59e 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdater.java @@ -1,5 +1,6 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors; +import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig; @@ -15,8 +16,8 @@ import java.util.Set; @Slf4j public class CoherentAttributeUpdater extends SingleFileAttributeUpdater { - public CoherentAttributeUpdater(InputConfig config, FileProcessor processor, AttributeChangeProcessor attributeChangeProcessor) { - super(config, processor, attributeChangeProcessor); + public CoherentAttributeUpdater(InputConfig config, FileProcessor processor, AttributeChangeProcessor attributeChangeProcessor, LastExecutionHandler lastExecutionHandler) { + super(config, processor, attributeChangeProcessor, lastExecutionHandler); } @Override diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java index 7e6f97e..6e4bd4f 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdater.java @@ -1,5 +1,6 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors; +import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig; import lombok.extern.slf4j.Slf4j; @@ -12,8 +13,8 @@ import java.util.List; @Slf4j public class SingleFileAttributeUpdater extends AttributeUpdater { - public SingleFileAttributeUpdater(InputConfig config, FileProcessor processor, AttributeChangeProcessor attributeChangeProcessor) { - super(config, processor, attributeChangeProcessor); + public SingleFileAttributeUpdater(InputConfig config, FileProcessor processor, AttributeChangeProcessor attributeChangeProcessor, LastExecutionHandler lastExecutionHandler) { + super(config, processor, attributeChangeProcessor, lastExecutionHandler); } @Override diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java index e610348..b0d4d56 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java @@ -53,8 +53,8 @@ public class InputConfig implements CommandLine.IVersionProvider { private boolean forceCoherent; // TODO: implement usage -// @Option(names = {"-n", "--only-new-file"}, description = "sets filter-date to last successful execution (overwrites input of filter-date)") -// private boolean onlyNewFiles; + @Option(names = {"-n", "--only-new-file"}, description = "ignores all files unchanged and previously processed") + private boolean onlyNewFiles; @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; @Option(names = {"-i", "--include-pattern"}, defaultValue = ".*", description = "include files matching pattern") diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilterTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilterTest.java index 5fae36f..acafa6d 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilterTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilterTest.java @@ -71,7 +71,7 @@ class FileFilterTest { when(file.toPath()).thenReturn(Path.of(TEST_FILE)); long currentTime = System.currentTimeMillis(); - FileFilter fileFilter = new FileFilter(excludedDirs, Pattern.compile(pattern), new Date(currentTime + filterDateOffset)); + FileFilter fileFilter = new FileFilter(excludedDirs, Pattern.compile(pattern), new Date(currentTime + filterDateOffset), null); try (MockedStatic mockedFiles = Mockito.mockStatic(DateUtils.class)) { mockedFiles diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java new file mode 100644 index 0000000..b079cca --- /dev/null +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java @@ -0,0 +1,72 @@ +package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.time.Instant; + +import static org.junit.jupiter.api.Assertions.*; + +class LastExecutionHandlerTest { + private static final String LAST_EXECUTION_YML = "./last-execution.yml"; + + @AfterEach + void destruct() { + File file = new File("./last-execution.yml"); + if (file.exists()) file.delete(); + } + + @Test + void missingFile() throws IOException { + LastExecutionHandler underTest = new LastExecutionHandler("./last-execution.yml"); + assertNull(underTest.get("/arst")); + underTest.update("/arst"); + assertNotNull(underTest.get("/arst")); + underTest.persist(); + File file = new File("./last-execution.yml"); + assertTrue(file.exists()); + assertTrue(Files.readString(file.toPath()).contains("/arst: ")); + } + + @Test + void emptyFile() throws IOException { + File file = new File(LAST_EXECUTION_YML); + file.createNewFile(); + missingFile(); // does the checks needed for empty file case + } + + @Test + void existingFileNoChanges() throws IOException { + File file = new File(LAST_EXECUTION_YML); + file.createNewFile(); + Files.writeString(file.toPath(), "/arst: \"" + Instant.now() + "\""); + String expected = Files.readString(file.toPath()); + + LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_YML); + assertNotNull(underTest.get("/arst")); + underTest.persist(); + File file1 = new File(LAST_EXECUTION_YML); + assertTrue(file1.exists()); + assertEquals(expected, Files.readString(file.toPath())); + } + + @Test + void existingFileWithChanges() throws IOException { + File file = new File(LAST_EXECUTION_YML); + file.createNewFile(); + Files.writeString(file.toPath(), "/arst: \"" + Instant.now() + "\""); + String expected = Files.readString(file.toPath()); + + LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_YML); + assertNotNull(underTest.get("/arst")); + underTest.update("/arst"); + assertNotNull(underTest.get("/arst")); + underTest.persist(); + File file1 = new File(LAST_EXECUTION_YML); + assertTrue(file1.exists()); + assertNotEquals(expected, Files.readString(file.toPath())); + } +} \ No newline at end of file diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdaterTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdaterTest.java index d40c790..a6a4140 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdaterTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdaterTest.java @@ -1,5 +1,6 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors; +import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; @@ -36,7 +37,7 @@ class AttributeUpdaterTest { InputConfig config = new InputConfig(); config.setThreads(1); config.setSafeMode(true); - AttributeUpdater underTest = new AttributeUpdater(config, null, null) { + AttributeUpdater underTest = new AttributeUpdater(config, null, null, new LastExecutionHandler("")) { @Override protected List getFiles() { return List.of(); diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java index 08ef0e5..c202f03 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/CoherentAttributeUpdaterTest.java @@ -1,12 +1,12 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors; import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.CommandRunner; +import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes; import org.apache.commons.lang3.tuple.Pair; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -66,7 +66,8 @@ class CoherentAttributeUpdaterTest { new CommandLine(commandRunner).parseArgs("-a", "ger:ger", "/arst"); InputConfig config = commandRunner.getConfig(); AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(config.getPreferredSubtitles().toArray(new String[0]), config.getForcedKeywords(), config.getCommentaryKeywords(), config.getHearingImpaired()); - CoherentAttributeUpdater updater = new CoherentAttributeUpdater(config, fileProcessor, attributeChangeProcessor); + LastExecutionHandler lastExecutionHandler = new LastExecutionHandler(""); + CoherentAttributeUpdater updater = new CoherentAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler); Set matchedFiles = new HashSet<>(fileInfoMock.size() * 2); List files = new ArrayList<>(); @@ -193,7 +194,8 @@ class CoherentAttributeUpdaterTest { doReturn(testMkvFiles).when(fileProcessor).loadFiles(any()); AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{"pref"}, Set.of("forced"), Set.of("commentary"), Set.of("SDH")); - CoherentAttributeUpdater underTest = new CoherentAttributeUpdater(config, fileProcessor, attributeChangeProcessor); + LastExecutionHandler lastExecutionHandler = new LastExecutionHandler(""); + CoherentAttributeUpdater underTest = new CoherentAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler); underTest.process(new File("")); diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdaterTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdaterTest.java index 3b19d02..ac549d6 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdaterTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/SingleFileAttributeUpdaterTest.java @@ -1,10 +1,10 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors; +import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -16,7 +16,6 @@ import java.util.Set; import java.util.stream.Stream; import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_DIR; -import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_FILE; import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TrackAttributeUtil.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; @@ -78,7 +77,8 @@ class SingleFileAttributeUpdaterTest { FileProcessor fileProcessor = spy(FileProcessor.class); doReturn(fileInfo).when(fileProcessor).readAttributes(any()); AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{"pref"}, Set.of("forced"), Set.of("commentary"), Set.of("SDH")); - SingleFileAttributeUpdater underTest = new SingleFileAttributeUpdater(config, fileProcessor, attributeChangeProcessor); + LastExecutionHandler lastExecutionHandler = new LastExecutionHandler(""); + SingleFileAttributeUpdater underTest = new SingleFileAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler); underTest.process(fileInfo.getFile()); From 8dbfb22ed88f746151f34a85a772d3bb473774ae Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Mon, 12 Jan 2026 19:01:10 +0100 Subject: [PATCH 07/15] Fix log4j2 and jackson.databind dependency --- pom.xml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 092264f..3935b53 100644 --- a/pom.xml +++ b/pom.xml @@ -381,19 +381,19 @@ org.apache.logging.log4j log4j-api - 2.25.3 + 3.0.0-beta2 org.apache.logging.log4j log4j-core - 2.25.3 + 3.0.0-beta2 org.apache.logging.log4j log4j-slf4j2-impl - 2.25.3 + 3.0.0-beta2 @@ -421,6 +421,13 @@ commons-lang3 3.20.0 + + + commons-io + commons-io + 2.21.0 + compile + me.tongfei From 3c57eb44ef6249ae985d1110a19d63397cdb866e Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Mon, 12 Jan 2026 19:02:06 +0100 Subject: [PATCH 08/15] Migrate last execution handler to properties --- .../impl/FileFilter.java | 16 +++---- .../impl/LastExecutionHandler.java | 48 ++++++++----------- .../impl/processors/MkvFileProcessor.java | 2 +- .../model/InputConfig.java | 2 +- .../impl/LastExecutionHandlerTest.java | 33 ++++++------- 5 files changed, 48 insertions(+), 53 deletions(-) diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java index b1045a0..4a84e70 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java @@ -1,7 +1,6 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic; -import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.DateUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -9,6 +8,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.attribute.BasicFileAttributes; +import java.time.Instant; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -53,21 +53,21 @@ public class FileFilter { } private boolean isNewer(File pathName, Date date) { + return isNewer(pathName, date.toInstant()); + } + + private boolean isNewer(File pathName, Instant date) { if (date == null) return true; try { BasicFileAttributes attributes = Files.readAttributes(pathName.toPath(), BasicFileAttributes.class); - return isNewer(DateUtils.convert(attributes.creationTime().toMillis()), date) - || isNewer(DateUtils.convert(attributes.lastModifiedTime().toMillis()), date); + return attributes.creationTime().toInstant().isAfter(date) + || attributes.lastModifiedTime().toInstant().isAfter(date); } catch (IOException e) { log.warn("File attributes could not be read", e); } return true; } - private boolean isNewer(Date creationDate, Date compareDate) { - return creationDate.toInstant().isAfter(compareDate.toInstant()); - } - private boolean isExcluded(File pathName, Set excludedDirs) { if (excludedDirs.contains(pathName.getPath())) return true; @@ -89,7 +89,7 @@ public class FileFilter { } private boolean isNewOrChanged(File pathname) { - Date lastExecutionDate = lastExecutionHandler.get(pathname.getAbsolutePath()); + Instant lastExecutionDate = lastExecutionHandler.get(pathname.getAbsolutePath()); return lastExecutionDate == null || isNewer(pathname, lastExecutionDate); } } diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java index 596d41d..5ac8f53 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java @@ -1,59 +1,53 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl; -import at.pcgamingfreaks.yaml.YAML; -import at.pcgamingfreaks.yaml.YamlInvalidContentException; -import at.pcgamingfreaks.yaml.YamlKeyNotFoundException; import lombok.extern.slf4j.Slf4j; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.time.Instant; -import java.util.Date; +import java.util.Properties; @Slf4j public class LastExecutionHandler { private final File file; - private YAML lastFileExecution; + private final Properties lastFileExecution; public LastExecutionHandler(String path) { file = new File(path); - try { - lastFileExecution = loadLastFileExecution(file); - } catch (YamlInvalidContentException | IOException e) { - log.warn("Couldn't find or read {}", path, e); - } + lastFileExecution = loadLastFileExecution(file); } - public YAML loadLastFileExecution(File file) throws YamlInvalidContentException, IOException { - if (file.exists() && file.isFile()) { - return new YAML(file); + public Properties loadLastFileExecution(File file) { + Properties properties = new Properties(); + try (FileInputStream in = new FileInputStream(file)) { + properties.load(in); + } catch (IOException e) { + log.warn("Couldn't find or read {}", file.getPath(), e); } - return new YAML(""); + return properties; } - public Date get(String path) { - if (!lastFileExecution.isSet(path)) return null; - try { - return Date.from(Instant.parse(lastFileExecution.getString(path))); - } catch (YamlKeyNotFoundException e) { - throw new RuntimeException(e); - } + public Instant get(String path) { + if (!lastFileExecution.containsKey(path)) return null; + return Instant.parse(lastFileExecution.getProperty(path)); } public void update(String path) { - update(path, Date.from(Instant.now())); + update(path, Instant.now()); } - public void update(String path, Date execution) { + public void update(String path, Instant execution) { if (lastFileExecution == null) return; - lastFileExecution.set(path, execution.toInstant()); + lastFileExecution.put(path, execution.toString()); } public void persist() { - try { - lastFileExecution.save(file); + try (FileOutputStream out = new FileOutputStream(file)) { + lastFileExecution.store(out, "MKVAudioSubtitleChanger - Last file execution"); } catch (IOException e) { - log.warn("", e); + log.warn("Persisting last file execution dates failed", e); } } } diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/MkvFileProcessor.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/MkvFileProcessor.java index 2d3c113..267879f 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/MkvFileProcessor.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/MkvFileProcessor.java @@ -5,7 +5,7 @@ import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileFilter; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.*; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.logging.log4j.core.util.IOUtils; +import org.apache.commons.io.IOUtils; import tools.jackson.databind.ObjectMapper; import java.io.File; diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java index b0d4d56..0a9db0e 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java @@ -53,7 +53,7 @@ public class InputConfig implements CommandLine.IVersionProvider { private boolean forceCoherent; // TODO: implement usage - @Option(names = {"-n", "--only-new-file"}, description = "ignores all files unchanged and previously processed") + @Option(names = {"-n", "--only-new-files"}, description = "ignores all files unchanged and previously processed") private boolean onlyNewFiles; @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; diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java index b079cca..70b8702 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java @@ -11,24 +11,25 @@ import java.time.Instant; import static org.junit.jupiter.api.Assertions.*; class LastExecutionHandlerTest { - private static final String LAST_EXECUTION_YML = "./last-execution.yml"; + private static final String LAST_EXECUTION_YML = "./last-execution.properties"; + private static final String TEST_MKV_FILE = "/arst/file.mkv"; @AfterEach void destruct() { - File file = new File("./last-execution.yml"); + File file = new File(LAST_EXECUTION_YML); if (file.exists()) file.delete(); } @Test void missingFile() throws IOException { - LastExecutionHandler underTest = new LastExecutionHandler("./last-execution.yml"); - assertNull(underTest.get("/arst")); - underTest.update("/arst"); - assertNotNull(underTest.get("/arst")); + LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_YML); + assertNull(underTest.get(TEST_MKV_FILE)); + underTest.update(TEST_MKV_FILE); + assertNotNull(underTest.get(TEST_MKV_FILE)); underTest.persist(); - File file = new File("./last-execution.yml"); + File file = new File(LAST_EXECUTION_YML); assertTrue(file.exists()); - assertTrue(Files.readString(file.toPath()).contains("/arst: ")); + assertTrue(Files.readString(file.toPath()).contains(TEST_MKV_FILE + "=")); } @Test @@ -42,28 +43,28 @@ class LastExecutionHandlerTest { void existingFileNoChanges() throws IOException { File file = new File(LAST_EXECUTION_YML); file.createNewFile(); - Files.writeString(file.toPath(), "/arst: \"" + Instant.now() + "\""); - String expected = Files.readString(file.toPath()); + Files.writeString(file.toPath(), TEST_MKV_FILE + "=" + Instant.now()); + String expected = Files.readString(file.toPath()).replace(":", "\\:"); LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_YML); - assertNotNull(underTest.get("/arst")); + assertNotNull(underTest.get(TEST_MKV_FILE)); underTest.persist(); File file1 = new File(LAST_EXECUTION_YML); assertTrue(file1.exists()); - assertEquals(expected, Files.readString(file.toPath())); + assertTrue(Files.readString(file.toPath()).contains(expected), "File contains expected value"); } @Test void existingFileWithChanges() throws IOException { File file = new File(LAST_EXECUTION_YML); file.createNewFile(); - Files.writeString(file.toPath(), "/arst: \"" + Instant.now() + "\""); + Files.writeString(file.toPath(), TEST_MKV_FILE + "=" + Instant.now()); String expected = Files.readString(file.toPath()); LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_YML); - assertNotNull(underTest.get("/arst")); - underTest.update("/arst"); - assertNotNull(underTest.get("/arst")); + assertNotNull(underTest.get(TEST_MKV_FILE)); + underTest.update(TEST_MKV_FILE); + assertNotNull(underTest.get(TEST_MKV_FILE)); underTest.persist(); File file1 = new File(LAST_EXECUTION_YML); assertTrue(file1.exists()); From 41e107ef85a61edf743f03e9cb3349fb2be6af58 Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Tue, 13 Jan 2026 15:35:16 +0100 Subject: [PATCH 09/15] Migrate from date to instant --- .../impl/FileFilter.java | 6 +--- .../model/InputConfig.java | 3 +- .../util/DateUtils.java | 29 ------------------- .../impl/FileFilterTest.java | 18 +++++++----- .../util/DateUtilsTest.java | 23 --------------- 5 files changed, 13 insertions(+), 66 deletions(-) delete mode 100644 src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/DateUtils.java delete mode 100644 src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/DateUtilsTest.java diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java index 4a84e70..1d7d79b 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java @@ -18,7 +18,7 @@ import java.util.regex.Pattern; public class FileFilter { private final Set excluded; private final Pattern includePattern; - private final Date filterDate; + private final Instant filterDate; private final LastExecutionHandler lastExecutionHandler; private final String EXTENSION_GROUP = "extension"; @@ -52,10 +52,6 @@ public class FileFilter { return includePattern.matcher(pathName.getName()).matches(); } - private boolean isNewer(File pathName, Date date) { - return isNewer(pathName, date.toInstant()); - } - private boolean isNewer(File pathName, Instant date) { if (date == null) return true; try { diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java index 0a9db0e..5e9c629 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java @@ -12,6 +12,7 @@ import org.apache.commons.lang3.SystemUtils; import picocli.CommandLine; import java.io.File; +import java.time.Instant; import java.util.*; import java.util.regex.Pattern; import picocli.CommandLine.Option; @@ -56,7 +57,7 @@ public class InputConfig implements CommandLine.IVersionProvider { @Option(names = {"-n", "--only-new-files"}, description = "ignores all files unchanged and previously processed") private boolean onlyNewFiles; @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 Instant filterDate; @Option(names = {"-i", "--include-pattern"}, defaultValue = ".*", description = "include files matching pattern") private Pattern includePattern; @Option(names = {"-e", "--exclude"}, arity = "1..*", diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/DateUtils.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/DateUtils.java deleted file mode 100644 index 61eaa20..0000000 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/DateUtils.java +++ /dev/null @@ -1,29 +0,0 @@ -package at.pcgamingfreaks.mkvaudiosubtitlechanger.util; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; - -public class DateUtils { - private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy-HH:mm:ss"); - - public static Date convert(long millis) { - return new Date(millis); - } - - /** - * Convert String to date. - * @return parsed date, defaultDate if exception occurs - */ - public static Date convert(String date, Date defaultDate) { - try { - return dateFormat.parse(date); - } catch (ParseException e) { - return defaultDate; - } - } - - public static String convert(Date date) { - return dateFormat.format(date); - } -} diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilterTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilterTest.java index acafa6d..0ce5d1f 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilterTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilterTest.java @@ -1,8 +1,6 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl; -import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic; -import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.DateUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -14,11 +12,13 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.File; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; -import java.util.Date; +import java.nio.file.attribute.FileTime; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -71,12 +71,14 @@ class FileFilterTest { when(file.toPath()).thenReturn(Path.of(TEST_FILE)); long currentTime = System.currentTimeMillis(); - FileFilter fileFilter = new FileFilter(excludedDirs, Pattern.compile(pattern), new Date(currentTime + filterDateOffset), null); + when(attributes.creationTime()).thenReturn(FileTime.fromMillis(currentTime)); + when(attributes.lastModifiedTime()).thenReturn(FileTime.fromMillis(currentTime)); + FileFilter fileFilter = new FileFilter(excludedDirs, Pattern.compile(pattern), Instant.ofEpochMilli(currentTime).plus(filterDateOffset, ChronoUnit.SECONDS), null); - try (MockedStatic mockedFiles = Mockito.mockStatic(DateUtils.class)) { + try (MockedStatic mockedFiles = Mockito.mockStatic(Files.class)) { mockedFiles - .when(() -> DateUtils.convert(anyLong())) - .thenReturn(new Date(currentTime)); + .when(() -> Files.readAttributes(any(), eq(BasicFileAttributes.class))) + .thenReturn(attributes); assertEquals(acceptanceExpected, fileFilter.accept(file, new HashSet<>(extensions)), "File is accepted"); assertEquals(excluded, ResultStatistic.getInstance().getExcluded() > 0, "Is counted in excluded statistic"); diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/DateUtilsTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/DateUtilsTest.java deleted file mode 100644 index 917a8ea..0000000 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/util/DateUtilsTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package at.pcgamingfreaks.mkvaudiosubtitlechanger.util; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import java.util.Date; - -import static org.junit.jupiter.api.Assertions.*; - -@Disabled -class DateUtilsTest { - - @Test - void convert() { - Date expectedDate = new Date(0); - String expectedString = "01.01.1970-01:00:00"; - - assertEquals(expectedDate, DateUtils.convert(0)); - assertEquals(expectedDate, DateUtils.convert(expectedString, expectedDate)); - assertEquals(expectedDate, DateUtils.convert("1234;15", expectedDate)); - assertEquals(expectedString, DateUtils.convert(expectedDate)); - } -} \ No newline at end of file From 906ec944eb8ec178cdb8bb0ef2b95816143ce5d4 Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Tue, 13 Jan 2026 15:50:38 +0100 Subject: [PATCH 10/15] Fix filter-date format & add test for filter-date --- .../impl/FileFilter.java | 7 +----- .../model/InputConfig.java | 2 +- .../config/PicoCliTest.java | 23 +++++++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/config/PicoCliTest.java diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java index 1d7d79b..938283b 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/FileFilter.java @@ -33,7 +33,7 @@ public class FileFilter { if (!hasMatchingPattern(pathName) || isExcluded(pathName, new HashSet<>(excluded)) - || lastExecutionHandler != null && !isNewOrChanged(pathName) + || lastExecutionHandler != null && !isNewer(pathName, lastExecutionHandler.get(pathName.getAbsolutePath())) || !isNewer(pathName, filterDate)) { log.debug("Excluded {}", pathName); ResultStatistic.getInstance().excluded(); @@ -83,9 +83,4 @@ public class FileFilter { return false; } - - private boolean isNewOrChanged(File pathname) { - Instant lastExecutionDate = lastExecutionHandler.get(pathname.getAbsolutePath()); - return lastExecutionDate == null || isNewer(pathname, lastExecutionDate); - } } diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java index 5e9c629..76a5dd9 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/model/InputConfig.java @@ -56,7 +56,7 @@ public class InputConfig implements CommandLine.IVersionProvider { // TODO: implement usage @Option(names = {"-n", "--only-new-files"}, description = "ignores all files unchanged and previously processed") private boolean onlyNewFiles; - @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 (following ISO-8601 yyyy-MM-ddTHH:mm:ss.sssZ)") private Instant filterDate; @Option(names = {"-i", "--include-pattern"}, defaultValue = ".*", description = "include files matching pattern") private Pattern includePattern; diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/config/PicoCliTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/config/PicoCliTest.java new file mode 100644 index 0000000..e0b30e8 --- /dev/null +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/config/PicoCliTest.java @@ -0,0 +1,23 @@ +package at.pcgamingfreaks.mkvaudiosubtitlechanger.config; + +import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.CommandRunner; +import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation.ValidationExecutionStrategy; +import org.junit.jupiter.api.Test; +import picocli.CommandLine; + +import java.time.Instant; + +import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_FILE; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PicoCliTest { + + @Test + void loadFilterDate() { + CommandRunner underTest = new CommandRunner(); + new CommandLine(underTest) + .setExecutionStrategy(new ValidationExecutionStrategy()) + .parseArgs("-d", "2012-12-12T12:12:12.00Z", TEST_FILE); + assertEquals(Instant.parse("2012-12-12T12:12:12.00Z"), underTest.getConfig().getFilterDate()); + } +} From ff38457af1fdf275f4016d1fe5789f6d7bb35b14 Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Thu, 15 Jan 2026 11:35:16 +0100 Subject: [PATCH 11/15] Revert lib updates of jackson & log4j2 --- pom.xml | 18 +++++++++--------- .../impl/processors/MkvFileProcessor.java | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 3935b53..750a7b1 100644 --- a/pom.xml +++ b/pom.xml @@ -381,33 +381,33 @@ org.apache.logging.log4j log4j-api - 3.0.0-beta2 + 2.25.3 org.apache.logging.log4j log4j-core - 3.0.0-beta2 + 2.25.3 org.apache.logging.log4j log4j-slf4j2-impl - 3.0.0-beta2 + 2.25.3 - + - tools.jackson.dataformat + com.fasterxml.jackson.dataformat jackson-dataformat-yaml - 3.0.3 + 2.20.0 - + - tools.jackson.core + com.fasterxml.jackson.core jackson-databind - 3.0.3 + 2.20.0 diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/MkvFileProcessor.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/MkvFileProcessor.java index 267879f..5b207c4 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/MkvFileProcessor.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/MkvFileProcessor.java @@ -3,10 +3,10 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors; import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException; import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileFilter; import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.*; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; -import tools.jackson.databind.ObjectMapper; import java.io.File; import java.io.IOException; From cce84f5c15889af8cdcc078d603e1aee84d93301 Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Thu, 15 Jan 2026 12:40:09 +0100 Subject: [PATCH 12/15] Make path of lastFileExecution platform dependent --- .../impl/CommandRunner.java | 28 ++++++++++++++++++- .../impl/LastExecutionHandler.java | 5 ++-- .../impl/LastExecutionHandlerTest.java | 23 +++++++-------- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java index 4539d08..c9f2827 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java @@ -6,6 +6,10 @@ import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.ProjectUtil; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.appender.RollingFileAppender; +import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.Configurator; import picocli.CommandLine; @@ -39,7 +43,14 @@ public class CommandRunner implements Runnable { System.out.println("Safemode active. No files will be changed!"); } - LastExecutionHandler lastExecutionHandler = config.isOnlyNewFiles() ? new LastExecutionHandler("./last-executions.yml") : null; + String userLocal = getLogDirectory(); + if (userLocal == null) { + log.error("Could not load log4j2 log info"); + System.out.println("Could not load log4j2 log info"); + System.exit(1); + } + + LastExecutionHandler lastExecutionHandler = config.isOnlyNewFiles() ? new LastExecutionHandler(userLocal) : null; FileFilter fileFilter = new FileFilter(config.getExcluded(), config.getIncludePattern(), config.getFilterDate(), lastExecutionHandler); FileProcessor fileProcessor = new CachedFileProcessor(new MkvFileProcessor(config.getMkvToolNix(), fileFilter)); AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(config.getPreferredSubtitles().toArray(new String[0]), config.getForcedKeywords(), config.getCommentaryKeywords(), config.getHearingImpaired()); @@ -49,4 +60,19 @@ public class CommandRunner implements Runnable { : new SingleFileAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler); kernel.execute(); } + + public String getLogDirectory() { + LoggerContext context = (LoggerContext) LogManager.getContext(false); + Configuration config = context.getConfiguration(); + + for (org.apache.logging.log4j.core.Appender appender : config.getAppenders().values()) { + if (appender instanceof RollingFileAppender rollingFileAppender) { + String fileName = rollingFileAppender.getFileName(); + return new java.io.File(fileName).getParentFile().getParent(); + } + } + + log.warn("No file appender found in configuration"); + return null; + } } diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java index 5ac8f53..16d4472 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandler.java @@ -11,11 +11,12 @@ import java.util.Properties; @Slf4j public class LastExecutionHandler { + private static final String FILE_NAME = "last-execution.properties"; private final File file; private final Properties lastFileExecution; public LastExecutionHandler(String path) { - file = new File(path); + file = new File(path + "/" + FILE_NAME); lastFileExecution = loadLastFileExecution(file); } @@ -24,7 +25,7 @@ public class LastExecutionHandler { try (FileInputStream in = new FileInputStream(file)) { properties.load(in); } catch (IOException e) { - log.warn("Couldn't find or read {}", file.getPath(), e); + log.info("Couldn't find or read {}", file.getPath(), e); } return properties; } diff --git a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java index 70b8702..51e002c 100644 --- a/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java +++ b/src/test/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/LastExecutionHandlerTest.java @@ -11,62 +11,63 @@ import java.time.Instant; import static org.junit.jupiter.api.Assertions.*; class LastExecutionHandlerTest { - private static final String LAST_EXECUTION_YML = "./last-execution.properties"; + private static final String LAST_EXECUTION_PATH = "."; + private static final String LAST_EXECUTION_FILE = "./last-execution.properties"; private static final String TEST_MKV_FILE = "/arst/file.mkv"; @AfterEach void destruct() { - File file = new File(LAST_EXECUTION_YML); + File file = new File(LAST_EXECUTION_FILE); if (file.exists()) file.delete(); } @Test void missingFile() throws IOException { - LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_YML); + LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_PATH); assertNull(underTest.get(TEST_MKV_FILE)); underTest.update(TEST_MKV_FILE); assertNotNull(underTest.get(TEST_MKV_FILE)); underTest.persist(); - File file = new File(LAST_EXECUTION_YML); + File file = new File(LAST_EXECUTION_FILE); assertTrue(file.exists()); assertTrue(Files.readString(file.toPath()).contains(TEST_MKV_FILE + "=")); } @Test void emptyFile() throws IOException { - File file = new File(LAST_EXECUTION_YML); + File file = new File(LAST_EXECUTION_FILE); file.createNewFile(); missingFile(); // does the checks needed for empty file case } @Test void existingFileNoChanges() throws IOException { - File file = new File(LAST_EXECUTION_YML); + File file = new File(LAST_EXECUTION_FILE); file.createNewFile(); Files.writeString(file.toPath(), TEST_MKV_FILE + "=" + Instant.now()); String expected = Files.readString(file.toPath()).replace(":", "\\:"); - LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_YML); + LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_PATH); assertNotNull(underTest.get(TEST_MKV_FILE)); underTest.persist(); - File file1 = new File(LAST_EXECUTION_YML); + File file1 = new File(LAST_EXECUTION_FILE); assertTrue(file1.exists()); assertTrue(Files.readString(file.toPath()).contains(expected), "File contains expected value"); } @Test void existingFileWithChanges() throws IOException { - File file = new File(LAST_EXECUTION_YML); + File file = new File(LAST_EXECUTION_FILE); file.createNewFile(); Files.writeString(file.toPath(), TEST_MKV_FILE + "=" + Instant.now()); String expected = Files.readString(file.toPath()); - LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_YML); + LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_PATH); assertNotNull(underTest.get(TEST_MKV_FILE)); underTest.update(TEST_MKV_FILE); assertNotNull(underTest.get(TEST_MKV_FILE)); underTest.persist(); - File file1 = new File(LAST_EXECUTION_YML); + File file1 = new File(LAST_EXECUTION_FILE); assertTrue(file1.exists()); assertNotEquals(expected, Files.readString(file.toPath())); } From a97ed89d089f0ffc0fcb5ebc2d8bb0a2e1b69d12 Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Thu, 15 Jan 2026 13:32:35 +0100 Subject: [PATCH 13/15] Parameterize log4j2 configs --- pom.xml | 6 +++--- src/deb/bin/mkvaudiosubtitlechanger | 2 +- .../impl/CommandRunner.java | 15 +++++---------- .../{log4j2.yaml => log4j2-debian.yml} | 16 ++++++++++++---- .../{log4j2-dev.yaml => log4j2-dev.yml} | 17 ++++++++++++----- .../{log4j2-debian.yaml => log4j2-windows.yml} | 18 +++++++++++++----- .../{log4j2-windows.yaml => log4j2.yml} | 16 ++++++++++++---- 7 files changed, 58 insertions(+), 32 deletions(-) rename src/main/resources/{log4j2.yaml => log4j2-debian.yml} (63%) rename src/main/resources/{log4j2-dev.yaml => log4j2-dev.yml} (69%) rename src/main/resources/{log4j2-debian.yaml => log4j2-windows.yml} (59%) rename src/main/resources/{log4j2-windows.yaml => log4j2.yml} (66%) diff --git a/pom.xml b/pom.xml index 750a7b1..fe821ef 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ src/main/resources - log4j2.yaml + log4j2.yml @@ -64,7 +64,7 @@ src/main/resources - log4j2-windows.yaml + log4j2-windows.yml @@ -161,7 +161,7 @@ src/main/resources - log4j2-debian.yaml + log4j2-debian.yml diff --git a/src/deb/bin/mkvaudiosubtitlechanger b/src/deb/bin/mkvaudiosubtitlechanger index dcc8207..c0bbcab 100644 --- a/src/deb/bin/mkvaudiosubtitlechanger +++ b/src/deb/bin/mkvaudiosubtitlechanger @@ -1 +1 @@ -java -Dlog4j2.configurationFile=log4j2-debian.yaml -jar /usr/lib/${project.artifactId}/${project.artifactId}-${project.version}.jar "$@" \ No newline at end of file +java -Dlog4j2.configurationFile=log4j2-debian.yml -jar /usr/lib/${project.artifactId}/${project.artifactId}-${project.version}.jar "$@" \ No newline at end of file diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java index c9f2827..fd65ff2 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/CommandRunner.java @@ -43,14 +43,7 @@ public class CommandRunner implements Runnable { System.out.println("Safemode active. No files will be changed!"); } - String userLocal = getLogDirectory(); - if (userLocal == null) { - log.error("Could not load log4j2 log info"); - System.out.println("Could not load log4j2 log info"); - System.exit(1); - } - - LastExecutionHandler lastExecutionHandler = config.isOnlyNewFiles() ? new LastExecutionHandler(userLocal) : null; + LastExecutionHandler lastExecutionHandler = config.isOnlyNewFiles() ? new LastExecutionHandler(getApplicationHome()) : null; FileFilter fileFilter = new FileFilter(config.getExcluded(), config.getIncludePattern(), config.getFilterDate(), lastExecutionHandler); FileProcessor fileProcessor = new CachedFileProcessor(new MkvFileProcessor(config.getMkvToolNix(), fileFilter)); AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(config.getPreferredSubtitles().toArray(new String[0]), config.getForcedKeywords(), config.getCommentaryKeywords(), config.getHearingImpaired()); @@ -61,7 +54,7 @@ public class CommandRunner implements Runnable { kernel.execute(); } - public String getLogDirectory() { + public String getApplicationHome() { LoggerContext context = (LoggerContext) LogManager.getContext(false); Configuration config = context.getConfiguration(); @@ -72,7 +65,9 @@ public class CommandRunner implements Runnable { } } - log.warn("No file appender found in configuration"); + log.error("Could not load log4j2 path info"); + System.out.println("Could not load log4j2 path info"); + System.exit(1); return null; } } diff --git a/src/main/resources/log4j2.yaml b/src/main/resources/log4j2-debian.yml similarity index 63% rename from src/main/resources/log4j2.yaml rename to src/main/resources/log4j2-debian.yml index b909c8d..109f2b2 100644 --- a/src/main/resources/log4j2.yaml +++ b/src/main/resources/log4j2-debian.yml @@ -1,12 +1,20 @@ Configuration: - name: DefaultLogger + name: MainConfig + + Properties: + Property: + - name: logDir + value: ${sys:user.home}/.local/mkvaudiosubtitlechanger/logs + - name: logPattern + value: "%d{DEFAULT} | %-5level | %msg %n %throwable" + Appenders: RollingFile: name: FileAppender - fileName: logs/application.log - filePattern: logs/archive/application-%d{yyyy-MM-dd}-%i.log.gz + fileName: ${logDir}/application.log + filePattern: ${logDir}/archive/application-%d{yyyy-MM-dd}-%i.log.gz PatternLayout: - Pattern: "%d{DEFAULT} | %-5level | %msg %n %throwable" + Pattern: logPattern ThresholdFilter: level: debug Policies: diff --git a/src/main/resources/log4j2-dev.yaml b/src/main/resources/log4j2-dev.yml similarity index 69% rename from src/main/resources/log4j2-dev.yaml rename to src/main/resources/log4j2-dev.yml index 71280f7..4949d72 100644 --- a/src/main/resources/log4j2-dev.yaml +++ b/src/main/resources/log4j2-dev.yml @@ -1,18 +1,25 @@ Configuration: - name: DefaultLogger + + Properties: + Property: + - name: logDir + value: "./logs" + - name: log_pattern + value: "%d{DEFAULT} | %-5level | %thread | %C{1} | %msg %n %throwable" + Appenders: Console: name: Console_Out PatternLayout: - Pattern: "%d{DEFAULT} | %-5level | %thread | %C{1} | %msg %n %throwable" + Pattern: ${log_pattern} ThresholdFilter: level: debug RollingFile: name: FileAppender - fileName: logs/application.log - filePattern: logs/archive/application-%d{yyyy-MM-dd}-%i.log.gz + fileName: ${logDir}/application.log + filePattern: ${logDir}/archive/application-%d{yyyy-MM-dd}-%i.log.gz PatternLayout: - Pattern: "%d{DEFAULT} | %-5level | %thread | %C{1} | %msg %n %throwable" + Pattern: logPattern ThresholdFilter: level: debug Policies: diff --git a/src/main/resources/log4j2-debian.yaml b/src/main/resources/log4j2-windows.yml similarity index 59% rename from src/main/resources/log4j2-debian.yaml rename to src/main/resources/log4j2-windows.yml index 1ad3cd5..8c24f4e 100644 --- a/src/main/resources/log4j2-debian.yaml +++ b/src/main/resources/log4j2-windows.yml @@ -1,12 +1,20 @@ Configuration: - name: DefaultLogger + name: WindowsConfig + + Properties: + Property: + - name: logDir + value: ${sys:user.home}/AppData/Local/MKVAudioSubtitleChanger/logs + - name: logPattern + value: "%d{DEFAULT} | %-5level | %msg %n %throwable" + Appenders: RollingFile: name: FileAppender - fileName: ${sys:user.home}/.local/mkvaudiosubtitlechanger/logs/application.log - filePattern: ${sys:user.home}/.local/mkvaudiosubtitlechanger/logs/archive/application-%d{yyyy-MM-dd}-%i.log.gz + fileName: ${logDir}/application.log + filePattern: ${logDir}/archive/application-%d{yyyy-MM-dd}-%i.log.gz PatternLayout: - Pattern: "%d{DEFAULT} | %-5level | %msg %n %throwable" + Pattern: logPattern ThresholdFilter: level: debug Policies: @@ -15,7 +23,7 @@ Configuration: DefaultRolloverStrategy: max: 30 Delete: - basePath: archive + basePath: logs/archive maxDepth: 1 IfLastModified: age: 30d diff --git a/src/main/resources/log4j2-windows.yaml b/src/main/resources/log4j2.yml similarity index 66% rename from src/main/resources/log4j2-windows.yaml rename to src/main/resources/log4j2.yml index e39795f..3a01cc2 100644 --- a/src/main/resources/log4j2-windows.yaml +++ b/src/main/resources/log4j2.yml @@ -1,12 +1,20 @@ Configuration: - name: DefaultLogger + name: MainConfig + + Properties: + Property: + - name: logDir + value: ./logs + - name: logPattern + value: "%d{DEFAULT} | %-5level | %msg %n %throwable" + Appenders: RollingFile: name: FileAppender - fileName: ${sys:user.home}/AppData/Local/MKVAudioSubtitleChanger/logs/application.log - filePattern: ${sys:user.home}/AppData/Local/MKVAudioSubtitleChanger/logs/archive/application-%d{yyyy-MM-dd}-%i.log.gz + fileName: ${logDir}/application.log + filePattern: ${logDir}/archive/application-%d{yyyy-MM-dd}-%i.log.gz PatternLayout: - Pattern: "%d{DEFAULT} | %-5level | %msg %n %throwable" + Pattern: logPattern ThresholdFilter: level: debug Policies: From a5fce22b95bc4b2b507a8140fe44066c901fcbe8 Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Thu, 15 Jan 2026 21:14:54 +0100 Subject: [PATCH 14/15] Fix lastExecutionHandler NPE --- .../impl/processors/AttributeUpdater.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdater.java b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdater.java index 2df82e5..38caa1a 100644 --- a/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdater.java +++ b/src/main/java/at/pcgamingfreaks/mkvaudiosubtitlechanger/impl/processors/AttributeUpdater.java @@ -65,7 +65,7 @@ public abstract class AttributeUpdater { executor.awaitTermination(1, TimeUnit.DAYS); } - lastExecutionHandler.persist(); + if (lastExecutionHandler != null) lastExecutionHandler.persist(); statistic.stopTimer(); statistic.print(); From e80331beefa7b1d2c8b6d2f30fcd53390a884775 Mon Sep 17 00:00:00 2001 From: RatzzFatzz Date: Thu, 15 Jan 2026 21:16:10 +0100 Subject: [PATCH 15/15] Fix log4j2 file link on windows --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fe821ef..4699ade 100644 --- a/pom.xml +++ b/pom.xml @@ -138,7 +138,7 @@ false false - -Dlog4j2.configurationFile=log4j2-windows.yaml + -Dlog4j2.configurationFile=log4j2-windows.yml