mirror of
https://github.com/RatzzFatzz/MKVAudioSubtitleChanger.git
synced 2026-02-11 02:05:56 +01:00
Change track selection
This commit is contained in:
5
pom.xml
5
pom.xml
@@ -149,6 +149,11 @@
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
</dependency>
|
||||
<!-- endregion -->
|
||||
<!-- region unit-tests -->
|
||||
<dependency>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileCollector;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileProcessor;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.intimpl.ConfigProcessor;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.intimpl.MkvFileCollector;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.ConfigProcessorOld;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
@@ -16,19 +18,30 @@ import java.util.concurrent.*;
|
||||
public class AttributeUpdaterKernel {
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(Config.getInstance().getThreadCount());
|
||||
MkvFileCollector collector = new MkvFileCollector();
|
||||
FileCollector collector;
|
||||
FileProcessor processor;
|
||||
int filesChangedAmount = 0;
|
||||
int filesNotChangedAmount = 0;
|
||||
long runtime = 0;
|
||||
|
||||
public AttributeUpdaterKernel(FileCollector collector, FileProcessor processor) {
|
||||
this.collector = collector;
|
||||
this.processor = processor;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void execute() {
|
||||
long beforeTimer = System.currentTimeMillis();
|
||||
|
||||
collector.loadFiles(Config.getInstance().getLibraryPath())
|
||||
.forEach(file -> executor.submit(() -> process(file)));
|
||||
|
||||
|
||||
List<File> files = collector.loadFiles(Config.getInstance().getLibraryPath());
|
||||
files.forEach(file -> executor.submit(() -> process(file)));
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(1, TimeUnit.DAYS);
|
||||
|
||||
|
||||
|
||||
runtime = System.currentTimeMillis() - beforeTimer;
|
||||
|
||||
System.out.printf("%nFiles %schanged: %s%n",
|
||||
@@ -40,13 +53,13 @@ public class AttributeUpdaterKernel {
|
||||
System.out.printf("Runtime: %ss%n", runtime / 1000);
|
||||
}
|
||||
|
||||
private void process(File file) {
|
||||
List<FileAttribute> attributes = collector.loadAttributes(file);
|
||||
private void processOld(File file) {
|
||||
List<FileAttribute> attributes = processor.loadAttributes(file);
|
||||
boolean fileHasChanged = false;
|
||||
|
||||
if (attributes.isEmpty()) return;
|
||||
for(AttributeConfig config : Config.getInstance().getAttributeConfig()){
|
||||
fileHasChanged = new ConfigProcessor(config).processConfig(file, attributes);
|
||||
fileHasChanged = new ConfigProcessorOld(config).processConfig(file, attributes);
|
||||
if(fileHasChanged) break;
|
||||
}
|
||||
if(!fileHasChanged){
|
||||
@@ -57,4 +70,12 @@ public class AttributeUpdaterKernel {
|
||||
}
|
||||
System.out.print(".");
|
||||
}
|
||||
|
||||
private void process(File file) {
|
||||
List<FileAttribute> attributes = processor.loadAttributes(file);
|
||||
FileInfoDto fileInfo = processor.filterAttributes(attributes);
|
||||
if (fileInfo.isChangeNecessary() && !Config.getInstance().isSafeMode()) {
|
||||
processor.update(file, fileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.MkvFileCollector;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.MkvFileProcessor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.commons.cli.*;
|
||||
|
||||
@@ -10,7 +12,7 @@ import static java.lang.Integer.parseInt;
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
initConfig(args);
|
||||
AttributeUpdaterKernel kernel = new AttributeUpdaterKernel();
|
||||
AttributeUpdaterKernel kernel = new AttributeUpdaterKernel(new MkvFileCollector(), new MkvFileProcessor());
|
||||
kernel.execute();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.intimpl;
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
@@ -17,12 +17,12 @@ import java.util.List;
|
||||
import static java.lang.String.format;
|
||||
|
||||
@Log4j2
|
||||
public class ConfigProcessor {
|
||||
public class ConfigProcessorOld {
|
||||
private int audioDefault = - 1;
|
||||
private int subtitleDefault = - 1;
|
||||
private final AttributeConfig config;
|
||||
|
||||
public ConfigProcessor(AttributeConfig config) {
|
||||
public ConfigProcessorOld(AttributeConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@@ -59,22 +59,22 @@ public class ConfigProcessor {
|
||||
if(elem.isDefaultTrack()){
|
||||
audioDefault = elem.getId();
|
||||
}
|
||||
if(config.getAudio().contains("OFF")){
|
||||
if(config.getAudioLanguage().contains("OFF")){
|
||||
transfer.setAudioOn(false);
|
||||
transfer.setAudioIndex(- 2);
|
||||
}
|
||||
if(! config.getAudio().contains(elem.getLanguage())){
|
||||
if(! config.getAudioLanguage().contains(elem.getLanguage())){
|
||||
iterator.remove();
|
||||
}
|
||||
}else if("subtitles".equals(elem.getType())){
|
||||
if(elem.isDefaultTrack()){
|
||||
subtitleDefault = elem.getId();
|
||||
}
|
||||
if(config.getSubtitle().contains("OFF")){
|
||||
if(config.getSubtitleLanguage().contains("OFF")){
|
||||
transfer.setSubtitleOn(false);
|
||||
transfer.setSubtitleIndex(- 2);
|
||||
}
|
||||
if(! config.getSubtitle().contains(elem.getLanguage())){
|
||||
if(! config.getSubtitleLanguage().contains(elem.getLanguage())){
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public interface FileCollector {
|
||||
|
||||
/**
|
||||
* @param path leads to one file directly or a directory which will be loaded recursively
|
||||
* @return list of all files within the directory
|
||||
*/
|
||||
List<File> loadFiles(String path);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public interface FileProcessor {
|
||||
|
||||
/**
|
||||
* @param file Takes the file from which the attributes will be returned
|
||||
* @return list of all important attributes
|
||||
*/
|
||||
List<FileAttribute> loadAttributes(File file);
|
||||
|
||||
FileInfoDto filterAttributes(List<FileAttribute> attributes);
|
||||
|
||||
void update(File file, FileInfoDto fileInfo);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
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;
|
||||
|
||||
@Log4j2
|
||||
public class MkvFileCollector implements FileCollector {
|
||||
|
||||
@Override
|
||||
public List<File> loadFiles(String path) {
|
||||
File file = new File(path);
|
||||
if (file.isFile() && file.getAbsolutePath().endsWith(".mkv")) {
|
||||
return new ArrayList<File>() {{
|
||||
add(file);
|
||||
}};
|
||||
} else if (file.isDirectory()) {
|
||||
try (Stream<Path> paths = Files.walk(Paths.get(path))) {
|
||||
return paths
|
||||
.filter(Files::isRegularFile)
|
||||
.map(Path::toFile)
|
||||
.filter(f -> f.getAbsolutePath().endsWith(".mkv"))
|
||||
.collect(Collectors.toList());
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't find file or directory!", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.*;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.LaneType.AUDIO;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.LaneType.SUBTITLES;
|
||||
|
||||
@Log4j2
|
||||
public class MkvFileProcessor implements FileProcessor {
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public List<FileAttribute> loadAttributes(File file) {
|
||||
Map<String, Object> jsonMap;
|
||||
List<FileAttribute> fileAttributes = new ArrayList<>();
|
||||
try {
|
||||
String command = String.format("\"%s\"", Config.getInstance().getPathFor(MkvToolNix.MKV_MERGER));
|
||||
String[] arguments = new String[]{
|
||||
command,
|
||||
"--identify",
|
||||
"--identification-format",
|
||||
"json",
|
||||
file.getAbsoluteFile().toString()
|
||||
};
|
||||
|
||||
InputStream inputStream = Runtime.getRuntime().exec(arguments).getInputStream();
|
||||
jsonMap = mapper.readValue(inputStream, Map.class);
|
||||
List<Map<String, Object>> tracks = (List<Map<String, Object>>) jsonMap.get("tracks");
|
||||
if (tracks == null) {
|
||||
log.warn("Couldn't retrieve information of {}", file.getAbsoluteFile().toString());
|
||||
return new ArrayList<>();
|
||||
}
|
||||
for (Map<String, Object> attribute : tracks) {
|
||||
if (!"video".equals(attribute.get("type"))) {
|
||||
Map<String, Object> properties = (Map<String, Object>) attribute.get("properties");
|
||||
fileAttributes.add(new FileAttribute(
|
||||
(int) properties.get("number"),
|
||||
(String) properties.get("language"),
|
||||
(String) properties.get("track_name"),
|
||||
(Boolean) properties.getOrDefault("default_track", false),
|
||||
(Boolean) properties.getOrDefault("forced_track", false),
|
||||
LaneType.valueOf(((String) attribute.get("type")).toUpperCase(Locale.ENGLISH))));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error("File could not be found or loaded!");
|
||||
}
|
||||
return fileAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileInfoDto filterAttributes(List<FileAttribute> attributes) {
|
||||
FileInfoDto info = new FileInfoDto();
|
||||
for (FileAttribute attribute: attributes) {
|
||||
if (attribute.isDefaultTrack() && AUDIO.equals(attribute.getType())) info.setDefaultAudioLane(attribute);
|
||||
if (attribute.isForcedTrack() && AUDIO.equals(attribute.getType())) info.setForcedAudioLane(attribute);
|
||||
|
||||
if (attribute.isDefaultTrack() && SUBTITLES.equals(attribute.getType())) info.setDefaultSubtitleLane(attribute);
|
||||
if (attribute.isForcedTrack() && SUBTITLES.equals(attribute.getType())) info.setForcedSubtitleLane(attribute);
|
||||
}
|
||||
List<FileAttribute> nonForcedTracks = attributes.stream()
|
||||
.filter(elem -> !StringUtils.containsIgnoreCase(elem.getTrackName(), "forced"))
|
||||
.collect(Collectors.toList());
|
||||
for (AttributeConfig config: Config.getInstance().getAttributeConfig()) {
|
||||
FileAttribute desiredAudio = null;
|
||||
FileAttribute desiredSubtitle = null;
|
||||
for (FileAttribute attribute: nonForcedTracks) {
|
||||
if ( attribute.getLanguage().equals(config.getAudioLanguage())
|
||||
&& AUDIO.equals(attribute.getType())) desiredAudio = attribute;
|
||||
if ( attribute.getLanguage().equals(config.getSubtitleLanguage())
|
||||
&& SUBTITLES.equals(attribute.getType())) desiredSubtitle = attribute;
|
||||
}
|
||||
if (desiredAudio != null && desiredSubtitle != null) {
|
||||
info.setDesiredAudioLane(desiredAudio);
|
||||
info.setDesiredSubtitleLane(desiredSubtitle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(File file, FileInfoDto fileInfo) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.intimpl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public interface FileCollector {
|
||||
List<File> loadFiles(String path);
|
||||
|
||||
List<FileAttribute> loadAttributes(File file);
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.intimpl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.MkvToolNix;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
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.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Log4j2
|
||||
public class MkvFileCollector implements FileCollector {
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* @param path Is entered path, which leads to one file directly or a directory which will be loaded recursive
|
||||
* @return list of all files within the directory, if it's only a file, the file will be returned in a list
|
||||
*/
|
||||
@Override
|
||||
public List<File> loadFiles(String path) {
|
||||
File file = new File(path);
|
||||
if (file.isFile() && file.getAbsolutePath().endsWith(".mkv")) {
|
||||
return new ArrayList<File>() {{
|
||||
add(file);
|
||||
}};
|
||||
} else if (file.isDirectory()) {
|
||||
try (Stream<Path> paths = Files.walk(Paths.get(path))) {
|
||||
return paths
|
||||
.filter(Files::isRegularFile)
|
||||
.map(Path::toFile)
|
||||
.filter(f -> f.getAbsolutePath().endsWith(".mkv"))
|
||||
.collect(Collectors.toList());
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't find file or directory!", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file Takes the file from which the attributes will be returned
|
||||
* @return list of all important attributes
|
||||
*/
|
||||
@Override
|
||||
public List<FileAttribute> loadAttributes(File file) {
|
||||
Map<String, Object> jsonMap;
|
||||
List<FileAttribute> fileAttributes = new ArrayList<>();
|
||||
try {
|
||||
String command = String.format("\"%s\"", Config.getInstance().getPathFor(MkvToolNix.MKV_MERGER));
|
||||
String[] array = new String[]{
|
||||
command,
|
||||
"--identify",
|
||||
"--identification-format",
|
||||
"json",
|
||||
file.getAbsoluteFile().toString()
|
||||
};
|
||||
|
||||
InputStream inputStream = Runtime.getRuntime().exec(array).getInputStream();
|
||||
jsonMap = mapper.readValue(inputStream, Map.class);
|
||||
List<Map<String, Object>> tracks = (List<Map<String, Object>>) jsonMap.get("tracks");
|
||||
if (tracks == null) {
|
||||
log.warn("Couldn't retrieve information of {}", file.getAbsoluteFile().toString());
|
||||
return new ArrayList<>();
|
||||
}
|
||||
for (Map<String, Object> attribute : tracks) {
|
||||
if (!"video".equals(attribute.get("type"))) {
|
||||
Map<String, Object> properties = (Map<String, Object>) attribute.get("properties");
|
||||
fileAttributes.add(new FileAttribute(
|
||||
(int) properties.get("number"),
|
||||
(String) properties.get("language"),
|
||||
(String) properties.get("track_name"),
|
||||
(Boolean) properties.getOrDefault("default_track", false),
|
||||
(Boolean) properties.getOrDefault("forced_track", false),
|
||||
(String) attribute.get("type")));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error("File could not be found or loaded!");
|
||||
}
|
||||
return fileAttributes;
|
||||
}
|
||||
}
|
||||
@@ -3,29 +3,26 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Log4j2
|
||||
@Getter
|
||||
public class AttributeConfig {
|
||||
private String audio;
|
||||
private String subtitle;
|
||||
private String audioLanguage;
|
||||
private String subtitleLanguage;
|
||||
|
||||
public AttributeConfig(String audio, String subtitle) {
|
||||
this.audio = audio;
|
||||
this.subtitle = subtitle;
|
||||
public AttributeConfig(String audioLanguage, String subtitleLanguage) {
|
||||
this.audioLanguage = audioLanguage;
|
||||
this.subtitleLanguage = subtitleLanguage;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return audio != null && subtitle != null;
|
||||
return audioLanguage != null && subtitleLanguage != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("AttributeConfig{");
|
||||
sb.append("audio='").append(audio).append('\'');
|
||||
sb.append(", subtitle='").append(subtitle).append('\'');
|
||||
sb.append("audioLanguage='").append(audioLanguage).append('\'');
|
||||
sb.append(", subtitleLanguage='").append(subtitleLanguage).append('\'');
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -20,9 +20,10 @@ public class FileAttribute {
|
||||
private String trackName;
|
||||
private boolean defaultTrack;
|
||||
private boolean forcedTrack;
|
||||
private String type;
|
||||
private LaneType type;
|
||||
|
||||
public FileAttribute(int id, String language, String trackName, boolean defaultTrack, boolean forcedTrack, String type) {
|
||||
public FileAttribute(int id, String language, String trackName, boolean defaultTrack, boolean forcedTrack,
|
||||
LaneType type) {
|
||||
this.id = id;
|
||||
this.language = language;
|
||||
this.trackName = trackName;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class FileInfoDto {
|
||||
private FileAttribute defaultAudioLane;
|
||||
private FileAttribute defaultSubtitleLane;
|
||||
private FileAttribute forcedAudioLane;
|
||||
private FileAttribute forcedSubtitleLane;
|
||||
private FileAttribute desiredAudioLane;
|
||||
private FileAttribute desiredSubtitleLane;
|
||||
|
||||
public boolean isChangeNecessary() {
|
||||
return defaultAudioLane.getId() != desiredAudioLane.getId()
|
||||
|| defaultSubtitleLane.getId() != desiredSubtitleLane.getId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
public enum LaneType {
|
||||
AUDIO,
|
||||
SUBTITLES;
|
||||
|
||||
LaneType() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
public class ResultStatistic {
|
||||
}
|
||||
Reference in New Issue
Block a user