mirror of
https://github.com/RatzzFatzz/MKVAudioSubtitleChanger.git
synced 2026-02-11 02:05:56 +01:00
Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36bd93bb50 | ||
|
|
ecc5c56c8c | ||
|
|
f6310c71ee | ||
|
|
bb4a686dfc | ||
|
|
c63fcd4f37 | ||
|
|
9f15b542bd | ||
|
|
76321bb904 | ||
|
|
895597b91f | ||
|
|
4fa5448e1c | ||
|
|
f3accd77d6 | ||
|
|
2710ea2602 | ||
|
|
547b5ad86c | ||
|
|
1863432dc6 | ||
|
|
7ea0ab17b0 | ||
|
|
47b4cdc896 | ||
|
|
b638d93358 | ||
|
|
939f6053dd | ||
|
|
4714ef8db1 | ||
|
|
321115b9ca | ||
|
|
ed8e592963 | ||
|
|
0a7996f049 | ||
|
|
dd60ca93da | ||
|
|
ba770abb6a | ||
|
|
91f1e8f7bf | ||
| 0fda98426e | |||
| c74cdde442 | |||
| a8551fdbd5 | |||
|
|
b2e9762366 | ||
| cafb12f22a | |||
|
|
f6d65c2d53 | ||
| 1963d1cc5c | |||
| 686a9a0da1 | |||
| e19f780ff0 | |||
| f928cb035e | |||
|
|
fd9a421edc | ||
| 285533bb28 | |||
| 9330deb75f | |||
| 094b772257 | |||
|
|
873f6fca6d | ||
| 4309109583 | |||
| e3baae55d9 | |||
| 7ee51421e0 | |||
|
|
df6a82fd62 | ||
|
|
c551e2e2a5 | ||
| 943308dd59 | |||
| ba4c1bc1fe | |||
|
|
62f75818d9 | ||
| cf64833d3e | |||
| 6372cc560c | |||
| 8317e97639 | |||
| 440251c7c9 | |||
| b07f6894aa | |||
| 73be93a4b6 | |||
| 143206b08c | |||
|
|
80348756f9 | ||
| 773018e3bc | |||
|
|
923b4d06c5 | ||
| d7cd74bfaf | |||
|
|
9f40d97d8a | ||
| 156e327943 | |||
|
|
5f72f4545f | ||
|
|
1e341a0112 | ||
| fe30d186df | |||
| ce9a2fc805 | |||
| f69fbedee0 | |||
| d0c4b07f52 | |||
| 313abd311a | |||
| 47f6d65eb2 | |||
| 33276b7aa2 | |||
| 51b4885e65 | |||
|
|
847a3f1f68 | ||
|
|
937c644b32 | ||
| 1d6098efc1 | |||
| b5030f9401 | |||
| 892bc59803 | |||
| 1bd136af6a | |||
| d8f0dcdc87 | |||
| 10cfb07457 | |||
|
|
17158e3f15 | ||
| 5e27a72499 | |||
| 658849417a | |||
| 8d63c02abd | |||
| bad2a39614 | |||
|
|
2d861b0f1c |
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
github: [RatzzFatzz]
|
||||
custom: "https://paypal.me/ratzmichael"
|
||||
76
.github/workflows/release.yml
vendored
Normal file
76
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
# This workflow will run every time a new release is created.
|
||||
|
||||
name: Build and release
|
||||
on:
|
||||
release:
|
||||
types: [ created ]
|
||||
|
||||
jobs:
|
||||
portable-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install mkvtoolnix
|
||||
run: sudo apt-get install -y mkvtoolnix
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set timezone
|
||||
uses: szenius/set-timezone@v2.0
|
||||
with:
|
||||
timezoneLinux: "Europe/Berlin"
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4.7.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
|
||||
- name: Setup workspace
|
||||
run: mkdir artifacts
|
||||
|
||||
- name: Build with Maven
|
||||
run: |
|
||||
mvn clean package --file pom.xml -P portable
|
||||
cp target/M*.{zip,tar} artifacts/
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: skx/github-action-publish-binaries@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
args: 'artifacts/M*'
|
||||
|
||||
windows-installer-build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Install mkvtoolnix
|
||||
uses: crazy-max/ghaction-chocolatey@v3
|
||||
with:
|
||||
args: install mkvtoolnix -y
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set timezone
|
||||
uses: szenius/set-timezone@v2.0
|
||||
with:
|
||||
timezoneWindows: "Berlin Standard Time"
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4.7.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
|
||||
- name: Setup workspace
|
||||
run: mkdir artifacts
|
||||
|
||||
- name: Build with Maven
|
||||
run: mvn clean package --file pom.xml -P windows
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: AButler/upload-release-assets@v3.0
|
||||
with:
|
||||
files: 'target/installer/*'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
86
README.md
86
README.md
@@ -1,59 +1,55 @@
|
||||
### Table of content
|
||||
- Introduction
|
||||
- Requirements
|
||||
- Running
|
||||
- Configuration
|
||||
- Additional parameters
|
||||
## Introduction
|
||||
|
||||
### Introduction
|
||||
This program helps to change audio and subtitle tracks of mkv files without rewriting the file. Only track properties will be updated.
|
||||

