mirror of
https://github.com/RatzzFatzz/MKVAudioSubtitleChanger.git
synced 2026-02-10 17:55:57 +01:00
Reimplement coherent updating
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class LoadFilesTest {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
int depth = 2;
|
||||||
|
|
||||||
|
getDirectoriesAtDepth("/mnt/media/Anime", 2);
|
||||||
|
|
||||||
|
try (Stream<Path> paths = Files.walk(Paths.get("/mnt/media/Anime"), 2)) {
|
||||||
|
List<File> result = paths.map(Path::toFile)
|
||||||
|
.filter(File::isDirectory)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
System.out.println(result);
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<File> getDirectoriesAtDepth(String path, int depth) {
|
||||||
|
List<File> result = new ArrayList<>();
|
||||||
|
File rootDir = Path.of(path).toFile();
|
||||||
|
if (!rootDir.exists()) throw new RuntimeException("Invalid path");
|
||||||
|
|
||||||
|
exploreDirectory(rootDir, 0, depth, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively explores directories to find items at the target depth.
|
||||||
|
*
|
||||||
|
* @param currentDir The current directory being explored
|
||||||
|
* @param currentDepth The current depth level
|
||||||
|
* @param targetDepth The target depth to collect files
|
||||||
|
* @param result The collection to store found files
|
||||||
|
*/
|
||||||
|
private static void exploreDirectory(File currentDir, int currentDepth, int targetDepth, List<File> result) {
|
||||||
|
if (currentDepth == targetDepth) {
|
||||||
|
// We've reached the target depth, add this directory to results
|
||||||
|
result.add(currentDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all files and directories in the current directory
|
||||||
|
File[] files = currentDir.listFiles();
|
||||||
|
if (files == null) return;
|
||||||
|
|
||||||
|
// Recursively explore subdirectories
|
||||||
|
for (File file : files) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
exploreDirectory(file, currentDepth + 1, targetDepth, result);
|
||||||
|
} else if (currentDepth + 1 == targetDepth) {
|
||||||
|
// If files at the next level would be at the target depth, include them
|
||||||
|
result.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel.AttributeUpdaterKernel;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.AttributeUpdater;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel.CoherentAttributeUpdaterKernel;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.CoherentAttributeUpdater;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel.DefaultAttributeUpdaterKernel;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.SingleFileAttributeUpdater;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.CachedFileProcessor;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.CachedFileProcessor;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.FileProcessor;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.FileProcessor;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.MkvFileProcessor;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.MkvFileProcessor;
|
||||||
@@ -42,9 +42,9 @@ public class CommandRunner implements Runnable {
|
|||||||
FileFilter fileFilter = new FileFilter(config.getExcluded(), config.getIncludePattern(), config.getFilterDate());
|
FileFilter fileFilter = new FileFilter(config.getExcluded(), config.getIncludePattern(), config.getFilterDate());
|
||||||
FileProcessor fileProcessor = new CachedFileProcessor(new MkvFileProcessor(config.getMkvToolNix(), fileFilter));
|
FileProcessor fileProcessor = new CachedFileProcessor(new MkvFileProcessor(config.getMkvToolNix(), fileFilter));
|
||||||
|
|
||||||
AttributeUpdaterKernel kernel = config.getCoherent() != null
|
AttributeUpdater kernel = config.getCoherent() != null
|
||||||
? new CoherentAttributeUpdaterKernel(config, fileProcessor)
|
? new CoherentAttributeUpdater(config, fileProcessor)
|
||||||
: new DefaultAttributeUpdaterKernel(config, fileProcessor);
|
: new SingleFileAttributeUpdater(config, fileProcessor);
|
||||||
kernel.execute();
|
kernel.execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel;
|
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.FileProcessor;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import me.tongfei.progressbar.ProgressBarBuilder;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class CoherentAttributeUpdaterKernel extends AttributeUpdaterKernel {
|
|
||||||
|
|
||||||
public CoherentAttributeUpdaterKernel(InputConfig config, FileProcessor processor) {
|
|
||||||
super(config, processor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ProgressBarBuilder pbBuilder() {
|
|
||||||
return super.pbBuilder()
|
|
||||||
.setUnit(" directories", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<File> loadFiles(String path, int depth) {
|
|
||||||
List<File> directories = processor.loadDirectories(path, depth)
|
|
||||||
.stream()
|
|
||||||
// .filter(file -> !excludedFiles.contains(file))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
return directories.stream()
|
|
||||||
.filter(dir -> isParentDirectory(dir, directories))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isParentDirectory(File directory, List<File> directories) {
|
|
||||||
String path = directory.getAbsolutePath();
|
|
||||||
return directories.stream()
|
|
||||||
.noneMatch(dir -> dir.getAbsolutePath().contains(path) && !StringUtils.equals(path, dir.getAbsolutePath()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update files in directory, if possible, with the same {@link AttributeConfig}.
|
|
||||||
* If {@link InputConfig#isForceCoherent()} then there will be no changes to the file if they don't match the same config.
|
|
||||||
* Otherwise, the default behaviour is executed.
|
|
||||||
* This method is called by the executor and is run in parallel.
|
|
||||||
*
|
|
||||||
* @param file directory containing files
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
void process(File file) {
|
|
||||||
process(file, config.getCoherent());
|
|
||||||
}
|
|
||||||
|
|
||||||
void process(File file, int depth) {
|
|
||||||
// TODO: Implement level crawl if coherence is not possible on user entered depth
|
|
||||||
// IMPL idea: recursive method call, cache needs to be implemented
|
|
||||||
// List<FileInfoOld> fileInfoOlds = collector.loadFiles(file.getAbsolutePath()).stream()
|
|
||||||
// .map(FileInfoOld::new)
|
|
||||||
// .collect(Collectors.toList());
|
|
||||||
|
|
||||||
for (AttributeConfig config : config.getAttributeConfig()) {
|
|
||||||
|
|
||||||
// for (FileInfoOld fileInfoOld : fileInfoOlds) {
|
|
||||||
// List<TrackAttributes> attributes = processor.readAttributes(fileInfoOld.getFile());
|
|
||||||
//
|
|
||||||
// List<TrackAttributes> nonForcedTracks = processor.retrieveNonForcedTracks(attributes);
|
|
||||||
// List<TrackAttributes> nonCommentaryTracks = processor.retrieveNonCommentaryTracks(attributes);
|
|
||||||
//
|
|
||||||
// processor.detectDefaultTracks(fileInfoOld, attributes, nonForcedTracks);
|
|
||||||
// processor.detectDesiredTracks(fileInfoOld, nonForcedTracks, nonCommentaryTracks, config);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (fileInfoOlds.stream().allMatch(elem -> ("OFF".equals(config.getSubtitleLanguage()) || elem.getDesiredDefaultSubtitleLane() != null)
|
|
||||||
// && elem.getDesiredDefaultAudioLane() != null)) {
|
|
||||||
// log.info("Found {} match for {}", config.toStringShort(), file.getAbsolutePath());
|
|
||||||
// fileInfoOlds.forEach(this::updateFile);
|
|
||||||
// return; // match found, end process here
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fileInfoOlds.forEach(f -> {
|
|
||||||
// f.setDesiredDefaultAudioLane(null);
|
|
||||||
// f.setDesiredDefaultSubtitleLane(null);
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("No coherent match found for {}", file.getAbsoluteFile());
|
|
||||||
|
|
||||||
// for (FileInfoOld fileInfoOld : fileInfoOlds) {
|
|
||||||
// if (!InputConfig.getInstance().isForceCoherent()) {
|
|
||||||
// super.process(fileInfoOld.getFile());
|
|
||||||
// } else {
|
|
||||||
// statistic.excluded();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel;
|
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.FileProcessor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import me.tongfei.progressbar.ProgressBarBuilder;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class DefaultAttributeUpdaterKernel extends AttributeUpdaterKernel {
|
|
||||||
|
|
||||||
public DefaultAttributeUpdaterKernel(InputConfig config, FileProcessor processor) {
|
|
||||||
super(config, processor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ProgressBarBuilder pbBuilder() {
|
|
||||||
return super.pbBuilder()
|
|
||||||
.setUnit(" files", 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel;
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.AttributeProcessor;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.FileProcessor;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
|
||||||
@@ -20,18 +18,18 @@ import java.util.concurrent.Executors;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class AttributeUpdaterKernel {
|
public abstract class AttributeUpdater {
|
||||||
|
|
||||||
protected final InputConfig config;
|
protected final InputConfig config;
|
||||||
protected final FileProcessor processor;
|
protected final FileProcessor fileProcessor;
|
||||||
protected final AttributeProcessor attributeProcessor;
|
protected final AttributeProcessor attributeProcessor;
|
||||||
protected final ResultStatistic statistic = ResultStatistic.getInstance();
|
protected final ResultStatistic statistic = ResultStatistic.getInstance();
|
||||||
|
|
||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
|
|
||||||
public AttributeUpdaterKernel(InputConfig config, FileProcessor processor) {
|
public AttributeUpdater(InputConfig config, FileProcessor fileProcessor) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.processor = processor;
|
this.fileProcessor = fileProcessor;
|
||||||
this.attributeProcessor = new AttributeProcessor(config.getPreferredSubtitles().toArray(new String[0]), config.getForcedKeywords(), config.getCommentaryKeywords(), config.getHearingImpaired());
|
this.attributeProcessor = new AttributeProcessor(config.getPreferredSubtitles().toArray(new String[0]), config.getForcedKeywords(), config.getCommentaryKeywords(), config.getHearingImpaired());
|
||||||
this.executor = Executors.newFixedThreadPool(config.getThreads());
|
this.executor = Executors.newFixedThreadPool(config.getThreads());
|
||||||
}
|
}
|
||||||
@@ -48,7 +46,9 @@ public abstract class AttributeUpdaterKernel {
|
|||||||
statistic.startTimer();
|
statistic.startTimer();
|
||||||
|
|
||||||
try (ProgressBar progressBar = pbBuilder().build()) {
|
try (ProgressBar progressBar = pbBuilder().build()) {
|
||||||
List<File> files = processor.loadFiles(config.getLibraryPath().getAbsolutePath());
|
List<File> files = config.getCoherent() != null
|
||||||
|
? fileProcessor.loadDirectory(config.getLibraryPath().getPath(), config.getCoherent())
|
||||||
|
: fileProcessor.loadFiles(config.getLibraryPath().getPath());
|
||||||
|
|
||||||
progressBar.maxHint(files.size());
|
progressBar.maxHint(files.size());
|
||||||
progressBar.refresh();
|
progressBar.refresh();
|
||||||
@@ -74,22 +74,7 @@ public abstract class AttributeUpdaterKernel {
|
|||||||
*
|
*
|
||||||
* @param file file or directory to update
|
* @param file file or directory to update
|
||||||
*/
|
*/
|
||||||
void process(File file) {
|
protected abstract void process(File file);
|
||||||
FileInfo fileInfo = processor.readAttributes(file);
|
|
||||||
|
|
||||||
if (fileInfo.getTracks().isEmpty()) {
|
|
||||||
log.warn("No attributes found for file {}", file);
|
|
||||||
statistic.failure();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
attributeProcessor.findDefaultMatchAndApplyChanges(fileInfo, config.getAttributeConfig());
|
|
||||||
attributeProcessor.findForcedTracksAndApplyChanges(fileInfo, config.isOverwriteForced());
|
|
||||||
attributeProcessor.findCommentaryTracksAndApplyChanges(fileInfo);
|
|
||||||
attributeProcessor.findHearingImpairedTracksAndApplyChanges(fileInfo);
|
|
||||||
|
|
||||||
checkStatusAndUpdate(fileInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist file changes.
|
* Persist file changes.
|
||||||
@@ -119,12 +104,12 @@ public abstract class AttributeUpdaterKernel {
|
|||||||
if (config.isSafeMode()) return;
|
if (config.isSafeMode()) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
processor.update(fileInfo);
|
fileProcessor.update(fileInfo);
|
||||||
statistic.success();
|
statistic.success();
|
||||||
log.info("Commited {} to '{}'", fileInfo.getMatchedConfig().toStringShort(), fileInfo.getFile().getAbsolutePath());
|
log.info("Commited {} to '{}'", fileInfo.getMatchedConfig().toStringShort(), fileInfo.getFile().getPath());
|
||||||
} catch (IOException | MkvToolNixException e) {
|
} catch (IOException | MkvToolNixException e) {
|
||||||
statistic.failedChanging();
|
statistic.failedChanging();
|
||||||
log.warn("Couldn't commit {} to '{}'", fileInfo.getMatchedConfig().toStringShort(), fileInfo.getFile().getAbsoluteFile(), e);
|
log.warn("Couldn't commit {} to '{}'", fileInfo.getMatchedConfig().toStringShort(), fileInfo.getFile().getPath(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,8 +25,8 @@ public class CachedFileProcessor implements FileProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<File> loadDirectories(String path, int depth) {
|
public List<File> loadDirectory(String path, int depth) {
|
||||||
return directoryCache.retrieve(Pair.of(path, depth), key -> processor.loadDirectories(key.getLeft(), key.getRight()));
|
return directoryCache.retrieve(Pair.of(path, depth), key -> processor.loadDirectory(key.getLeft(), key.getRight()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||||
|
|
||||||
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||||
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
||||||
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import me.tongfei.progressbar.ProgressBarBuilder;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class CoherentAttributeUpdater extends SingleFileAttributeUpdater {
|
||||||
|
|
||||||
|
public CoherentAttributeUpdater(InputConfig config, FileProcessor processor) {
|
||||||
|
super(config, processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ProgressBarBuilder pbBuilder() {
|
||||||
|
return super.pbBuilder()
|
||||||
|
.setUnit(" directories", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(File rootDir) {
|
||||||
|
if (rootDir.isFile()) {
|
||||||
|
super.process(rootDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<File> files = fileProcessor.loadFiles(rootDir.getPath());
|
||||||
|
Set<FileInfo> matchedFiles = new HashSet<>(files.size() * 2);
|
||||||
|
|
||||||
|
AttributeConfig matchedConfig = null;
|
||||||
|
for (AttributeConfig config: config.getAttributeConfig()) {
|
||||||
|
for (File file: files) {
|
||||||
|
FileInfo fileInfo = fileProcessor.readAttributes(file);
|
||||||
|
fileInfo.resetChanges();
|
||||||
|
fileInfo.setMatchedConfig(null);
|
||||||
|
|
||||||
|
if (fileInfo.getTracks().isEmpty()) {
|
||||||
|
log.warn("No attributes found for file {}", file);
|
||||||
|
statistic.failure();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeProcessor.findDefaultMatchAndApplyChanges(fileInfo, config);
|
||||||
|
|
||||||
|
if (matchedConfig == null) matchedConfig = fileInfo.getMatchedConfig();
|
||||||
|
matchedFiles.add(fileInfo);
|
||||||
|
if (matchedConfig != fileInfo.getMatchedConfig()) {
|
||||||
|
matchedConfig = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchedConfig != null) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchedConfig != null) {
|
||||||
|
matchedFiles.forEach(fileInfo -> {
|
||||||
|
attributeProcessor.findForcedTracksAndApplyChanges(fileInfo, config.isOverwriteForced());
|
||||||
|
attributeProcessor.findCommentaryTracksAndApplyChanges(fileInfo);
|
||||||
|
attributeProcessor.findHearingImpairedTracksAndApplyChanges(fileInfo);
|
||||||
|
|
||||||
|
checkStatusAndUpdate(fileInfo);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
log.info("No coherent match found, trying to find coherent match in child directories: {}", rootDir.getPath());
|
||||||
|
matchedFiles.forEach(fileInfo -> {
|
||||||
|
fileInfo.resetChanges();
|
||||||
|
fileInfo.setMatchedConfig(null);
|
||||||
|
});
|
||||||
|
for (File dir: fileProcessor.loadDirectory(rootDir.getPath(), 1)) this.process(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,14 +16,16 @@ public interface FileProcessor {
|
|||||||
List<File> loadFiles(String path);
|
List<File> loadFiles(String path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load all directories from path, but only until depth is reached.
|
* Load only directories and files at depth, ignoring everything between root dir and dir at depth.
|
||||||
* This will ignore all files between root and directories at depth.
|
* E.g. with file structure /base/depth1/depth2/depth3.file
|
||||||
|
* - with depth 1: return /base/depth1/
|
||||||
|
* - with depth 2: returns /base/depth1/depth2/
|
||||||
*
|
*
|
||||||
* @param path leads to a directory which will be loaded recursively until depth
|
* @param path directory which will be loaded recursively until depth
|
||||||
* @param depth limit directory crawling
|
* @param depth limit directory crawling
|
||||||
* @return list of directory until depth
|
* @return list of directory at depth
|
||||||
*/
|
*/
|
||||||
List<File> loadDirectories(String path, int depth);
|
List<File> loadDirectory(String path, int depth);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load track information from file.
|
* Load track information from file.
|
||||||
|
|||||||
@@ -58,15 +58,45 @@ public class MkvFileProcessor implements FileProcessor {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
// does this load /arst/arst & /arst ?
|
// does this load /arst/arst & /arst ?
|
||||||
public List<File> loadDirectories(String path, int depth) {
|
public List<File> loadDirectory(String path, int depth) {
|
||||||
try (Stream<Path> paths = Files.walk(Paths.get(path), depth)) {
|
File rootDir = Path.of(path).toFile();
|
||||||
return paths.map(Path::toFile)
|
if (!rootDir.exists()) {
|
||||||
.filter(File::isDirectory)
|
log.error("Couldn't find file or directory!");
|
||||||
.collect(Collectors.toList());
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("Couldn't find file or directory!", e);
|
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<File> result = new ArrayList<>();
|
||||||
|
exploreDirectory(rootDir, 0, depth, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively explores directories to find items at the target depth.
|
||||||
|
*
|
||||||
|
* @param currentDir The current directory being explored
|
||||||
|
* @param currentDepth The current depth level
|
||||||
|
* @param targetDepth The target depth to collect files
|
||||||
|
* @param result The collection to store found files
|
||||||
|
*/
|
||||||
|
private static void exploreDirectory(File currentDir, int currentDepth, int targetDepth, List<File> result) {
|
||||||
|
if (currentDepth == targetDepth) {
|
||||||
|
result.add(currentDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all files and directories in the current directory
|
||||||
|
File[] files = currentDir.listFiles();
|
||||||
|
if (files == null) return;
|
||||||
|
|
||||||
|
// Recursively explore subdirectories
|
||||||
|
for (File file : files) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
exploreDirectory(file, currentDepth + 1, targetDepth, result);
|
||||||
|
} else if (currentDepth + 1 == targetDepth) {
|
||||||
|
// If files at the next level would be at the target depth, include them
|
||||||
|
result.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||||
|
|
||||||
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||||
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import me.tongfei.progressbar.ProgressBarBuilder;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class SingleFileAttributeUpdater extends AttributeUpdater {
|
||||||
|
|
||||||
|
public SingleFileAttributeUpdater(InputConfig config, FileProcessor processor) {
|
||||||
|
super(config, processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ProgressBarBuilder pbBuilder() {
|
||||||
|
return super.pbBuilder()
|
||||||
|
.setUnit(" files", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(File file) {
|
||||||
|
FileInfo fileInfo = fileProcessor.readAttributes(file);
|
||||||
|
|
||||||
|
if (fileInfo.getTracks().isEmpty()) {
|
||||||
|
log.warn("No attributes found for file {}", file);
|
||||||
|
statistic.failure();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeProcessor.findDefaultMatchAndApplyChanges(fileInfo, config.getAttributeConfig());
|
||||||
|
attributeProcessor.findForcedTracksAndApplyChanges(fileInfo, config.isOverwriteForced());
|
||||||
|
attributeProcessor.findCommentaryTracksAndApplyChanges(fileInfo);
|
||||||
|
attributeProcessor.findHearingImpairedTracksAndApplyChanges(fileInfo);
|
||||||
|
|
||||||
|
checkStatusAndUpdate(fileInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ public class FileInfo {
|
|||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
private final List<TrackAttributes> subtitleTracks = new ArrayList<>();
|
private final List<TrackAttributes> subtitleTracks = new ArrayList<>();
|
||||||
|
|
||||||
private final PlannedChange changes = new PlannedChange();
|
private PlannedChange changes = new PlannedChange();
|
||||||
@Setter
|
@Setter
|
||||||
private AttributeConfig matchedConfig;
|
private AttributeConfig matchedConfig;
|
||||||
|
|
||||||
@@ -50,6 +50,10 @@ public class FileInfo {
|
|||||||
return Collections.unmodifiableList(subtitleTracks);
|
return Collections.unmodifiableList(subtitleTracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resetChanges() {
|
||||||
|
changes = new PlannedChange();
|
||||||
|
}
|
||||||
|
|
||||||
public FileStatus getStatus() {
|
public FileStatus getStatus() {
|
||||||
if (!changes.isEmpty()) return FileStatus.CHANGE_NECESSARY;
|
if (!changes.isEmpty()) return FileStatus.CHANGE_NECESSARY;
|
||||||
if (matchedConfig == null) return FileStatus.NO_SUITABLE_CONFIG;
|
if (matchedConfig == null) return FileStatus.NO_SUITABLE_CONFIG;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import picocli.CommandLine.Option;
|
|||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@CommandLine.Command
|
@CommandLine.Command
|
||||||
public class InputConfig {
|
public class InputConfig implements CommandLine.IVersionProvider {
|
||||||
|
|
||||||
private File configPath;
|
private File configPath;
|
||||||
|
|
||||||
@@ -108,5 +108,10 @@ public class InputConfig {
|
|||||||
.add("attributeConfig=" + attributeConfig)
|
.add("attributeConfig=" + attributeConfig)
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getVersion() throws Exception {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user