|
||||
|
||||
This program helps to change audio and subtitle lines of mkv files.
|
||||
|
||||
### Requirements
|
||||
## Requirements
|
||||
|
||||
- Java 11 or higher
|
||||
- mkvtoolnix installation
|
||||
|
||||
### Running
|
||||
## Execution
|
||||
**Minimal usage:**
|
||||
`java -jar mkvaudiosubtitlechanger.jar --library "X:/Files" --attribute-config eng:ger eng:OFF`
|
||||
|
||||
1. Extract downloaded archive
|
||||
2. Copy `config-template.yaml` to `config.yaml`
|
||||
3. Update `config.yaml` to fit your needs
|
||||
4. Open terminal / cmd in the directory of the jar and the config file
|
||||
5. Execute following commands:
|
||||
1. (Optional) `java -jar mkvaudiosubtitleschanger.jar -l [path to mkv or dir with mkv] --safe-mode`
|
||||
2. To permanently apply changes: `java -jar mkvaudiosubtitleschanger.jar -l [path to mkv or dir with mkv]`
|
||||
**Safe usage (best for testing before applying to whole library):**
|
||||
`java -jar mkvaudiosubtitlechanger.jar --library "X:/Files" --attribute-config eng:ger eng:OFF --safe-mode`
|
||||
|
||||
### Configuration
|
||||
Attribute-config must be entered in pairs: `audio:subtitle`; Example: `jpn:eng`. More about this topic
|
||||
[here](https://github.com/RatzzFatzz/MKVAudioSubtitleChanger/wiki/Attribute-Config).
|
||||
|
||||
Config file needs to be placed in the same directory as the jar or path to config has to be passed via command line
|
||||
argument.
|
||||
|
||||
The list of language configurations can be expanded. Use `OFF` if you want to turn of the audio or subtitle lane.
|
||||
Players probably will display forced subtitles nonetheless.
|
||||
```yaml
|
||||
config:
|
||||
1:
|
||||
audio: ger
|
||||
subtitle: OFF
|
||||
2:
|
||||
audio: eng
|
||||
subtitle: ger
|
||||
## Available parameters
|
||||
```
|
||||
Subtitle lanes recognized as forced will be set as one. Already existing ones will not be overwritten or changed.
|
||||
|
||||
|
||||
### Additional arameters
|
||||
These properties overwrite already existing values in the config file.
|
||||
```properties
|
||||
-c,--config <arg> Path to config file
|
||||
-e,--exclude-directories <arg> Directories to be excluded, combines with config file
|
||||
-h,--help "for help this is" - Yoda
|
||||
-i,--include-pattern <arg> Include files matching pattern
|
||||
-k,--forcedKeywords <arg> Additional keywords to identify forced tracks, combines with config file
|
||||
-l,--library <arg> Path to library
|
||||
-l,--library-path <arg> Path to library
|
||||
-a,--attribute-config <arg> Attribute config to decide which tracks to choose when
|
||||
-p,--config-path <arg> Path to config file
|
||||
-m,--mkvtoolnix <arg> Path to mkv tool nix installation
|
||||
-s,--safe-mode Test run (no files will be changes)
|
||||
-t,--threads <arg> thread count (default: 2)
|
||||
-c,--coherent <arg> Try to match all files in dir of depth with the same config
|
||||
-cf,--force-coherent Force coherent and don't update anything if config fits not whole config (default: false)
|
||||
-n,--only-new-files Sets filter-date to last successful execution (Overwrites input of filter-date)
|
||||
-d,--filter-date <arg> Only consider files created newer than entered date (format: "dd.MM.yyyy-HH:mm:ss")
|
||||
-t,--threads <arg> Thread count (default: 2)
|
||||
-i,--include-pattern <arg> Include files matching pattern (default: ".*")
|
||||
-e,--excluded-directories <arg> Directories to be excluded, combines with config file
|
||||
-fk,--forced-keywords <arg> Additional keywords to identify forced tracks
|
||||
-ck,--commentary-keywords <arg> Additional keywords to identify commentary tracks
|
||||
-ps,--preferred-subtitles <arg> Additional keywords to prefer specific subtitle tracks
|
||||
-v,--version Display version
|
||||
-h,--help "For help this is" - Yoda
|
||||
```
|
||||
If you need more information about how each parameter works, check out [this wiki page](https://github.com/RatzzFatzz/MKVAudioSubtitleChanger/wiki/Parameters).
|
||||
|
||||
All parameters can also be defined in a [config file](https://github.com/RatzzFatzz/MKVAudioSubtitleChanger/wiki/How-to-config-file).
|
||||
|
||||
## Build requirements
|
||||
- JDK 11 or higher
|
||||
- Maven 3
|
||||
- Git
|
||||
|
||||
## Build from source
|
||||
```shell
|
||||
git clone https://github.com/RatzzFatzz/MKVAudioSubtitleChanger.git
|
||||
cd MKVAudioSubtitleChanger
|
||||
mvn package
|
||||
```
|
||||
@@ -1,6 +1,7 @@
|
||||
mkvtoolnix: C:\Program Files\MKVToolNix
|
||||
library: X:/Files
|
||||
|
||||
config:
|
||||
attribute-config:
|
||||
1:
|
||||
audio: ger
|
||||
subtitle: OFF
|
||||
@@ -10,9 +11,24 @@ config:
|
||||
|
||||
# Recommendations for data stored on HDDs, increase when using SSDs
|
||||
#threads: 2
|
||||
#forcedKeywords: ["forced", "signs"]
|
||||
|
||||
#forced-keywords: ["forced", "signs"]
|
||||
#commentary-keywords: ["commentary", "director"]
|
||||
#preferred-subtitles: ["unstyled"]
|
||||
|
||||
#exclude-directories:
|
||||
# - "D:/Path/To/File.mkv"
|
||||
# - "D:/Path/To/Directory"
|
||||
|
||||
# If pattern is negated, can be used to exclude files
|
||||
#include-pattern: "regex"
|
||||
|
||||
# Only files newer than
|
||||
#filter-date: 20.03.2021-10:11:12
|
||||
|
||||
safe-mode:
|
||||
#coherent:
|
||||
#force-coherent:
|
||||
#only-new-files:
|
||||
|
||||
|
||||
|
||||
BIN
example.gif
Normal file
BIN
example.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
404
pom.xml
404
pom.xml
@@ -4,87 +4,37 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>MKVAudioSubtileChanger</groupId>
|
||||
<groupId>at.pcgamingfreaks</groupId>
|
||||
<artifactId>MKVAudioSubtitleChanger</artifactId>
|
||||
<version>2.0</version>
|
||||
<version>4.0.0</version>
|
||||
|
||||
<properties>
|
||||
<mainClass>at.pcgamingfreaks.mkvaudiosubtitlechanger.Main</mainClass>
|
||||
|
||||
<lombok-version>1.18.36</lombok-version>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>portable</id>
|
||||
<build>
|
||||
<defaultGoal>clean package</defaultGoal>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>./</directory>
|
||||
<includes>
|
||||
<include>language-codes</include>
|
||||
<include>version.properties</include>
|
||||
<include>log4j2.yml</include>
|
||||
<include>log4j2-debug.yml</include>
|
||||
</includes>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>test/resources</directory>
|
||||
</testResource>
|
||||
</testResources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Main-Class>at/pcgamingfreaks/mkvaudiosubtitlechanger/Main</Main-Class>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>*:*</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>archive</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
@@ -100,6 +50,220 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>windows</id>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>log4j2-installed.yaml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.panteleyev</groupId>
|
||||
<artifactId>jpackage-maven-plugin</artifactId>
|
||||
<version>1.6.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>windows</id>
|
||||
<configuration>
|
||||
<resourceDir>${project.basedir}/src/main/resources/</resourceDir>
|
||||
<type>EXE</type>
|
||||
<winConsole>true</winConsole>
|
||||
<winShortcut>false</winShortcut>
|
||||
<winMenu>false</winMenu>
|
||||
<javaOptions>
|
||||
<javaOption>-Dlog4j.configurationFile=log4j2-installed.yaml</javaOption>
|
||||
</javaOptions>
|
||||
</configuration>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jpackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>linux</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.panteleyev</groupId>
|
||||
<artifactId>jpackage-maven-plugin</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>linux-deb</id>
|
||||
<configuration>
|
||||
<type>DEB</type>
|
||||
<installDir>/usr/local/bin</installDir>
|
||||
<linuxShortcut>false</linuxShortcut>
|
||||
<linuxPackageName>mkvasc</linuxPackageName>
|
||||
<linuxDebMaintainer>your@email.com</linuxDebMaintainer>
|
||||
</configuration>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jpackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<defaultGoal>clean package</defaultGoal>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>./</directory>
|
||||
<includes>
|
||||
<include>language-codes</include>
|
||||
<include>project.properties</include>
|
||||
<include>LICENSE</include>
|
||||
</includes>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>test/resources</directory>
|
||||
</testResource>
|
||||
</testResources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.4.2</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Main-Class>${mainClass}</Main-Class>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>*:*</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<manifestEntries>
|
||||
<Multi-Release>true</Multi-Release>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.5.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
<compilerArgs>
|
||||
<arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
|
||||
</compilerArgs>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok-version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>info.picocli</groupId>
|
||||
<artifactId>picocli-codegen</artifactId>
|
||||
<version>4.7.6</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-jpackage-input</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/jpackage-input</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<includes>
|
||||
<include>${project.artifactId}-${project.version}.jar</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.panteleyev</groupId>
|
||||
<artifactId>jpackage-maven-plugin</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<configuration>
|
||||
<name>${project.artifactId}</name>
|
||||
<vendor>RatzzFatzz</vendor>
|
||||
<appVersion>${project.version}</appVersion>
|
||||
|
||||
<destination>target/installer</destination>
|
||||
|
||||
<input>target/jpackage-input</input>
|
||||
<mainClass>at.pcgamingfreaks.mkvaudiosubtitlechanger.Main</mainClass>
|
||||
<mainJar>${project.artifactId}-${project.version}.jar</mainJar>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
@@ -109,101 +273,151 @@
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!-- https://mvnrepository.com/artifact/com.intellij/forms_rt -->
|
||||
<dependency>
|
||||
<groupId>com.intellij</groupId>
|
||||
<artifactId>forms_rt</artifactId>
|
||||
<version>7.0.3</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.8</version>
|
||||
<version>${lombok-version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/info.picocli/picocli -->
|
||||
<dependency>
|
||||
<groupId>info.picocli</groupId>
|
||||
<artifactId>picocli</artifactId>
|
||||
<version>4.7.6</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Hibernate Validator -->
|
||||
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>8.0.2.Final</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Expression Language Implementation -->
|
||||
<!-- https://mvnrepository.com/artifact/jakarta.el/jakarta.el-api -->
|
||||
<dependency>
|
||||
<groupId>jakarta.el</groupId>
|
||||
<artifactId>jakarta.el-api</artifactId>
|
||||
<version>6.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>jakarta.el</artifactId>
|
||||
<version>5.0.0-M1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Jakarta Bean Validation -->
|
||||
<!-- https://mvnrepository.com/artifact/jakarta.validation/jakarta.validation-api -->
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- region logging -->
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.17.1</version>
|
||||
<version>2.24.3</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.17.1</version>
|
||||
<version>2.24.3</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j18-impl -->
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j2-impl -->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>2.17.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.28</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>1.7.28</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
<version>1.7.28</version>
|
||||
<artifactId>log4j-slf4j2-impl</artifactId>
|
||||
<version>2.24.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
<version>2.13.1</version>
|
||||
<version>2.18.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.13.2.1</version>
|
||||
<version>2.18.2</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/commons-cli/commons-cli -->
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.5.0</version>
|
||||
<version>1.9.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
<version>3.17.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/me.tongfei/progressbar -->
|
||||
<dependency>
|
||||
<groupId>me.tongfei</groupId>
|
||||
<artifactId>progressbar</artifactId>
|
||||
<version>0.9.3</version>
|
||||
<version>0.10.1</version>
|
||||
</dependency>
|
||||
<!-- endregion -->
|
||||
<!-- region unit-tests -->
|
||||
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.4.2</version>
|
||||
<version>5.11.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.4.2</version>
|
||||
<version>5.11.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
|
||||
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>5.15.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>5.11.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- endregion -->
|
||||
<dependency>
|
||||
<groupId>at.pcgamingfreaks</groupId>
|
||||
<artifactId>YAML-Parser</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/net.harawata/appdirs -->
|
||||
<dependency>
|
||||
<groupId>net.harawata</groupId>
|
||||
<artifactId>appdirs</artifactId>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
2
project.properties
Normal file
2
project.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
version=${project.version}
|
||||
project_name=${project.artifactId}
|
||||
@@ -1,92 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileCollector;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileProcessor;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import me.tongfei.progressbar.ProgressBar;
|
||||
import me.tongfei.progressbar.ProgressBarBuilder;
|
||||
import me.tongfei.progressbar.ProgressBarStyle;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Log4j2
|
||||
public class AttributeUpdaterKernel {
|
||||
|
||||
private final ExecutorService executor = Executors.newFixedThreadPool(Config.getInstance().getThreadCount());
|
||||
private final FileCollector collector;
|
||||
private final FileProcessor processor;
|
||||
private final ResultStatistic statistic = new ResultStatistic();
|
||||
|
||||
public AttributeUpdaterKernel(FileCollector collector, FileProcessor processor) {
|
||||
this.collector = collector;
|
||||
this.processor = processor;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void execute() {
|
||||
statistic.startTimer();
|
||||
|
||||
try (ProgressBar progressBar = pbBuilder().build()) {
|
||||
List<File> excludedFiles = Config.getInstance().getExcludedDirectories().stream()
|
||||
.map(collector::loadFiles)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
List<File> files = collector.loadFiles(Config.getInstance().getLibraryPath()).stream()
|
||||
.filter(file -> !excludedFiles.contains(file))
|
||||
.collect(Collectors.toList());
|
||||
progressBar.maxHint(files.size());
|
||||
files.forEach(file -> executor.submit(() -> process(file, progressBar)));
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(1, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
statistic.stopTimer();
|
||||
System.out.println(statistic);
|
||||
log.info(statistic);
|
||||
}
|
||||
|
||||
private void process(File file, ProgressBar progressBar) {
|
||||
List<FileAttribute> attributes = processor.loadAttributes(file);
|
||||
FileInfoDto fileInfo = processor.filterAttributes(attributes);
|
||||
statistic.total();
|
||||
if (fileInfo.isChangeNecessary()) {
|
||||
statistic.shouldChange();
|
||||
if (!Config.getInstance().isSafeMode()) {
|
||||
try {
|
||||
processor.update(file, fileInfo);
|
||||
statistic.success();
|
||||
log.info("Updated {}", file.getAbsolutePath());
|
||||
} catch (IOException e) {
|
||||
statistic.failedChanging();
|
||||
log.warn("File couldn't be updated: {}", file.getAbsoluteFile());
|
||||
}
|
||||
}
|
||||
} else if (fileInfo.isUnableToApplyConfig()) {
|
||||
statistic.noSuitableConfigFound();
|
||||
} else if (fileInfo.isAlreadySuitable()){
|
||||
statistic.alreadyFits();
|
||||
} else {
|
||||
statistic.failure();
|
||||
}
|
||||
progressBar.step();
|
||||
}
|
||||
|
||||
private static ProgressBarBuilder pbBuilder() {
|
||||
return new ProgressBarBuilder()
|
||||
.setStyle(ProgressBarStyle.ASCII)
|
||||
.setUpdateIntervalMillis(250)
|
||||
.setMaxRenderedLength(75);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,65 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.CachedMkvFileProcessor;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel.AttributeUpdaterKernel;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel.CoherentAttributeUpdaterKernel;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel.DefaultAttributeUpdaterKernel;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.MkvFileCollector;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.MkvFileProcessor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.ProjectUtil;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@Log4j2
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Config.getInstance().initConfig(args);
|
||||
AttributeUpdaterKernel kernel = new AttributeUpdaterKernel(new MkvFileCollector(), new MkvFileProcessor());
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
@CommandLine.Command(
|
||||
name = "mkvasc",
|
||||
usageHelpWidth = 120,
|
||||
customSynopsis = {
|
||||
"mkvasc -a <attributeConfig>... -l <libraryPath> [-s]",
|
||||
"Example: mkvasc -a eng:eng eng:ger -l /mnt/media/ -s"
|
||||
},
|
||||
mixinStandardHelpOptions = true,
|
||||
versionProvider = ProjectUtil.class
|
||||
)
|
||||
public class Main implements Runnable {
|
||||
|
||||
@Getter
|
||||
@CommandLine.ArgGroup(exclusive = false)
|
||||
private Config config;
|
||||
|
||||
@CommandLine.Spec
|
||||
CommandLine.Model.CommandSpec spec;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
validate();
|
||||
Config.setInstance(config);
|
||||
AttributeUpdaterKernel kernel = Config.getInstance().getCoherent() != null
|
||||
? new CoherentAttributeUpdaterKernel(new MkvFileCollector(), new CachedMkvFileProcessor())
|
||||
: new DefaultAttributeUpdaterKernel(new MkvFileCollector(), new CachedMkvFileProcessor());
|
||||
kernel.execute();
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
Set<ConstraintViolation<Config>> violations = validator.validate(config);
|
||||
|
||||
if (!violations.isEmpty()) {
|
||||
StringBuilder errorMsg = new StringBuilder();
|
||||
for (ConstraintViolation<Config> violation : violations) {
|
||||
errorMsg.append("ERROR: ").append(violation.getPropertyPath()).append(" ").append(violation.getMessage()).append("\n");
|
||||
}
|
||||
throw new CommandLine.ParameterException(spec.commandLine(), errorMsg.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new CommandLine(Main.class).execute(args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.LanguageValidatorUtil.isAudioLanguageValid;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.LanguageValidatorUtil.isLanguageValid;
|
||||
|
||||
public class AttributeConfigConverter implements CommandLine.ITypeConverter<AttributeConfig> {
|
||||
private static final String SEPARATOR = ":";
|
||||
private static final Pattern PATTERN = Pattern.compile("^.{3}:.{3}$");
|
||||
|
||||
/**
|
||||
* Converts the input string into an AttributeConfig object.
|
||||
*
|
||||
* @param s The input string containing audio and subtitle language configuration in format "audioLang:subtitleLang"
|
||||
* @return An AttributeConfig object representing the parsed configuration
|
||||
* @throws CommandLine.TypeConversionException if the input string is invalid or contains invalid language codes
|
||||
*/
|
||||
@Override
|
||||
public AttributeConfig convert(String s) throws Exception {
|
||||
validateInput(s);
|
||||
|
||||
String[] split = s.split(SEPARATOR);
|
||||
AttributeConfig attr = new AttributeConfig(split[0], split[1]);
|
||||
|
||||
validateResult(attr);
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the input string matches the expected pattern.
|
||||
*
|
||||
* @param s String to validate
|
||||
* @throws CommandLine.TypeConversionException if the value doesn't match the expected pattern
|
||||
*/
|
||||
private static void validateInput(String s) {
|
||||
if (!PATTERN.matcher(s).matches()) {
|
||||
throw new CommandLine.TypeConversionException("Invalid Attribute config: " + s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that both language codes in the AttributeConfig object are valid.
|
||||
*
|
||||
* @param attr AttributeConfig object to validate
|
||||
* @throws CommandLine.TypeConversionException if either language code is invalid
|
||||
*/
|
||||
private static void validateResult(AttributeConfig attr) {
|
||||
if (!isAudioLanguageValid(attr.getAudioLanguage()))
|
||||
throw new CommandLine.TypeConversionException("Audio language invalid: " + attr.getAudioLanguage());
|
||||
if (!isLanguageValid(attr.getSubtitleLanguage()))
|
||||
throw new CommandLine.TypeConversionException("Subtitle language invalid: " + attr.getSubtitleLanguage());
|
||||
}
|
||||
}
|
||||
@@ -2,222 +2,150 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.MkvToolNix;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.VersionUtil;
|
||||
import at.pcgamingfreaks.yaml.YAML;
|
||||
import at.pcgamingfreaks.yaml.YamlInvalidContentException;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.commons.cli.*;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty.*;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.CommandLineOptionsUtil.optionOf;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.LanguageValidatorUtil.isLanguageValid;
|
||||
|
||||
@Log4j2
|
||||
@Slf4j
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class Config {
|
||||
@Getter(AccessLevel.NONE)
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
@Getter(AccessLevel.NONE)
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
|
||||
@Getter(AccessLevel.NONE)
|
||||
@Setter(AccessLevel.NONE)
|
||||
private static Config config = null;
|
||||
|
||||
private File configPath;
|
||||
private String libraryPath;
|
||||
private boolean isSafeMode;
|
||||
|
||||
private int threadCount;
|
||||
private Pattern includePattern;
|
||||
@Getter(AccessLevel.NONE)
|
||||
private String mkvToolNixPath;
|
||||
|
||||
private boolean isWindows;
|
||||
|
||||
private final Set<String> forcedKeywords = new HashSet<>(Arrays.asList("forced", "signs"));
|
||||
private final Set<String> excludedDirectories = new HashSet<>();
|
||||
@CommandLine.Spec
|
||||
CommandLine.Model.CommandSpec spec;
|
||||
|
||||
@CommandLine.Option(names = {"-a", "--attribute-config"}, required = true, arity = "1..*", converter = AttributeConfigConverter.class)
|
||||
private List<AttributeConfig> attributeConfig;
|
||||
|
||||
@Setter(AccessLevel.NONE)
|
||||
private File libraryPath;
|
||||
|
||||
@CommandLine.Option(names = {"-s", "--safemode"}, description = "test run (no files will be changes)")
|
||||
private boolean safeMode;
|
||||
|
||||
@Setter(AccessLevel.NONE)
|
||||
private File mkvToolNix;
|
||||
|
||||
@Min(value = 1)
|
||||
@CommandLine.Option(names = {"-t", "--threads"}, defaultValue = "2", description = "thread count (default: ${DEFAULT-VALUE})")
|
||||
private int threads;
|
||||
|
||||
@CommandLine.Option(names = {"-c", "--coherent"}, description = "try to match all files in dir of depth with the same attribute config")
|
||||
private Integer coherent;
|
||||
@CommandLine.Option(names = {"-cf", "--force-coherent"}, description = "changes are only applied if it's a coherent match")
|
||||
private boolean forceCoherent;
|
||||
|
||||
@CommandLine.Option(names = {"-n"}, description = "sets filter-date to last successful execution (overwrites input of filter-date)")
|
||||
private boolean onlyNewFiles;
|
||||
@CommandLine.Option(names = {"-d", "--filter-date"}, defaultValue = CommandLine.Option.NULL_VALUE, description = "only consider files created newer than entered date (format: \"dd.MM.yyyy-HH:mm:ss\")")
|
||||
private Date filterDate;
|
||||
@CommandLine.Option(names = {"-i", "--include-pattern"}, defaultValue = ".*", description = "include files matching pattern (default: \".*\")")
|
||||
private Pattern includePattern;
|
||||
@CommandLine.Option(names = {"-e", "--excluded-directory"}, arity = "1..*",
|
||||
description = "Directories to be excluded, combines with config file")
|
||||
private Set<String> excludedDirectories = new HashSet<>();
|
||||
|
||||
@CommandLine.Option(names = {"-fk", "--force-keywords"}, arity = "1..*",
|
||||
description = "Additional keywords to identify forced tracks (Defaults are will be overwritten; Default: ${DEFAULT-VALUE}")
|
||||
private Set<String> forcedKeywords = new HashSet<>(Arrays.asList("forced", "signs", "songs"));
|
||||
@CommandLine.Option(names = {"-ck", "--commentary-keywords"}, arity = "1..*",
|
||||
description = "Additional keywords to identify commentary tracks (Defaults are will be overwritten; Default: ${DEFAULT-VALUE}")
|
||||
private Set<String> commentaryKeywords = new HashSet<>(Arrays.asList("commentary", "director"));
|
||||
@CommandLine.Option(names = {"-ps", "--preferred-subtiltes"}, arity = "1..*",
|
||||
description = "Additional keywords to prefer specific subtitle tracks (Defaults are will be overwritten; Default: ${DEFAULT-VALUE}")
|
||||
private Set<String> preferredSubtitles = new HashSet<>(Arrays.asList("unstyled"));
|
||||
|
||||
@CommandLine.Option(names = {"-l", "--library"}, required = true, description = "path to library")
|
||||
public void setLibraryPath(File libraryPath) {
|
||||
if (!libraryPath.exists()) throw new CommandLine.ParameterException(spec.commandLine(), "Path does not exist: " + libraryPath.getAbsolutePath());
|
||||
this.libraryPath = libraryPath;
|
||||
}
|
||||
|
||||
static {
|
||||
// Set default value into system properties to picocli can read the conditional value
|
||||
System.setProperty("DEFAULT_MKV_TOOL_NIX", SystemUtils.IS_OS_WINDOWS ? "C:\\Program Files\\MKVToolNix" : "/usr/bin/");
|
||||
}
|
||||
|
||||
@CommandLine.Option(names = {"-m", "--mkvtoolnix"}, defaultValue = "${DEFAULT_MKV_TOOL_NIX}", description = "path to mkvtoolnix installation")
|
||||
public void setMkvToolNix(File mkvToolNix) {
|
||||
this.mkvToolNix = mkvToolNix;
|
||||
if (!mkvToolNix.exists()
|
||||
|| !Path.of(getPathFor(MkvToolNix.MKV_MERGE, SystemUtils.IS_OS_WINDOWS)).toFile().exists()
|
||||
|| !Path.of(getPathFor(MkvToolNix.MKV_PROP_EDIT, SystemUtils.IS_OS_WINDOWS)).toFile().exists()) {
|
||||
throw new CommandLine.ParameterException(spec.commandLine(),
|
||||
"Invalid path to mkvtoolnix installation: " + mkvToolNix.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
public static Config getInstance() {
|
||||
if (config == null) {
|
||||
return getInstance(false);
|
||||
}
|
||||
|
||||
public static Config getInstance(boolean reset) {
|
||||
if (config == null || reset) {
|
||||
config = new Config();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public void initConfig(String[] args) throws InvalidConfigException {
|
||||
ConfigErrors errors = new ConfigErrors();
|
||||
CommandLine cmd = null;
|
||||
Options options = initOptions();
|
||||
|
||||
try {
|
||||
cmd = parser.parse(options, args);
|
||||
if (cmd == null) throw new NullPointerException();
|
||||
} catch (ParseException | NullPointerException e) {
|
||||
formatter.printHelp(106, "java -jar MKVAudioSubtitlesChanger.jar -l <path_to_library>",
|
||||
"\nParameters:", options,
|
||||
"\nFeature requests and bug reports: https://github.com/RatzzFatzz/MKVAudioSubtitleChanger/issues");
|
||||
System.exit(1);
|
||||
public static void setInstance(Config c) {
|
||||
config = c;
|
||||
}
|
||||
|
||||
exitIfHelp(cmd, options);
|
||||
exitIfVersion(cmd);
|
||||
|
||||
configPath = loadConfigPath(cmd, errors);
|
||||
libraryPath = loadLibraryPath(cmd, errors);
|
||||
isSafeMode = cmd.hasOption(SAFE_MODE.prop());
|
||||
|
||||
try (YAML config = new YAML(configPath)) {
|
||||
threadCount = loadThreadCount(cmd, config);
|
||||
includePattern = loadIncludePattern(cmd, config, errors);
|
||||
mkvToolNixPath = loadMkvToolNixPath(cmd, config, errors);
|
||||
|
||||
isWindows = loadOperatingSystem();
|
||||
|
||||
loadForcedKeywords(cmd, config);
|
||||
loadExcludedDirectories(cmd, config);
|
||||
|
||||
attributeConfig = loadAttributeConfig(config, errors);
|
||||
} catch (IOException | YamlInvalidContentException ignored) {}
|
||||
|
||||
if (errors.hasErrors()) {
|
||||
throw new InvalidConfigException(errors);
|
||||
}
|
||||
/**
|
||||
* Get path to specific mkvtoolnix application.
|
||||
*
|
||||
* @return absolute path to desired application.
|
||||
*/
|
||||
public String getPathFor(MkvToolNix application) {
|
||||
return mkvToolNix.getAbsolutePath().endsWith("/")
|
||||
? mkvToolNix.getAbsolutePath() + application
|
||||
: mkvToolNix.getAbsolutePath() + "/" + application;
|
||||
}
|
||||
|
||||
private static Options initOptions() {
|
||||
Options options = new Options();
|
||||
options.addOption(optionOf(HELP, "h", false));
|
||||
options.addOption(optionOf(VERSION, "v", false));
|
||||
options.addOption(optionOf(LIBRARY, "l", true));
|
||||
options.addOption(optionOf(MKV_TOOL_NIX, "m", true));
|
||||
options.addOption(optionOf(CONFIG_PATH, "c", true));
|
||||
options.addOption(optionOf(THREADS, "t", true));
|
||||
options.addOption(optionOf(SAFE_MODE, "s", false));
|
||||
options.addOption(optionOf(FORCED_KEYWORDS, "k", Option.UNLIMITED_VALUES, false));
|
||||
options.addOption(optionOf(EXCLUDE_DIRECTORY, "e", Option.UNLIMITED_VALUES, false));
|
||||
options.addOption(optionOf(INCLUDE_PATTERN, "i", true));
|
||||
return options;
|
||||
public String getPathFor(MkvToolNix application, boolean isWindows) {
|
||||
return getPathFor(application) + (isWindows ? ".exe" : "");
|
||||
}
|
||||
|
||||
private void exitIfHelp(CommandLine cmd, Options options) {
|
||||
if (cmd.hasOption("help")) {
|
||||
formatter.printHelp(106, "java -jar MKVAudioSubtitlesChanger.jar -l <path_to_library>",
|
||||
"\nParameters:", options,
|
||||
"\nFeature requests and bug reports: https://github.com/RatzzFatzz/MKVAudioSubtitleChanger/issues");
|
||||
System.exit(0);
|
||||
}
|
||||
public String getNormalizedLibraryPath() {
|
||||
return this.getLibraryPath().getAbsolutePath().replace("\\", "/");
|
||||
}
|
||||
|
||||
private void exitIfVersion(CommandLine cmd) {
|
||||
if (cmd.hasOption(VERSION.prop())) {
|
||||
System.out.printf("MKV Audio Subtitle Changer Version %s%n", VersionUtil.getVersion());
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
private File loadConfigPath(CommandLine cmd, ConfigErrors errors) {
|
||||
File configPath = new File(cmd.getOptionValue(CONFIG_PATH.prop(), "config.yaml"));
|
||||
if (configPath.isFile()) return configPath;
|
||||
|
||||
errors.add("invalid config path");
|
||||
return null;
|
||||
}
|
||||
|
||||
private String loadLibraryPath(CommandLine cmd, ConfigErrors errors) {
|
||||
if (cmd.hasOption(LIBRARY.prop())) {
|
||||
File libraryPath = new File(cmd.getOptionValue(LIBRARY.prop()));
|
||||
if (libraryPath.isFile() || libraryPath.isDirectory()) {
|
||||
return libraryPath.getAbsolutePath();
|
||||
} else {
|
||||
errors.add("invalid library path");
|
||||
}
|
||||
} else {
|
||||
errors.add("missing library path");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int loadThreadCount(CommandLine cmd, YAML config) {
|
||||
return cmd.hasOption(THREADS.prop())
|
||||
? Integer.parseInt(cmd.getOptionValue(THREADS.prop()))
|
||||
: config.getInt(THREADS.prop(), 2);
|
||||
}
|
||||
|
||||
private Pattern loadIncludePattern(CommandLine cmd, YAML config, ConfigErrors errors) {
|
||||
try {
|
||||
return Pattern.compile(cmd.hasOption(INCLUDE_PATTERN.prop())
|
||||
? cmd.getOptionValue(INCLUDE_PATTERN.prop())
|
||||
: config.getString(INCLUDE_PATTERN.prop(), ".*"));
|
||||
} catch (PatternSyntaxException e) {
|
||||
errors.add("invalid regex pattern");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private String loadMkvToolNixPath(CommandLine cmd, YAML config, ConfigErrors errors){
|
||||
if (cmd.hasOption(MKV_TOOL_NIX.prop())) return cmd.getOptionValue(MKV_TOOL_NIX.prop());
|
||||
if (config.isSet(MKV_TOOL_NIX.prop())) return config.getString(MKV_TOOL_NIX.prop());
|
||||
errors.add("path to mkv tool nix installation missing");
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean loadOperatingSystem() {
|
||||
return System.getProperty("os.name").toLowerCase().contains("windows");
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void loadForcedKeywords(CommandLine cmd, YAML config) {
|
||||
if (cmd.hasOption(FORCED_KEYWORDS.prop())) forcedKeywords.addAll(List.of(cmd.getOptionValues(FORCED_KEYWORDS.prop())));
|
||||
if (config.isSet(FORCED_KEYWORDS.prop())) forcedKeywords.addAll(config.getStringList(FORCED_KEYWORDS.prop()));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void loadExcludedDirectories(CommandLine cmd, YAML config) {
|
||||
if (cmd.hasOption(EXCLUDE_DIRECTORY.prop())) excludedDirectories.addAll(List.of(cmd.getOptionValues(EXCLUDE_DIRECTORY.prop())));
|
||||
if (config.isSet(EXCLUDE_DIRECTORY.prop())) excludedDirectories.addAll(config.getStringList(EXCLUDE_DIRECTORY.prop()));
|
||||
}
|
||||
|
||||
private List<AttributeConfig> loadAttributeConfig(YAML config, ConfigErrors errors) {
|
||||
Function<String, String> audio = key -> config.getString(key + ".audio", null);
|
||||
Function<String, String> subtitle = key -> config.getString(key + ".subtitle", null);
|
||||
|
||||
List<AttributeConfig> attributeConfigs = config.getKeysFiltered(".*audio.*").stream()
|
||||
.sorted()
|
||||
.map(key -> key.replace(".audio", ""))
|
||||
.map(key -> new AttributeConfig(audio.apply(key), subtitle.apply(key)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (attributeConfigs.isEmpty()) {
|
||||
errors.add("no language configuration");
|
||||
} else {
|
||||
for (AttributeConfig attributeConfig : attributeConfigs) {
|
||||
isLanguageValid(attributeConfig.getAudioLanguage(), errors);
|
||||
isLanguageValid(attributeConfig.getSubtitleLanguage(), errors);
|
||||
}
|
||||
}
|
||||
|
||||
return attributeConfigs;
|
||||
}
|
||||
|
||||
public String getPathFor(MkvToolNix exe) {
|
||||
return mkvToolNixPath.endsWith("/") ? mkvToolNixPath + exe : mkvToolNixPath + "/" + exe;
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", Config.class.getSimpleName() + "[", "]")
|
||||
.add("configPath=" + configPath)
|
||||
.add("libraryPath=" + libraryPath)
|
||||
.add("mkvToolNix=" + mkvToolNix)
|
||||
.add("threads=" + threads)
|
||||
.add("includePattern=" + includePattern)
|
||||
.add("safeMode=" + safeMode)
|
||||
.add("coherent=" + coherent)
|
||||
.add("forceCoherent=" + forceCoherent)
|
||||
.add("onlyNewFiles=" + onlyNewFiles)
|
||||
.add("filterDate=" + filterDate)
|
||||
.add("forcedKeywords=" + forcedKeywords)
|
||||
.add("commentaryKeywords=" + commentaryKeywords)
|
||||
.add("excludedDirectories=" + excludedDirectories)
|
||||
.add("preferredSubtitles=" + preferredSubtitles)
|
||||
.add("attributeConfig=" + attributeConfig)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ConfigErrors {
|
||||
private final List<String> errors = new ArrayList<>();
|
||||
|
||||
public void add(String errorMessage) {
|
||||
errors.add(errorMessage);
|
||||
}
|
||||
|
||||
public boolean hasErrors() {
|
||||
return !errors.isEmpty();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return StringUtils.capitalize(String.join(", ", errors));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
public class InvalidConfigException extends RuntimeException{
|
||||
public InvalidConfigException(ConfigErrors errors) {
|
||||
super("Errors in config: " + errors);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
@Deprecated
|
||||
public enum ValidationResult {
|
||||
VALID,
|
||||
DEFAULT,
|
||||
NOT_PRESENT,
|
||||
MISSING,
|
||||
INVALID;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import at.pcgamingfreaks.yaml.YAML;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.LanguageValidatorUtil.isAudioLanguageValid;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.LanguageValidatorUtil.isLanguageValid;
|
||||
|
||||
@Deprecated
|
||||
public class AttributeConfigValidator extends ConfigValidator<List<AttributeConfig>> {
|
||||
private static final String SEPARATOR = ":";
|
||||
|
||||
public AttributeConfigValidator() {
|
||||
super(ConfigProperty.ATTRIBUTE_CONFIG, true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public ValidationResult validate(YAML yaml, CommandLine cmd) {
|
||||
System.out.printf("%s: ", property.prop());
|
||||
List<AttributeConfig> result;
|
||||
|
||||
|
||||
if (cmd.hasOption(property.prop())) {
|
||||
List<String> values = List.of(cmd.getOptionValues(property.prop()));
|
||||
result = values.stream().anyMatch(pair -> !pair.contains(SEPARATOR))
|
||||
? List.of()
|
||||
: values.stream().map(pair -> pair.split(SEPARATOR))
|
||||
.map(split -> new AttributeConfig(split[0], split[1]))
|
||||
.collect(Collectors.toList());
|
||||
} else if(yaml.getKeysFiltered(property.prop() + ".*").size() > 0) {
|
||||
Function<String, String> audio = key -> yaml.getString(key + ".audio", null);
|
||||
Function<String, String> subtitle = key -> yaml.getString(key + ".subtitle", null);
|
||||
|
||||
result = yaml.getKeysFiltered(".*audio.*").stream()
|
||||
.sorted()
|
||||
.map(key -> key.replace(".audio", ""))
|
||||
.map(key -> new AttributeConfig(audio.apply(key), subtitle.apply(key)))
|
||||
.collect(Collectors.toList());
|
||||
} else if (required) {
|
||||
System.out.println("missing");
|
||||
return ValidationResult.MISSING;
|
||||
} else {
|
||||
System.out.println("ok");
|
||||
return ValidationResult.NOT_PRESENT;
|
||||
}
|
||||
|
||||
if (!isValid(result) || !setValue(result)) {
|
||||
System.out.println("invalid");
|
||||
return ValidationResult.INVALID;
|
||||
}
|
||||
|
||||
System.out.println("ok");
|
||||
return ValidationResult.VALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
List<AttributeConfig> parse(String value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
boolean isValid(List<AttributeConfig> result) {
|
||||
if (result.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
boolean isValid;
|
||||
for (AttributeConfig attributeConfig : result) {
|
||||
isValid = isAudioLanguageValid(attributeConfig.getAudioLanguage())
|
||||
&& isLanguageValid(attributeConfig.getSubtitleLanguage());
|
||||
if (!isValid) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import at.pcgamingfreaks.yaml.YAML;
|
||||
import at.pcgamingfreaks.yaml.YamlKeyNotFoundException;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty.ARGUMENTS;
|
||||
|
||||
@Deprecated
|
||||
public class BooleanValidator extends ConfigValidator<Boolean> {
|
||||
|
||||
public BooleanValidator(ConfigProperty property, boolean required) {
|
||||
super(property, required, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected BiFunction<YAML, ConfigProperty, Optional<Boolean>> provideDataYaml() {
|
||||
return (yaml, property) -> {
|
||||
if (yaml.isSet(ARGUMENTS.prop())
|
||||
&& yaml.getStringList(ARGUMENTS.prop(), List.of()).contains(property.prop())) {
|
||||
return Optional.of(true);
|
||||
}
|
||||
return Optional.empty();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected BiFunction<CommandLine, ConfigProperty, Optional<Boolean>> provideDataCmd() {
|
||||
return (cmd, property) -> {
|
||||
if (cmd.hasOption(property.prop())) {
|
||||
return Optional.of(true);
|
||||
}
|
||||
return Optional.empty();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* This should not be used.
|
||||
*/
|
||||
@Override
|
||||
Boolean parse(String value) {
|
||||
throw new RuntimeException("This should not be called");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* Validation is skipped.
|
||||
*/
|
||||
@Override
|
||||
boolean isValid(Boolean result) {
|
||||
return true; // skip
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
@Deprecated
|
||||
public class CoherentConfigValidator extends ConfigValidator<Integer> {
|
||||
private static final Integer DISABLED = -1;
|
||||
|
||||
public CoherentConfigValidator(ConfigProperty property, boolean required) {
|
||||
super(property, required, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
Integer parse(String value) {
|
||||
return NumberUtils.isParsable(value) ? Integer.parseInt(value) : DISABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isValid(Integer result) {
|
||||
return result >= 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import at.pcgamingfreaks.yaml.YAML;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@Deprecated
|
||||
public class ConfigPathValidator extends PathValidator {
|
||||
public ConfigPathValidator(ConfigProperty property, boolean required) {
|
||||
super(property, required, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected BiFunction<YAML, ConfigProperty, Optional<File>> provideDataYaml() {
|
||||
return (yaml, property) -> Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected boolean isValid(File result) {
|
||||
return super.isValid(result) && (result.getAbsolutePath().endsWith(".yml") || result.getAbsolutePath().endsWith(".yaml"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight() {
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import at.pcgamingfreaks.yaml.YAML;
|
||||
import at.pcgamingfreaks.yaml.YamlKeyNotFoundException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Deprecated
|
||||
public abstract class ConfigValidator<FieldType> {
|
||||
protected final ConfigProperty property;
|
||||
protected final boolean required;
|
||||
protected final FieldType defaultValue;
|
||||
|
||||
/**
|
||||
* Validate the user input. Parameters of cmd are prioritised.
|
||||
*
|
||||
* @param yaml config file
|
||||
* @param cmd command line parameters
|
||||
* @return {@link ValidationResult} containing validity of input.
|
||||
*/
|
||||
public ValidationResult validate(YAML yaml, CommandLine cmd) {
|
||||
System.out.printf("%s: ", property.prop());
|
||||
FieldType result;
|
||||
|
||||
Optional<FieldType> cmdResult = provideDataCmd().apply(cmd, property);
|
||||
Optional<FieldType> yamlResult = provideDataYaml().apply(yaml, property);
|
||||
|
||||
if (isOverwritingNecessary()) {
|
||||
result = overwriteValue();
|
||||
} else if (cmdResult.isPresent()) {
|
||||
result = cmdResult.get();
|
||||
} else if (yamlResult.isPresent()) {
|
||||
result = yamlResult.get();
|
||||
} else {
|
||||
if (defaultValue != null) {
|
||||
if (setValue(defaultValue)) {
|
||||
System.out.println("default");
|
||||
return ValidationResult.DEFAULT;
|
||||
} else {
|
||||
System.out.println("invalid");
|
||||
return ValidationResult.INVALID;
|
||||
}
|
||||
}
|
||||
if (required) {
|
||||
System.out.println("missing");
|
||||
return ValidationResult.MISSING;
|
||||
} else {
|
||||
System.out.println("ok");
|
||||
return ValidationResult.NOT_PRESENT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid(result) || !setValue(result)) {
|
||||
System.out.println("invalid");
|
||||
return ValidationResult.INVALID;
|
||||
}
|
||||
|
||||
System.out.println("ok");
|
||||
return ValidationResult.VALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return parsed input of yaml config for property
|
||||
*/
|
||||
protected BiFunction<YAML, ConfigProperty, Optional<FieldType>> provideDataYaml() {
|
||||
return (yaml, property) -> {
|
||||
if (yaml.isSet(property.prop())) {
|
||||
try {
|
||||
return Optional.of(parse(yaml.getString(property.prop())));
|
||||
} catch (YamlKeyNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return parsed input of command line parameters config for property
|
||||
*/
|
||||
protected BiFunction<CommandLine, ConfigProperty, Optional<FieldType>> provideDataCmd() {
|
||||
return (cmd, property) -> {
|
||||
if (cmd.hasOption(property.prop())) {
|
||||
return Optional.of(parse(cmd.getOptionValue(property.prop())));
|
||||
}
|
||||
return Optional.empty();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if overwriting this property is necessary.
|
||||
*/
|
||||
protected boolean isOverwritingNecessary() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link FieldType} to overwrite result with.
|
||||
*/
|
||||
protected FieldType overwriteValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse input parameter to desired format.
|
||||
*
|
||||
* @param value input parameter
|
||||
* @return parsed property
|
||||
*/
|
||||
abstract FieldType parse(String value);
|
||||
|
||||
/**
|
||||
* Validate if the data has the desired and allowed format.
|
||||
*
|
||||
* @param result parsed property
|
||||
* @return true if data is in desired format.
|
||||
*/
|
||||
abstract boolean isValid(FieldType result);
|
||||
|
||||
/**
|
||||
* Sets valid properties to {@link Config} via reflections.
|
||||
*
|
||||
* @param result parsed property
|
||||
* @return false if method invocation failed
|
||||
*/
|
||||
protected boolean setValue(FieldType result) {
|
||||
for (Method method : Config.getInstance().getClass().getDeclaredMethods()) {
|
||||
if (containsSetterOf(property).test(method)) {
|
||||
try {
|
||||
method.invoke(Config.getInstance(), result);
|
||||
return true;
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected Predicate<Method> containsSetterOf(ConfigProperty property) {
|
||||
return method -> StringUtils.startsWith(method.getName(), "set")
|
||||
&& StringUtils.equalsIgnoreCase(method.getName().replace("set", ""), property.prop().replace("-", ""));
|
||||
}
|
||||
|
||||
protected Predicate<Method> containsGetterOf(ConfigProperty property) {
|
||||
return method -> StringUtils.startsWith(method.getName(), "get")
|
||||
&& StringUtils.equalsIgnoreCase(method.getName().replace("get", ""), property.prop().replace("-", ""));
|
||||
}
|
||||
|
||||
public int getWeight() {
|
||||
return 50;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", ConfigValidator.class.getSimpleName() + "[", "]")
|
||||
.add("property=" + property)
|
||||
.add("required=" + required)
|
||||
.add("defaultValue=" + defaultValue)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.DateUtils;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.ProjectUtil;
|
||||
import at.pcgamingfreaks.yaml.YAML;
|
||||
import at.pcgamingfreaks.yaml.YamlInvalidContentException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.harawata.appdirs.AppDirsFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Date;
|
||||
|
||||
@Slf4j
|
||||
@Deprecated
|
||||
public class DateValidator extends ConfigValidator<Date> {
|
||||
private static final Date INVALID_DATE = new Date(0);
|
||||
private static final Date DEFAULT_DATE = new Date(1000);
|
||||
|
||||
public DateValidator(ConfigProperty property, boolean required) {
|
||||
super(property, required, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isOverwritingNecessary() {
|
||||
return Config.getInstance().isOnlyNewFiles();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Date overwriteValue() {
|
||||
try {
|
||||
String filePath = AppDirsFactory.getInstance().getUserConfigDir(ProjectUtil.getProjectName(), null, null);
|
||||
File lastExecutionFile = Path.of(filePath + "/last-execution.yml").toFile();
|
||||
if (!lastExecutionFile.exists()) {
|
||||
return DEFAULT_DATE;
|
||||
}
|
||||
YAML yaml = new YAML(lastExecutionFile);
|
||||
return parse(yaml.getString(Config.getInstance().getNormalizedLibraryPath(), DateUtils.convert(DEFAULT_DATE)));
|
||||
} catch (YamlInvalidContentException | IOException e) {
|
||||
log.error("Couldn't open last-execution.properties");
|
||||
return INVALID_DATE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Date parse(String value) {
|
||||
return DateUtils.convert(value, INVALID_DATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isValid(Date result) {
|
||||
return !result.equals(INVALID_DATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight() {
|
||||
return 40;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.MkvToolNix.MKV_MERGE;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.MkvToolNix.MKV_PROP_EDIT;
|
||||
|
||||
@Deprecated
|
||||
public class MkvToolNixPathValidator extends PathValidator {
|
||||
private static final String EXE = ".exe";
|
||||
|
||||
public MkvToolNixPathValidator(ConfigProperty property, boolean required, File defaultValue) {
|
||||
super(property, required, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValid(File result) {
|
||||
return result.isDirectory()
|
||||
&& (Path.of(result.getAbsolutePath() + "/" + MKV_MERGE + EXE).toFile().isFile()
|
||||
&& Path.of(result.getAbsolutePath() + "/" + MKV_PROP_EDIT + EXE).toFile().isFile())
|
||||
|| (Path.of(result.getAbsolutePath() + "/" + MKV_MERGE).toFile().isFile()
|
||||
&& Path.of(result.getAbsolutePath() + "/" + MKV_PROP_EDIT).toFile().isFile());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@Deprecated
|
||||
public class PathValidator extends ConfigValidator<File> {
|
||||
|
||||
public PathValidator(ConfigProperty property, boolean required, File defaultValue) {
|
||||
super(property, required, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected File parse(String value) {
|
||||
return Path.of(value).toFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected boolean isValid(File result) {
|
||||
return result.isDirectory() || result.isFile();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
@Deprecated
|
||||
public class PatternValidator extends ConfigValidator<Pattern> {
|
||||
private static final Pattern EMPTY_PATTERN = Pattern.compile("");
|
||||
|
||||
public PatternValidator(ConfigProperty property, boolean required, Pattern defaultValue) {
|
||||
super(property, required, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
Pattern parse(String value) {
|
||||
try {
|
||||
return Pattern.compile(value);
|
||||
} catch (PatternSyntaxException e) {
|
||||
return EMPTY_PATTERN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
boolean isValid(Pattern result) {
|
||||
return !result.equals(EMPTY_PATTERN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import at.pcgamingfreaks.yaml.YAML;
|
||||
import at.pcgamingfreaks.yaml.YamlKeyNotFoundException;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Deprecated
|
||||
public class SetValidator extends ConfigValidator<Set<String>> {
|
||||
private final boolean append;
|
||||
|
||||
public SetValidator(ConfigProperty property, boolean required, boolean append) {
|
||||
super(property, required, null);
|
||||
this.append = append;
|
||||
}
|
||||
|
||||
public ValidationResult validate(YAML yaml, CommandLine cmd) {
|
||||
System.out.printf("%s: ", property.prop());
|
||||
List<String> resultList = null;
|
||||
|
||||
if (cmd.hasOption(property.prop())) {
|
||||
resultList = List.of(cmd.getOptionValues(property.prop()));
|
||||
} else if (yaml.isSet(property.prop())) {
|
||||
try {
|
||||
resultList = yaml.getStringList(property.prop());
|
||||
} catch (YamlKeyNotFoundException ignored) {}
|
||||
} else if (required) {
|
||||
System.out.println("missing");
|
||||
return ValidationResult.MISSING;
|
||||
} else {
|
||||
System.out.println("ok");
|
||||
return ValidationResult.NOT_PRESENT;
|
||||
}
|
||||
|
||||
Set<String> result = parse(resultList);
|
||||
|
||||
if (!isValid(result) || !setValue(result)) {
|
||||
System.out.println("invalid");
|
||||
return ValidationResult.INVALID;
|
||||
}
|
||||
|
||||
System.out.println("ok");
|
||||
return ValidationResult.VALID;
|
||||
}
|
||||
|
||||
protected BiFunction<YAML, ConfigProperty, Optional<Set<String>>> provideDataYaml() {
|
||||
return (yaml, property) -> {
|
||||
if (yaml.isSet(property.prop())) {
|
||||
try {
|
||||
return Optional.of(parse(yaml.getStringList(property.prop())));
|
||||
} catch (YamlKeyNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
};
|
||||
}
|
||||
|
||||
protected BiFunction<CommandLine, ConfigProperty, Optional<Set<String>>> provideDataCmd() {
|
||||
return (cmd, property) -> {
|
||||
if (cmd.hasOption(property.prop())) {
|
||||
return Optional.of(parse(List.of(cmd.getOptionValues(property.prop()))));
|
||||
}
|
||||
return Optional.empty();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<String> parse(String value) {
|
||||
throw new RuntimeException("This should not be called");
|
||||
}
|
||||
|
||||
protected Set<String> parse(List<String> value) {
|
||||
return new HashSet<>(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isValid(Set<String> result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean setValue(Set<String> result) {
|
||||
List<Method> methods = append
|
||||
? Arrays.stream(Config.getInstance().getClass().getDeclaredMethods())
|
||||
.filter(containsGetterOf(property))
|
||||
.collect(Collectors.toList())
|
||||
: Arrays.stream(Config.getInstance().getClass().getDeclaredMethods())
|
||||
.filter(containsSetterOf(property))
|
||||
.collect(Collectors.toList());
|
||||
if (methods.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (append) {
|
||||
((Set<String>) methods.get(0).invoke(Config.getInstance())).addAll(result);
|
||||
} else {
|
||||
methods.get(0).invoke(Config.getInstance(), result);
|
||||
}
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
@Deprecated
|
||||
public class ThreadValidator extends ConfigValidator<Integer>{
|
||||
public ThreadValidator(ConfigProperty property, boolean required, Integer defaultValue) {
|
||||
super(property, required, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
Integer parse(String value) {
|
||||
return NumberUtils.isParsable(value) ? Integer.parseInt(value) : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
boolean isValid(Integer result) {
|
||||
return result > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions;
|
||||
|
||||
public class MkvToolNixException extends RuntimeException{
|
||||
|
||||
public MkvToolNixException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return super.getMessage().replaceAll("\\r|\\n", " ");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Cache<Key, Value> {
|
||||
private final Map<Key, Value> cache = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Retrieve {@link Value} from Cache or run creationFunction and return its value.
|
||||
* @param key key of cache map
|
||||
* @param creationFunction function to create missing values
|
||||
* @return {@link Value} from Cache, or if missing result from creationFunction.
|
||||
*/
|
||||
public synchronized Value retrieve(Key key, Function<Key, Value> creationFunction) {
|
||||
return cache.computeIfAbsent(key, creationFunction::apply);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class CachedMkvFileProcessor extends MkvFileProcessor {
|
||||
Cache<File, List<FileAttribute>> cache = new Cache<>();
|
||||
|
||||
@Override
|
||||
public List<FileAttribute> loadAttributes(File file) {
|
||||
return cache.retrieve(file, super::loadAttributes);
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,13 @@ public interface FileCollector {
|
||||
* @return list of all files within the directory
|
||||
*/
|
||||
List<File> loadFiles(String path);
|
||||
|
||||
/**
|
||||
* Load all directories from path, but only until depth is reached.
|
||||
*
|
||||
* @param path leads to a directory which will be loaded recursively until depth
|
||||
* @param depth limit directory crawling
|
||||
* @return list of directory until depth
|
||||
*/
|
||||
List<File> loadDirectories(String path, int depth);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,52 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.DateUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Date;
|
||||
|
||||
@Slf4j
|
||||
public class FileFilter {
|
||||
static boolean accept(File pathName, String[] fileExtensions) {
|
||||
return StringUtils.endsWithAny(pathName.getAbsolutePath().toLowerCase(), fileExtensions)
|
||||
&& Config.getInstance().getIncludePattern().matcher(pathName.getName()).matches();
|
||||
if (hasProperFileExtension(pathName, fileExtensions)
|
||||
&& hasMatchingPattern(pathName)
|
||||
&& isNewer(pathName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ResultStatistic.getInstance().total();
|
||||
ResultStatistic.getInstance().excluded();
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasProperFileExtension(File pathName, String[] fileExtensions) {
|
||||
return StringUtils.endsWithAny(pathName.getAbsolutePath().toLowerCase(), fileExtensions);
|
||||
}
|
||||
|
||||
private static boolean hasMatchingPattern(File pathName) {
|
||||
return Config.getInstance().getIncludePattern().matcher(pathName.getName()).matches();
|
||||
}
|
||||
|
||||
private static boolean isNewer(File pathName) {
|
||||
Config config = Config.getInstance();
|
||||
if (config.getFilterDate() == null) return true;
|
||||
try {
|
||||
BasicFileAttributes attributes = Files.readAttributes(pathName.toPath(), BasicFileAttributes.class);
|
||||
return isNewer(DateUtils.convert(attributes.creationTime().toMillis()));
|
||||
} catch (IOException e) {
|
||||
log.warn("File attributes could not be read.", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isNewer(Date creationDate) {
|
||||
return creationDate.toInstant().isAfter(Config.getInstance().getFilterDate().toInstant());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
|
||||
|
||||
@@ -10,12 +12,40 @@ import java.util.List;
|
||||
public interface FileProcessor {
|
||||
|
||||
/**
|
||||
* Load track information from file.
|
||||
*
|
||||
* @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);
|
||||
/**
|
||||
* Populate FileInfoDto with the currently set default tracks.
|
||||
* @param info to be populated
|
||||
* @param attributes Track information of FileInfoDto
|
||||
* @param nonForcedTracks List of all not forced tracks
|
||||
*/
|
||||
void detectDefaultTracks(FileInfoDto info, List<FileAttribute> attributes, List<FileAttribute> nonForcedTracks);
|
||||
|
||||
void update(File file, FileInfoDto fileInfo) throws IOException;
|
||||
/**
|
||||
* Populate FileInfoDto with the desired tracks, based on AttributeConfig.
|
||||
* @param info to be populated
|
||||
* @param nonForcedTracks List of all non-forced tracks
|
||||
* @param nonCommentaryTracks List of all non-commentary tracks
|
||||
*/
|
||||
void detectDesiredTracks(FileInfoDto info, List<FileAttribute> nonForcedTracks, List<FileAttribute> nonCommentaryTracks,
|
||||
AttributeConfig... configs);
|
||||
|
||||
List<FileAttribute> retrieveNonForcedTracks(List<FileAttribute> attributes);
|
||||
|
||||
List<FileAttribute> retrieveNonCommentaryTracks(List<FileAttribute> attributes);
|
||||
|
||||
/**
|
||||
* Update the file.
|
||||
* @param file to be updated
|
||||
* @param fileInfo information to update file
|
||||
* @throws IOException when error occurs accessing file retrieving information
|
||||
* @throws MkvToolNixException when error occurs while sending query to mkvpropedit
|
||||
*/
|
||||
void update(File file, FileInfoDto fileInfo) throws IOException, MkvToolNixException;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -12,10 +12,13 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Log4j2
|
||||
@Slf4j
|
||||
public class MkvFileCollector implements FileCollector {
|
||||
private static final String[] fileExtensions = new String[]{".mkv", ".mka", ".mks", ".mk3d"};
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<File> loadFiles(String path) {
|
||||
try (Stream<Path> paths = Files.walk(Paths.get(path))) {
|
||||
@@ -28,4 +31,19 @@ public class MkvFileCollector implements FileCollector {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<File> loadDirectories(String path, int depth) {
|
||||
try (Stream<Path> paths = Files.walk(Paths.get(path), depth)) {
|
||||
return paths.map(Path::toFile)
|
||||
.filter(File::isDirectory)
|
||||
.collect(Collectors.toList());
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't find file or directory!", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.*;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.SetUtils;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.core.util.IOUtils;
|
||||
|
||||
@@ -13,26 +15,31 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.LaneType.AUDIO;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.LaneType.SUBTITLES;
|
||||
import static java.lang.String.format;
|
||||
|
||||
@Log4j2
|
||||
@Slf4j
|
||||
public class MkvFileProcessor implements FileProcessor {
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
private final String[] forcedKeywords = new String[]{"forced", "signs"};
|
||||
|
||||
private static final SubtitleTrackComparator subtitleTrackComparator =
|
||||
new SubtitleTrackComparator(Config.getInstance().getPreferredSubtitles().toArray(new String[0]));
|
||||
|
||||
private static final String DISABLE_DEFAULT_TRACK = "--edit track:%s --set flag-default=0 ";
|
||||
private static final String ENABLE_DEFAULT_TRACK = "--edit track:%s --set flag-default=1 ";
|
||||
private static final String DISABLE_FORCED_TRACK = "--edit track:%s --set flag-forced=0 ";
|
||||
private static final String ENABLE_FORCED_TRACK = "--edit track:%s --set flag-forced=1 ";
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<FileAttribute> loadAttributes(File file) {
|
||||
Map<String, Object> jsonMap;
|
||||
List<FileAttribute> fileAttributes = new ArrayList<>();
|
||||
try {
|
||||
String command = format("\"%s\"", Config.getInstance().getPathFor(MkvToolNix.MKV_MERGER));
|
||||
String command = format("\"%s\"", Config.getInstance().getPathFor(MkvToolNix.MKV_MERGE));
|
||||
String[] arguments = new String[]{
|
||||
command,
|
||||
"--identify",
|
||||
@@ -61,92 +68,126 @@ public class MkvFileProcessor implements FileProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
log.debug(fileAttributes);
|
||||
log.debug(fileAttributes.toString());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error("File could not be found or loaded!");
|
||||
log.error("File could not be found or loaded: ", e);
|
||||
System.out.println("File could not be found or loaded: " + file.getAbsolutePath());
|
||||
}
|
||||
return fileAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public FileInfoDto filterAttributes(List<FileAttribute> attributes) {
|
||||
FileInfoDto info = new FileInfoDto();
|
||||
List<FileAttribute> nonForcedTracks = attributes.stream()
|
||||
.filter(elem -> !StringUtils.containsAnyIgnoreCase(elem.getTrackName(), forcedKeywords))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
detectDefaultTracks(attributes, info, nonForcedTracks);
|
||||
detectDesiredTracks(info, nonForcedTracks);
|
||||
log.debug(info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private void detectDefaultTracks(List<FileAttribute> attributes, FileInfoDto info, List<FileAttribute> nonForcedTracks) {
|
||||
Set<FileAttribute> detectedForcedSubtitleLanes = new HashSet<>();
|
||||
public void detectDefaultTracks(FileInfoDto info, List<FileAttribute> attributes, List<FileAttribute> nonForcedTracks) {
|
||||
for (FileAttribute attribute : attributes) {
|
||||
if (attribute.isDefaultTrack() && AUDIO.equals(attribute.getType()))
|
||||
info.getDefaultAudioLanes().add(attribute);
|
||||
if (attribute.isDefaultTrack() && SUBTITLES.equals(attribute.getType()))
|
||||
info.getDefaultSubtitleLanes().add(attribute);
|
||||
if (attribute.isForcedTrack() && SUBTITLES.equals(attribute.getType()))
|
||||
detectedForcedSubtitleLanes.add(attribute);
|
||||
if (AUDIO.equals(attribute.getType())) {
|
||||
if (attribute.isDefaultTrack()) info.getExistingDefaultAudioLanes().add(attribute);
|
||||
if (attribute.isForcedTrack()) info.getExistingForcedAudioLanes().add(attribute);
|
||||
} else if (SUBTITLES.equals(attribute.getType())) {
|
||||
if (attribute.isDefaultTrack()) info.getExistingDefaultSubtitleLanes().add(attribute);
|
||||
|
||||
if (attribute.isForcedTrack()) info.getExistingForcedSubtitleLanes().add(attribute);
|
||||
else if (!nonForcedTracks.contains(attribute)) info.getDesiredForcedSubtitleLanes().add(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info.setDesiredForcedSubtitleLanes(attributes.stream()
|
||||
.filter(e -> !nonForcedTracks.contains(e))
|
||||
.filter(e -> !detectedForcedSubtitleLanes.contains(e))
|
||||
.collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void detectDesiredTracks(FileInfoDto info, List<FileAttribute> nonForcedTracks, List<FileAttribute> nonCommentaryTracks,
|
||||
AttributeConfig... configs) {
|
||||
Set<FileAttribute> tracks = SetUtils.retainOf(nonForcedTracks, nonCommentaryTracks);
|
||||
Set<FileAttribute> audioTracks = tracks.stream().filter(a -> AUDIO.equals(a.getType())).collect(Collectors.toSet());
|
||||
Set<FileAttribute> subtitleTracks = tracks.stream().filter(a -> SUBTITLES.equals(a.getType())).collect(Collectors.toSet());
|
||||
|
||||
private void detectDesiredTracks(FileInfoDto info, List<FileAttribute> nonForcedTracks) {
|
||||
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);
|
||||
for (AttributeConfig config : configs) {
|
||||
Optional<FileAttribute> desiredAudio = detectDesiredTrack(config.getAudioLanguage(), audioTracks).findFirst();
|
||||
Optional<FileAttribute> desiredSubtitle = detectDesiredSubtitleTrack(config.getSubtitleLanguage(), subtitleTracks).findFirst();
|
||||
|
||||
if (desiredAudio.isPresent() && ("OFF".equals(config.getSubtitleLanguage()) || desiredSubtitle.isPresent())) {
|
||||
info.setMatchedConfig(config);
|
||||
info.setDesiredDefaultAudioLane(desiredAudio.get());
|
||||
info.setDesiredDefaultSubtitleLane(desiredSubtitle.orElse(null));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Stream<FileAttribute> detectDesiredTrack(String language, Set<FileAttribute> tracks) {
|
||||
return tracks.stream().filter(track -> language.equals(track.getLanguage()));
|
||||
}
|
||||
|
||||
private Stream<FileAttribute> detectDesiredSubtitleTrack(String language, Set<FileAttribute> tracks) {
|
||||
return detectDesiredTrack(language, tracks)
|
||||
.sorted(subtitleTrackComparator.reversed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(File file, FileInfoDto fileInfo) throws IOException {
|
||||
public List<FileAttribute> retrieveNonForcedTracks(List<FileAttribute> attributes) {
|
||||
return attributes.stream()
|
||||
.filter(elem -> !StringUtils.containsAnyIgnoreCase(elem.getTrackName(),
|
||||
Config.getInstance().getForcedKeywords().toArray(new CharSequence[0])))
|
||||
.filter(elem -> !elem.isForcedTrack())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileAttribute> retrieveNonCommentaryTracks(List<FileAttribute> attributes) {
|
||||
return attributes.stream()
|
||||
.filter(elem -> !StringUtils.containsAnyIgnoreCase(elem.getTrackName(),
|
||||
Config.getInstance().getCommentaryKeywords().toArray(new CharSequence[0])))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void update(File file, FileInfoDto fileInfo) throws IOException, MkvToolNixException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(format("\"%s\" ", Config.getInstance().getPathFor(MkvToolNix.MKV_PROP_EDIT)));
|
||||
sb.append(format("\"%s\" ", file.getAbsolutePath()));
|
||||
|
||||
if (fileInfo.isAudioDifferent()) {
|
||||
if (fileInfo.getDefaultAudioLanes() != null && !fileInfo.getDefaultSubtitleLanes().isEmpty()) {
|
||||
for (FileAttribute track: fileInfo.getDefaultAudioLanes()) {
|
||||
if (fileInfo.getExistingDefaultAudioLanes() != null && !fileInfo.getExistingDefaultAudioLanes().isEmpty()) {
|
||||
for (FileAttribute track: fileInfo.getExistingDefaultAudioLanes()) {
|
||||
sb.append(format(DISABLE_DEFAULT_TRACK, track.getId()));
|
||||
}
|
||||
}
|
||||
sb.append(format(ENABLE_DEFAULT_TRACK, fileInfo.getDesiredAudioLane().getId()));
|
||||
sb.append(format(ENABLE_DEFAULT_TRACK, fileInfo.getDesiredDefaultAudioLane().getId()));
|
||||
}
|
||||
|
||||
if (!fileInfo.getExistingForcedAudioLanes().isEmpty()) {
|
||||
for (FileAttribute track: fileInfo.getExistingForcedAudioLanes()) {
|
||||
sb.append(format(DISABLE_FORCED_TRACK, track.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
if (fileInfo.isSubtitleDifferent()) {
|
||||
if (fileInfo.getDefaultSubtitleLanes() != null && !fileInfo.getDefaultSubtitleLanes().isEmpty()) {
|
||||
for (FileAttribute track: fileInfo.getDefaultSubtitleLanes()) {
|
||||
if (fileInfo.getExistingDefaultSubtitleLanes() != null && !fileInfo.getExistingDefaultSubtitleLanes().isEmpty()) {
|
||||
for (FileAttribute track: fileInfo.getExistingDefaultSubtitleLanes()) {
|
||||
sb.append(format(DISABLE_DEFAULT_TRACK, track.getId()));
|
||||
}
|
||||
}
|
||||
sb.append(format(ENABLE_DEFAULT_TRACK, fileInfo.getDesiredSubtitleLane().getId()));
|
||||
if (fileInfo.getDesiredDefaultSubtitleLane() != null) {
|
||||
sb.append(format(ENABLE_DEFAULT_TRACK, fileInfo.getDesiredDefaultSubtitleLane().getId()));
|
||||
}
|
||||
}
|
||||
|
||||
if (fileInfo.areForcedTracksDifferent()) {
|
||||
for (FileAttribute attribute : fileInfo.getDesiredForcedSubtitleLanes()) {
|
||||
sb.append(format(ENABLE_FORCED_TRACK, attribute.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
log.info(sb.toString());
|
||||
InputStream inputstream = Runtime.getRuntime().exec(sb.toString()).getInputStream();
|
||||
log.debug(IOUtils.toString(new InputStreamReader(inputstream)));
|
||||
String output = IOUtils.toString(new InputStreamReader(inputstream));
|
||||
log.debug(output);
|
||||
if (output.contains("Error")) throw new MkvToolNixException(output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SubtitleTrackComparator implements Comparator<FileAttribute> {
|
||||
private final String[] preferredSubtitles;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int compare(FileAttribute track1, FileAttribute track2) {
|
||||
int result = 0;
|
||||
|
||||
if (StringUtils.containsAnyIgnoreCase(track1.getTrackName(), preferredSubtitles)) {
|
||||
result++;
|
||||
}
|
||||
if (StringUtils.containsAnyIgnoreCase(track2.getTrackName(), preferredSubtitles)) {
|
||||
result--;
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
if (track1.isDefaultTrack()) result++;
|
||||
if (track2.isDefaultTrack()) result--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileCollector;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileProcessor;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.DateUtils;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.ProjectUtil;
|
||||
import at.pcgamingfreaks.yaml.YAML;
|
||||
import at.pcgamingfreaks.yaml.YamlInvalidContentException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.tongfei.progressbar.ProgressBar;
|
||||
import me.tongfei.progressbar.ProgressBarBuilder;
|
||||
import me.tongfei.progressbar.ProgressBarStyle;
|
||||
import net.harawata.appdirs.AppDirsFactory;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public abstract class AttributeUpdaterKernel {
|
||||
|
||||
protected final FileCollector collector;
|
||||
protected final FileProcessor processor;
|
||||
protected final ResultStatistic statistic = ResultStatistic.getInstance();
|
||||
private final ExecutorService executor = Executors.newFixedThreadPool(Config.getInstance().getThreads());
|
||||
|
||||
protected ProgressBarBuilder pbBuilder() {
|
||||
return new ProgressBarBuilder()
|
||||
.setStyle(ProgressBarStyle.ASCII)
|
||||
.setUpdateIntervalMillis(250)
|
||||
.setMaxRenderedLength(75);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void execute() {
|
||||
statistic.startTimer();
|
||||
|
||||
try (ProgressBar progressBar = pbBuilder().build()) {
|
||||
List<File> files = loadFiles(Config.getInstance().getLibraryPath().getAbsolutePath());
|
||||
progressBar.maxHint(files.size());
|
||||
|
||||
files.forEach(file -> executor.submit(() -> {
|
||||
process(file);
|
||||
progressBar.step();
|
||||
}));
|
||||
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(1, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
endProcess();
|
||||
|
||||
statistic.stopTimer();
|
||||
statistic.printResult();
|
||||
}
|
||||
|
||||
protected List<File> loadExcludedFiles() {
|
||||
List<File> excludedFiles = Config.getInstance().getExcludedDirectories().stream()
|
||||
.map(collector::loadFiles)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
statistic.increaseTotalBy(excludedFiles.size());
|
||||
statistic.increaseExcludedBy(excludedFiles.size());
|
||||
return excludedFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load files or directories to update.
|
||||
* Remove excluded directories.
|
||||
*
|
||||
* @param path Path to library
|
||||
* @return List of files to update.
|
||||
*/
|
||||
abstract List<File> loadFiles(String path);
|
||||
|
||||
/**
|
||||
* Start of the file updating process.
|
||||
* This method is called by the executor and its contents are executed in parallel.
|
||||
*
|
||||
* @param file file or directory to update
|
||||
*/
|
||||
void process(File file) {
|
||||
FileInfoDto fileInfo = new FileInfoDto(file);
|
||||
List<FileAttribute> attributes = processor.loadAttributes(file);
|
||||
|
||||
List<FileAttribute> nonForcedTracks = processor.retrieveNonForcedTracks(attributes);
|
||||
List<FileAttribute> nonCommentaryTracks = processor.retrieveNonCommentaryTracks(attributes);
|
||||
|
||||
processor.detectDefaultTracks(fileInfo, attributes, nonForcedTracks);
|
||||
processor.detectDesiredTracks(fileInfo, nonForcedTracks, nonCommentaryTracks,
|
||||
Config.getInstance().getAttributeConfig().toArray(new AttributeConfig[]{}));
|
||||
|
||||
updateFile(fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist file changes.
|
||||
*
|
||||
* @param fileInfoDto contains information about file and desired configuration.
|
||||
*/
|
||||
protected void updateFile(FileInfoDto fileInfoDto) {
|
||||
statistic.total();
|
||||
switch (fileInfoDto.getStatus()) {
|
||||
case CHANGE_NECESSARY:
|
||||
statistic.shouldChange();
|
||||
commitChange(fileInfoDto);
|
||||
break;
|
||||
case UNABLE_TO_APPLY:
|
||||
statistic.noSuitableConfigFound();
|
||||
break;
|
||||
case ALREADY_SUITED:
|
||||
statistic.alreadyFits();
|
||||
break;
|
||||
case UNKNOWN:
|
||||
default:
|
||||
statistic.failure();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void commitChange(FileInfoDto fileInfo) {
|
||||
if (Config.getInstance().isSafeMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
processor.update(fileInfo.getFile(), fileInfo);
|
||||
statistic.success();
|
||||
log.info("Updated {}", fileInfo.getFile().getAbsolutePath());
|
||||
} catch (IOException | MkvToolNixException e) {
|
||||
statistic.failedChanging();
|
||||
log.warn("File couldn't be updated: '{}', Error: {}", fileInfo.getFile().getAbsoluteFile(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected void endProcess() {
|
||||
if (Config.getInstance().isSafeMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String filePath = AppDirsFactory.getInstance().getUserConfigDir(ProjectUtil.getProjectName(), null, null);
|
||||
|
||||
File configDir = Path.of(filePath).toFile();
|
||||
if (!configDir.exists()) configDir.mkdirs();
|
||||
|
||||
File lastExecutionFile = Path.of(filePath + "/last-execution.yml").toFile();
|
||||
if (!lastExecutionFile.exists()) lastExecutionFile.createNewFile();
|
||||
|
||||
YAML yaml = new YAML(lastExecutionFile);
|
||||
yaml.set(Config.getInstance().getNormalizedLibraryPath(), DateUtils.convert(new Date()));
|
||||
yaml.save(lastExecutionFile);
|
||||
} catch (IOException | YamlInvalidContentException e) {
|
||||
log.error("last-execution.yml could not be created or read.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileCollector;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileProcessor;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.tongfei.progressbar.ProgressBarBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class CoherentAttributeUpdaterKernel extends AttributeUpdaterKernel {
|
||||
|
||||
public CoherentAttributeUpdaterKernel(FileCollector collector, FileProcessor processor) {
|
||||
super(collector, processor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProgressBarBuilder pbBuilder() {
|
||||
return super.pbBuilder()
|
||||
.setUnit(" directories", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
List<File> loadFiles(String path) {
|
||||
return loadFiles(path, Config.getInstance().getCoherent());
|
||||
}
|
||||
|
||||
List<File> loadFiles(String path, int depth) {
|
||||
List<File> excludedFiles = loadExcludedFiles();
|
||||
List<File> directories = collector.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 Config#isForceCoherent()} then there will be no changes to the file if they don't match the same config.
|
||||
* Otherwise, the default behaviour is executed.
|
||||
* This method is called by the executor and is run in parallel.
|
||||
*
|
||||
* @param file directory containing files
|
||||
*/
|
||||
@Override
|
||||
void process(File file) {
|
||||
process(file, Config.getInstance().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<FileInfoDto> fileInfos = collector.loadFiles(file.getAbsolutePath()).stream()
|
||||
.map(FileInfoDto::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (AttributeConfig config : Config.getInstance().getAttributeConfig()) {
|
||||
|
||||
for (FileInfoDto fileInfo : fileInfos) {
|
||||
List<FileAttribute> attributes = processor.loadAttributes(fileInfo.getFile());
|
||||
|
||||
List<FileAttribute> nonForcedTracks = processor.retrieveNonForcedTracks(attributes);
|
||||
List<FileAttribute> nonCommentaryTracks = processor.retrieveNonCommentaryTracks(attributes);
|
||||
|
||||
processor.detectDefaultTracks(fileInfo, attributes, nonForcedTracks);
|
||||
processor.detectDesiredTracks(fileInfo, nonForcedTracks, nonCommentaryTracks, config);
|
||||
}
|
||||
|
||||
if (fileInfos.stream().allMatch(elem -> ("OFF".equals(config.getSubtitleLanguage()) || elem.getDesiredDefaultSubtitleLane() != null)
|
||||
&& elem.getDesiredDefaultAudioLane() != null)) {
|
||||
log.info("Found {}/{} match for {}", config.getAudioLanguage(), config.getSubtitleLanguage(), file.getAbsolutePath());
|
||||
fileInfos.forEach(this::updateFile);
|
||||
return; // match found, end process here
|
||||
}
|
||||
|
||||
fileInfos.forEach(f -> {
|
||||
f.setDesiredDefaultAudioLane(null);
|
||||
f.setDesiredDefaultSubtitleLane(null);
|
||||
});
|
||||
}
|
||||
|
||||
log.info("No coherent match found for {}", file.getAbsoluteFile());
|
||||
|
||||
for (FileInfoDto fileInfo : fileInfos) {
|
||||
if (!Config.getInstance().isForceCoherent()) {
|
||||
super.process(fileInfo.getFile());
|
||||
} else {
|
||||
statistic.excluded();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.kernel;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileCollector;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileProcessor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.tongfei.progressbar.ProgressBarBuilder;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class DefaultAttributeUpdaterKernel extends AttributeUpdaterKernel {
|
||||
|
||||
public DefaultAttributeUpdaterKernel(FileCollector collector, FileProcessor processor) {
|
||||
super(collector, processor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProgressBarBuilder pbBuilder() {
|
||||
return super.pbBuilder()
|
||||
.setUnit(" files", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
List<File> loadFiles(String path) {
|
||||
List<File> excludedFiles = loadExcludedFiles();
|
||||
return collector.loadFiles(Config.getInstance().getLibraryPath().getAbsolutePath()).stream()
|
||||
.filter(file -> !excludedFiles.contains(file))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,30 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Log4j2
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class AttributeConfig {
|
||||
private final String audioLanguage;
|
||||
private final String subtitleLanguage;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(audioLanguage, subtitleLanguage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AttributeConfig{"
|
||||
|
||||
@@ -1,28 +1,68 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.cli.Option;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum ConfigProperty {
|
||||
CONFIG_PATH("config", "Path to config file"),
|
||||
LIBRARY("library", "Path to library"),
|
||||
SAFE_MODE("safe-mode", "Test run (no files will be changes)"),
|
||||
THREADS("threads", "thread count (default: 2)"),
|
||||
INCLUDE_PATTERN("include-pattern", "Include files matching pattern"),
|
||||
MKV_TOOL_NIX("mkvtoolnix", "Path to mkv tool nix installation"),
|
||||
FORCED_KEYWORDS("forcedKeywords", "Additional keywords to identify forced tracks, combines with config file"),
|
||||
EXCLUDE_DIRECTORY("exclude-directories", "Directories to be excluded, combines with config file"),
|
||||
HELP("help", "\"for help this is\" - Yoda"),
|
||||
VERSION("version", "Display version");
|
||||
LIBRARY("library-path", "Path to library", "l", 1),
|
||||
ATTRIBUTE_CONFIG("attribute-config", "Attribute config to decide which tracks to choose when", "a", Option.UNLIMITED_VALUES),
|
||||
CONFIG_PATH("config-path", "Path to config file", "p", 1),
|
||||
MKV_TOOL_NIX("mkvtoolnix", "Path to mkv tool nix installation", "m", 1),
|
||||
SAFE_MODE("safe-mode", "Test run (no files will be changes)", "s", 0),
|
||||
COHERENT("coherent", "Try to match all files in dir of depth with the same config", "c", 1),
|
||||
FORCE_COHERENT("force-coherent", "Force coherent and don't update anything if config fits not whole config (default: false)", "cf", 0),
|
||||
WINDOWS("windows", "Is operating system windows", null, 0),
|
||||
ONLY_NEW_FILES("only-new-files", "Sets filter-date to last successful execution (Overwrites input of filter-date)", "n", 0),
|
||||
FILTER_DATE("filter-date", "Only consider files created newer than entered date (format: \"dd.MM.yyyy-HH:mm:ss\")", "d", 1),
|
||||
THREADS("threads", "Thread count (default: 2)", "t", 1),
|
||||
INCLUDE_PATTERN("include-pattern", "Include files matching pattern (default: \".*\")", "i", 1),
|
||||
EXCLUDED_DIRECTORY("excluded-directories", "Directories to be excluded, combines with config file", "e", Option.UNLIMITED_VALUES),
|
||||
FORCED_KEYWORDS("forced-keywords", "Additional keywords to identify forced tracks", "fk", Option.UNLIMITED_VALUES),
|
||||
COMMENTARY_KEYWORDS("commentary-keywords", "Additional keywords to identify commentary tracks", "ck", Option.UNLIMITED_VALUES),
|
||||
PREFERRED_SUBTITLES("preferred-subtitles", "Additional keywords to prefer specific subtitle tracks", "ps", Option.UNLIMITED_VALUES),
|
||||
ARGUMENTS("arguments", "List of arguments", null, 0),
|
||||
VERSION("version", "Display version", "v", 0),
|
||||
HELP("help", "\"For help this is\" - Yoda", "h", 0);
|
||||
|
||||
/*
|
||||
* Verify at startup that there are no duplicated shortParameters.
|
||||
*/
|
||||
static {
|
||||
Set<String> shortParameters = new HashSet<>();
|
||||
for (String param : Arrays.stream(ConfigProperty.values()).map(ConfigProperty::abrv).collect(Collectors.toList())) {
|
||||
if (shortParameters.contains(param)) {
|
||||
throw new IllegalStateException("It is not allowed to have multiple properties with the same abbreviation!");
|
||||
}
|
||||
if (param != null) {
|
||||
shortParameters.add(param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final String property;
|
||||
private final String description;
|
||||
private final String shortParameter;
|
||||
private final int args;
|
||||
|
||||
public String prop() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public String desc() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String prop() {
|
||||
return property;
|
||||
public String abrv() {
|
||||
return shortParameter;
|
||||
}
|
||||
|
||||
public int args() {
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Log4j2
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class FileAttribute {
|
||||
@@ -15,6 +17,24 @@ public class FileAttribute {
|
||||
private final boolean forcedTrack;
|
||||
private final LaneType type;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
FileAttribute attribute = (FileAttribute) o;
|
||||
return id == attribute.id
|
||||
&& defaultTrack == attribute.defaultTrack
|
||||
&& forcedTrack == attribute.forcedTrack
|
||||
&& Objects.equals(language, attribute.language)
|
||||
&& Objects.equals(trackName, attribute.trackName)
|
||||
&& type == attribute.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, language, trackName, defaultTrack, forcedTrack, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + "id=" + id +
|
||||
|
||||
@@ -1,53 +1,79 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@RequiredArgsConstructor
|
||||
public class FileInfoDto {
|
||||
private Set<FileAttribute> defaultAudioLanes = new HashSet<>();
|
||||
private Set<FileAttribute> defaultSubtitleLanes = new HashSet<>();
|
||||
private Set<FileAttribute> desiredForcedSubtitleLanes;
|
||||
private FileAttribute desiredAudioLane;
|
||||
private FileAttribute desiredSubtitleLane;
|
||||
private final File file;
|
||||
|
||||
public boolean isUnableToApplyConfig() {
|
||||
return desiredAudioLane == null && desiredSubtitleLane == null;
|
||||
}
|
||||
private Set<FileAttribute> existingDefaultAudioLanes = new HashSet<>();
|
||||
private Set<FileAttribute> existingForcedAudioLanes = new HashSet<>();
|
||||
|
||||
public boolean isAlreadySuitable() {
|
||||
return defaultAudioLanes.contains(desiredAudioLane) && defaultSubtitleLanes.contains(desiredSubtitleLane);
|
||||
}
|
||||
private Set<FileAttribute> existingDefaultSubtitleLanes = new HashSet<>();
|
||||
private Set<FileAttribute> existingForcedSubtitleLanes = new HashSet<>();
|
||||
|
||||
public boolean isChangeNecessary() {
|
||||
return isAudioDifferent() || isSubtitleDifferent() || areForcedTracksDifferent();
|
||||
}
|
||||
private Set<FileAttribute> desiredForcedSubtitleLanes = new HashSet<>();
|
||||
private FileAttribute desiredDefaultAudioLane;
|
||||
private FileAttribute desiredDefaultSubtitleLane;
|
||||
private AttributeConfig matchedConfig;
|
||||
|
||||
public boolean isAudioDifferent() {
|
||||
return desiredAudioLane != null &&
|
||||
(defaultAudioLanes == null || !defaultAudioLanes.contains(desiredAudioLane));
|
||||
return desiredDefaultAudioLane != null &&
|
||||
(existingDefaultAudioLanes == null || !existingDefaultAudioLanes.contains(desiredDefaultAudioLane) || existingDefaultAudioLanes.size() > 1);
|
||||
}
|
||||
|
||||
public boolean isSubtitleDifferent() {
|
||||
return desiredSubtitleLane != null &&
|
||||
(defaultSubtitleLanes == null || !defaultSubtitleLanes.contains(desiredSubtitleLane));
|
||||
return isSubtitleMatchDifferent() || isSubtitleOFF();
|
||||
}
|
||||
|
||||
private boolean isSubtitleMatchDifferent() {
|
||||
return desiredDefaultSubtitleLane != null
|
||||
&& (existingDefaultSubtitleLanes == null || !existingDefaultSubtitleLanes.contains(desiredDefaultSubtitleLane) || existingDefaultSubtitleLanes.size() > 1);
|
||||
}
|
||||
|
||||
private boolean isSubtitleOFF() {
|
||||
return desiredDefaultSubtitleLane == null && "OFF".equals(matchedConfig.getSubtitleLanguage()) &&
|
||||
(existingDefaultSubtitleLanes != null && !existingDefaultSubtitleLanes.isEmpty());
|
||||
}
|
||||
|
||||
public boolean areForcedTracksDifferent() {
|
||||
return desiredForcedSubtitleLanes.size() > 0;
|
||||
return !desiredForcedSubtitleLanes.isEmpty();
|
||||
}
|
||||
|
||||
public FileStatus getStatus() {
|
||||
if (isChangeNecessary()) return FileStatus.CHANGE_NECESSARY;
|
||||
if (isUnableToApplyConfig()) return FileStatus.UNABLE_TO_APPLY;
|
||||
if (isAlreadySuitable()) return FileStatus.ALREADY_SUITED;
|
||||
return FileStatus.UNKNOWN;
|
||||
}
|
||||
|
||||
private boolean isUnableToApplyConfig() {
|
||||
return desiredDefaultAudioLane == null && desiredDefaultSubtitleLane == null;
|
||||
}
|
||||
|
||||
private boolean isAlreadySuitable() {
|
||||
return existingDefaultAudioLanes.contains(desiredDefaultAudioLane) && existingDefaultSubtitleLanes.contains(desiredDefaultSubtitleLane);
|
||||
}
|
||||
|
||||
private boolean isChangeNecessary() {
|
||||
return isAudioDifferent() || isSubtitleDifferent() || areForcedTracksDifferent() || !existingForcedAudioLanes.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + "defaultAudioLanes=" + defaultAudioLanes +
|
||||
", defaultSubtitleLanes=" + defaultSubtitleLanes +
|
||||
return "[" + "defaultAudioLanes=" + existingDefaultAudioLanes +
|
||||
", defaultSubtitleLanes=" + existingDefaultSubtitleLanes +
|
||||
", desiredForcedSubtitleLanes=" + desiredForcedSubtitleLanes +
|
||||
", desiredAudioLane=" + desiredAudioLane +
|
||||
", desiredSubtitleLane=" + desiredSubtitleLane +
|
||||
", desiredAudioLane=" + desiredDefaultAudioLane +
|
||||
", desiredSubtitleLane=" + desiredDefaultSubtitleLane +
|
||||
']';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
public enum FileStatus {
|
||||
CHANGE_NECESSARY,
|
||||
UNABLE_TO_APPLY,
|
||||
ALREADY_SUITED,
|
||||
UNKNOWN;
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import lombok.AllArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum MkvToolNix {
|
||||
MKV_MERGER("mkvmerge.exe"),
|
||||
MKV_PROP_EDIT("mkvpropedit.exe");
|
||||
MKV_MERGE("mkvmerge"),
|
||||
MKV_PROP_EDIT("mkvpropedit");
|
||||
|
||||
private final String file;
|
||||
|
||||
|
||||
@@ -2,19 +2,23 @@ package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class ResultStatistic {
|
||||
private static final String result = "Total files: %s%n" +
|
||||
"├─ Excluded: %s%n" +
|
||||
"├─ Should change: %s%n" +
|
||||
"│ ├─ Failed changing: %s%n" +
|
||||
"│ └─ Successfully changed: %s%n" +
|
||||
"├─ No suitable config found: %s%n" +
|
||||
"├─ Already fit config: %s%n" +
|
||||
"└─ Failed: %s%n" +
|
||||
"Runtime: %ss";
|
||||
|
||||
"Runtime: %s";
|
||||
private static ResultStatistic instance;
|
||||
private int filesTotal = 0;
|
||||
private int excluded = 0;
|
||||
|
||||
private int shouldChange = 0;
|
||||
private int failedChanging = 0;
|
||||
@@ -28,10 +32,29 @@ public class ResultStatistic {
|
||||
private long startTime = 0;
|
||||
private long runtime = 0;
|
||||
|
||||
public static ResultStatistic getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new ResultStatistic();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void increaseTotalBy(int amount) {
|
||||
filesTotal += amount;
|
||||
}
|
||||
|
||||
public synchronized void total() {
|
||||
filesTotal++;
|
||||
}
|
||||
|
||||
public void increaseExcludedBy(int amount) {
|
||||
excluded += amount;
|
||||
}
|
||||
|
||||
public synchronized void excluded() {
|
||||
excluded++;
|
||||
}
|
||||
|
||||
public synchronized void shouldChange() {
|
||||
shouldChange++;
|
||||
}
|
||||
@@ -64,9 +87,43 @@ public class ResultStatistic {
|
||||
runtime = System.currentTimeMillis() - startTime;
|
||||
}
|
||||
|
||||
public void printResult() {
|
||||
System.out.println(prettyPrint());
|
||||
log.info(this.toString());
|
||||
}
|
||||
|
||||
private String formatTimer() {
|
||||
int seconds = (int) (runtime / 1000);
|
||||
int minutes = seconds / 60;
|
||||
int hours = minutes / 60;
|
||||
int days = hours / 24;
|
||||
|
||||
if (days >= 1) {
|
||||
return String.format("%sd %sh %sm %ss", days, hours % 24, minutes % 60, seconds % 60);
|
||||
} else if (hours >= 1) {
|
||||
return String.format("%sh %sm %ss", hours, minutes % 60, seconds % 60);
|
||||
} else if (minutes >= 1) {
|
||||
return String.format("%sm %ss", minutes, seconds % 60);
|
||||
} else {
|
||||
return String.format("%ss", seconds % 60);
|
||||
}
|
||||
}
|
||||
|
||||
public String prettyPrint() {
|
||||
return String.format(result, filesTotal, excluded, shouldChange, failedChanging, successfullyChanged,
|
||||
noSuitableConfigFound, alreadyFits, failed, formatTimer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(result, filesTotal, shouldChange, failedChanging, successfullyChanged,
|
||||
noSuitableConfigFound, alreadyFits, failed, runtime / 1000);
|
||||
return "ResultStatistic: " + "filesTotal=" + filesTotal +
|
||||
", excluded=" + excluded +
|
||||
", shouldChange=" + shouldChange +
|
||||
" (failedChanging=" + failedChanging +
|
||||
", successfullyChanged=" + successfullyChanged +
|
||||
"), noSuitableConfigFound=" + noSuitableConfigFound +
|
||||
", alreadyFits=" + alreadyFits +
|
||||
", failed=" + failed +
|
||||
", runtime=" + formatTimer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import org.apache.commons.cli.Option;
|
||||
|
||||
public class CommandLineOptionsUtil {
|
||||
public static Option optionOf(ConfigProperty property, String opt, boolean hasArg) {
|
||||
return optionOf(property, opt, hasArg ? 1 : 0, false);
|
||||
public static Option optionOf(ConfigProperty property, String opt, int args) {
|
||||
return optionOf(property, opt, args, false);
|
||||
}
|
||||
|
||||
public static Option optionOf(ConfigProperty property, String opt, boolean hasArg, boolean required) {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ConfigErrors;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -25,13 +23,11 @@ public class LanguageValidatorUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isAudioLanguageValid(String language) {
|
||||
return !language.equals("OFF") && isLanguageValid(language);
|
||||
}
|
||||
|
||||
public static boolean isLanguageValid(String language) {
|
||||
return ISO3_LANGUAGES.contains(language);
|
||||
}
|
||||
|
||||
public static void isLanguageValid(String language, ConfigErrors errors) {
|
||||
if (!isLanguageValid(language)) {
|
||||
errors.add(String.format("%s is not a valid language", language));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
public class ProjectUtil implements CommandLine.IVersionProvider {
|
||||
private static final Properties PROJECT_PROPERTIES = new Properties();
|
||||
|
||||
static {
|
||||
try (InputStream propertiesStream = ProjectUtil.class.getClassLoader().getResourceAsStream("project.properties")) {
|
||||
PROJECT_PROPERTIES.load(propertiesStream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getVersion() {
|
||||
return new String[] {getProjectName() + " " + PROJECT_PROPERTIES.getProperty("version")};
|
||||
}
|
||||
|
||||
public static String getProjectName() {
|
||||
return PROJECT_PROPERTIES.getProperty("project_name");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class SetUtils {
|
||||
public static <T> Set<T> retainOf(List<T> list1, List<T> list2) {
|
||||
Set<T> set = new HashSet<>(list1);
|
||||
set.retainAll(list2);
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
public class VersionUtil {
|
||||
public static String getVersion() {
|
||||
try (InputStream propertiesStream = VersionUtil.class.getClassLoader().getResourceAsStream("version.properties")) {
|
||||
Properties properties = new Properties();
|
||||
properties.load(propertiesStream);
|
||||
|
||||
return properties.getProperty("version");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,17 @@ Configuration:
|
||||
Pattern: "%d{DEFAULT} | %-5level | %thread | %msg %n %throwable"
|
||||
ThresholdFilter:
|
||||
level: debug
|
||||
File:
|
||||
RollingFile:
|
||||
name: FileAppender
|
||||
fileName: default.log
|
||||
fileName: logs/application.log
|
||||
filePattern: logs/archive/application-%d{yyyy-MM-dd}-%i.log
|
||||
PatternLayout:
|
||||
Pattern: "%d{DEFAULT} | %-5level | %thread | %msg %n %throwable"
|
||||
ThresholdFilter:
|
||||
level: debug
|
||||
Policies:
|
||||
OnStartupTriggeringPolicy:
|
||||
minSize: 0
|
||||
Loggers:
|
||||
Root:
|
||||
level: debug
|
||||
31
src/main/resources/log4j2-installed.yaml
Normal file
31
src/main/resources/log4j2-installed.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
Configuration:
|
||||
name: DefaultLogger
|
||||
Appenders:
|
||||
File:
|
||||
name: FileAppender
|
||||
fileName: ${sys:user.home}/AppData/Roaming/MyApplication/MyApplication.log
|
||||
PatternLayout:
|
||||
Pattern: "%d{DEFAULT} | %-5level | %thread | %msg %n %throwable"
|
||||
ThresholdFilter:
|
||||
level: info
|
||||
RollingFile:
|
||||
name: FileAppender
|
||||
fileName: ${sys:user.home}/AppData/Roaming/MKVAudioSubtitleChanger/logs/application.log
|
||||
filePattern: ${sys:user.home}/AppData/Roaming/MKVAudioSubtitleChanger/logs/archive/application-%d{yyyy-MM-dd}-%i.log
|
||||
PatternLayout:
|
||||
Pattern: "%d{DEFAULT} | %-5level | %thread | %msg %n %throwable"
|
||||
ThresholdFilter:
|
||||
level: info
|
||||
Policies:
|
||||
OnStartupTriggeringPolicy:
|
||||
minSize: 0
|
||||
Loggers:
|
||||
Root:
|
||||
level: info
|
||||
AppenderRef:
|
||||
- ref: FileAppender
|
||||
Logger:
|
||||
name: "com.zaxxer.hikari.HikariConfig"
|
||||
level: info
|
||||
AppenderRef:
|
||||
- ref: FileAppender
|
||||
@@ -1,13 +1,17 @@
|
||||
Configuration:
|
||||
name: DefaultLogger
|
||||
Appenders:
|
||||
File:
|
||||
RollingFile:
|
||||
name: FileAppender
|
||||
fileName: default.log
|
||||
fileName: logs/application.log
|
||||
filePattern: logs/archive/application-%d{yyyy-MM-dd}-%i.log
|
||||
PatternLayout:
|
||||
Pattern: "%d{DEFAULT} | %-5level | %thread | %msg %n %throwable"
|
||||
ThresholdFilter:
|
||||
level: info
|
||||
Policies:
|
||||
OnStartupTriggeringPolicy:
|
||||
minSize: 0
|
||||
Loggers:
|
||||
Root:
|
||||
level: info
|
||||
|
||||
144
src/main/resources/main.wxs
Normal file
144
src/main/resources/main.wxs
Normal file
@@ -0,0 +1,144 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
||||
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||
|
||||
<?ifdef JpIsSystemWide ?>
|
||||
<?define JpInstallScope="perMachine"?>
|
||||
<?else?>
|
||||
<?define JpInstallScope="perUser"?>
|
||||
<?endif?>
|
||||
|
||||
<?define JpProductLanguage=1033 ?>
|
||||
<?define JpInstallerVersion=200 ?>
|
||||
<?define JpCompressedMsi=yes ?>
|
||||
|
||||
<?ifdef JpAllowUpgrades ?>
|
||||
<?define JpUpgradeVersionOnlyDetectUpgrade="no"?>
|
||||
<?else?>
|
||||
<?define JpUpgradeVersionOnlyDetectUpgrade="yes"?>
|
||||
<?endif?>
|
||||
<?ifdef JpAllowDowngrades ?>
|
||||
<?define JpUpgradeVersionOnlyDetectDowngrade="no"?>
|
||||
<?else?>
|
||||
<?define JpUpgradeVersionOnlyDetectDowngrade="yes"?>
|
||||
<?endif?>
|
||||
|
||||
<?define JpProductCode="*"?>
|
||||
<?define JpAppName="MKVAudioSubtitleChanger"?>
|
||||
<?define JpAppVersion="4.0.0"?>
|
||||
<?define JpAppVendor="RatzzFatzz"?>
|
||||
<?define JpProductUpgradeCode="a9527300-d364-4cc3-a392-94035065d8c9"?>
|
||||
<?define JpAppDescription="Change audio and subtitle tracks for .mkv files"?>
|
||||
<?define JpHelpURL="github.com/RatzzFatzz"?>
|
||||
|
||||
<Product
|
||||
Id="$(var.JpProductCode)"
|
||||
Name="$(var.JpAppName)"
|
||||
Language="$(var.JpProductLanguage)"
|
||||
Version="$(var.JpAppVersion)"
|
||||
Manufacturer="$(var.JpAppVendor)"
|
||||
UpgradeCode="$(var.JpProductUpgradeCode)">
|
||||
|
||||
<Package
|
||||
Description="$(var.JpAppDescription)"
|
||||
Manufacturer="$(var.JpAppVendor)"
|
||||
InstallerVersion="$(var.JpInstallerVersion)"
|
||||
Compressed="$(var.JpCompressedMsi)"
|
||||
InstallScope="$(var.JpInstallScope)" Platform="x64"
|
||||
/>
|
||||
|
||||
<Media Id="1" Cabinet="Data.cab" EmbedCab="yes" />
|
||||
|
||||
<Upgrade Id="$(var.JpProductUpgradeCode)">
|
||||
<UpgradeVersion
|
||||
OnlyDetect="$(var.JpUpgradeVersionOnlyDetectUpgrade)"
|
||||
Property="JP_UPGRADABLE_FOUND"
|
||||
Maximum="$(var.JpAppVersion)"
|
||||
MigrateFeatures="yes"
|
||||
IncludeMaximum="$(var.JpUpgradeVersionOnlyDetectUpgrade)" />
|
||||
<UpgradeVersion
|
||||
OnlyDetect="$(var.JpUpgradeVersionOnlyDetectDowngrade)"
|
||||
Property="JP_DOWNGRADABLE_FOUND"
|
||||
Minimum="$(var.JpAppVersion)"
|
||||
MigrateFeatures="yes"
|
||||
IncludeMinimum="$(var.JpUpgradeVersionOnlyDetectDowngrade)" />
|
||||
</Upgrade>
|
||||
|
||||
<?ifndef JpAllowUpgrades ?>
|
||||
<CustomAction Id="JpDisallowUpgrade" Error="!(loc.DisallowUpgradeErrorMessage)" />
|
||||
<?endif?>
|
||||
<?ifndef JpAllowDowngrades ?>
|
||||
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
|
||||
<?endif?>
|
||||
|
||||
<Binary Id="JpCaDll" SourceFile="wixhelper.dll"/>
|
||||
|
||||
<CustomAction Id="JpFindRelatedProducts" BinaryKey="JpCaDll" DllEntry="FindRelatedProductsEx" />
|
||||
|
||||
<!-- Standard required root -->
|
||||
<Directory Id="TARGETDIR" Name="SourceDir"/>
|
||||
|
||||
<Feature Id="DefaultFeature" Title="!(loc.MainFeatureTitle)" Level="1">
|
||||
<ComponentGroupRef Id="Shortcuts"/>
|
||||
<ComponentGroupRef Id="Files"/>
|
||||
<ComponentGroupRef Id="FileAssociations"/>
|
||||
<Component Id="pathEnvironmentVariable" Guid="$(var.JpProductUpgradeCode)" KeyPath="yes" Directory="TARGETDIR">
|
||||
<Environment Id="MyPathVariable" Name="Path" Value="[INSTALLDIR]" Action="set" System="no" Permanent="no" Part="last" Separator=";" />
|
||||
</Component>
|
||||
</Feature>
|
||||
|
||||
<CustomAction Id="JpSetARPINSTALLLOCATION" Property="ARPINSTALLLOCATION" Value="[INSTALLDIR]" />
|
||||
<CustomAction Id="JpSetARPCOMMENTS" Property="ARPCOMMENTS" Value="$(var.JpAppDescription)" />
|
||||
<CustomAction Id="JpSetARPCONTACT" Property="ARPCONTACT" Value="$(var.JpAppVendor)" />
|
||||
<!-- <CustomAction Id="JpSetARPSIZE" Property="ARPSIZE" Value="$(var.JpAppSizeKb)" /> -->
|
||||
|
||||
<?ifdef JpHelpURL ?>
|
||||
<CustomAction Id="JpSetARPHELPLINK" Property="ARPHELPLINK" Value="$(var.JpHelpURL)" />
|
||||
<?endif?>
|
||||
|
||||
<?ifdef JpAboutURL ?>
|
||||
<CustomAction Id="JpSetARPURLINFOABOUT" Property="ARPURLINFOABOUT" Value="$(var.JpAboutURL)" />
|
||||
<?endif?>
|
||||
|
||||
<?ifdef JpUpdateURL ?>
|
||||
<CustomAction Id="JpSetARPURLUPDATEINFO" Property="ARPURLUPDATEINFO" Value="$(var.JpUpdateURL)" />
|
||||
<?endif?>
|
||||
|
||||
<?ifdef JpIcon ?>
|
||||
<Property Id="ARPPRODUCTICON" Value="JpARPPRODUCTICON"/>
|
||||
<Icon Id="JpARPPRODUCTICON" SourceFile="$(var.JpIcon)"/>
|
||||
<?endif?>
|
||||
|
||||
<UIRef Id="JpUI"/>
|
||||
|
||||
<InstallExecuteSequence>
|
||||
<Custom Action="JpSetARPINSTALLLOCATION" After="CostFinalize">Not Installed</Custom>
|
||||
<Custom Action="JpSetARPCOMMENTS" After="CostFinalize">Not Installed</Custom>
|
||||
<Custom Action="JpSetARPCONTACT" After="CostFinalize">Not Installed</Custom>
|
||||
<!-- <Custom Action="JpSetARPSIZE" After="CostFinalize">Not Installed</Custom> -->
|
||||
<?ifdef JpHelpURL ?>
|
||||
<Custom Action="JpSetARPHELPLINK" After="CostFinalize">Not Installed</Custom>
|
||||
<?endif?>
|
||||
<?ifdef JpAboutURL ?>
|
||||
<Custom Action="JpSetARPURLINFOABOUT" After="CostFinalize">Not Installed</Custom>
|
||||
<?endif?>
|
||||
<?ifdef JpUpdateURL ?>
|
||||
<Custom Action="JpSetARPURLUPDATEINFO" After="CostFinalize">Not Installed</Custom>
|
||||
<?endif?>
|
||||
|
||||
<?ifndef JpAllowUpgrades ?>
|
||||
<Custom Action="JpDisallowUpgrade" After="JpFindRelatedProducts">JP_UPGRADABLE_FOUND</Custom>
|
||||
<?endif?>
|
||||
<?ifndef JpAllowDowngrades ?>
|
||||
<Custom Action="JpDisallowDowngrade" After="JpFindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
|
||||
<?endif?>
|
||||
<RemoveExistingProducts Before="CostInitialize"/>
|
||||
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
|
||||
</InstallExecuteSequence>
|
||||
|
||||
<InstallUISequence>
|
||||
<Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
|
||||
</InstallUISequence>
|
||||
|
||||
</Product>
|
||||
</Wix>
|
||||
@@ -0,0 +1,61 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.Main;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
|
||||
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 picocli.CommandLine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class AttributeConfigTest {
|
||||
|
||||
private static Stream<Arguments> provideTestCases() {
|
||||
return Stream.of(
|
||||
Arguments.of(args("-a", "jpn:ger"), attrConf("jpn", "ger")),
|
||||
Arguments.of(args("-a", "jpn:ger", "jpn:eng"), attrConf("jpn", "ger", "jpn", "eng")),
|
||||
Arguments.of(args("-a", "jpn:ger", "jpn:OFF"), attrConf("jpn", "ger", "jpn", "OFF"))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCases")
|
||||
void validate(String[] cmdArgs, List<AttributeConfig> expectedConfig) {
|
||||
Main underTest = new Main();
|
||||
CommandLine.populateCommand(underTest, cmdArgs);
|
||||
assertIterableEquals(expectedConfig, underTest.getConfig().getAttributeConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate() {
|
||||
Main sut = new Main();
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, new String[]{"-l", "/"}));
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, new String[]{"-l", "/", "-a"}));
|
||||
assertThrows(CommandLine.ParameterException.class, () -> CommandLine.populateCommand(sut, new String[]{"-l", "/", "-a", "ger:"}));
|
||||
assertThrows(CommandLine.ParameterException.class,
|
||||
() -> CommandLine.populateCommand(sut, new String[]{"-l", "/", "-a", "ger:qwf"})); // Invalid language code
|
||||
}
|
||||
|
||||
private static String[] args(String... args) {
|
||||
String[] staticArray = new String[]{"-l", "/"};
|
||||
String[] result = new String[staticArray.length + args.length];
|
||||
System.arraycopy(staticArray, 0, result, 0, staticArray.length);
|
||||
System.arraycopy(args, 0, result, staticArray.length, args.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<AttributeConfig> attrConf(String... languages) {
|
||||
List<AttributeConfig> conf = new ArrayList<>();
|
||||
for (int i = 0; i < languages.length; i += 2) {
|
||||
conf.add(new AttributeConfig(languages[i], languages[i+1]));
|
||||
}
|
||||
return conf;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.Main;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.args;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class BooleanConfigParameterTest {
|
||||
|
||||
private static Stream<Arguments> provideTestCases() {
|
||||
return Stream.of(
|
||||
Arguments.of(args("-s"), true, (Function<Config, Boolean>) Config::isSafeMode),
|
||||
Arguments.of(args("--safemode"), true, (Function<Config, Boolean>) Config::isSafeMode),
|
||||
Arguments.of(args(), false, (Function<Config, Boolean>) Config::isSafeMode),
|
||||
Arguments.of(args("-cf"), true, (Function<Config, Boolean>) Config::isForceCoherent),
|
||||
Arguments.of(args("--force-coherent"), true, (Function<Config, Boolean>) Config::isForceCoherent),
|
||||
Arguments.of(args(), false, (Function<Config, Boolean>) Config::isForceCoherent),
|
||||
Arguments.of(args("-n"), true, (Function<Config, Boolean>) Config::isOnlyNewFiles),
|
||||
Arguments.of(args(), false, (Function<Config, Boolean>) Config::isOnlyNewFiles)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCases")
|
||||
void validate(String[] cmdArgs, boolean expected, Function<Config, Boolean> fieldUnderTest) {
|
||||
Main sut = new Main();
|
||||
CommandLine.populateCommand(sut, cmdArgs);
|
||||
assertEquals(expected, fieldUnderTest.apply(sut.getConfig()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_FILE;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ConfigTest {
|
||||
|
||||
@Test
|
||||
void initConfig() {
|
||||
String[] sut = new String[]{"-a", "ger:ger", "eng:eng", "-l", TEST_FILE,
|
||||
"-s", "-cf", "-n",
|
||||
"-c", "2",
|
||||
"-t", "4",
|
||||
"-i", ".*[abc].*",
|
||||
"-fk", "testForced",
|
||||
"-ck", "testCommentary",
|
||||
"-ps", "testPreferred"
|
||||
};
|
||||
CommandLine.populateCommand(Config.getInstance(), sut);
|
||||
|
||||
assertTrue(Config.getInstance().getLibraryPath().exists());
|
||||
assertEquals(List.of(new AttributeConfig("ger", "ger"), new AttributeConfig("eng", "eng")),
|
||||
Config.getInstance().getAttributeConfig());
|
||||
|
||||
assertTrue(Config.getInstance().isSafeMode());
|
||||
assertTrue(Config.getInstance().isForceCoherent());
|
||||
assertTrue(Config.getInstance().isOnlyNewFiles());
|
||||
assertNull(Config.getInstance().getFilterDate());
|
||||
|
||||
assertEquals(2, Config.getInstance().getCoherent());
|
||||
assertEquals(4, Config.getInstance().getThreads());
|
||||
assertEquals(".*[abc].*", Config.getInstance().getIncludePattern().pattern());
|
||||
assertTrue(Config.getInstance().getForcedKeywords().contains("testForced"));
|
||||
assertTrue(Config.getInstance().getCommentaryKeywords().contains("testCommentary"));
|
||||
assertTrue(Config.getInstance().getPreferredSubtitles().contains("testPreferred"));
|
||||
|
||||
assertNull(Config.getInstance().getConfigPath());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.Main;
|
||||
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 picocli.CommandLine;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.args;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class IntegerConfigParameterTest {
|
||||
|
||||
private static Stream<Arguments> provideTestCases() {
|
||||
return Stream.of(
|
||||
Arguments.of(args(), 2, (Function<Config, Integer>) Config::getThreads),
|
||||
Arguments.of(args("-t", "5"), 5, (Function<Config, Integer>) Config::getThreads),
|
||||
Arguments.of(args("--threads", "5"), 5, (Function<Config, Integer>) Config::getThreads)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCases")
|
||||
void validate(String[] cmdArgs, int expected, Function<Config, Integer> fieldUnderTest) {
|
||||
Main sut = new Main();
|
||||
CommandLine.populateCommand(sut, cmdArgs);
|
||||
assertEquals(expected, fieldUnderTest.apply(sut.getConfig()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate() {
|
||||
Main sut = new Main();
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, args("-t")));
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, args("--threads")));
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
PrintWriter printWriter = new PrintWriter(writer);
|
||||
CommandLine underTest = new CommandLine(sut);
|
||||
underTest = underTest.setErr(printWriter);
|
||||
underTest.execute(args("-t", "0"));
|
||||
printWriter.flush();
|
||||
assertTrue(writer.toString().contains("ERROR: threads must be greater than or equal to 1"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.Main;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
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 picocli.CommandLine;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.args;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class MkvToolNixPathConfigParameterTest {
|
||||
|
||||
private static final String TEST_INVALID_DIR = "src/test/resources/test-dir";
|
||||
private static final String TEST_MKVTOOLNIX_DIR = "src/test/resources/mkvtoolnix";
|
||||
private static final String TEST_MKVTOOLNIX_EXE_DIR = "src/test/resources/mkvtoolnix_exe";
|
||||
|
||||
private static Stream<Arguments> provideTestCases() {
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
return Stream.of(
|
||||
Arguments.of(args("-m", TEST_MKVTOOLNIX_EXE_DIR), TEST_MKVTOOLNIX_EXE_DIR, (Function<Config, File>) Config::getMkvToolNix),
|
||||
Arguments.of(args("--mkvtoolnix", TEST_MKVTOOLNIX_EXE_DIR), TEST_MKVTOOLNIX_EXE_DIR, (Function<Config, File>) Config::getMkvToolNix)
|
||||
);
|
||||
}
|
||||
|
||||
return Stream.of(
|
||||
Arguments.of(args("-m", TEST_MKVTOOLNIX_DIR), TEST_MKVTOOLNIX_DIR, (Function<Config, File>) Config::getMkvToolNix),
|
||||
Arguments.of(args("--mkvtoolnix", TEST_MKVTOOLNIX_DIR), TEST_MKVTOOLNIX_DIR, (Function<Config, File>) Config::getMkvToolNix)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCases")
|
||||
void validate(String[] cmdArgs, String expected, Function<Config, File> fieldUnderTest) {
|
||||
Main sut = new Main();
|
||||
CommandLine.populateCommand(sut, cmdArgs);
|
||||
assertEquals(Path.of(expected).toFile().getAbsolutePath(), fieldUnderTest.apply(sut.getConfig()).getAbsolutePath());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate() {
|
||||
Main sut = new Main();
|
||||
assertThrows(CommandLine.ParameterException.class, () -> CommandLine.populateCommand(sut, args("-m", TEST_INVALID_DIR)));
|
||||
assertThrows(CommandLine.ParameterException.class, () -> CommandLine.populateCommand(sut, args("-m")));
|
||||
assertThrows(CommandLine.ParameterException.class, () -> CommandLine.populateCommand(sut, args("")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.Main;
|
||||
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 picocli.CommandLine;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Function;
|
||||
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 org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class PathConfigParameterTest {
|
||||
|
||||
private static Stream<Arguments> provideTestCases() {
|
||||
return Stream.of(
|
||||
Arguments.of(args("-l", TEST_DIR), Path.of(TEST_DIR).toFile(), true, (Function<Config, File>) Config::getLibraryPath),
|
||||
Arguments.of(args("-l", TEST_FILE), Path.of(TEST_FILE).toFile(), true, (Function<Config, File>) Config::getLibraryPath)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCases")
|
||||
void validate(String[] cmdArgs, File expected, boolean exists, Function<Config, File> fieldUnderTest) {
|
||||
Main sut = new Main();
|
||||
CommandLine.populateCommand(sut, cmdArgs);
|
||||
assertEquals(expected.getAbsolutePath(), fieldUnderTest.apply(sut.getConfig()).getAbsolutePath());
|
||||
assertEquals(exists, fieldUnderTest.apply(sut.getConfig()).exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate() {
|
||||
Main sut = new Main();
|
||||
assertThrows(CommandLine.ParameterException.class, () -> CommandLine.populateCommand(sut, args("-l", "arst")));
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, args("-l")));
|
||||
assertThrows(CommandLine.UnmatchedArgumentException.class, () -> CommandLine.populateCommand(sut, args("")));
|
||||
}
|
||||
|
||||
private static String[] args(String... args) {
|
||||
String[] staticArray = new String[]{"-a", "ger:ger"};
|
||||
String[] result = new String[staticArray.length + args.length];
|
||||
System.arraycopy(staticArray, 0, result, 0, staticArray.length);
|
||||
System.arraycopy(args, 0, result, staticArray.length, args.length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.Main;
|
||||
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 picocli.CommandLine;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.args;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class PatternConfigParameterTest {
|
||||
|
||||
private static Stream<Arguments> provideTestCases() {
|
||||
return Stream.of(
|
||||
Arguments.of(args("-i", "[abd]?.*"), Pattern.compile("[abd]?.*"), (Function<Config, Pattern>) Config::getIncludePattern),
|
||||
Arguments.of(args("-i", ".*"), Pattern.compile(".*"), (Function<Config, Pattern>) Config::getIncludePattern),
|
||||
Arguments.of(args(), Pattern.compile(".*"), (Function<Config, Pattern>) Config::getIncludePattern)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCases")
|
||||
void validate(String[] cmdArgs, Pattern expected, Function<Config, Pattern> fieldUnderTest) {
|
||||
Main sut = new Main();
|
||||
CommandLine.populateCommand(sut, cmdArgs);
|
||||
assertEquals(expected.pattern(), fieldUnderTest.apply(sut.getConfig()).pattern());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate() {
|
||||
Main sut = new Main();
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, args("-i")));
|
||||
assertThrows(CommandLine.ParameterException.class, () -> CommandLine.populateCommand(sut, args("-i", "[")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.Main;
|
||||
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 picocli.CommandLine;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.args;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class SetConfigParameterTest {
|
||||
|
||||
private static Stream<Arguments> provideTestCases() {
|
||||
return Stream.of(
|
||||
Arguments.of(args("-ck", "test"), 1, (Function<Config, Set<String>>) Config::getCommentaryKeywords),
|
||||
Arguments.of(args("-ck", "test", "test1", "test2", "test3", "test4"), 5, (Function<Config, Set<String>>) Config::getCommentaryKeywords),
|
||||
Arguments.of(args(), 2, (Function<Config, Set<String>>) Config::getCommentaryKeywords),
|
||||
Arguments.of(args("-fk", "test"), 1, (Function<Config, Set<String>>) Config::getForcedKeywords),
|
||||
Arguments.of(args("-fk", "test", "test1", "test2", "test3", "test4"), 5, (Function<Config, Set<String>>) Config::getForcedKeywords),
|
||||
Arguments.of(args(), 3, (Function<Config, Set<String>>) Config::getForcedKeywords),
|
||||
Arguments.of(args("-ps", "test"), 1, (Function<Config, Set<String>>) Config::getPreferredSubtitles),
|
||||
Arguments.of(args("-ps", "test", "test1", "test2", "test3", "test4"), 5, (Function<Config, Set<String>>) Config::getPreferredSubtitles),
|
||||
Arguments.of(args(), 1, (Function<Config, Set<String>>) Config::getPreferredSubtitles)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCases")
|
||||
void validate(String[] cmdArgs, int expectedSize, Function<Config, Set<String>> fieldUnderTest) {
|
||||
Main sut = new Main();
|
||||
CommandLine.populateCommand(sut, cmdArgs);
|
||||
assertEquals(expectedSize, fieldUnderTest.apply(sut.getConfig()).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate() {
|
||||
Main sut = new Main();
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, args("-ck")));
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, args("-fk")));
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, args("-e")));
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, args("-ps")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import at.pcgamingfreaks.yaml.YAML;
|
||||
import at.pcgamingfreaks.yaml.YamlInvalidContentException;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.DefaultParser;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult.*;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult.INVALID;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty.CONFIG_PATH;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.CommandLineOptionsUtil.optionOf;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ConfigPathValidatorTest {
|
||||
private static final String TEST_FILE = "src/test/resources/test-dir/test-file.mkv";
|
||||
private static final String TEST_CONFIG = "src/test/resources/test-dir/test-config.yaml";
|
||||
|
||||
private static CommandLineParser parser;
|
||||
private static Options options;
|
||||
|
||||
@BeforeAll
|
||||
static void before() {
|
||||
parser = new DefaultParser();
|
||||
options = new Options();
|
||||
options.addOption(optionOf(CONFIG_PATH, CONFIG_PATH.abrv(), CONFIG_PATH.args()));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> provideTestCases() {
|
||||
return Stream.of(
|
||||
Arguments.of(CONFIG_PATH, true, "", new String[]{"-p", TEST_CONFIG}, VALID),
|
||||
Arguments.of(CONFIG_PATH, true, "config-path: " + TEST_CONFIG, new String[]{}, MISSING),
|
||||
Arguments.of(CONFIG_PATH, false, "config-path: " + TEST_CONFIG, new String[]{}, NOT_PRESENT),
|
||||
Arguments.of(CONFIG_PATH, true, "", new String[]{"-p", TEST_FILE}, INVALID)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCases")
|
||||
void validate(ConfigProperty property, boolean required, String yamlArgs, String[] cmdArgs,
|
||||
ValidationResult expectedResult) throws ParseException, YamlInvalidContentException {
|
||||
ConfigPathValidator underTest = new ConfigPathValidator(property, required);
|
||||
|
||||
ValidationResult result = underTest.validate(new YAML(yamlArgs), parser.parse(options, cmdArgs));
|
||||
|
||||
assertEquals(expectedResult, result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.LaneType;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SubtitleTrackComparatorTest {
|
||||
private static final SubtitleTrackComparator comparator = new SubtitleTrackComparator(new String[]{"unstyled"});
|
||||
|
||||
private static Stream<Arguments> compareArguments() {
|
||||
return Stream.of(
|
||||
Arguments.of(List.of(attr("unstyled sub", false), attr("styled sub", false)),
|
||||
List.of(attr("unstyled sub", false), attr("styled sub", false))),
|
||||
Arguments.of(List.of(attr("styled sub", false), attr("unstyled sub", false)),
|
||||
List.of(attr("unstyled sub", false), attr("styled sub", false))),
|
||||
|
||||
Arguments.of(List.of(attr("unstyled sub", true), attr("styled sub", false)),
|
||||
List.of(attr("unstyled sub", true), attr("styled sub", false))),
|
||||
Arguments.of(List.of(attr("styled sub", true), attr("unstyled sub", false)),
|
||||
List.of(attr("unstyled sub", false), attr("styled sub", true))),
|
||||
|
||||
Arguments.of(List.of(attr("unstyled sub", true), attr("unstyled sub", false)),
|
||||
List.of(attr("unstyled sub", true), attr("unstyled sub", false)))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("compareArguments")
|
||||
void compare(List<FileAttribute> input, List<FileAttribute> expected) {
|
||||
List<FileAttribute> result = input.stream().sorted(comparator.reversed()).collect(Collectors.toList());
|
||||
|
||||
assertArrayEquals(expected.toArray(new FileAttribute[0]), result.toArray(new FileAttribute[0]));
|
||||
}
|
||||
|
||||
private static FileAttribute attr(String trackName, boolean defaultTrack) {
|
||||
return new FileAttribute(0, "", trackName, defaultTrack, false, LaneType.SUBTITLES);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.createFileInfo;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class FileInfoDtoTest {
|
||||
private static final FileAttribute AUDIO_GER_DEFAULT = new FileAttribute(0, "ger", "", true, false, LaneType.AUDIO);
|
||||
private static final FileAttribute AUDIO_GER = new FileAttribute(0, "ger", "", false, false, LaneType.AUDIO);
|
||||
private static final FileAttribute AUDIO_ENG_DEFAULT = new FileAttribute(1, "eng", "", true, false, LaneType.AUDIO);
|
||||
private static final FileAttribute AUDIO_ENG = new FileAttribute(1, "eng", "", false, false, LaneType.AUDIO);
|
||||
|
||||
private static final FileAttribute SUB_GER_DEFAULT = new FileAttribute(0, "ger", "", true, false, LaneType.SUBTITLES);
|
||||
private static final FileAttribute SUB_GER = new FileAttribute(0, "ger", "", false, false, LaneType.SUBTITLES);
|
||||
private static final FileAttribute SUB_ENG_DEFAULT = new FileAttribute(1, "eng", "", true, false, LaneType.SUBTITLES);
|
||||
private static final FileAttribute SUB_ENG = new FileAttribute(1, "eng", "", false, false, LaneType.SUBTITLES);
|
||||
|
||||
|
||||
private static Stream<Arguments> isAudioDifferent() {
|
||||
return Stream.of(
|
||||
Arguments.of(createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_GER_DEFAULT), false),
|
||||
Arguments.of(createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_ENG), true),
|
||||
Arguments.of(createFileInfo(Set.of(AUDIO_GER_DEFAULT, AUDIO_ENG_DEFAULT), AUDIO_GER_DEFAULT), true),
|
||||
Arguments.of(createFileInfo(Set.of(), AUDIO_GER), true),
|
||||
Arguments.of(createFileInfo(null, AUDIO_GER), true)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void isAudioDifferent(FileInfoDto underTest, boolean expected) {
|
||||
assertEquals(expected, underTest.isAudioDifferent());
|
||||
}
|
||||
|
||||
private static Stream<Arguments> isSubtitleDifferent() {
|
||||
return Stream.of(
|
||||
Arguments.of(createFileInfo(Set.of(SUB_GER_DEFAULT), SUB_GER_DEFAULT, new AttributeConfig("", "ger")), false),
|
||||
Arguments.of(createFileInfo(Set.of(SUB_GER_DEFAULT), SUB_ENG, new AttributeConfig("", "eng")), true),
|
||||
Arguments.of(createFileInfo(Set.of(SUB_GER_DEFAULT, SUB_ENG_DEFAULT), SUB_ENG, new AttributeConfig("", "eng")), true),
|
||||
Arguments.of(createFileInfo(Set.of(), SUB_ENG, new AttributeConfig("", "ger")), true),
|
||||
Arguments.of(createFileInfo(null, SUB_GER, new AttributeConfig("", "ger")), true),
|
||||
Arguments.of(createFileInfo(null, null, new AttributeConfig("", "OFF")), false),
|
||||
Arguments.of(createFileInfo(Set.of(), null, new AttributeConfig("", "OFF")), false),
|
||||
Arguments.of(createFileInfo(Set.of(SUB_GER_DEFAULT), null, new AttributeConfig("", "OFF")), true)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void isSubtitleDifferent(FileInfoDto underTest, boolean expected) {
|
||||
assertEquals(expected, underTest.isSubtitleDifferent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
public class PathUtils {
|
||||
public static final String TEST_DIR = "src/test/resources/test-dir";
|
||||
public static final String TEST_FILE = "src/test/resources/test-dir/test-file.mkv";
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SetUtilsTest {
|
||||
|
||||
@Test
|
||||
void retainOf() {
|
||||
List<Integer> list1 = List.of(1, 2, 3, 4, 5);
|
||||
List<Integer> list2 = List.of(2, 4, 6, 8, 10);
|
||||
Set<Integer> expected = Set.of(2, 4);
|
||||
|
||||
assertEquals(expected, SetUtils.retainOf(list1, list2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
public class TestUtil {
|
||||
public static String yamlList(ConfigProperty main, ConfigProperty... child) {
|
||||
return main.prop() + ":\n" + Arrays.stream(child)
|
||||
.map(ConfigProperty::prop)
|
||||
.collect(joining("\n", " - ", ""));
|
||||
}
|
||||
|
||||
public static <T> Arguments argumentsOf(ConfigProperty property, boolean required, T defaultValue, String yaml, String[] cmd,
|
||||
ValidationResult result) {
|
||||
return Arguments.of(property, required, defaultValue, yaml, cmd, result);
|
||||
}
|
||||
|
||||
public static Arguments argumentsOf(ConfigProperty property, boolean required, boolean append, String yaml, String[] cmd,
|
||||
ValidationResult result, int expectedSize) {
|
||||
return Arguments.of(property, required, append, yaml, cmd, result, expectedSize);
|
||||
}
|
||||
|
||||
public static FileInfoDto createFileInfo(Set<FileAttribute> defaultAudio, FileAttribute desiredAudio) {
|
||||
FileInfoDto fileInfoDto = new FileInfoDto(null);
|
||||
fileInfoDto.setExistingDefaultAudioLanes(defaultAudio);
|
||||
fileInfoDto.setDesiredDefaultAudioLane(desiredAudio);
|
||||
return fileInfoDto;
|
||||
}
|
||||
|
||||
public static FileInfoDto createFileInfo(Set<FileAttribute> defaultSubtitle, FileAttribute desiredSubtitle, AttributeConfig config) {
|
||||
FileInfoDto fileInfoDto = new FileInfoDto(null);
|
||||
fileInfoDto.setExistingDefaultSubtitleLanes(defaultSubtitle);
|
||||
fileInfoDto.setDesiredDefaultSubtitleLane(desiredSubtitle);
|
||||
fileInfoDto.setMatchedConfig(config);
|
||||
return fileInfoDto;
|
||||
}
|
||||
|
||||
public static String[] args(String... args) {
|
||||
String[] staticArray = new String[]{"-l", "/", "-a", "jpn:ger"};
|
||||
String[] result = new String[staticArray.length + args.length];
|
||||
System.arraycopy(staticArray, 0, result, 0, staticArray.length);
|
||||
System.arraycopy(args, 0, result, staticArray.length, args.length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
0
src/test/resources/mkvtoolnix_exe/mkvmerge.exe
Normal file
0
src/test/resources/mkvtoolnix_exe/mkvmerge.exe
Normal file
0
src/test/resources/mkvtoolnix_exe/mkvpropedit.exe
Normal file
0
src/test/resources/mkvtoolnix_exe/mkvpropedit.exe
Normal file
0
src/test/resources/test-dir/test-config.yaml
Normal file
0
src/test/resources/test-dir/test-config.yaml
Normal file
0
src/test/resources/test-dir/test-file.mkv
Normal file
0
src/test/resources/test-dir/test-file.mkv
Normal file
@@ -1 +0,0 @@
|
||||
version=${project.version}
|
||||
Reference in New Issue
Block a user