mirror of
https://github.com/RatzzFatzz/MKVAudioSubtitleChanger.git
synced 2026-02-11 10:05:58 +01:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
906ec944eb | ||
|
|
41e107ef85 | ||
|
|
3c57eb44ef | ||
|
|
8dbfb22ed8 | ||
|
|
a5aae0acf4 | ||
|
|
1165dd8380 | ||
|
|
be004e6146 | ||
|
|
31b155d8a3 | ||
|
|
94365651ff | ||
|
|
f18fdbdda6 | ||
|
|
13283e3765 | ||
|
|
949261fb17 | ||
|
|
6cf42e5915 | ||
|
|
842b97dcb6 | ||
|
|
c2af135a57 | ||
|
|
280771e545 | ||
|
|
7620771aed | ||
|
|
c7670e36c1 | ||
|
|
1e31326ea2 | ||
|
|
7230134de6 | ||
|
|
fa84c483d9 | ||
|
|
62b637c241 | ||
|
|
1bdd3874e7 | ||
|
|
3e74e23512 | ||
|
|
5f2248653b | ||
|
|
15128583df | ||
|
|
76d25fca1b | ||
|
|
957295127a | ||
|
|
15177268a0 | ||
|
|
fa572030da | ||
|
|
cc23d8c5bc | ||
|
|
94a3b419e0 | ||
|
|
f88fcd0bd5 | ||
|
|
69a70eb66f | ||
|
|
e5e5f56aed | ||
|
|
b6b15faf7d | ||
|
|
ca29c22f00 | ||
|
|
1ae5b1bef1 | ||
|
|
cf04e14de2 | ||
|
|
2ecea906b1 | ||
|
|
d3248e646b | ||
|
|
a51922968e | ||
|
|
80c46508b8 | ||
|
|
a5b24e907d | ||
|
|
7427e3aa27 | ||
|
|
d7ae865d55 | ||
|
|
363492be43 | ||
|
|
37cedecea7 | ||
|
|
04722d9279 | ||
|
|
0b61deccbf | ||
|
|
d5e452557c | ||
|
|
e7a13c9f1d | ||
|
|
63bcd92db9 | ||
|
|
0b8dfa7464 | ||
|
|
f08a6ef1da | ||
|
|
ae541e6fdf | ||
|
|
aa5fd26b32 | ||
|
|
181c718e7a | ||
|
|
5eca28ecb9 | ||
|
|
9ab417f71d | ||
|
|
0f6bc271b1 | ||
|
|
99f929aabb | ||
|
|
a156db16fe |
41
.github/workflows/release.yml
vendored
41
.github/workflows/release.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# This workflow will run every time a new release is created.
|
||||
# This workflow will run every time a tag starting with v is created.
|
||||
|
||||
name: Build and release
|
||||
on:
|
||||
@@ -8,12 +8,23 @@ on:
|
||||
jobs:
|
||||
portable-build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Install mkvtoolnix
|
||||
run: sudo apt-get install -y mkvtoolnix
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
|
||||
- name: Extract version from tag
|
||||
id: get_version
|
||||
run: echo "VERSION=$(echo ${GITHUB_REF_NAME#v} | cut -d'-' -f1)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Set timezone
|
||||
uses: szenius/set-timezone@v2.0
|
||||
@@ -31,7 +42,7 @@ jobs:
|
||||
|
||||
- name: Build with Maven
|
||||
run: |
|
||||
mvn clean package --file pom.xml -P portable
|
||||
mvn clean package --file pom.xml -P portable -Drevision="${{ steps.get_version.outputs.VERSION }}"
|
||||
cp target/M*.{zip,tar} artifacts/
|
||||
|
||||
- name: Upload artifacts
|
||||
@@ -43,12 +54,23 @@ jobs:
|
||||
|
||||
debian-build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Install mkvtoolnix
|
||||
run: sudo apt-get install -y mkvtoolnix
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
|
||||
- name: Extract version from tag
|
||||
id: get_version
|
||||
run: echo "VERSION=$(echo ${GITHUB_REF_NAME#v} | cut -d'-' -f1)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Set timezone
|
||||
uses: szenius/set-timezone@v2.0
|
||||
@@ -66,7 +88,7 @@ jobs:
|
||||
|
||||
- name: Build with Maven
|
||||
run: |
|
||||
mvn clean package --file pom.xml -P linux
|
||||
mvn clean package --file pom.xml -P debian -Drevision="${{ steps.get_version.outputs.VERSION }}"
|
||||
cp target/M*.deb artifacts/
|
||||
|
||||
- name: Upload artifacts
|
||||
@@ -78,6 +100,10 @@ jobs:
|
||||
|
||||
windows-installer-build:
|
||||
runs-on: windows-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Install mkvtoolnix
|
||||
uses: crazy-max/ghaction-chocolatey@v3
|
||||
@@ -86,6 +112,13 @@ jobs:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
|
||||
- name: Extract version from tag
|
||||
id: get_version
|
||||
run: echo "VERSION=$(echo ${GITHUB_REF_NAME#v} | cut -d'-' -f1)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Set timezone
|
||||
uses: szenius/set-timezone@v2.0
|
||||
@@ -102,7 +135,7 @@ jobs:
|
||||
run: mkdir artifacts
|
||||
|
||||
- name: Build with Maven
|
||||
run: mvn clean package --file pom.xml -P windows
|
||||
run: mvn clean package --file pom.xml -P windows -Drevision="${{ steps.get_version.outputs.VERSION }}"
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: AButler/upload-release-assets@v3.0
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
76
README.md
76
README.md
@@ -1,8 +1,7 @@
|
||||
## Introduction
|
||||
|
||||
A streamlined solution for managing MKV files, this program leverages MKVToolNix to modify audio and subtitle track properties without the need for time-consuming file reencoding. Users can easily set their track preferences, and the application intelligently applies the best matching configuration. The tool focuses on metadata modification rather than full file rewriting, ensuring quick operations while maintaining the original file integrity. This makes it an ideal choice for managing multilingual media collections or batch processing multiple MKV files.
|
||||
|
||||

|
||||
This CLI tool uses MKVToolNix to quickly modify track properties in MKV files without reencoding. Use profiles to set default audio/subtitle tracks and add commentary, hearing impaired, and forced flags in bulk.
|
||||

|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -10,47 +9,56 @@ A streamlined solution for managing MKV files, this program leverages MKVToolNix
|
||||
- mkvtoolnix installation
|
||||
|
||||
## Execution
|
||||
Portable:
|
||||
```
|
||||
java -jar mkvaudiosubtitlechanger-<version>.jar --library "X:/Files" --attribute-config eng:ger eng:OFF
|
||||
```
|
||||
Windows & Linux (installed):
|
||||
```
|
||||
mkvaudiosubtitlechanger --library "X:/Files" --attribute-config eng:ger eng:OFF
|
||||
```shell
|
||||
# Portable
|
||||
java -jar mkvaudiosubtitlechanger.jar --attribute-config eng:ger -s ./videos
|
||||
# Installed
|
||||
mkvaudiosubtitlechanger.jar --attribute-config eng:ger -s ./videos
|
||||
```
|
||||
Remove `--safemode` or `-s` to actually apply the changes. Using safemode for the first execution is recommended.
|
||||
|
||||
Add `--safe-mode` oder `-s` to not change any files. This is recommended for the first executions.
|
||||
### Update defaults
|
||||
To update the default flag for tracks use `--attribute-config` or `-a`.
|
||||
This parameter takes in a list of pairs `audio:subtitle` (E.g. `eng:ger`).
|
||||
The order of these configs matters, because they are processed in order.
|
||||
The matching stops when the first match was found or when no match was found.
|
||||
For example `-a ger:OFF eng:ger` first tries to find a match for german audio, if that is not possible it tries the same for english with german subs.
|
||||
This can be extended indefinitely.
|
||||
|
||||
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).
|
||||
Using this parameter is not required, but it is the reason I originally started developing this tool.
|
||||
|
||||
### Available parameters
|
||||
```
|
||||
-a, --attribute-config=<attributeConfig>...
|
||||
List of audio:subtitle pairs used to match in order and update files accordingly (e.g. jpn:eng jpn:ger)
|
||||
-c, --coherent=<coherent> try to match all files in dir of depth with the same attribute config
|
||||
-cf, --force-coherent changes are only applied if it's a coherent match
|
||||
--commentary-keywords=<commentaryKeywords>[, <commentaryKeywords>...]...
|
||||
Keywords to identify commentary tracks (Defaults will be overwritten; Default: commentary, director)
|
||||
-d, --filter-date=<filterDate>
|
||||
only consider files created newer than entered date (format: "dd.MM.yyyy-HH:mm:ss")
|
||||
--debug Enable debug logging
|
||||
-e, --excluded-directory=<excludedDirectories>...
|
||||
Directories to be excluded, combines with config file
|
||||
--forced-keywords=<forcedKeywords>[, <forcedKeywords>...]...
|
||||
Keywords to identify forced tracks (Defaults will be overwritten; Default: forced, signs, songs)
|
||||
-h, --help Show this help message and exit.
|
||||
-i, --include-pattern=<includePattern>
|
||||
include files matching pattern (default: ".*")
|
||||
-l, --library=<libraryPath>
|
||||
path to library
|
||||
List of audio:subtitle pairs for matching defaults in order (e.g. jpn:eng jpn:ger)
|
||||
-m, --mkvtoolnix=<mkvToolNix>
|
||||
path to mkvtoolnix installation
|
||||
-n, --only-new-file sets filter-date to last successful execution (overwrites input of filter-date)
|
||||
--preferred-subtitles=<preferredSubtitles>[, <preferredSubtitles>...]...
|
||||
Keywords to prefer specific subtitle tracks (Defaults will be overwritten; Default: unstyled)
|
||||
-s, --safemode test run (no files will be changes)
|
||||
-t, --threads=<threads> thread count (default: 2)
|
||||
-t, --threads=<threads> thread count
|
||||
Default: 2
|
||||
-c, --coherent=<coherent> try to match all files in dir of depth with the same attribute config. Attempting increasing deeper levels until match is found (worst case applying config on single file basis)
|
||||
-cf, --force-coherent only applies changes if a coherent match was found for the specifically entered depth
|
||||
-d, --filter-date=<filterDate>
|
||||
only consider files created newer than entered date (format: "dd.MM.yyyy-HH:mm:ss")
|
||||
-i, --include-pattern=<includePattern>
|
||||
include files matching pattern
|
||||
-e, --exclude=<excluded>...
|
||||
relative directories and files to be excluded (no wildcard)
|
||||
-o, -overwrite-forced remove all forced flags
|
||||
--forced-keywords=<forcedKeywords>[, <forcedKeywords>...]...
|
||||
Keywords to identify forced tracks (Defaults will be overwritten)
|
||||
Default: forced, signs, songs
|
||||
--commentary-keywords=<commentaryKeywords>[, <commentaryKeywords>...]...
|
||||
Keywords to identify commentary tracks (Defaults will be overwritten)
|
||||
Default: comment, commentary, director
|
||||
--hearing-impaired=<hearingImpaired>[, <hearingImpaired>...]...
|
||||
Keywords to identify hearing impaired tracks (Defaults will be overwritten
|
||||
Default: SDH, CC
|
||||
--preferred-subtitles=<preferredSubtitles>[, <preferredSubtitles>...]...
|
||||
Keywords to prefer specific subtitle tracks (Defaults will be overwritten)
|
||||
Default: unstyled
|
||||
--debug Enable debug logging
|
||||
-h, --help Show this help message and exit.
|
||||
-V, --version Print version information and exit.
|
||||
```
|
||||
If you need more information how each parameter works, check out [this wiki page](https://github.com/RatzzFatzz/MKVAudioSubtitleChanger/wiki/Parameters-v4).
|
||||
|
||||
BIN
example.gif
BIN
example.gif
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 145 KiB |
91
pom.xml
91
pom.xml
@@ -6,9 +6,10 @@
|
||||
|
||||
<groupId>at.pcgamingfreaks</groupId>
|
||||
<artifactId>MKVAudioSubtitleChanger</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>${revision}</version>
|
||||
|
||||
<properties>
|
||||
<revision>1.0.0-SNAPSHOT</revision>
|
||||
<mainClass>at.pcgamingfreaks.mkvaudiosubtitlechanger.Main</mainClass>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
@@ -63,11 +64,19 @@
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>log4j2-installed.yaml</include>
|
||||
<include>log4j2-windows.yaml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.4.2</version>
|
||||
<configuration>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
@@ -84,7 +93,7 @@
|
||||
<resource>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<includes>
|
||||
<include>${project.artifactId}-${project.version}.jar</include>
|
||||
<include>${project.artifactId}.jar</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
@@ -121,7 +130,7 @@
|
||||
|
||||
<input>target/jpackage-input</input>
|
||||
<mainClass>at.pcgamingfreaks.mkvaudiosubtitlechanger.Main</mainClass>
|
||||
<mainJar>${project.artifactId}-${project.version}.jar</mainJar>
|
||||
<mainJar>${project.artifactId}.jar</mainJar>
|
||||
|
||||
<resourceDir>${project.build.directory}/wix-resources/</resourceDir>
|
||||
<type>EXE</type>
|
||||
@@ -129,7 +138,7 @@
|
||||
<winShortcut>false</winShortcut>
|
||||
<winMenu>false</winMenu>
|
||||
<javaOptions>
|
||||
<javaOption>-Dlog4j.configurationFile=log4j2-windows.yaml</javaOption>
|
||||
<javaOption>-Dlog4j2.configurationFile=log4j2-windows.yaml</javaOption>
|
||||
</javaOptions>
|
||||
</configuration>
|
||||
<executions>
|
||||
@@ -146,7 +155,7 @@
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>linux</id>
|
||||
<id>debian</id>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
@@ -325,12 +334,6 @@
|
||||
</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>
|
||||
@@ -345,12 +348,19 @@
|
||||
<version>4.7.7</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>4.0.0-M1</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>
|
||||
<version>9.1.0.Final</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Expression Language Implementation -->
|
||||
@@ -358,68 +368,65 @@
|
||||
<dependency>
|
||||
<groupId>jakarta.el</groupId>
|
||||
<artifactId>jakarta.el-api</artifactId>
|
||||
<version>6.1.0-M1</version>
|
||||
<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.1.1</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.24.3</version>
|
||||
<version>3.0.0-beta2</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.24.3</version>
|
||||
<version>3.0.0-beta2</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-slf4j2-impl</artifactId>
|
||||
<version>2.24.3</version>
|
||||
<version>3.0.0-beta2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml -->
|
||||
<!-- https://mvnrepository.com/artifact/tools.jackson.dataformat/jackson-dataformat-yaml -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<groupId>tools.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
<version>2.20.0</version>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
|
||||
<!-- https://mvnrepository.com/artifact/tools.jackson.core/jackson-databind -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<groupId>tools.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.20.0</version>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/commons-cli/commons-cli -->
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<version>1.11.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.19.0</version>
|
||||
<version>3.20.0</version>
|
||||
</dependency>
|
||||
<!-- Source: https://mvnrepository.com/artifact/commons-io/commons-io -->
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.21.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/me.tongfei/progressbar -->
|
||||
<dependency>
|
||||
@@ -433,28 +440,34 @@
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>6.0.0</version>
|
||||
<version>6.0.1</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>6.0.0</version>
|
||||
<version>6.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>5.20.0</version>
|
||||
<version>5.21.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<version>5.21.0</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>6.0.0</version>
|
||||
<version>6.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
java -Dlog4j.configurationFile=log4j2-debian.yaml -jar /usr/lib/${project.artifactId}/${project.artifactId}-${project.version}.jar "$@"
|
||||
java -Dlog4j2.configurationFile=log4j2-debian.yaml -jar /usr/lib/${project.artifactId}/${project.artifactId}-${project.version}.jar "$@"
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: ${project.artifactId}
|
||||
Version: ${project.version}
|
||||
Package: [[artifactId]]
|
||||
Version: [[version]]
|
||||
Section: misc
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
|
||||
@@ -1,74 +1,18 @@
|
||||
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.util.ProjectUtil;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.CommandRunner;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation.ValidationExecutionStrategy;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
@CommandLine.Command(
|
||||
name = "mkvaudiosubtitlechanger",
|
||||
usageHelpWidth = 120,
|
||||
customSynopsis = {
|
||||
"mkvaudiosubtitlechanger -a <attributeConfig>... -l <libraryPath> [-s]",
|
||||
"Example: mkvaudiosubtitlechanger -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() {
|
||||
if (config.isDebug()) {
|
||||
Configurator.setRootLevel(Level.DEBUG);
|
||||
}
|
||||
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 class Main {
|
||||
public static void main(String[] args) {
|
||||
if (args.length == 0) {
|
||||
args = new String[] { "--help" };
|
||||
CommandLine.usage(CommandRunner.class, System.out);
|
||||
return;
|
||||
}
|
||||
new CommandLine(Main.class).execute(args);
|
||||
|
||||
new CommandLine(CommandRunner.class)
|
||||
.setExecutionStrategy(new ValidationExecutionStrategy())
|
||||
.execute(args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
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.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 (!isLanguageValid(attr.getAudioLanguage()))
|
||||
throw new CommandLine.TypeConversionException("Audio language invalid: " + attr.getAudioLanguage());
|
||||
if (!isLanguageValid(attr.getSubtitleLanguage()))
|
||||
throw new CommandLine.TypeConversionException("Subtitle language invalid: " + attr.getSubtitleLanguage());
|
||||
}
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.MkvToolNix;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
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.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class Config implements CommandLine.IVersionProvider {
|
||||
@Getter(AccessLevel.NONE)
|
||||
@Setter(AccessLevel.NONE)
|
||||
private static Config config = null;
|
||||
|
||||
private File configPath;
|
||||
|
||||
@CommandLine.Spec
|
||||
CommandLine.Model.CommandSpec spec;
|
||||
|
||||
@CommandLine.Option(names = {"-a", "--attribute-config"}, required = true, arity = "1..*", converter = AttributeConfigConverter.class,
|
||||
description = "List of audio:subtitle pairs used to match in order and update files accordingly (e.g. jpn:eng jpn:ger)")
|
||||
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", "--only-new-file"}, 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 = {"--forced-keywords"}, arity = "1..*", defaultValue = "forced, signs, songs", split = ", ",
|
||||
description = "Keywords to identify forced tracks (Defaults will be overwritten; Default: ${DEFAULT-VALUE})")
|
||||
private Set<String> forcedKeywords;
|
||||
@CommandLine.Option(names = {"--commentary-keywords"}, arity = "1..*", defaultValue = "commentary, director", split = ", ",
|
||||
description = "Keywords to identify commentary tracks (Defaults will be overwritten; Default: ${DEFAULT-VALUE})")
|
||||
private Set<String> commentaryKeywords;
|
||||
@CommandLine.Option(names = {"--preferred-subtitles"}, arity = "1..*", defaultValue = "unstyled", split = ", ",
|
||||
description = "Keywords to prefer specific subtitle tracks (Defaults will be overwritten; Default: ${DEFAULT-VALUE})")
|
||||
private Set<String> preferredSubtitles;
|
||||
|
||||
@CommandLine.Option(names = {"--debug"}, description = "Enable debug logging")
|
||||
private boolean debug;
|
||||
|
||||
@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() {
|
||||
return getInstance(false);
|
||||
}
|
||||
|
||||
public static Config getInstance(boolean reset) {
|
||||
if (config == null || reset) {
|
||||
config = new Config();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public static void setInstance(Config c) {
|
||||
config = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
public String getPathFor(MkvToolNix application, boolean isWindows) {
|
||||
return getPathFor(application) + (isWindows ? ".exe" : "");
|
||||
}
|
||||
|
||||
public String getNormalizedLibraryPath() {
|
||||
return this.getLibraryPath().getAbsolutePath().replace("\\", "/");
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getVersion() throws Exception {
|
||||
return new String[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
@Deprecated
|
||||
public enum ValidationResult {
|
||||
VALID,
|
||||
DEFAULT,
|
||||
NOT_PRESENT,
|
||||
MISSING,
|
||||
INVALID;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
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.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 = isLanguageValid(attributeConfig.getAudioLanguage())
|
||||
&& isLanguageValid(attributeConfig.getSubtitleLanguage());
|
||||
if (!isValid) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
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", e);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors.*;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.ProjectUtil;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@Slf4j
|
||||
@CommandLine.Command(
|
||||
name = "mkvaudiosubtitlechanger",
|
||||
usageHelpAutoWidth = true,
|
||||
customSynopsis = {
|
||||
"mkvaudiosubtitlechanger [-a <attributeConfig> [...<attributeConfig>]] [-s] <libraryPath>",
|
||||
"Example: mkvaudiosubtitlechanger -a eng:eng eng:ger -s /mnt/media/",
|
||||
""
|
||||
},
|
||||
requiredOptionMarker = '*',
|
||||
sortOptions = false,
|
||||
mixinStandardHelpOptions = true,
|
||||
versionProvider = ProjectUtil.class
|
||||
)
|
||||
public class CommandRunner implements Runnable {
|
||||
@Getter
|
||||
@CommandLine.ArgGroup(exclusive = false)
|
||||
private InputConfig config;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (config.isDebug()) {
|
||||
Configurator.setRootLevel(Level.DEBUG);
|
||||
}
|
||||
|
||||
if (config.isSafeMode()) {
|
||||
log.info("Safemode active. No files will be changed!");
|
||||
System.out.println("Safemode active. No files will be changed!");
|
||||
}
|
||||
|
||||
LastExecutionHandler lastExecutionHandler = config.isOnlyNewFiles() ? new LastExecutionHandler("./last-executions.yml") : null;
|
||||
FileFilter fileFilter = new FileFilter(config.getExcluded(), config.getIncludePattern(), config.getFilterDate(), lastExecutionHandler);
|
||||
FileProcessor fileProcessor = new CachedFileProcessor(new MkvFileProcessor(config.getMkvToolNix(), fileFilter));
|
||||
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(config.getPreferredSubtitles().toArray(new String[0]), config.getForcedKeywords(), config.getCommentaryKeywords(), config.getHearingImpaired());
|
||||
|
||||
AttributeUpdater kernel = config.getCoherent() != null
|
||||
? new CoherentAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler)
|
||||
: new SingleFileAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler);
|
||||
kernel.execute();
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public interface FileCollector {
|
||||
|
||||
/**
|
||||
* @param path leads to one file directly or a directory which will be loaded recursively
|
||||
* @return list of all files within the directory
|
||||
*/
|
||||
List<File> loadFiles(String path);
|
||||
|
||||
/**
|
||||
* 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,52 +1,86 @@
|
||||
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.RequiredArgsConstructor;
|
||||
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;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class FileFilter {
|
||||
static boolean accept(File pathName, String[] fileExtensions) {
|
||||
if (hasProperFileExtension(pathName, fileExtensions)
|
||||
&& hasMatchingPattern(pathName)
|
||||
&& isNewer(pathName)) {
|
||||
return true;
|
||||
private final Set<String> excluded;
|
||||
private final Pattern includePattern;
|
||||
private final Instant filterDate;
|
||||
private final LastExecutionHandler lastExecutionHandler;
|
||||
|
||||
private final String EXTENSION_GROUP = "extension";
|
||||
private final Pattern extensionPattern = Pattern.compile(String.format(".*(?<%s>\\..*)", EXTENSION_GROUP));
|
||||
|
||||
public boolean accept(File pathName, Set<String> fileExtensions) {
|
||||
// Ignore files irrelevant for statistics
|
||||
if (!hasProperFileExtension(pathName, new HashSet<>(fileExtensions))) {
|
||||
log.debug("Ignored {}", pathName);
|
||||
return false;
|
||||
}
|
||||
|
||||
ResultStatistic.getInstance().total();
|
||||
ResultStatistic.getInstance().excluded();
|
||||
return false;
|
||||
if (!hasMatchingPattern(pathName)
|
||||
|| isExcluded(pathName, new HashSet<>(excluded))
|
||||
|| lastExecutionHandler != null && !isNewer(pathName, lastExecutionHandler.get(pathName.getAbsolutePath()))
|
||||
|| !isNewer(pathName, filterDate)) {
|
||||
log.debug("Excluded {}", pathName);
|
||||
ResultStatistic.getInstance().excluded();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean hasProperFileExtension(File pathName, String[] fileExtensions) {
|
||||
return StringUtils.endsWithAny(pathName.getAbsolutePath().toLowerCase(), fileExtensions);
|
||||
private boolean hasProperFileExtension(File pathName, Set<String> fileExtensions) {
|
||||
Matcher matcher = extensionPattern.matcher(pathName.getName());
|
||||
return matcher.find() && fileExtensions.contains(matcher.group(EXTENSION_GROUP));
|
||||
}
|
||||
|
||||
private static boolean hasMatchingPattern(File pathName) {
|
||||
return Config.getInstance().getIncludePattern().matcher(pathName.getName()).matches();
|
||||
private boolean hasMatchingPattern(File pathName) {
|
||||
return includePattern.matcher(pathName.getName()).matches();
|
||||
}
|
||||
|
||||
private static boolean isNewer(File pathName) {
|
||||
Config config = Config.getInstance();
|
||||
if (config.getFilterDate() == null) return true;
|
||||
private boolean isNewer(File pathName, Instant date) {
|
||||
if (date == null) return true;
|
||||
try {
|
||||
BasicFileAttributes attributes = Files.readAttributes(pathName.toPath(), BasicFileAttributes.class);
|
||||
return isNewer(DateUtils.convert(attributes.creationTime().toMillis()));
|
||||
return attributes.creationTime().toInstant().isAfter(date)
|
||||
|| attributes.lastModifiedTime().toInstant().isAfter(date);
|
||||
} catch (IOException e) {
|
||||
log.warn("File attributes could not be read", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isNewer(Date creationDate) {
|
||||
return creationDate.toInstant().isAfter(Config.getInstance().getFilterDate().toInstant());
|
||||
private boolean isExcluded(File pathName, Set<String> excludedDirs) {
|
||||
if (excludedDirs.contains(pathName.getPath())) return true;
|
||||
|
||||
String[] pathSplit = pathName.getPath().split("/");
|
||||
for (String excludedDir : excludedDirs) {
|
||||
String[] excludeSplit = excludedDir.split("/");
|
||||
if (excludeSplit.length > pathSplit.length) continue;
|
||||
boolean matchingPaths = true;
|
||||
for (int i = 0; i < excludeSplit.length; i++) {
|
||||
if (!excludeSplit[i].equals(pathSplit[i])) {
|
||||
matchingPaths = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchingPaths) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
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.FileInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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(FileInfo info, List<FileAttribute> attributes, List<FileAttribute> nonForcedTracks);
|
||||
|
||||
/**
|
||||
* 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(FileInfo 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 used 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, FileInfo fileInfo) throws IOException, MkvToolNixException;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.Properties;
|
||||
|
||||
@Slf4j
|
||||
public class LastExecutionHandler {
|
||||
private final File file;
|
||||
private final Properties lastFileExecution;
|
||||
|
||||
public LastExecutionHandler(String path) {
|
||||
file = new File(path);
|
||||
lastFileExecution = loadLastFileExecution(file);
|
||||
}
|
||||
|
||||
public Properties loadLastFileExecution(File file) {
|
||||
Properties properties = new Properties();
|
||||
try (FileInputStream in = new FileInputStream(file)) {
|
||||
properties.load(in);
|
||||
} catch (IOException e) {
|
||||
log.warn("Couldn't find or read {}", file.getPath(), e);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
public Instant get(String path) {
|
||||
if (!lastFileExecution.containsKey(path)) return null;
|
||||
return Instant.parse(lastFileExecution.getProperty(path));
|
||||
}
|
||||
|
||||
public void update(String path) {
|
||||
update(path, Instant.now());
|
||||
}
|
||||
|
||||
public void update(String path, Instant execution) {
|
||||
if (lastFileExecution == null) return;
|
||||
lastFileExecution.put(path, execution.toString());
|
||||
}
|
||||
|
||||
public void persist() {
|
||||
try (FileOutputStream out = new FileOutputStream(file)) {
|
||||
lastFileExecution.store(out, "MKVAudioSubtitleChanger - Last file execution");
|
||||
} catch (IOException e) {
|
||||
log.warn("Persisting last file execution dates failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@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))) {
|
||||
return paths.filter(Files::isRegularFile)
|
||||
.map(Path::toFile)
|
||||
.filter(file -> FileFilter.accept(file, fileExtensions))
|
||||
.collect(Collectors.toList());
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't find file or directory!", e);
|
||||
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,198 +0,0 @@
|
||||
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.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.core.util.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
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;
|
||||
|
||||
@Slf4j
|
||||
public class MkvFileProcessor implements FileProcessor {
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
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 = new String[]{
|
||||
Config.getInstance().getPathFor(MkvToolNix.MKV_MERGE),
|
||||
"--identify",
|
||||
"--identification-format",
|
||||
"json",
|
||||
file.getAbsolutePath()
|
||||
};
|
||||
|
||||
log.debug("Executing '{}': {}", file.getAbsolutePath(), String.join(" ", command));
|
||||
InputStream inputStream = Runtime.getRuntime().exec(command)
|
||||
.getInputStream();
|
||||
jsonMap = mapper.readValue(inputStream, Map.class);
|
||||
List<Map<String, Object>> tracks = (List<Map<String, Object>>) jsonMap.get("tracks");
|
||||
if (tracks == null) {
|
||||
log.warn("Couldn't retrieve information of {}", file.getAbsolutePath());
|
||||
return new ArrayList<>();
|
||||
}
|
||||
for (Map<String, Object> attribute : tracks) {
|
||||
if (!"video".equals(attribute.get("type"))) {
|
||||
Map<String, Object> properties = (Map<String, Object>) attribute.get("properties");
|
||||
fileAttributes.add(new FileAttribute(
|
||||
(int) properties.get("number"),
|
||||
(String) properties.get("language"),
|
||||
(String) properties.get("track_name"),
|
||||
(Boolean) properties.getOrDefault("default_track", false),
|
||||
(Boolean) properties.getOrDefault("forced_track", false),
|
||||
LaneType.valueOf(((String) attribute.get("type")).toUpperCase(Locale.ENGLISH))));
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("File attributes of '{}': {}", file.getAbsolutePath(), fileAttributes.toString());
|
||||
} catch (IOException e) {
|
||||
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 void detectDefaultTracks(FileInfo info, List<FileAttribute> attributes, List<FileAttribute> nonForcedTracks) {
|
||||
for (FileAttribute attribute : attributes) {
|
||||
if (AUDIO.equals(attribute.type())) {
|
||||
if (attribute.defaultTrack()) info.getExistingDefaultAudioLanes().add(attribute);
|
||||
if (attribute.forcedTrack()) info.getExistingForcedAudioLanes().add(attribute);
|
||||
} else if (SUBTITLES.equals(attribute.type())) {
|
||||
if (attribute.defaultTrack()) info.getExistingDefaultSubtitleLanes().add(attribute);
|
||||
|
||||
if (attribute.forcedTrack()) info.getExistingForcedSubtitleLanes().add(attribute);
|
||||
else if (!nonForcedTracks.contains(attribute)) info.getDesiredForcedSubtitleLanes().add(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void detectDesiredTracks(FileInfo 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.type())).collect(Collectors.toSet());
|
||||
Set<FileAttribute> subtitleTracks = tracks.stream().filter(a -> SUBTITLES.equals(a.type())).collect(Collectors.toSet());
|
||||
|
||||
for (AttributeConfig config : configs) {
|
||||
Optional<FileAttribute> desiredAudio = detectDesiredTrack(config.getAudioLanguage(), audioTracks).findFirst();
|
||||
Optional<FileAttribute> desiredSubtitle = detectDesiredSubtitleTrack(config.getSubtitleLanguage(), subtitleTracks).findFirst();
|
||||
|
||||
if (("OFF".equals(config.getAudioLanguage()) || desiredAudio.isPresent())
|
||||
&& ("OFF".equals(config.getSubtitleLanguage()) || desiredSubtitle.isPresent())) {
|
||||
info.setMatchedConfig(config);
|
||||
info.setDesiredDefaultAudioLane(desiredAudio.orElse(null));
|
||||
info.setDesiredDefaultSubtitleLane(desiredSubtitle.orElse(null));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Stream<FileAttribute> detectDesiredTrack(String language, Set<FileAttribute> tracks) {
|
||||
return tracks.stream().filter(track -> language.equals(track.language()));
|
||||
}
|
||||
|
||||
private Stream<FileAttribute> detectDesiredSubtitleTrack(String language, Set<FileAttribute> tracks) {
|
||||
return detectDesiredTrack(language, tracks)
|
||||
.sorted(subtitleTrackComparator.reversed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileAttribute> retrieveNonForcedTracks(List<FileAttribute> attributes) {
|
||||
return attributes.stream()
|
||||
.filter(elem -> !StringUtils.containsAnyIgnoreCase(elem.trackName(),
|
||||
Config.getInstance().getForcedKeywords().toArray(new CharSequence[0])))
|
||||
.filter(elem -> !elem.forcedTrack())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileAttribute> retrieveNonCommentaryTracks(List<FileAttribute> attributes) {
|
||||
return attributes.stream()
|
||||
.filter(elem -> !StringUtils.containsAnyIgnoreCase(elem.trackName(),
|
||||
Config.getInstance().getCommentaryKeywords().toArray(new CharSequence[0])))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void update(File file, FileInfo fileInfo) throws IOException, MkvToolNixException {
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add(Config.getInstance().getPathFor(MkvToolNix.MKV_PROP_EDIT));
|
||||
command.add(String.format(file.getAbsolutePath()));
|
||||
|
||||
if (fileInfo.isAudioDifferent()) {
|
||||
removeExistingAndAddDesiredLanes(fileInfo.getExistingDefaultAudioLanes(), fileInfo.getDesiredDefaultAudioLane(), command);
|
||||
}
|
||||
|
||||
if (!fileInfo.getExistingForcedAudioLanes().isEmpty()) {
|
||||
for (FileAttribute track : fileInfo.getExistingForcedAudioLanes()) {
|
||||
command.addAll(format(DISABLE_FORCED_TRACK, track.id()));
|
||||
}
|
||||
}
|
||||
|
||||
if (fileInfo.isSubtitleDifferent()) {
|
||||
removeExistingAndAddDesiredLanes(fileInfo.getExistingDefaultSubtitleLanes(), fileInfo.getDesiredDefaultSubtitleLane(), command);
|
||||
}
|
||||
|
||||
if (fileInfo.areForcedTracksDifferent()) {
|
||||
for (FileAttribute track : fileInfo.getDesiredForcedSubtitleLanes()) {
|
||||
command.addAll(format(ENABLE_FORCED_TRACK, track.id()));
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Executing '{}'", String.join(" ", command));
|
||||
InputStream inputstream = Runtime.getRuntime().exec(command.toArray(new String[0])).getInputStream();
|
||||
String output = IOUtils.toString(new InputStreamReader(inputstream));
|
||||
log.debug("Result: {}", output);
|
||||
if (output.contains("Error")) throw new MkvToolNixException(output);
|
||||
}
|
||||
|
||||
private void removeExistingAndAddDesiredLanes(Set<FileAttribute> existingDefaultLanes, FileAttribute desiredDefaultLanes, List<String> command) {
|
||||
if (existingDefaultLanes != null && !existingDefaultLanes.isEmpty()) {
|
||||
for (FileAttribute track : existingDefaultLanes) {
|
||||
command.addAll(format(DISABLE_DEFAULT_TRACK, track.id()));
|
||||
}
|
||||
}
|
||||
if (desiredDefaultLanes != null) {
|
||||
command.addAll(format(ENABLE_DEFAULT_TRACK, desiredDefaultLanes.id()));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> format(String format, Object... args) {
|
||||
return Arrays.asList(String.format(format, args).split(" "));
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,49 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SubtitleTrackComparator implements Comparator<FileAttribute> {
|
||||
private final String[] preferredSubtitles;
|
||||
public class SubtitleTrackComparator implements Comparator<TrackAttributes> {
|
||||
private final Set<String> preferredSubtitles;
|
||||
private final Set<String> hearingImpairedKeywords;
|
||||
|
||||
public SubtitleTrackComparator(Collection<String> preferredSubtitles, Collection<String> hearingImpairedKeywords) {
|
||||
this.preferredSubtitles = new HashSet<>(preferredSubtitles.stream().map(String::toLowerCase).toList());
|
||||
this.hearingImpairedKeywords = new HashSet<>(hearingImpairedKeywords.stream().map(String::toLowerCase).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int compare(FileAttribute track1, FileAttribute track2) {
|
||||
public int compare(TrackAttributes track1, TrackAttributes track2) {
|
||||
int result = 0;
|
||||
|
||||
if (StringUtils.containsAnyIgnoreCase(track1.trackName(), preferredSubtitles)) {
|
||||
result++;
|
||||
}
|
||||
if (StringUtils.containsAnyIgnoreCase(track2.trackName(), preferredSubtitles)) {
|
||||
result--;
|
||||
}
|
||||
String track1Name = Strings.isNotBlank(track1.trackName()) ? track1.trackName().toLowerCase() : "";
|
||||
String track2Name = Strings.isNotBlank(track2.trackName()) ? track2.trackName().toLowerCase() : "";
|
||||
|
||||
if (preferredSubtitles.contains(track1Name)) result++;
|
||||
else for (String keyword: preferredSubtitles) if (track1Name.contains(keyword)) result++;
|
||||
|
||||
if (preferredSubtitles.contains(track2Name)) result--;
|
||||
else for (String keyword: preferredSubtitles) if (track2Name.contains(keyword)) result--;
|
||||
|
||||
|
||||
if (track1.hearingImpaired()) result--;
|
||||
else if (hearingImpairedKeywords.contains(track1Name)) result--;
|
||||
else for (String keyword: hearingImpairedKeywords) if (track1Name.contains(keyword)) result--;
|
||||
if (track2.hearingImpaired()) result++;
|
||||
else if (hearingImpairedKeywords.contains(track2Name)) result++;
|
||||
else for (String keyword: hearingImpairedKeywords) if (track2Name.contains(keyword)) result++;
|
||||
|
||||
if (result == 0) {
|
||||
if (track1.defaultTrack()) result++;
|
||||
if (track2.defaultTrack()) result--;
|
||||
if (track1.defaultt()) result++;
|
||||
if (track2.defaultt()) result--;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.converter;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.LanguageValidatorUtil.isLanguageValid;
|
||||
|
||||
public class AttributeConfigConverter implements CommandLine.ITypeConverter<AttributeConfig> {
|
||||
private static final String AUDIO_GROUP = "audio";
|
||||
private static final String SUB_GROUP = "sub";
|
||||
private static final Pattern PATTERN = Pattern.compile(String.format("^(?<%s>.{3}):(?<%s>.{3})$", AUDIO_GROUP, SUB_GROUP));
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
Matcher matcher = PATTERN.matcher(s);
|
||||
|
||||
if (!matcher.find()) throw new CommandLine.TypeConversionException("Invalid Attribute config: " + s);
|
||||
|
||||
return validateResult(new AttributeConfig(matcher.group(AUDIO_GROUP), matcher.group(SUB_GROUP)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that both language codes in the {@link AttributeConfig} object are valid.
|
||||
*
|
||||
* @param attr {@link AttributeConfig} object to validate
|
||||
* @throws CommandLine.TypeConversionException if either language code is invalid
|
||||
* @return valid {@link AttributeConfig}
|
||||
*/
|
||||
private static AttributeConfig validateResult(AttributeConfig attr) {
|
||||
if (!isLanguageValid(attr.getAudioLang()))
|
||||
throw new CommandLine.TypeConversionException("Audio language invalid: " + attr.getAudioLang());
|
||||
if (!isLanguageValid(attr.getSubLang()))
|
||||
throw new CommandLine.TypeConversionException("Subtitle language invalid: " + attr.getSubLang());
|
||||
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
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.FileInfo;
|
||||
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 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) {
|
||||
FileInfo fileInfo = new FileInfo(file);
|
||||
List<FileAttribute> attributes = processor.loadAttributes(file);
|
||||
|
||||
if (attributes == null || attributes.isEmpty()) {
|
||||
log.warn("No attributes found for file {}", file);
|
||||
statistic.total();
|
||||
statistic.failure();
|
||||
return;
|
||||
}
|
||||
|
||||
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 fileInfo contains information about file and desired configuration.
|
||||
*/
|
||||
protected void updateFile(FileInfo fileInfo) {
|
||||
statistic.total();
|
||||
switch (fileInfo.getStatus()) {
|
||||
case CHANGE_NECESSARY:
|
||||
statistic.shouldChange();
|
||||
commitChange(fileInfo);
|
||||
break;
|
||||
case NO_SUITABLE_CONFIG:
|
||||
statistic.noSuitableConfigFound();
|
||||
break;
|
||||
case ALREADY_SUITED:
|
||||
statistic.alreadyFits();
|
||||
break;
|
||||
case UNKNOWN:
|
||||
default:
|
||||
statistic.failure();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void commitChange(FileInfo fileInfo) {
|
||||
if (Config.getInstance().isSafeMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
processor.update(fileInfo.getFile(), fileInfo);
|
||||
statistic.success();
|
||||
log.info("Commited {} to '{}'", fileInfo.getMatchedConfig().toStringShort(), fileInfo.getFile().getAbsolutePath());
|
||||
} catch (IOException | MkvToolNixException e) {
|
||||
statistic.failedChanging();
|
||||
log.warn("Couldn't commit {} to '{}'", fileInfo.getMatchedConfig().toStringShort(), fileInfo.getFile().getAbsoluteFile(), e);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
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.FileInfo;
|
||||
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<FileInfo> fileInfos = collector.loadFiles(file.getAbsolutePath()).stream()
|
||||
.map(FileInfo::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (AttributeConfig config : Config.getInstance().getAttributeConfig()) {
|
||||
|
||||
for (FileInfo 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.toStringShort(), 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 (FileInfo fileInfo : fileInfos) {
|
||||
if (!Config.getInstance().isForceCoherent()) {
|
||||
super.process(fileInfo.getFile());
|
||||
} else {
|
||||
statistic.excluded();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.SubtitleTrackComparator;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class AttributeChangeProcessor {
|
||||
private final SubtitleTrackComparator subtitleTrackComparator;
|
||||
private final Set<String> commentaryKeywords;
|
||||
private final Set<String> hearingImpairedKeywords;
|
||||
private final Set<String> forcedKeywords;
|
||||
|
||||
public AttributeChangeProcessor(String[] preferredSubtitles, Set<String> forcedKeywords, Set<String> commentaryKeywords, Set<String> hearingImpairedKeywords) {
|
||||
this.subtitleTrackComparator = new SubtitleTrackComparator(Arrays.stream(preferredSubtitles).toList(), hearingImpairedKeywords);
|
||||
this.commentaryKeywords = commentaryKeywords;
|
||||
this.hearingImpairedKeywords = hearingImpairedKeywords;
|
||||
this.forcedKeywords = forcedKeywords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for default matches and applies them if found.
|
||||
*/
|
||||
public void findAndApplyDefaultMatch(FileInfo fileInfo, AttributeConfig... configs) {
|
||||
Map<String, List<TrackAttributes>> audiosByLanguage = new HashMap<>(fileInfo.getTracks().size());
|
||||
Map<String, List<TrackAttributes>> subsByLanguage = new HashMap<>(fileInfo.getTracks().size());
|
||||
getPossibleDefaults(fileInfo.getTracks()).forEach(track -> {
|
||||
if (TrackType.AUDIO.equals(track.type()))
|
||||
audiosByLanguage.computeIfAbsent(track.language(), (k) -> new ArrayList<>()).add(track);
|
||||
else if (TrackType.SUBTITLES.equals(track.type()))
|
||||
subsByLanguage.computeIfAbsent(track.language(), (k) -> new ArrayList<>()).add(track);
|
||||
});
|
||||
|
||||
for (AttributeConfig config : configs) {
|
||||
if (("OFF".equals(config.getAudioLang()) || audiosByLanguage.containsKey(config.getAudioLang()))
|
||||
&& ("OFF".equals(config.getSubLang()) || subsByLanguage.containsKey(config.getSubLang()))) {
|
||||
fileInfo.setMatchedConfig(config);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileInfo.getMatchedConfig() == null) return;
|
||||
|
||||
AttributeConfig match = fileInfo.getMatchedConfig();
|
||||
removeExistingDefaults(fileInfo);
|
||||
if (!"OFF".equals(match.getAudioLang())) applyNewDefault(fileInfo, audiosByLanguage.get(fileInfo.getMatchedConfig().getAudioLang()).get(0));
|
||||
if (!"OFF".equals(match.getSubLang())) applyNewDefault(fileInfo, subsByLanguage.get(fileInfo.getMatchedConfig().getSubLang()).stream().max(subtitleTrackComparator).get());
|
||||
}
|
||||
|
||||
/**
|
||||
* If match with xxx:OFF was found forced track in audio language is applied as default.
|
||||
* Forced track detection takes changes of {@link AttributeChangeProcessor#findAndApplyForcedTracks} into consideration.
|
||||
*/
|
||||
public void applyForcedAsDefault(FileInfo fileInfo) {
|
||||
AttributeConfig c = fileInfo.getMatchedConfig();
|
||||
if (c == null) return;
|
||||
if (!"OFF".equals(c.getAudioLang()) && "OFF".equals(c.getSubLang())) {
|
||||
getForcedTracks(fileInfo)
|
||||
.filter(track -> c.getAudioLang().equals(track.language()))
|
||||
.findFirst()
|
||||
.ifPresent(track -> applyNewDefault(fileInfo, track));
|
||||
}
|
||||
}
|
||||
|
||||
private Stream<TrackAttributes> getPossibleDefaults(List<TrackAttributes> tracks) {
|
||||
Stream<TrackAttributes> attributes = tracks.stream();
|
||||
|
||||
return attributes
|
||||
.filter(attr -> !attr.commentary())
|
||||
.filter(attr -> {
|
||||
if (attr.trackName() == null) return true;
|
||||
return commentaryKeywords.stream().noneMatch(keyword -> keyword.compareToIgnoreCase(attr.trackName()) == 0);
|
||||
})
|
||||
.filter(attr -> !attr.forced())
|
||||
.filter(attr -> {
|
||||
if (attr.trackName() == null) return true;
|
||||
return forcedKeywords.stream().noneMatch(keyword -> keyword.compareToIgnoreCase(attr.trackName()) == 0);
|
||||
});
|
||||
}
|
||||
|
||||
private void removeExistingDefaults(FileInfo fileInfo) {
|
||||
fileInfo.getTracks().stream()
|
||||
.filter(TrackAttributes::defaultt)
|
||||
.forEach(attr -> fileInfo.getChanges().getDefaultTrack().put(attr, false));
|
||||
}
|
||||
|
||||
private void applyNewDefault(FileInfo fileInfo, TrackAttributes targetDefault) {
|
||||
Map<TrackAttributes, Boolean> changes = fileInfo.getChanges().getDefaultTrack();
|
||||
if (changes.containsKey(targetDefault)) {
|
||||
changes.remove(targetDefault);
|
||||
} else {
|
||||
changes.put(targetDefault, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void findAndApplyForcedTracks(FileInfo fileInfo, boolean overwrite) {
|
||||
Stream<TrackAttributes> forcedTracks = getForcedTracks(fileInfo);
|
||||
|
||||
if (overwrite) {
|
||||
fileInfo.getTracks().stream().filter(TrackAttributes::forced).forEach(attr -> {
|
||||
fileInfo.getChanges().getForcedTrack().put(attr, false);
|
||||
});
|
||||
} else {
|
||||
forcedTracks = forcedTracks.filter(attr -> !attr.forced());
|
||||
}
|
||||
|
||||
forcedTracks.forEach(attr -> {
|
||||
fileInfo.getChanges().getForcedTrack().put(attr, true);
|
||||
});
|
||||
}
|
||||
|
||||
private Stream<TrackAttributes> getForcedTracks(FileInfo fileInfo) {
|
||||
return fileInfo.getTracks().stream()
|
||||
.filter(track -> {
|
||||
if (fileInfo.getChanges().getForcedTrack().containsKey(track)) return fileInfo.getChanges().getForcedTrack().get(track);
|
||||
return matchesForcedKeywords(track) || track.forced();
|
||||
});
|
||||
}
|
||||
|
||||
private boolean matchesForcedKeywords(TrackAttributes track) {
|
||||
return track.trackName() != null && forcedKeywords.stream().anyMatch(keyword -> track.trackName().toLowerCase().contains(keyword.toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
|
||||
public void findAndApplyCommentaryTracks(FileInfo fileInfo) {
|
||||
fileInfo.getTracks().stream()
|
||||
.filter(track -> !track.commentary())
|
||||
.filter(track -> track.trackName() != null)
|
||||
.filter(track -> commentaryKeywords.stream().anyMatch(keyword -> track.trackName().toLowerCase().contains(keyword.toLowerCase(Locale.ROOT))))
|
||||
.forEach(attr -> {
|
||||
fileInfo.getChanges().getCommentaryTrack().put(attr, true);
|
||||
});
|
||||
}
|
||||
|
||||
public void findAndApplyHearingImpairedTracks(FileInfo fileInfo) {
|
||||
fileInfo.getTracks().stream()
|
||||
.filter(track -> !track.hearingImpaired())
|
||||
.filter(track -> track.trackName() != null)
|
||||
.filter(track -> hearingImpairedKeywords.stream().anyMatch(keyword -> track.trackName().toLowerCase().contains(keyword.toLowerCase(Locale.ROOT))))
|
||||
.forEach(attr -> {
|
||||
fileInfo.getChanges().getHearingImpairedTrack().put(attr, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.ArrayList;
|
||||
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
|
||||
public abstract class AttributeUpdater {
|
||||
|
||||
protected final InputConfig config;
|
||||
protected final FileProcessor fileProcessor;
|
||||
protected final AttributeChangeProcessor attributeChangeProcessor;
|
||||
protected final LastExecutionHandler lastExecutionHandler;
|
||||
protected final ResultStatistic statistic = ResultStatistic.getInstance();
|
||||
|
||||
private final ExecutorService executor;
|
||||
|
||||
public AttributeUpdater(InputConfig config, FileProcessor fileProcessor, AttributeChangeProcessor attributeChangeProcessor, LastExecutionHandler lastExecutionHandler) {
|
||||
this.config = config;
|
||||
this.fileProcessor = fileProcessor;
|
||||
this.attributeChangeProcessor = attributeChangeProcessor;
|
||||
this.lastExecutionHandler = lastExecutionHandler;
|
||||
this.executor = Executors.newFixedThreadPool(config.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 = getFiles();
|
||||
|
||||
progressBar.maxHint(files.size());
|
||||
progressBar.refresh();
|
||||
|
||||
files.forEach(file -> executor.submit(() -> {
|
||||
process(file);
|
||||
progressBar.step();
|
||||
}));
|
||||
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(1, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
lastExecutionHandler.persist();
|
||||
|
||||
statistic.stopTimer();
|
||||
statistic.print();
|
||||
}
|
||||
|
||||
protected abstract List<File> getFiles();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected abstract void process(File file);
|
||||
|
||||
/**
|
||||
* Persist file changes.
|
||||
*
|
||||
* @param fileInfo contains information about file and desired configuration.
|
||||
*/
|
||||
protected void checkStatusAndUpdate(FileInfo fileInfo) {
|
||||
if (lastExecutionHandler != null) lastExecutionHandler.update(fileInfo.getFile().getAbsolutePath());
|
||||
if (!fileInfo.getChanges().isEmpty()) {
|
||||
statistic.changePlanned();
|
||||
|
||||
if (config.isSafeMode()) {
|
||||
log.info("Planned changes [{}] for {}", changeLog(fileInfo), fileInfo.getFile().getPath());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("Committing changes [{}] to {}", changeLog(fileInfo), fileInfo.getFile().getPath());
|
||||
fileProcessor.update(fileInfo);
|
||||
statistic.changeSuccessful();
|
||||
} catch (IOException | MkvToolNixException e) {
|
||||
statistic.changeFailed();
|
||||
log.warn("Couldn't commit changes [{}] to {}", changeLog(fileInfo), fileInfo.getFile().getPath(), e);
|
||||
}
|
||||
} else if (fileInfo.getChanges().isEmpty()) {
|
||||
statistic.unchanged();
|
||||
} else {
|
||||
statistic.unknownFailed();
|
||||
}
|
||||
}
|
||||
|
||||
private String changeLog(FileInfo fileInfo) {
|
||||
List<String> changes = new ArrayList<>();
|
||||
if (fileInfo.getMatchedConfig() != null) changes.add("defaults " + fileInfo.getMatchedConfig().toStringShort());
|
||||
if (!fileInfo.getChanges().getForcedTrack().isEmpty()) changes.add("forced tags");
|
||||
if (!fileInfo.getChanges().getCommentaryTrack().isEmpty()) changes.add("commentary tags");
|
||||
if (!fileInfo.getChanges().getHearingImpairedTrack().isEmpty()) changes.add("hearing impaired tags");
|
||||
return String.join(", ", changes);
|
||||
}
|
||||
|
||||
// should this be here?
|
||||
// protected void writeLastExecutionDate() {
|
||||
// if (config.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.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,42 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.Cache;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class CachedFileProcessor implements FileProcessor {
|
||||
private final FileProcessor processor;
|
||||
Cache<String, List<File>> fileCache = new Cache<>();
|
||||
Cache<Pair<String, Integer>, List<File>> directoryCache = new Cache<>();
|
||||
Cache<File, FileInfo> attributeCache = new Cache<>();
|
||||
|
||||
public CachedFileProcessor(FileProcessor processor) {
|
||||
this.processor = processor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<File> loadFiles(String path) {
|
||||
return fileCache.retrieve(path, processor::loadFiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<File> loadDirectory(String path, int depth) {
|
||||
return directoryCache.retrieve(Pair.of(path, depth), key -> processor.loadDirectory(key.getLeft(), key.getRight()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileInfo readAttributes(File file) {
|
||||
return attributeCache.retrieve(file, processor::readAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(FileInfo fileInfo) throws IOException, MkvToolNixException {
|
||||
processor.update(fileInfo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.tongfei.progressbar.ProgressBarBuilder;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
public class CoherentAttributeUpdater extends SingleFileAttributeUpdater {
|
||||
|
||||
public CoherentAttributeUpdater(InputConfig config, FileProcessor processor, AttributeChangeProcessor attributeChangeProcessor, LastExecutionHandler lastExecutionHandler) {
|
||||
super(config, processor, attributeChangeProcessor, lastExecutionHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProgressBarBuilder pbBuilder() {
|
||||
return super.pbBuilder()
|
||||
.setUnit(" directories", 1);
|
||||
}
|
||||
|
||||
protected List<File> getFiles() {
|
||||
return Arrays.stream(config.getLibraryPath())
|
||||
.flatMap(path -> fileProcessor.loadDirectory(path.getPath(), config.getCoherent()).stream())
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(File rootDir) {
|
||||
if (rootDir.isFile()) {
|
||||
super.process(rootDir);
|
||||
return;
|
||||
}
|
||||
|
||||
List<File> files = fileProcessor.loadFiles(rootDir.getPath());
|
||||
Set<FileInfo> matchedFiles = new HashSet<>(files.size() * 2);
|
||||
|
||||
for (AttributeConfig config: config.getAttributeConfig()) {
|
||||
AttributeConfig matchedConfig = findMatch(config, matchedFiles, files);
|
||||
|
||||
if (matchedConfig == null) continue;
|
||||
if (matchedFiles.size() != files.size()) {
|
||||
log.warn("Skip applying changes: Found coherent match, but matched count is different than file count (matched: {}, files: {}, dir: {})",
|
||||
matchedFiles.size(), files.size(), rootDir.getPath());
|
||||
}
|
||||
|
||||
log.info("Found coherent match {} for {}", matchedConfig.toStringShort(), rootDir.getPath());
|
||||
matchedFiles.forEach(fileInfo -> {
|
||||
attributeChangeProcessor.findAndApplyForcedTracks(fileInfo, this.config.isOverwriteForced());
|
||||
attributeChangeProcessor.applyForcedAsDefault(fileInfo);
|
||||
attributeChangeProcessor.findAndApplyCommentaryTracks(fileInfo);
|
||||
attributeChangeProcessor.findAndApplyHearingImpairedTracks(fileInfo);
|
||||
|
||||
checkStatusAndUpdate(fileInfo);
|
||||
});
|
||||
return; // match was found and process must be stopped
|
||||
}
|
||||
|
||||
// Couldn't match any config at current level. Resetting changes and trying one level deeper
|
||||
matchedFiles.forEach(fileInfo -> {
|
||||
fileInfo.resetChanges();
|
||||
fileInfo.setMatchedConfig(null);
|
||||
});
|
||||
|
||||
if (config.isForceCoherent()) {
|
||||
log.info("No coherent match found, skipping {}", rootDir.getPath());
|
||||
statistic.increaseUnchangedBy(files.size());
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("No coherent match found, attempting to find coherent match in child directories of {}", rootDir.getPath());
|
||||
for (File dir: fileProcessor.loadDirectory(rootDir.getPath(), 1)) this.process(dir);
|
||||
}
|
||||
|
||||
private AttributeConfig findMatch(AttributeConfig config, Set<FileInfo> matchedFiles, List<File> files) {
|
||||
AttributeConfig matchedConfig = null;
|
||||
matchedFiles.clear();
|
||||
|
||||
for (File file: files) {
|
||||
FileInfo fileInfo = fileProcessor.readAttributes(file);
|
||||
fileInfo.resetChanges();
|
||||
fileInfo.setMatchedConfig(null);
|
||||
|
||||
if (fileInfo.getTracks().isEmpty()) {
|
||||
log.warn("No attributes found for {}", file);
|
||||
statistic.unknownFailed();
|
||||
break;
|
||||
}
|
||||
|
||||
attributeChangeProcessor.findAndApplyDefaultMatch(fileInfo, config);
|
||||
|
||||
if (matchedConfig == null) matchedConfig = fileInfo.getMatchedConfig();
|
||||
if (matchedConfig == null || matchedConfig != fileInfo.getMatchedConfig()) {
|
||||
matchedConfig = null;
|
||||
break;
|
||||
}
|
||||
matchedFiles.add(fileInfo);
|
||||
}
|
||||
|
||||
return matchedConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
public interface FileProcessor {
|
||||
|
||||
/**
|
||||
* @param path leads to one file directly or a directory which will be loaded recursively
|
||||
* @return list of all files within the directory
|
||||
*/
|
||||
List<File> loadFiles(String path);
|
||||
|
||||
/**
|
||||
* Load only directories and files at depth, ignoring everything between root dir and dir at depth.
|
||||
* E.g. with file structure /base/depth1/depth2/depth3.file
|
||||
* - with depth 1: return /base/depth1/
|
||||
* - with depth 2: returns /base/depth1/depth2/
|
||||
*
|
||||
* @param path directory which will be loaded recursively until depth
|
||||
* @param depth limit directory crawling
|
||||
* @return list of directory at depth
|
||||
*/
|
||||
List<File> loadDirectory(String path, int depth);
|
||||
|
||||
/**
|
||||
* Load track information from file.
|
||||
*
|
||||
* @param file Takes the file from which the attributes will be returned
|
||||
* @return list of all important attributes
|
||||
*/
|
||||
FileInfo readAttributes(File file);
|
||||
|
||||
/**
|
||||
* Update the file.
|
||||
*
|
||||
* @param fileInfo information used to update file
|
||||
* @throws IOException when error occurs accessing file retrieving information
|
||||
* @throws MkvToolNixException when error occurs while sending query to mkvpropedit
|
||||
*/
|
||||
void update(FileInfo fileInfo) throws IOException, MkvToolNixException;
|
||||
|
||||
default InputStream run(String[] command) throws IOException {
|
||||
return Runtime.getRuntime().exec(command).getInputStream();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.exceptions.MkvToolNixException;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.FileFilter;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.FileUtils.getPathFor;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class MkvFileProcessor implements FileProcessor {
|
||||
protected final File mkvToolNixInstallation;
|
||||
protected final FileFilter fileFilter;
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private static final Set<String> fileExtensions = new HashSet<>(Set.of(".mkv", ".mka", ".mks", ".mk3d"));
|
||||
|
||||
private static final String DEFAULT_TRACK = "--edit track:%s --set flag-default=%s";
|
||||
private static final String FORCED_TRACK = "--edit track:%s --set flag-forced=%s";
|
||||
private static final String COMMENTARY_TRACK = "--edit track:%s --set flag-commentary=%s";
|
||||
private static final String HEARING_IMPAIRED_TRACK = "--edit track:%s --set flag-hearing-impaired=%s";
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<File> loadFiles(String path) {
|
||||
try (Stream<Path> paths = Files.walk(Paths.get(path))) {
|
||||
return paths
|
||||
.filter(Files::isRegularFile)
|
||||
.map(Path::toFile)
|
||||
.filter(file -> fileFilter.accept(file, fileExtensions))
|
||||
.collect(Collectors.toList());
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't find file or directory!", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
// does this load /arst/arst & /arst ?
|
||||
public List<File> loadDirectory(String path, int depth) {
|
||||
File rootDir = Path.of(path).toFile();
|
||||
if (!rootDir.exists()) {
|
||||
log.error("Couldn't find file or directory!");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<File> result = new ArrayList<>();
|
||||
exploreDirectory(rootDir, 0, depth, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively explores directories to find items at the target depth.
|
||||
*
|
||||
* @param currentDir The current directory being explored
|
||||
* @param currentDepth The current depth level
|
||||
* @param targetDepth The target depth to collect files
|
||||
* @param result The collection to store found files
|
||||
*/
|
||||
private static void exploreDirectory(File currentDir, int currentDepth, int targetDepth, List<File> result) {
|
||||
if (currentDepth == targetDepth) {
|
||||
result.add(currentDir);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all files and directories in the current directory
|
||||
File[] files = currentDir.listFiles();
|
||||
if (files == null) return;
|
||||
|
||||
// Recursively explore subdirectories
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
exploreDirectory(file, currentDepth + 1, targetDepth, result);
|
||||
} else if (currentDepth + 1 == targetDepth) {
|
||||
// If files at the next level would be at the target depth, include them
|
||||
result.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public FileInfo readAttributes(File file) {
|
||||
FileInfo fileInfo = new FileInfo(file);
|
||||
try {
|
||||
String[] command = new String[]{
|
||||
getPathFor(mkvToolNixInstallation, MkvToolNix.MKV_MERGE).getAbsolutePath(),
|
||||
"--identify",
|
||||
"--identification-format",
|
||||
"json",
|
||||
file.getAbsolutePath()
|
||||
};
|
||||
|
||||
log.debug("Executing: {}", String.join(" ", command));
|
||||
InputStream inputStream = run(command);
|
||||
Map<String, Object> jsonMap = mapper.readValue(inputStream, Map.class);
|
||||
List<Map<String, Object>> tracks = (List<Map<String, Object>>) jsonMap.get("tracks");
|
||||
if (tracks != null) {
|
||||
for (Map<String, Object> attribute : tracks) {
|
||||
if (!"video".equals(attribute.get("type"))) {
|
||||
Map<String, Object> properties = (Map<String, Object>) attribute.get("properties");
|
||||
// mkvpropedit takes in the n-th track, based on the order of mkvmerge --idenfity
|
||||
fileInfo.addTrack(new TrackAttributes(
|
||||
(int) properties.get("number"),
|
||||
(String) properties.get("language"),
|
||||
(String) properties.get("track_name"),
|
||||
(Boolean) properties.getOrDefault("default_track", false),
|
||||
(Boolean) properties.getOrDefault("forced_track", false),
|
||||
(Boolean) properties.getOrDefault("commentary_track", false),
|
||||
(Boolean) properties.getOrDefault("flag_hearing_impaired", false),
|
||||
TrackType.valueOf(((String) attribute.get("type")).toUpperCase(Locale.ENGLISH))));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("Couldn't retrieve information of {}", file.getAbsolutePath());
|
||||
}
|
||||
|
||||
log.debug("File attributes of '{}': {}", file.getAbsolutePath(), fileInfo.getTracks());
|
||||
} catch (IOException e) {
|
||||
log.error("File could not be found or loaded: ", e);
|
||||
System.out.println("File could not be found or loaded: " + file.getAbsolutePath());
|
||||
}
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void update(FileInfo fileInfo) throws IOException, MkvToolNixException {
|
||||
String[] command = getUpdateCommand(fileInfo);
|
||||
log.debug("Executing '{}'", String.join(" ", command));
|
||||
InputStream inputstream = run(command);
|
||||
String output = IOUtils.toString(new InputStreamReader(inputstream));
|
||||
log.debug("Result: {}", output.replaceAll("\\n", " '"));
|
||||
if (output.contains("Error")) throw new MkvToolNixException(output);
|
||||
}
|
||||
|
||||
private String[] getUpdateCommand(FileInfo fileInfo) {
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add(getPathFor(mkvToolNixInstallation, MkvToolNix.MKV_PROP_EDIT).getAbsolutePath());
|
||||
command.add(String.format(fileInfo.getFile().getAbsolutePath()));
|
||||
|
||||
PlannedChange changes = fileInfo.getChanges();
|
||||
changes.getDefaultTrack().forEach((key, value) -> command.addAll(format(DEFAULT_TRACK, key.id(), value ? 1 : 0)));
|
||||
changes.getForcedTrack().forEach((key, value) -> command.addAll(format(FORCED_TRACK, key.id(), value ? 1 : 0)));
|
||||
changes.getCommentaryTrack().forEach((key, value) -> command.addAll(format(COMMENTARY_TRACK, key.id(), value ? 1 : 0)));
|
||||
changes.getHearingImpairedTrack().forEach((key, value) -> command.addAll(format(HEARING_IMPAIRED_TRACK, key.id(), value ? 1 : 0)));
|
||||
return command.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private List<String> format(String format, Object... args) {
|
||||
return Arrays.asList(String.format(format, args).split(" "));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.tongfei.progressbar.ProgressBarBuilder;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class SingleFileAttributeUpdater extends AttributeUpdater {
|
||||
|
||||
public SingleFileAttributeUpdater(InputConfig config, FileProcessor processor, AttributeChangeProcessor attributeChangeProcessor, LastExecutionHandler lastExecutionHandler) {
|
||||
super(config, processor, attributeChangeProcessor, lastExecutionHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProgressBarBuilder pbBuilder() {
|
||||
return super.pbBuilder()
|
||||
.setUnit(" files", 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<File> getFiles() {
|
||||
return Arrays.stream(config.getLibraryPath())
|
||||
.flatMap(path -> fileProcessor.loadFiles(path.getPath()).stream())
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(File file) {
|
||||
FileInfo fileInfo = fileProcessor.readAttributes(file);
|
||||
|
||||
if (fileInfo.getTracks().isEmpty()) {
|
||||
log.warn("No attributes found for {}", file);
|
||||
statistic.unknownFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
attributeChangeProcessor.findAndApplyDefaultMatch(fileInfo, config.getAttributeConfig());
|
||||
attributeChangeProcessor.findAndApplyForcedTracks(fileInfo, config.isOverwriteForced());
|
||||
attributeChangeProcessor.applyForcedAsDefault(fileInfo);
|
||||
attributeChangeProcessor.findAndApplyCommentaryTracks(fileInfo);
|
||||
attributeChangeProcessor.findAndApplyHearingImpairedTracks(fileInfo);
|
||||
|
||||
checkStatusAndUpdate(fileInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Documented
|
||||
@Constraint(validatedBy = ValidFileValidator.class)
|
||||
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ValidFile {
|
||||
String message() default "File does not exist";
|
||||
Class<?>[] groups() default {};
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation;
|
||||
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Slf4j
|
||||
public class ValidFileValidator implements ConstraintValidator<ValidFile, File[]> {
|
||||
@Override
|
||||
public void initialize(ValidFile constraintAnnotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(File[] files, ConstraintValidatorContext context) {
|
||||
if (files == null || files.length == 0) return false;
|
||||
for (File file: files) {
|
||||
if (!file.exists()) {
|
||||
log.error("{} does not exist", file.getPath());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Documented
|
||||
@Constraint(validatedBy = ValidMkvToolNixValidator.class)
|
||||
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ValidMkvToolNix {
|
||||
String message() default "MkvToolNix does not exist";
|
||||
Class<?>[] groups() default {};
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.MkvToolNix;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.FileUtils.getPathFor;
|
||||
|
||||
public class ValidMkvToolNixValidator implements ConstraintValidator<ValidMkvToolNix, File> {
|
||||
@Override
|
||||
public void initialize(ValidMkvToolNix constraintAnnotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(File file, ConstraintValidatorContext context) {
|
||||
return file != null && file.exists()
|
||||
&& getPathFor(file, MkvToolNix.MKV_MERGE).exists()
|
||||
&& getPathFor(file, MkvToolNix.MKV_PROP_EDIT).exists();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.CommandRunner;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.ValidationUtil;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.Validator;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class ValidationExecutionStrategy implements CommandLine.IExecutionStrategy {
|
||||
|
||||
public int execute(CommandLine.ParseResult parseResult) {
|
||||
if (!parseResult.isVersionHelpRequested() && !parseResult.isUsageHelpRequested()) validate(parseResult.commandSpec());
|
||||
return new CommandLine.RunLast().execute(parseResult);
|
||||
}
|
||||
|
||||
private static void validate(CommandLine.Model.CommandSpec spec) {
|
||||
Validator validator = ValidationUtil.getValidator();
|
||||
Set<ConstraintViolation<InputConfig>> violations = validator.validate(((CommandRunner)spec.userObject()).getConfig());
|
||||
|
||||
if (!violations.isEmpty()) {
|
||||
StringBuilder errors = new StringBuilder();
|
||||
for (ConstraintViolation<InputConfig> violation : violations) {
|
||||
errors.append(violation.getPropertyPath()).append(" ").append(violation.getMessage()).append("\n");
|
||||
}
|
||||
throw new CommandLine.ParameterException(spec.commandLine(), errors.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,31 +10,35 @@ import java.util.Objects;
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class AttributeConfig {
|
||||
private final String audioLanguage;
|
||||
private final String subtitleLanguage;
|
||||
private final String audioLang;
|
||||
private final String subLang;
|
||||
|
||||
public static AttributeConfig of(String audioLanguage, String subtitleLanguage) {
|
||||
return new AttributeConfig(audioLanguage, subtitleLanguage);
|
||||
}
|
||||
|
||||
@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);
|
||||
return Objects.equals(audioLang, that.audioLang) && Objects.equals(subLang, that.subLang);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(audioLanguage, subtitleLanguage);
|
||||
return Objects.hash(audioLang, subLang);
|
||||
}
|
||||
|
||||
public String toStringShort() {
|
||||
return audioLanguage + ":" + subtitleLanguage;
|
||||
return audioLang + ":" + subLang;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AttributeConfig{"
|
||||
+ "audioLanguage='" + audioLanguage + '\''
|
||||
+ ", subtitleLanguage='" + subtitleLanguage + '\'' +
|
||||
+ "audioLanguage='" + audioLang + '\''
|
||||
+ ", subtitleLanguage='" + subLang + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
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 {
|
||||
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 abrv() {
|
||||
return shortParameter;
|
||||
}
|
||||
|
||||
public int args() {
|
||||
return args;
|
||||
}
|
||||
}
|
||||
@@ -1,84 +1,56 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@RequiredArgsConstructor
|
||||
public class FileInfo {
|
||||
private final File file;
|
||||
|
||||
private Set<FileAttribute> existingDefaultAudioLanes = new HashSet<>();
|
||||
private Set<FileAttribute> existingForcedAudioLanes = new HashSet<>();
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final List<TrackAttributes> tracks = new ArrayList<>();
|
||||
|
||||
private Set<FileAttribute> existingDefaultSubtitleLanes = new HashSet<>();
|
||||
private Set<FileAttribute> existingForcedSubtitleLanes = new HashSet<>();
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final List<TrackAttributes> audioTracks = new ArrayList<>();
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final List<TrackAttributes> subtitleTracks = new ArrayList<>();
|
||||
|
||||
private Set<FileAttribute> desiredForcedSubtitleLanes = new HashSet<>();
|
||||
private FileAttribute desiredDefaultAudioLane;
|
||||
private FileAttribute desiredDefaultSubtitleLane;
|
||||
private PlannedChange changes = new PlannedChange();
|
||||
@Setter
|
||||
private AttributeConfig matchedConfig;
|
||||
|
||||
public boolean isAudioDifferent() {
|
||||
return isMatchDifferent(existingDefaultAudioLanes, desiredDefaultAudioLane)
|
||||
|| isLaneOff(existingDefaultAudioLanes, desiredDefaultAudioLane, AttributeConfig::getAudioLanguage);
|
||||
public void addTrack(TrackAttributes track) {
|
||||
tracks.add(track);
|
||||
if (TrackType.AUDIO.equals(track.type())) audioTracks.add(track);
|
||||
else if (TrackType.SUBTITLES.equals(track.type())) subtitleTracks.add(track);
|
||||
}
|
||||
|
||||
public boolean isSubtitleDifferent() {
|
||||
return isMatchDifferent(existingDefaultSubtitleLanes, desiredDefaultSubtitleLane)
|
||||
|| isLaneOff(existingDefaultSubtitleLanes, desiredDefaultSubtitleLane, AttributeConfig::getSubtitleLanguage);
|
||||
public void addTracks(Collection<TrackAttributes> tracks) {
|
||||
for (TrackAttributes track : tracks) addTrack(track);
|
||||
}
|
||||
|
||||
private boolean isMatchDifferent(Set<FileAttribute> existingDefault, FileAttribute desiredDefault) {
|
||||
return desiredDefault != null &&
|
||||
(existingDefault == null || !existingDefault.contains(desiredDefault) || existingDefault.size() > 1);
|
||||
public List<TrackAttributes> getTracks() {
|
||||
return Collections.unmodifiableList(tracks);
|
||||
}
|
||||
|
||||
private boolean isLaneOff(Set<FileAttribute> existingDefault, FileAttribute desiredDefault, Function<AttributeConfig, String> inputLane) {
|
||||
return desiredDefault == null
|
||||
&& (matchedConfig != null && "OFF".equals(inputLane.apply(matchedConfig)))
|
||||
&& (existingDefault != null && !existingDefault.isEmpty());
|
||||
public List<TrackAttributes> getAudioTracks() {
|
||||
return Collections.unmodifiableList(audioTracks);
|
||||
}
|
||||
|
||||
public boolean areForcedTracksDifferent() {
|
||||
return !desiredForcedSubtitleLanes.isEmpty() && !existingForcedSubtitleLanes.containsAll(desiredForcedSubtitleLanes);
|
||||
public List<TrackAttributes> getSubtitleTracks() {
|
||||
return Collections.unmodifiableList(subtitleTracks);
|
||||
}
|
||||
|
||||
public FileStatus getStatus() {
|
||||
if (isChangeNecessary()) return FileStatus.CHANGE_NECESSARY;
|
||||
if (isUnableToApplyConfig()) return FileStatus.NO_SUITABLE_CONFIG;
|
||||
if (isAlreadySuited()) return FileStatus.ALREADY_SUITED;
|
||||
return FileStatus.UNKNOWN;
|
||||
}
|
||||
|
||||
private boolean isUnableToApplyConfig() {
|
||||
return desiredDefaultAudioLane == null && !"OFF".equals(matchedConfig.getAudioLanguage())
|
||||
&& desiredDefaultSubtitleLane == null && !"OFF".equals(matchedConfig.getSubtitleLanguage());
|
||||
}
|
||||
|
||||
private boolean isAlreadySuited() {
|
||||
return (desiredDefaultAudioLane == null || existingDefaultAudioLanes.contains(desiredDefaultAudioLane))
|
||||
&& (desiredDefaultSubtitleLane == null || existingDefaultSubtitleLanes.contains(desiredDefaultSubtitleLane));
|
||||
}
|
||||
|
||||
private boolean isChangeNecessary() {
|
||||
return isAudioDifferent() || isSubtitleDifferent() || areForcedTracksDifferent() || !existingForcedAudioLanes.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + "defaultAudioLanes=" + existingDefaultAudioLanes +
|
||||
", defaultSubtitleLanes=" + existingDefaultSubtitleLanes +
|
||||
", desiredForcedSubtitleLanes=" + desiredForcedSubtitleLanes +
|
||||
", desiredAudioLane=" + desiredDefaultAudioLane +
|
||||
", desiredSubtitleLane=" + desiredDefaultSubtitleLane +
|
||||
']';
|
||||
public void resetChanges() {
|
||||
changes = new PlannedChange();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
public enum FileStatus {
|
||||
CHANGE_NECESSARY,
|
||||
NO_SUITABLE_CONFIG,
|
||||
ALREADY_SUITED,
|
||||
UNKNOWN;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.converter.AttributeConfigConverter;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation.ValidFile;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation.ValidMkvToolNix;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import lombok.Getter;
|
||||
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.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import picocli.CommandLine.Option;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@CommandLine.Command
|
||||
public class InputConfig implements CommandLine.IVersionProvider {
|
||||
|
||||
private File configPath;
|
||||
|
||||
@CommandLine.Spec
|
||||
CommandLine.Model.CommandSpec spec;
|
||||
|
||||
@ValidFile(message = "does not exist")
|
||||
@CommandLine.Parameters(description = "paths to library", arity = "1..*")
|
||||
private File[] libraryPath;
|
||||
|
||||
@Option(names = {"-a", "--attribute-config"}, arity = "1..*", converter = AttributeConfigConverter.class,
|
||||
description = "List of audio:subtitle pairs for matching defaults in order (e.g. jpn:eng jpn:ger)")
|
||||
private AttributeConfig[] attributeConfig = new AttributeConfig[0];
|
||||
@ValidMkvToolNix(message = "does not exist")
|
||||
@Option(names = {"-m", "--mkvtoolnix"}, defaultValue = "${DEFAULT_MKV_TOOL_NIX}", description = "path to mkvtoolnix installation")
|
||||
private File mkvToolNix;
|
||||
|
||||
@Option(names = {"-s", "--safemode"}, description = "test run (no files will be changes)")
|
||||
private boolean safeMode;
|
||||
|
||||
@Min(1)
|
||||
@Option(names = {"-t", "--threads"}, defaultValue = "2", showDefaultValue = CommandLine.Help.Visibility.ALWAYS, description = "thread count")
|
||||
private int threads;
|
||||
|
||||
@Min(0)
|
||||
@Option(names = {"-c", "--coherent"}, description = "try to match all files in dir of depth with the same attribute config. Attempting increasing deeper levels until match is found (worst case applying config on single file basis)")
|
||||
private Integer coherent;
|
||||
@Option(names = {"-cf", "--force-coherent"}, description = "only applies changes if a coherent match was found for the specifically entered depth")
|
||||
private boolean forceCoherent;
|
||||
|
||||
// TODO: implement usage
|
||||
@Option(names = {"-n", "--only-new-files"}, description = "ignores all files unchanged and previously processed")
|
||||
private boolean onlyNewFiles;
|
||||
@Option(names = {"-d", "--filter-date"}, defaultValue = Option.NULL_VALUE, description = "only consider files created newer than entered date (following ISO-8601 yyyy-MM-ddTHH:mm:ss.sssZ)")
|
||||
private Instant filterDate;
|
||||
@Option(names = {"-i", "--include-pattern"}, defaultValue = ".*", description = "include files matching pattern")
|
||||
private Pattern includePattern;
|
||||
@Option(names = {"-e", "--exclude"}, arity = "1..*",
|
||||
description = "relative directories and files to be excluded (no wildcard)")
|
||||
private Set<String> excluded = new HashSet<>();
|
||||
|
||||
|
||||
@Option(names = {"-o", "-overwrite-forced"}, description = "remove all forced flags")
|
||||
private boolean overwriteForced;
|
||||
@Option(names = {"--forced-keywords"}, arity = "1..*", defaultValue = "forced, signs, songs", showDefaultValue = CommandLine.Help.Visibility.ALWAYS,
|
||||
split = ", ", description = "Keywords to identify forced tracks (Defaults will be overwritten)")
|
||||
private Set<String> forcedKeywords;
|
||||
@Option(names = {"--commentary-keywords"}, arity = "1..*", defaultValue = "comment, commentary, director", showDefaultValue = CommandLine.Help.Visibility.ALWAYS,
|
||||
split = ", ", description = "Keywords to identify commentary tracks (Defaults will be overwritten)")
|
||||
private Set<String> commentaryKeywords;
|
||||
@Option(names = {"--hearing-impaired"}, arity = "1..*", defaultValue = "SDH, CC", showDefaultValue = CommandLine.Help.Visibility.ALWAYS,
|
||||
split = ", ", description = "Keywords to identify hearing impaired tracks (Defaults will be overwritten")
|
||||
private Set<String> hearingImpaired;
|
||||
@Option(names = {"--preferred-subtitles"}, arity = "1..*", defaultValue = "unstyled", showDefaultValue = CommandLine.Help.Visibility.ALWAYS,
|
||||
split = ", ", description = "Keywords to prefer specific subtitle tracks (Defaults will be overwritten)")
|
||||
private Set<String> preferredSubtitles;
|
||||
@Option(names = {"--debug"}, description = "Enable debug logging")
|
||||
private boolean debug;
|
||||
|
||||
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/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", InputConfig.class.getSimpleName() + "[", "]")
|
||||
.add("configPath=" + configPath)
|
||||
.add("spec=" + spec)
|
||||
.add("libraryPath=" + libraryPath)
|
||||
.add("attributeConfig=" + Arrays.toString(attributeConfig))
|
||||
.add("mkvToolNix=" + mkvToolNix)
|
||||
.add("safeMode=" + safeMode)
|
||||
.add("threads=" + threads)
|
||||
.add("coherent=" + coherent)
|
||||
.add("forceCoherent=" + forceCoherent)
|
||||
.add("filterDate=" + filterDate)
|
||||
.add("includePattern=" + includePattern)
|
||||
.add("excluded=" + excluded)
|
||||
.add("overwriteForced=" + overwriteForced)
|
||||
.add("forcedKeywords=" + forcedKeywords)
|
||||
.add("commentaryKeywords=" + commentaryKeywords)
|
||||
.add("hearingImpaired=" + hearingImpaired)
|
||||
.add("preferredSubtitles=" + preferredSubtitles)
|
||||
.add("debug=" + debug)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getVersion() throws Exception {
|
||||
return new String[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
public class PlannedChange {
|
||||
private final Map<TrackAttributes, Boolean> defaultTrack = new HashMap<>();
|
||||
private final Map<TrackAttributes, Boolean> forcedTrack = new HashMap<>();
|
||||
private final Map<TrackAttributes, Boolean> commentaryTrack = new HashMap<>();
|
||||
private final Map<TrackAttributes, Boolean> hearingImpairedTrack = new HashMap<>();
|
||||
|
||||
public boolean isEmpty() {
|
||||
return defaultTrack.isEmpty() && forcedTrack.isEmpty() && commentaryTrack.isEmpty() && hearingImpairedTrack.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -1,82 +1,65 @@
|
||||
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: %s";
|
||||
private static final String PRINT_TEMPLATE = "Total: %s, Changing: %s (Successful: %s, Failed %s), Unchanged: %s, Excluded: %s, Unknown/Failed: %s\nRuntime: %s";
|
||||
private static ResultStatistic instance;
|
||||
private int filesTotal = 0;
|
||||
|
||||
private int changePlanned = 0;
|
||||
private int changeFailed = 0;
|
||||
private int changeSuccessful = 0;
|
||||
private int unchanged = 0;
|
||||
private int excluded = 0;
|
||||
private int unknownFailed = 0;
|
||||
|
||||
private int shouldChange = 0;
|
||||
private int failedChanging = 0;
|
||||
private int successfullyChanged = 0;
|
||||
|
||||
private int noSuitableConfigFound = 0;
|
||||
private int alreadyFits = 0;
|
||||
private int failed = 0;
|
||||
|
||||
@Getter(AccessLevel.NONE)
|
||||
private long startTime = 0;
|
||||
private long runtime = 0;
|
||||
|
||||
public static ResultStatistic getInstance() {
|
||||
if (instance == null) {
|
||||
return getInstance(false);
|
||||
}
|
||||
|
||||
public static ResultStatistic getInstance(boolean reset) {
|
||||
if (instance == null || reset) {
|
||||
instance = new ResultStatistic();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void increaseTotalBy(int amount) {
|
||||
filesTotal += amount;
|
||||
public int total() {
|
||||
return changePlanned + unchanged + excluded + unknownFailed;
|
||||
}
|
||||
|
||||
public synchronized void total() {
|
||||
filesTotal++;
|
||||
public synchronized void changePlanned() {
|
||||
changePlanned++;
|
||||
}
|
||||
|
||||
public void increaseExcludedBy(int amount) {
|
||||
excluded += amount;
|
||||
public synchronized void changeSuccessful() {
|
||||
changeSuccessful++;
|
||||
}
|
||||
|
||||
public synchronized void changeFailed() {
|
||||
changeFailed++;
|
||||
}
|
||||
|
||||
public synchronized void unchanged() {
|
||||
unchanged++;
|
||||
}
|
||||
|
||||
public synchronized void increaseUnchangedBy(int amount) {
|
||||
unchanged += amount;
|
||||
}
|
||||
|
||||
public synchronized void excluded() {
|
||||
excluded++;
|
||||
}
|
||||
|
||||
public synchronized void shouldChange() {
|
||||
shouldChange++;
|
||||
}
|
||||
|
||||
public synchronized void success() {
|
||||
successfullyChanged++;
|
||||
}
|
||||
|
||||
public synchronized void failedChanging() {
|
||||
failedChanging++;
|
||||
}
|
||||
|
||||
public synchronized void noSuitableConfigFound() {
|
||||
noSuitableConfigFound++;
|
||||
}
|
||||
|
||||
public synchronized void alreadyFits() {
|
||||
alreadyFits++;
|
||||
}
|
||||
|
||||
public synchronized void failure() {
|
||||
failed++;
|
||||
public synchronized void unknownFailed() {
|
||||
unknownFailed++;
|
||||
}
|
||||
|
||||
public void startTimer() {
|
||||
@@ -87,11 +70,6 @@ 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;
|
||||
@@ -109,21 +87,14 @@ public class ResultStatistic {
|
||||
}
|
||||
}
|
||||
|
||||
public String prettyPrint() {
|
||||
return String.format(result, filesTotal, excluded, shouldChange, failedChanging, successfullyChanged,
|
||||
noSuitableConfigFound, alreadyFits, failed, formatTimer());
|
||||
public void print() {
|
||||
String result = this.toString();
|
||||
System.out.println(result);
|
||||
log.info(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResultStatistic: " + "filesTotal=" + filesTotal +
|
||||
", excluded=" + excluded +
|
||||
", shouldChange=" + shouldChange +
|
||||
" (failedChanging=" + failedChanging +
|
||||
", successfullyChanged=" + successfullyChanged +
|
||||
"), noSuitableConfigFound=" + noSuitableConfigFound +
|
||||
", alreadyFits=" + alreadyFits +
|
||||
", failed=" + failed +
|
||||
", runtime=" + formatTimer();
|
||||
return String.format(PRINT_TEMPLATE, total(), changePlanned, changeSuccessful, changeFailed, unchanged, excluded, unknownFailed, formatTimer());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,19 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
public record FileAttribute(int id, String language, String trackName, boolean defaultTrack, boolean forcedTrack,
|
||||
LaneType type) {
|
||||
public record TrackAttributes(int id, String language, String trackName,
|
||||
boolean defaultt, boolean forced, boolean commentary, boolean hearingImpaired,
|
||||
TrackType type) {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
FileAttribute attribute = (FileAttribute) o;
|
||||
TrackAttributes attribute = (TrackAttributes) o;
|
||||
return id == attribute.id
|
||||
&& defaultTrack == attribute.defaultTrack
|
||||
&& forcedTrack == attribute.forcedTrack
|
||||
&& defaultt == attribute.defaultt
|
||||
&& forced == attribute.forced
|
||||
&& commentary == attribute.commentary
|
||||
&& hearingImpaired == attribute.hearingImpaired
|
||||
&& Objects.equals(language, attribute.language)
|
||||
&& Objects.equals(trackName, attribute.trackName)
|
||||
&& type == attribute.type;
|
||||
@@ -25,8 +28,10 @@ public record FileAttribute(int id, String language, String trackName, boolean d
|
||||
return "[" + "id=" + id +
|
||||
", language='" + language + '\'' +
|
||||
", trackName='" + trackName + '\'' +
|
||||
", defaultTrack=" + defaultTrack +
|
||||
", forcedTrack=" + forcedTrack +
|
||||
", defaultt=" + defaultt +
|
||||
", forced=" + forced +
|
||||
", commentary=" + commentary +
|
||||
", hearingImpaired=" + hearingImpaired +
|
||||
", type=" + type +
|
||||
']';
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||
|
||||
public enum LaneType {
|
||||
public enum TrackType {
|
||||
AUDIO,
|
||||
SUBTITLES;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
||||
import org.apache.commons.cli.Option;
|
||||
|
||||
public class CommandLineOptionsUtil {
|
||||
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) {
|
||||
return optionOf(property, opt, hasArg ? 1 : 0, required);
|
||||
}
|
||||
|
||||
public static Option optionOf(ConfigProperty property, String opt, int args, boolean required) {
|
||||
Option option = new Option(opt, property.desc());
|
||||
option.setArgs(args);
|
||||
option.setLongOpt(property.prop());
|
||||
option.setRequired(required);
|
||||
return option;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class DateUtils {
|
||||
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy-HH:mm:ss");
|
||||
|
||||
public static Date convert(long millis) {
|
||||
return new Date(millis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert String to date.
|
||||
* @return parsed date, defaultDate if exception occurs
|
||||
*/
|
||||
public static Date convert(String date, Date defaultDate) {
|
||||
try {
|
||||
return dateFormat.parse(date);
|
||||
} catch (ParseException e) {
|
||||
return defaultDate;
|
||||
}
|
||||
}
|
||||
|
||||
public static String convert(Date date) {
|
||||
return dateFormat.format(date);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.MkvToolNix;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FileUtils {
|
||||
private static final boolean isWindows = SystemUtils.IS_OS_WINDOWS;
|
||||
|
||||
private static String expandPath(File dir, MkvToolNix application) {
|
||||
return dir.getAbsolutePath().endsWith("/")
|
||||
? dir.getAbsolutePath() + application
|
||||
: dir.getAbsolutePath() + "/" + application;
|
||||
}
|
||||
|
||||
public static File getPathFor(File dir, MkvToolNix application) {
|
||||
return Path.of(expandPath(dir, application) + (isWindows ? ".exe" : "")).toFile();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.MkvToolNix;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Properties;
|
||||
|
||||
public class ProjectUtil implements CommandLine.IVersionProvider {
|
||||
@@ -17,11 +21,32 @@ public class ProjectUtil implements CommandLine.IVersionProvider {
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getVersion() {
|
||||
return new String[] {getProjectName() + " " + PROJECT_PROPERTIES.getProperty("version")};
|
||||
}
|
||||
|
||||
public static String getProjectName() {
|
||||
return PROJECT_PROPERTIES.getProperty("project_name");
|
||||
}
|
||||
|
||||
public String[] getVersion() throws IOException {
|
||||
String mkvpropeeditVersion = getVersion(MkvToolNix.MKV_PROP_EDIT);
|
||||
String mkvmergeVersion = getVersion(MkvToolNix.MKV_MERGE);
|
||||
|
||||
return new String[] {
|
||||
getProjectName() + " " + PROJECT_PROPERTIES.getProperty("version"),
|
||||
"Java ${java.version} (${java.vendor} ${java.vm.name} ${java.vm.version})",
|
||||
"${os.name} ${os.version} ${os.arch}",
|
||||
(!Strings.isBlank(mkvpropeeditVersion) ? mkvpropeeditVersion : "MkvPropEdit not found") + ", " + (!Strings.isBlank(mkvmergeVersion) ? mkvmergeVersion : "MkvMerge not found")
|
||||
};
|
||||
}
|
||||
|
||||
public static String getVersion(MkvToolNix app) throws IOException {
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(app.toString(), "--version");
|
||||
Process process = processBuilder.start();
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||
String version = reader.readLine();
|
||||
int exitCode = process.waitFor();
|
||||
|
||||
if (exitCode == 0) return version;
|
||||
} catch (IOException | InterruptedException ignored) {}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import jakarta.validation.ValidatorFactory;
|
||||
import lombok.Getter;
|
||||
|
||||
public class ValidationUtil {
|
||||
private static final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
|
||||
@Getter
|
||||
private static final Validator validator = factory.getValidator();
|
||||
}
|
||||
@@ -6,7 +6,7 @@ Configuration:
|
||||
fileName: ${sys:user.home}/.local/mkvaudiosubtitlechanger/logs/application.log
|
||||
filePattern: ${sys:user.home}/.local/mkvaudiosubtitlechanger/logs/archive/application-%d{yyyy-MM-dd}-%i.log.gz
|
||||
PatternLayout:
|
||||
Pattern: "%d{DEFAULT} | %-5level | %thread | %C{1} | %msg %n %throwable"
|
||||
Pattern: "%d{DEFAULT} | %-5level | %msg %n %throwable"
|
||||
ThresholdFilter:
|
||||
level: debug
|
||||
Policies:
|
||||
|
||||
@@ -3,10 +3,10 @@ Configuration:
|
||||
Appenders:
|
||||
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.gz
|
||||
fileName: ${sys:user.home}/AppData/Local/MKVAudioSubtitleChanger/logs/application.log
|
||||
filePattern: ${sys:user.home}/AppData/Local/MKVAudioSubtitleChanger/logs/archive/application-%d{yyyy-MM-dd}-%i.log.gz
|
||||
PatternLayout:
|
||||
Pattern: "%d{DEFAULT} | %-5level | %thread | %C{1} | %msg %n %throwable"
|
||||
Pattern: "%d{DEFAULT} | %-5level | %msg %n %throwable"
|
||||
ThresholdFilter:
|
||||
level: debug
|
||||
Policies:
|
||||
|
||||
@@ -6,7 +6,7 @@ Configuration:
|
||||
fileName: logs/application.log
|
||||
filePattern: logs/archive/application-%d{yyyy-MM-dd}-%i.log.gz
|
||||
PatternLayout:
|
||||
Pattern: "%d{DEFAULT} | %-5level | %thread | %C{1} | %msg %n %throwable"
|
||||
Pattern: "%d{DEFAULT} | %-5level | %msg %n %throwable"
|
||||
ThresholdFilter:
|
||||
level: debug
|
||||
Policies:
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
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()));
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
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].*",
|
||||
"--forced-keywords", "testForced",
|
||||
"--commentary-keywords", "testCommentary",
|
||||
"--preferred-subtitles", "testPreferred"
|
||||
};
|
||||
CommandLine.populateCommand(Config.getInstance(true), 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());
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
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"));
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
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("")));
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
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,23 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.CommandRunner;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation.ValidationExecutionStrategy;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_FILE;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class PicoCliTest {
|
||||
|
||||
@Test
|
||||
void loadFilterDate() {
|
||||
CommandRunner underTest = new CommandRunner();
|
||||
new CommandLine(underTest)
|
||||
.setExecutionStrategy(new ValidationExecutionStrategy())
|
||||
.parseArgs("-d", "2012-12-12T12:12:12.00Z", TEST_FILE);
|
||||
assertEquals(Instant.parse("2012-12-12T12:12:12.00Z"), underTest.getConfig().getFilterDate());
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
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("--commentary-keywords", "test"), 1, (Function<Config, Set<String>>) Config::getCommentaryKeywords),
|
||||
Arguments.of(args("--commentary-keywords", "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("--forced-keywords", "test"), 1, (Function<Config, Set<String>>) Config::getForcedKeywords),
|
||||
Arguments.of(args("--forced-keywords", "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("--preferred-subtitles", "test"), 1, (Function<Config, Set<String>>) Config::getPreferredSubtitles),
|
||||
Arguments.of(args("--preferred-subtitles", "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("--commentary-keywords")));
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, args("--forced-keywords")));
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, args("-e")));
|
||||
assertThrows(CommandLine.MissingParameterException.class, () -> CommandLine.populateCommand(sut, args("--preferred-subtitles")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.converter;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.converter.AttributeConfigConverter;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
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.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class AttributeConfigConverterTest {
|
||||
|
||||
private static Stream<Arguments> validData() {
|
||||
return Stream.of(
|
||||
Arguments.of("jpn:ger", new AttributeConfig("jpn", "ger")),
|
||||
Arguments.of("eng:eng", new AttributeConfig("eng", "eng")),
|
||||
Arguments.of("OFF:OFF", new AttributeConfig("OFF", "OFF"))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("validData")
|
||||
void convert(String input, AttributeConfig expected) {
|
||||
AttributeConfigConverter underTest = new AttributeConfigConverter();
|
||||
AttributeConfig actual = underTest.convert(input);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> invalidData() {
|
||||
return Stream.of(
|
||||
Arguments.of("ars:eng"),
|
||||
Arguments.of("ars:OFF"),
|
||||
Arguments.of("OFF:ars"),
|
||||
Arguments.of("ars:ars"),
|
||||
Arguments.of("arss:ars"),
|
||||
Arguments.of("ars:arsr")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("invalidData")
|
||||
void convertInvalid(String input) {
|
||||
AttributeConfigConverter underTest = new AttributeConfigConverter();
|
||||
assertThrows(CommandLine.TypeConversionException.class, () -> underTest.convert(input));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
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,87 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_FILE;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class FileFilterTest {
|
||||
@Mock(strictness = Mock.Strictness.LENIENT)
|
||||
File file;
|
||||
|
||||
@Mock(strictness = Mock.Strictness.LENIENT)
|
||||
BasicFileAttributes attributes;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
ResultStatistic.getInstance(true);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> accept() {
|
||||
return Stream.of(
|
||||
Arguments.of("~/video.mkv", Set.of(".mkv"), Set.of(), -1, ".*", true, false),
|
||||
Arguments.of("~/video.mp4", Set.of(".mkv"), Set.of(), -1, ".*", false, false),
|
||||
|
||||
Arguments.of("~/video.mkv", Set.of(".mkv"), Set.of(), -1, "v.*", true, false),
|
||||
Arguments.of("~/video.mkv", Set.of(".mkv"), Set.of(), -1, "a.*", false, true),
|
||||
|
||||
Arguments.of("~/video.mkv", Set.of(".mkv"), Set.of(), -1000, ".*", true, false),
|
||||
Arguments.of("~/video.mkv", Set.of(".mkv"), Set.of(), 1000, ".*", false, true),
|
||||
|
||||
Arguments.of("dir/video.mkv", Set.of(".mkv"), Set.of("dir"), -1, ".*", false, true),
|
||||
Arguments.of("dir/dir2/video.mkv", Set.of(".mkv"), Set.of("dir/dir2"), -1, ".*", false, true),
|
||||
Arguments.of("dir/video.mkv", Set.of(".mkv"), Set.of("dir/dir2"), -1, ".*", true, false),
|
||||
Arguments.of("dirr/video.mkv", Set.of(".mkv"), Set.of("dir"), -1, ".*", true, false)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filterDateOffset move filter data into the future or past by positive and negative values
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void accept(String path, Set<String> extensions, Set<String> excludedDirs, int filterDateOffset, String pattern, boolean acceptanceExpected, boolean excluded) {
|
||||
when(file.getAbsolutePath()).thenReturn(path);
|
||||
when(file.getPath()).thenReturn(path);
|
||||
String[] split = path.split("/");
|
||||
when(file.getName()).thenReturn(split[split.length - 1]);
|
||||
when(file.toPath()).thenReturn(Path.of(TEST_FILE));
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
when(attributes.creationTime()).thenReturn(FileTime.fromMillis(currentTime));
|
||||
when(attributes.lastModifiedTime()).thenReturn(FileTime.fromMillis(currentTime));
|
||||
FileFilter fileFilter = new FileFilter(excludedDirs, Pattern.compile(pattern), Instant.ofEpochMilli(currentTime).plus(filterDateOffset, ChronoUnit.SECONDS), null);
|
||||
|
||||
try (MockedStatic<Files> mockedFiles = Mockito.mockStatic(Files.class)) {
|
||||
mockedFiles
|
||||
.when(() -> Files.readAttributes(any(), eq(BasicFileAttributes.class)))
|
||||
.thenReturn(attributes);
|
||||
|
||||
assertEquals(acceptanceExpected, fileFilter.accept(file, new HashSet<>(extensions)), "File is accepted");
|
||||
assertEquals(excluded, ResultStatistic.getInstance().getExcluded() > 0, "Is counted in excluded statistic");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.time.Instant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class LastExecutionHandlerTest {
|
||||
private static final String LAST_EXECUTION_YML = "./last-execution.properties";
|
||||
private static final String TEST_MKV_FILE = "/arst/file.mkv";
|
||||
|
||||
@AfterEach
|
||||
void destruct() {
|
||||
File file = new File(LAST_EXECUTION_YML);
|
||||
if (file.exists()) file.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingFile() throws IOException {
|
||||
LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_YML);
|
||||
assertNull(underTest.get(TEST_MKV_FILE));
|
||||
underTest.update(TEST_MKV_FILE);
|
||||
assertNotNull(underTest.get(TEST_MKV_FILE));
|
||||
underTest.persist();
|
||||
File file = new File(LAST_EXECUTION_YML);
|
||||
assertTrue(file.exists());
|
||||
assertTrue(Files.readString(file.toPath()).contains(TEST_MKV_FILE + "="));
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyFile() throws IOException {
|
||||
File file = new File(LAST_EXECUTION_YML);
|
||||
file.createNewFile();
|
||||
missingFile(); // does the checks needed for empty file case
|
||||
}
|
||||
|
||||
@Test
|
||||
void existingFileNoChanges() throws IOException {
|
||||
File file = new File(LAST_EXECUTION_YML);
|
||||
file.createNewFile();
|
||||
Files.writeString(file.toPath(), TEST_MKV_FILE + "=" + Instant.now());
|
||||
String expected = Files.readString(file.toPath()).replace(":", "\\:");
|
||||
|
||||
LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_YML);
|
||||
assertNotNull(underTest.get(TEST_MKV_FILE));
|
||||
underTest.persist();
|
||||
File file1 = new File(LAST_EXECUTION_YML);
|
||||
assertTrue(file1.exists());
|
||||
assertTrue(Files.readString(file.toPath()).contains(expected), "File contains expected value");
|
||||
}
|
||||
|
||||
@Test
|
||||
void existingFileWithChanges() throws IOException {
|
||||
File file = new File(LAST_EXECUTION_YML);
|
||||
file.createNewFile();
|
||||
Files.writeString(file.toPath(), TEST_MKV_FILE + "=" + Instant.now());
|
||||
String expected = Files.readString(file.toPath());
|
||||
|
||||
LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_YML);
|
||||
assertNotNull(underTest.get(TEST_MKV_FILE));
|
||||
underTest.update(TEST_MKV_FILE);
|
||||
assertNotNull(underTest.get(TEST_MKV_FILE));
|
||||
underTest.persist();
|
||||
File file1 = new File(LAST_EXECUTION_YML);
|
||||
assertTrue(file1.exists());
|
||||
assertNotEquals(expected, Files.readString(file.toPath()));
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.FileInfoTestUtil.*;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.createFileInfo;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class MkvFileProcessorTest {
|
||||
|
||||
private static Stream<Arguments> detectDesiredTracks() {
|
||||
return Stream.of(
|
||||
Arguments.of(new AttributeConfig("ger", "OFF"), List.of(AUDIO_GER, AUDIO_ENG), new AttributeConfig[] {new AttributeConfig("ger", "OFF"), new AttributeConfig("eng", "OFF")}),
|
||||
Arguments.of(new AttributeConfig("eng", "OFF"), List.of(AUDIO_ENG), new AttributeConfig[] {new AttributeConfig("ger", "OFF"), new AttributeConfig("eng", "OFF")}),
|
||||
Arguments.of(new AttributeConfig("eng", "ger"), List.of(AUDIO_GER, AUDIO_ENG, SUB_GER, SUB_ENG), new AttributeConfig[] {new AttributeConfig("eng", "ger"), new AttributeConfig("ger", "eng")}),
|
||||
Arguments.of(new AttributeConfig("ger", "eng"), List.of(AUDIO_GER, SUB_GER, SUB_ENG), new AttributeConfig[] {new AttributeConfig("eng", "ger"), new AttributeConfig("ger", "eng")}),
|
||||
Arguments.of(new AttributeConfig("OFF", "ger"), List.of(AUDIO_GER, SUB_GER, SUB_ENG), new AttributeConfig[] {new AttributeConfig("OFF", "ger"), new AttributeConfig("ger", "eng")})
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void detectDesiredTracks(AttributeConfig expectedMatch, List<FileAttribute> tracks, AttributeConfig... configs) {
|
||||
Config.getInstance().setPreferredSubtitles(Set.of());
|
||||
FileInfo info = new FileInfo(null);
|
||||
MkvFileProcessor processor = new MkvFileProcessor();
|
||||
processor.detectDesiredTracks(info, tracks, tracks, configs);
|
||||
assertEquals(expectedMatch.getAudioLanguage(), info.getMatchedConfig().getAudioLanguage());
|
||||
assertEquals(expectedMatch.getSubtitleLanguage(), info.getMatchedConfig().getSubtitleLanguage());
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,59 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.LaneType;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackType;
|
||||
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(attr(""), attr(""), 0),
|
||||
Arguments.of(attr("pref"), attr(""), 1),
|
||||
Arguments.of(attr(""), attr("pref"), -1),
|
||||
Arguments.of(attr("pref"), attr("pref"), 0),
|
||||
|
||||
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(attr("", true), attr("", true), 0),
|
||||
Arguments.of(attr("", true), attr(""), -1),
|
||||
Arguments.of(attr("CC", true), attr(""), -1),
|
||||
Arguments.of(attr("CC"), attr(""), -1),
|
||||
Arguments.of(attr(""), attr("", true), 1),
|
||||
Arguments.of(attr(""), attr("CC", true), 1),
|
||||
Arguments.of(attr(""), attr("CC"), 1),
|
||||
|
||||
Arguments.of(List.of(attr("unstyled sub", true), attr("unstyled sub", false)),
|
||||
List.of(attr("unstyled sub", true), attr("unstyled sub", false)))
|
||||
Arguments.of(attr("pref", true), attr("pref"), -1),
|
||||
Arguments.of(attr("pref", true), attr("pref", true), 0),
|
||||
Arguments.of(attr("pref"), attr("pref", true), 1),
|
||||
Arguments.of(attr("", true), attr("pref"), -2),
|
||||
Arguments.of(attr("pref"), attr("", true), 2),
|
||||
|
||||
Arguments.of(attr(null), attr(null), 0),
|
||||
Arguments.of(attr(null), attr(""), 0),
|
||||
Arguments.of(attr(null), attr("pref"), -1),
|
||||
Arguments.of(attr(""), attr(null), 0),
|
||||
Arguments.of(attr("pref"), attr(null), 1)
|
||||
);
|
||||
}
|
||||
|
||||
@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]));
|
||||
void compare(TrackAttributes track1, TrackAttributes track2, int expected) {
|
||||
SubtitleTrackComparator comparator = new SubtitleTrackComparator(List.of("pref"), List.of("CC", "SDH"));
|
||||
int actual = comparator.compare(track1, track2);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
private static FileAttribute attr(String trackName, boolean defaultTrack) {
|
||||
return new FileAttribute(0, "", trackName, defaultTrack, false, LaneType.SUBTITLES);
|
||||
private static TrackAttributes attr(String trackname) {
|
||||
return attr(trackname, false);
|
||||
}
|
||||
|
||||
private static TrackAttributes attr(String trackName, boolean hearingImpaired) {
|
||||
return new TrackAttributes(0, "", trackName, false, false, false, hearingImpaired, TrackType.SUBTITLES);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TrackAttributeUtil.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class AttributeChangeProcessorTest {
|
||||
|
||||
private static Stream<Arguments> findAndApplyDefaultMatch() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
List.of(withName(AUDIO_ENG, null), SUB_ENG),
|
||||
arr(a("eng:eng")), "eng:eng",
|
||||
Map.ofEntries(on(withName(AUDIO_ENG, null)), on(SUB_ENG))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_ENG, SUB_ENG),
|
||||
arr(a("eng:eng")), "eng:eng",
|
||||
Map.ofEntries(on(AUDIO_ENG), on(SUB_ENG))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_ENG, AUDIO_GER, SUB_ENG, SUB_GER),
|
||||
arr(a("eng:eng")), "eng:eng",
|
||||
Map.ofEntries(on(AUDIO_ENG), on(SUB_ENG))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER, SUB_ENG, SUB_GER),
|
||||
arr(a("ger:eng")), "ger:eng",
|
||||
Map.ofEntries(off(AUDIO_ENG_DEFAULT), on(AUDIO_GER), on(SUB_ENG))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER, SUB_ENG, SUB_GER),
|
||||
arr(a("eng:ger")), "eng:ger",
|
||||
Map.ofEntries(on(SUB_GER))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER, SUB_ENG_DEFAULT, SUB_GER),
|
||||
arr(a("eng:OFF")), "eng:OFF",
|
||||
Map.ofEntries(off(SUB_ENG_DEFAULT))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER, SUB_ENG_DEFAULT, SUB_GER),
|
||||
arr(a("OFF:OFF")), "OFF:OFF",
|
||||
Map.ofEntries(off(AUDIO_ENG_DEFAULT), off(SUB_ENG_DEFAULT))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER, SUB_GER),
|
||||
arr(a("eng:eng"), a("eng:ger")), "eng:ger",
|
||||
Map.ofEntries(on(SUB_GER))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER_COMMENTARY, SUB_GER_FORCED),
|
||||
arr(a("ger:ger")), null,
|
||||
Map.ofEntries()
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_ENG_DEFAULT, AUDIO_GER_COMMENTARY, withName(SUB_GER, "forced")),
|
||||
arr(a("ger:ger")), null,
|
||||
Map.ofEntries()
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_GER, withName(SUB_GER, "SDH")),
|
||||
arr(a("ger:ger")), "ger:ger",
|
||||
Map.ofEntries(on(AUDIO_GER), on(withName(SUB_GER, "SDH")))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_GER, withName(SUB_GER, "SDH"), SUB_GER),
|
||||
arr(a("ger:ger")), "ger:ger",
|
||||
Map.ofEntries(on(AUDIO_GER), on(SUB_GER))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_GER, SUB_GER_HEARING),
|
||||
arr(a("ger:ger")), "ger:ger",
|
||||
Map.ofEntries(on(AUDIO_GER), on(SUB_GER_HEARING))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_GER, SUB_GER_HEARING, SUB_GER),
|
||||
arr(a("ger:ger")), "ger:ger",
|
||||
Map.ofEntries(on(AUDIO_GER), on(SUB_GER))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_GER, SUB_ENG_HEARING, SUB_GER),
|
||||
arr(a("ger:eng")), "ger:eng",
|
||||
Map.ofEntries(on(AUDIO_GER), on(SUB_ENG_HEARING))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("findAndApplyDefaultMatch")
|
||||
void findAndApplyDefaultMatch(List<TrackAttributes> tracks, AttributeConfig[] config, String expectedConfig, Map<TrackAttributes, Boolean> changes) {
|
||||
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of("forced"), Set.of("commentary"), Set.of("SDH"));
|
||||
FileInfo fileInfo = new FileInfo(null);
|
||||
fileInfo.addTracks(tracks);
|
||||
|
||||
attributeChangeProcessor.findAndApplyDefaultMatch(fileInfo, config);
|
||||
|
||||
assertEquals(expectedConfig, fileInfo.getMatchedConfig() != null ? fileInfo.getMatchedConfig().toStringShort() : fileInfo.getMatchedConfig());
|
||||
assertEquals(changes.size(), fileInfo.getChanges().getDefaultTrack().size());
|
||||
changes.forEach((key, value) -> {
|
||||
assertTrue(fileInfo.getChanges().getDefaultTrack().containsKey(key));
|
||||
assertEquals(value, fileInfo.getChanges().getDefaultTrack().get(key));
|
||||
});
|
||||
}
|
||||
|
||||
private static Stream<Arguments> applyForcedAsDefault() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
List.of(AUDIO_GER, SUB_GER_FORCED),
|
||||
a("ger:OFF"),
|
||||
Map.ofEntries(on(SUB_GER_FORCED))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_GER, SUB_GER_FORCED, SUB_GER),
|
||||
a("ger:OFF"),
|
||||
Map.ofEntries(on(SUB_GER_FORCED))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_GER, withName(SUB_GER, "forced")),
|
||||
a("ger:OFF"),
|
||||
Map.ofEntries(on(withName(SUB_GER, "forced")))
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_GER, SUB_GER_FORCED, SUB_ENG),
|
||||
a("ger:eng"),
|
||||
Map.ofEntries()
|
||||
),
|
||||
Arguments.of(
|
||||
List.of(AUDIO_GER, SUB_GER_FORCED, SUB_ENG),
|
||||
null,
|
||||
Map.ofEntries()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("applyForcedAsDefault")
|
||||
void applyForcedAsDefault(List<TrackAttributes> tracks, AttributeConfig config, Map<TrackAttributes, Boolean> changes) {
|
||||
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of("forced"), Set.of(""), Set.of(""));
|
||||
FileInfo fileInfo = new FileInfo(null);
|
||||
fileInfo.addTracks(tracks);
|
||||
fileInfo.setMatchedConfig(config);
|
||||
|
||||
attributeChangeProcessor.applyForcedAsDefault(fileInfo);
|
||||
|
||||
assertEquals(changes.size(), fileInfo.getChanges().getDefaultTrack().size());
|
||||
changes.forEach((key, value) -> {
|
||||
assertTrue(fileInfo.getChanges().getDefaultTrack().containsKey(key));
|
||||
assertEquals(value, fileInfo.getChanges().getDefaultTrack().get(key));
|
||||
});
|
||||
}
|
||||
|
||||
private static Stream<Arguments> getPossibleDefaults() {
|
||||
return Stream.of(
|
||||
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
|
||||
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "forced"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
|
||||
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "Forced"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
|
||||
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "commentary"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
|
||||
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "Commentary"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER)),
|
||||
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "SDH"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER, withName(AUDIO_GER, "SDH"))),
|
||||
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, withName(AUDIO_GER, "sdh"), SUB_GER), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER, withName(AUDIO_GER, "sdh"))),
|
||||
Arguments.of(List.of(AUDIO_GER, AUDIO_ENG, SUB_GER, SUB_GER_FORCED, AUDIO_GER_COMMENTARY, AUDIO_GER_HEARING), Set.of(AUDIO_GER, AUDIO_ENG, SUB_GER, AUDIO_GER_HEARING))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("getPossibleDefaults")
|
||||
void getPossibleDefaults(List<TrackAttributes> tracks, Set<TrackAttributes> expected) throws InvocationTargetException, IllegalAccessException {
|
||||
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of("forced"), Set.of("commentary"), Set.of("SDH"));
|
||||
Optional<Method> method = Arrays.stream(AttributeChangeProcessor.class.getDeclaredMethods())
|
||||
.filter(m -> m.getName().equals("getPossibleDefaults"))
|
||||
.findFirst();
|
||||
|
||||
assertTrue(method.isPresent());
|
||||
Method underTest = method.get();
|
||||
underTest.setAccessible(true);
|
||||
List<TrackAttributes> result = ((Stream<TrackAttributes>) underTest.invoke(attributeChangeProcessor, tracks)).toList();
|
||||
assertEquals(expected.size(), result.size());
|
||||
for (TrackAttributes track : result) {
|
||||
assertTrue(expected.contains(track));
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream<Arguments> getForcedTracks() {
|
||||
return Stream.of(
|
||||
Arguments.of(List.of(withName(SUB_GER, "forced")), Map.of(), Set.of(withName(SUB_GER, "forced"))),
|
||||
Arguments.of(List.of(SUB_GER_FORCED), Map.of(), Set.of(SUB_GER_FORCED)),
|
||||
Arguments.of(List.of(SUB_GER_FORCED, withName(SUB_GER, "forced")), Map.of(), Set.of(SUB_GER_FORCED, withName(SUB_GER, "forced"))),
|
||||
Arguments.of(List.of(SUB_GER_FORCED, withName(SUB_GER, "forced")), Map.of(SUB_GER_FORCED, false), Set.of(withName(SUB_GER, "forced"))),
|
||||
Arguments.of(List.of(SUB_GER, withName(SUB_GER, "forced")), Map.of(SUB_GER, true), Set.of(SUB_GER, withName(SUB_GER, "forced")))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("getForcedTracks")
|
||||
void getForcedTracks(List<TrackAttributes> tracks, Map<TrackAttributes, Boolean> changes, Set<TrackAttributes> expected) throws InvocationTargetException, IllegalAccessException {
|
||||
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of("forced"), Set.of(), Set.of());
|
||||
FileInfo fileInfo = new FileInfo(null);
|
||||
fileInfo.addTracks(tracks);
|
||||
changes.forEach((key, val) -> fileInfo.getChanges().getForcedTrack().put(key, val));
|
||||
Optional<Method> method = Arrays.stream(AttributeChangeProcessor.class.getDeclaredMethods())
|
||||
.filter(m -> m.getName().equals("getForcedTracks"))
|
||||
.findFirst();
|
||||
|
||||
assertTrue(method.isPresent());
|
||||
Method underTest = method.get();
|
||||
underTest.setAccessible(true);
|
||||
List<TrackAttributes> result = ((Stream<TrackAttributes>) underTest.invoke(attributeChangeProcessor, fileInfo)).toList();
|
||||
assertEquals(expected.size(), result.size());
|
||||
for (TrackAttributes track : result) {
|
||||
assertTrue(expected.contains(track));
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream<Arguments> findAndApplyForcedTracks() {
|
||||
return Stream.of(
|
||||
Arguments.of(List.of(),
|
||||
Set.of("song & signs"), false,
|
||||
Map.ofEntries()
|
||||
),
|
||||
Arguments.of(List.of(withName(SUB_GER, "song & signs"), SUB_GER),
|
||||
Set.of("song & signs"), false,
|
||||
Map.ofEntries(on(withName(SUB_GER, "song & signs")))
|
||||
),
|
||||
Arguments.of(List.of(withName(SUB_GER, "song & signs"), withName(SUB_GER, "")),
|
||||
Set.of("song & signs"), false,
|
||||
Map.ofEntries(on(withName(SUB_GER, "song & signs")))
|
||||
),
|
||||
Arguments.of(List.of(withName(SUB_GER, "song & signs"), withName(SUB_GER, null)),
|
||||
Set.of("song & signs"), false,
|
||||
Map.ofEntries(on(withName(SUB_GER, "song & signs")))
|
||||
),
|
||||
Arguments.of(List.of(withName(SUB_GER, "song & signs")),
|
||||
Set.of("song & signs"), false,
|
||||
Map.ofEntries(on(withName(SUB_GER, "song & signs")))
|
||||
),
|
||||
Arguments.of(List.of(withName(SUB_GER_FORCED, "song & signs")),
|
||||
Set.of("song & signs"), false,
|
||||
Map.ofEntries()
|
||||
),
|
||||
Arguments.of(List.of(SUB_GER_FORCED, withName(SUB_GER, "song & signs")),
|
||||
Set.of("song & signs"), true,
|
||||
Map.ofEntries(off(SUB_GER_FORCED), on(withName(SUB_GER, "song & signs")))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("findAndApplyForcedTracks")
|
||||
void findAndApplyForcedTracks(List<TrackAttributes> tracks, Set<String> keywords, boolean overwrite, Map<TrackAttributes, Boolean> changes) {
|
||||
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, keywords, Set.of(), Set.of());
|
||||
|
||||
FileInfo fileInfo = new FileInfo(null);
|
||||
fileInfo.addTracks(tracks);
|
||||
attributeChangeProcessor.findAndApplyForcedTracks(fileInfo, overwrite);
|
||||
|
||||
assertEquals(changes.size(), fileInfo.getChanges().getForcedTrack().size());
|
||||
changes.forEach((key, value) -> {
|
||||
assertTrue(fileInfo.getChanges().getForcedTrack().containsKey(key));
|
||||
assertEquals(value, fileInfo.getChanges().getForcedTrack().get(key));
|
||||
});
|
||||
}
|
||||
|
||||
private static Stream<Arguments> findAndApplyCommentaryTracks() {
|
||||
return Stream.of(
|
||||
Arguments.of(List.of(withName(SUB_GER, "commentary"), withName(SUB_GER, null)),
|
||||
Set.of("commentary"),
|
||||
Map.ofEntries(on(withName(SUB_GER, "commentary")))
|
||||
),
|
||||
Arguments.of(List.of(withName(SUB_GER, "commentary")),
|
||||
Set.of("commentary"),
|
||||
Map.ofEntries(on(withName(SUB_GER, "commentary")))
|
||||
),
|
||||
Arguments.of(List.of(withName(AUDIO_GER_COMMENTARY, "commentary")),
|
||||
Set.of("commentary"),
|
||||
Map.ofEntries()
|
||||
),
|
||||
Arguments.of(List.of(AUDIO_GER_COMMENTARY, withName(SUB_GER, "commentary")),
|
||||
Set.of("commentary"),
|
||||
Map.ofEntries(on(withName(SUB_GER, "commentary")))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("findAndApplyCommentaryTracks")
|
||||
void findAndApplyCommentaryTracks(List<TrackAttributes> tracks, Set<String> keywords, Map<TrackAttributes, Boolean> changes) {
|
||||
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of(), keywords, Set.of());
|
||||
|
||||
FileInfo fileInfo = new FileInfo(null);
|
||||
fileInfo.addTracks(tracks);
|
||||
attributeChangeProcessor.findAndApplyCommentaryTracks(fileInfo);
|
||||
|
||||
assertEquals(changes.size(), fileInfo.getChanges().getCommentaryTrack().size());
|
||||
changes.forEach((key, value) -> {
|
||||
assertTrue(fileInfo.getChanges().getCommentaryTrack().containsKey(key));
|
||||
assertEquals(value, fileInfo.getChanges().getCommentaryTrack().get(key));
|
||||
});
|
||||
}
|
||||
|
||||
private static Stream<Arguments> findAndApplyHearingImpairedTracks() {
|
||||
return Stream.of(
|
||||
Arguments.of(List.of(withName(SUB_GER, "SDH"), withName(SUB_GER, null)),
|
||||
Set.of("SDH"),
|
||||
Map.ofEntries(on(withName(SUB_GER, "SDH")))
|
||||
),
|
||||
Arguments.of(List.of(withName(SUB_GER, "SDH")),
|
||||
Set.of("SDH"),
|
||||
Map.ofEntries(on(withName(SUB_GER, "SDH")))
|
||||
),
|
||||
Arguments.of(List.of(withName(AUDIO_GER_HEARING, "SDH")),
|
||||
Set.of("SDH"),
|
||||
Map.ofEntries()
|
||||
),
|
||||
Arguments.of(List.of(AUDIO_GER_HEARING, withName(SUB_GER, "SDH")),
|
||||
Set.of("SDH"),
|
||||
Map.ofEntries(on(withName(SUB_GER, "SDH")))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("findAndApplyHearingImpairedTracks")
|
||||
void findAndApplyHearingImpairedTracks(List<TrackAttributes> tracks, Set<String> keywords, Map<TrackAttributes, Boolean> changes) {
|
||||
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{}, Set.of(), Set.of(), keywords);
|
||||
|
||||
FileInfo fileInfo = new FileInfo(null);
|
||||
fileInfo.addTracks(tracks);
|
||||
attributeChangeProcessor.findAndApplyHearingImpairedTracks(fileInfo);
|
||||
|
||||
assertEquals(changes.size(), fileInfo.getChanges().getHearingImpairedTrack().size());
|
||||
changes.forEach((key, value) -> {
|
||||
assertTrue(fileInfo.getChanges().getHearingImpairedTrack().containsKey(key));
|
||||
assertEquals(value, fileInfo.getChanges().getHearingImpairedTrack().get(key));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.*;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TrackAttributeUtil.AUDIO_GER;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_FILE;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class AttributeUpdaterTest {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
ResultStatistic.getInstance(true);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> checkStatusAndUpdate() {
|
||||
return Stream.of(
|
||||
Arguments.of(info(new AttributeConfig("ger", "ger"), AUDIO_GER), supplier(() -> ResultStatistic.getInstance().getChangePlanned())),
|
||||
Arguments.of(info(new AttributeConfig("ger", "ger"), null), supplier(() -> ResultStatistic.getInstance().getUnchanged())),
|
||||
Arguments.of(info(null, null), supplier(() -> ResultStatistic.getInstance().getUnchanged()))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("checkStatusAndUpdate")
|
||||
void checkStatusAndUpdate(FileInfo fileInfo, Supplier<Integer> getActual) {
|
||||
InputConfig config = new InputConfig();
|
||||
config.setThreads(1);
|
||||
config.setSafeMode(true);
|
||||
AttributeUpdater underTest = new AttributeUpdater(config, null, null, new LastExecutionHandler("")) {
|
||||
@Override
|
||||
protected List<File> getFiles() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void process(File file) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
underTest.checkStatusAndUpdate(fileInfo);
|
||||
assertEquals(1, getActual.get());
|
||||
}
|
||||
|
||||
private static Supplier<Integer> supplier(Supplier<Integer> supplier) {
|
||||
return supplier;
|
||||
}
|
||||
|
||||
private static FileInfo info(AttributeConfig config, TrackAttributes attr) {
|
||||
FileInfo fileInfo = new FileInfo(new File(TEST_FILE));
|
||||
fileInfo.setMatchedConfig(config);
|
||||
if(attr != null) fileInfo.getChanges().getDefaultTrack().put(attr, true);
|
||||
return fileInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.CommandRunner;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_DIR;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TrackAttributeUtil.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CoherentAttributeUpdaterTest {
|
||||
@Mock(lenient = true)
|
||||
FileProcessor fileProcessor;
|
||||
|
||||
private static Stream<Arguments> findMatch() {
|
||||
return Stream.of(
|
||||
Arguments.of(AttributeConfig.of("ger", "ger"),
|
||||
List.of(), false, 0),
|
||||
Arguments.of(AttributeConfig.of("ger", "ger"),
|
||||
List.of(fileInfoMock("test.mkv", AUDIO_GER, SUB_GER)), true, 1),
|
||||
Arguments.of(AttributeConfig.of("ger", "ger"),
|
||||
List.of(fileInfoMock("test.mkv", AUDIO_GER, SUB_GER),
|
||||
fileInfoMock("test2.mkv", AUDIO_GER, SUB_GER)), true, 2),
|
||||
Arguments.of(AttributeConfig.of("ger", "ger"),
|
||||
List.of(fileInfoMock("test.mkv", AUDIO_GER, SUB_GER),
|
||||
fileInfoMock("test2.mkv", AUDIO_ENG, SUB_ENG)), false, 1),
|
||||
Arguments.of(AttributeConfig.of("ger", "ger"),
|
||||
List.of(fileInfoMock("test.mkv", AUDIO_GER, SUB_GER),
|
||||
fileInfoMock("test2.mkv", AUDIO_GER, SUB_GER),
|
||||
fileInfoMock("test3.mkv", AUDIO_GER, SUB_GER),
|
||||
fileInfoMock("test4.mkv", AUDIO_GER, SUB_GER),
|
||||
fileInfoMock("test5.mkv", AUDIO_ENG, SUB_ENG)), false, 4),
|
||||
Arguments.of(AttributeConfig.of("ger", "ger"),
|
||||
List.of(fileInfoMock("test.mkv", AUDIO_GER, SUB_GER),
|
||||
fileInfoMock("test2.mkv", AUDIO_ENG, SUB_GER),
|
||||
fileInfoMock("test3.mkv", AUDIO_GER, SUB_GER),
|
||||
fileInfoMock("test4.mkv", AUDIO_GER, SUB_GER),
|
||||
fileInfoMock("test5.mkv", AUDIO_GER, SUB_ENG)), false, 1)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("findMatch")
|
||||
void findMatch(AttributeConfig attributeConfig, List<Pair<File, FileInfo>> fileInfoMock, boolean expectedMatch, int expectedMatchCount) throws InvocationTargetException, IllegalAccessException {
|
||||
CommandRunner commandRunner = new CommandRunner();
|
||||
new CommandLine(commandRunner).parseArgs("-a", "ger:ger", "/arst");
|
||||
InputConfig config = commandRunner.getConfig();
|
||||
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(config.getPreferredSubtitles().toArray(new String[0]), config.getForcedKeywords(), config.getCommentaryKeywords(), config.getHearingImpaired());
|
||||
LastExecutionHandler lastExecutionHandler = new LastExecutionHandler("");
|
||||
CoherentAttributeUpdater updater = new CoherentAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler);
|
||||
Set<FileInfo> matchedFiles = new HashSet<>(fileInfoMock.size() * 2);
|
||||
|
||||
List<File> files = new ArrayList<>();
|
||||
for (Pair<File, FileInfo> pair : fileInfoMock) {
|
||||
when(fileProcessor.readAttributes(pair.getKey())).thenReturn(pair.getRight());
|
||||
files.add(pair.getKey());
|
||||
}
|
||||
|
||||
Method underTest = Arrays.stream(updater.getClass().getDeclaredMethods()).filter(m -> "findMatch".equals(m.getName())).findFirst().get();
|
||||
underTest.setAccessible(true);
|
||||
AttributeConfig actualMatch = (AttributeConfig) underTest.invoke(updater, attributeConfig, matchedFiles, files);
|
||||
|
||||
assertEquals(expectedMatch ? attributeConfig : null, actualMatch, "Matched AttributeConfig");
|
||||
assertEquals(expectedMatchCount, matchedFiles.size(), "Matched files count");
|
||||
}
|
||||
|
||||
private static Pair<File, FileInfo> fileInfoMock(String path, TrackAttributes... tracks) {
|
||||
File file = new File(path);
|
||||
FileInfo fileInfo = new FileInfo(file);
|
||||
fileInfo.addTracks(List.of(tracks));
|
||||
return Pair.of(file, fileInfo);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> process() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
arr(a("ger:ger")), a("ger:ger"),
|
||||
List.of(
|
||||
List.of(AUDIO_GER, SUB_GER),
|
||||
List.of(AUDIO_GER, SUB_GER)
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(on(AUDIO_GER), on(SUB_GER)),
|
||||
Map.ofEntries(on(AUDIO_GER), on(SUB_GER))
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(),
|
||||
Map.ofEntries()
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(),
|
||||
Map.ofEntries()
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(),
|
||||
Map.ofEntries()
|
||||
)
|
||||
),
|
||||
Arguments.of(
|
||||
arr(a("eng:eng"), a("ger:ger")), a("ger:ger"),
|
||||
List.of(
|
||||
List.of(SUB_ENG, AUDIO_GER, SUB_GER),
|
||||
List.of(AUDIO_ENG, SUB_ENG, AUDIO_GER, SUB_GER)
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(on(AUDIO_GER), on(SUB_GER)),
|
||||
Map.ofEntries(on(AUDIO_GER), on(SUB_GER))
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(),
|
||||
Map.ofEntries()
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(),
|
||||
Map.ofEntries()
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(),
|
||||
Map.ofEntries()
|
||||
)
|
||||
),
|
||||
Arguments.of(
|
||||
arr(a("eng:eng"), a("ger:ger")), a("eng:eng"),
|
||||
List.of(
|
||||
List.of(AUDIO_ENG, withName(SUB_ENG, "SDH"), AUDIO_GER, SUB_GER),
|
||||
List.of(AUDIO_ENG, SUB_ENG, AUDIO_GER, SUB_GER)
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(on(AUDIO_ENG), on(withName(SUB_ENG, "SDH"))),
|
||||
Map.ofEntries(on(AUDIO_ENG), on(SUB_ENG))
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(),
|
||||
Map.ofEntries()
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(),
|
||||
Map.ofEntries()
|
||||
),
|
||||
List.of(
|
||||
Map.ofEntries(on(withName(SUB_ENG, "SDH"))),
|
||||
Map.ofEntries()
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("process")
|
||||
void process(AttributeConfig[] attributeConfigs, AttributeConfig expectedMatch,
|
||||
List<List<TrackAttributes>> tracks,
|
||||
List<Map<TrackAttributes, Boolean>> defaultExp,
|
||||
List<Map<TrackAttributes, Boolean>> forcedExp,
|
||||
List<Map<TrackAttributes, Boolean>> commentaryExp,
|
||||
List<Map<TrackAttributes, Boolean>> hearingImpairedExp) {
|
||||
InputConfig config = new InputConfig();
|
||||
config.setThreads(1);
|
||||
config.setSafeMode(true);
|
||||
config.setAttributeConfig(attributeConfigs);
|
||||
FileProcessor fileProcessor = spy(FileProcessor.class);
|
||||
|
||||
List<File> testMkvFiles = new ArrayList<>();
|
||||
List<FileInfo> testFileInfo = new ArrayList<>();
|
||||
for (int i = 0; i < tracks.size(); i++) {
|
||||
List<TrackAttributes> tracks1 = tracks.get(i);
|
||||
File file = new File(TEST_DIR + i);
|
||||
FileInfo fileInfo = new FileInfo(file);
|
||||
fileInfo.addTracks(tracks1);
|
||||
doReturn(fileInfo).when(fileProcessor).readAttributes(file);
|
||||
|
||||
testMkvFiles.add(file);
|
||||
testFileInfo.add(fileInfo);
|
||||
}
|
||||
doReturn(testMkvFiles).when(fileProcessor).loadFiles(any());
|
||||
|
||||
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{"pref"}, Set.of("forced"), Set.of("commentary"), Set.of("SDH"));
|
||||
LastExecutionHandler lastExecutionHandler = new LastExecutionHandler("");
|
||||
CoherentAttributeUpdater underTest = new CoherentAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler);
|
||||
|
||||
underTest.process(new File(""));
|
||||
|
||||
for (int i = 0; i < testFileInfo.size(); i++) {
|
||||
FileInfo fileInfo = testFileInfo.get(i);
|
||||
assertEquals(expectedMatch, fileInfo.getMatchedConfig());
|
||||
assertEquals(fileInfo.getChanges().getDefaultTrack().size(), defaultExp.get(i).size());
|
||||
defaultExp.get(i).forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getDefaultTrack().get(key), "Default track flag"));
|
||||
|
||||
assertEquals(fileInfo.getChanges().getForcedTrack().size(), forcedExp.get(i).size());
|
||||
forcedExp.get(i).forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getForcedTrack().get(key), "Forced track flag"));
|
||||
|
||||
assertEquals(fileInfo.getChanges().getCommentaryTrack().size(), commentaryExp.get(i).size());
|
||||
commentaryExp.get(i).forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getCommentaryTrack().get(key), "Commentary track flag"));
|
||||
|
||||
assertEquals(fileInfo.getChanges().getHearingImpairedTrack().size(), hearingImpairedExp.get(i).size());
|
||||
hearingImpairedExp.get(i).forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getHearingImpairedTrack().get(key), "Hearing Impaired track flag"));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class MkvFileProcessorTest {
|
||||
|
||||
@Test
|
||||
void readAttributes() throws IOException {
|
||||
String mkvmergeResponse = """
|
||||
{
|
||||
"tracks": [
|
||||
{
|
||||
"id": 0,
|
||||
"properties": {
|
||||
"default_track": true,
|
||||
"enabled_track": true,
|
||||
"forced_track": false,
|
||||
"language": "jpn",
|
||||
"number": 1
|
||||
},
|
||||
"type": "video"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"properties": {
|
||||
"track_name": "testing",
|
||||
"default_track": true,
|
||||
"enabled_track": true,
|
||||
"forced_track": false,
|
||||
"language": "jpn",
|
||||
"number": 2
|
||||
},
|
||||
"type": "audio"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"properties": {
|
||||
"default_track": true,
|
||||
"enabled_track": true,
|
||||
"forced_track": false,
|
||||
"commentary_track": true,
|
||||
"flag_hearing_impaired": true,
|
||||
"language": "eng",
|
||||
"number": 3
|
||||
},
|
||||
"type": "subtitles"
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
MkvFileProcessor underTest = spy(new MkvFileProcessor(new File("mkvtoolnix"), null));
|
||||
doReturn(new ByteArrayInputStream(mkvmergeResponse.getBytes(StandardCharsets.UTF_8)))
|
||||
.when(underTest).run(any(String[].class));
|
||||
|
||||
FileInfo result = underTest.readAttributes(new File("arst"));
|
||||
|
||||
TrackAttributes audio = result.getAudioTracks().get(0);
|
||||
assertEquals(2, audio.id());
|
||||
assertEquals("testing", audio.trackName());
|
||||
assertEquals("jpn", audio.language());
|
||||
assertTrue(audio.defaultt());
|
||||
assertFalse(audio.forced());
|
||||
assertFalse(audio.hearingImpaired());
|
||||
assertFalse(audio.commentary());
|
||||
assertEquals(TrackType.AUDIO, audio.type());
|
||||
|
||||
TrackAttributes sub = result.getSubtitleTracks().get(0);
|
||||
assertEquals(3, sub.id());
|
||||
assertNull(sub.trackName());
|
||||
assertEquals("eng", sub.language());
|
||||
assertTrue(sub.defaultt());
|
||||
assertFalse(sub.forced());
|
||||
assertTrue(sub.hearingImpaired());
|
||||
assertTrue(sub.commentary());
|
||||
assertEquals(TrackType.SUBTITLES, sub.type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getUpdateCommand() throws InvocationTargetException, IllegalAccessException {
|
||||
FileInfo fileInfo = new FileInfo(new File("./"));
|
||||
fileInfo.getChanges().getDefaultTrack().put(t(1), true);
|
||||
fileInfo.getChanges().getDefaultTrack().put(t(2), false);
|
||||
fileInfo.getChanges().getForcedTrack().put(t(3), true);
|
||||
fileInfo.getChanges().getForcedTrack().put(t(4), false);
|
||||
fileInfo.getChanges().getCommentaryTrack().put(t(5), true);
|
||||
fileInfo.getChanges().getCommentaryTrack().put(t(6), false);
|
||||
fileInfo.getChanges().getHearingImpairedTrack().put(t(7), true);
|
||||
fileInfo.getChanges().getHearingImpairedTrack().put(t(8), false);
|
||||
String[] expectedCommand = """
|
||||
--edit track:1 --set flag-default=1
|
||||
--edit track:2 --set flag-default=0
|
||||
--edit track:3 --set flag-forced=1
|
||||
--edit track:4 --set flag-forced=0
|
||||
--edit track:5 --set flag-commentary=1
|
||||
--edit track:6 --set flag-commentary=0
|
||||
--edit track:7 --set flag-hearing-impaired=1
|
||||
--edit track:8 --set flag-hearing-impaired=0
|
||||
""".split("\\n");
|
||||
|
||||
MkvFileProcessor mkvFileProcessor = new MkvFileProcessor(new File("mkvtoolnix"), null);
|
||||
Method underTest = Arrays.stream(mkvFileProcessor.getClass().getDeclaredMethods()).filter(m -> "getUpdateCommand".equals(m.getName())).findFirst().get();
|
||||
underTest.setAccessible(true);
|
||||
String[] actualCommand = (String[]) underTest.invoke(mkvFileProcessor, fileInfo);
|
||||
String[] trimmedActualCommand = Arrays.copyOfRange(actualCommand, 2, actualCommand.length);
|
||||
String actualCommandString = String.join(" ", trimmedActualCommand);
|
||||
assertTrue(expectedCommand.length * 4 == trimmedActualCommand.length, "Command length is equal");
|
||||
for (String commandPart: expectedCommand) {
|
||||
assertTrue(actualCommandString.contains(commandPart));
|
||||
}
|
||||
}
|
||||
|
||||
private static TrackAttributes t(int id) {
|
||||
return new TrackAttributes(id, "", "", false, false, false, false, TrackType.AUDIO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.processors;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.LastExecutionHandler;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfo;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.InputConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_DIR;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TrackAttributeUtil.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class SingleFileAttributeUpdaterTest {
|
||||
|
||||
private static Stream<Arguments> process() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
arr(a("ger:OFF")), a("ger:OFF"),
|
||||
List.of(AUDIO_GER, SUB_GER_FORCED),
|
||||
Map.ofEntries(on(AUDIO_GER), on(SUB_GER_FORCED)),
|
||||
Map.ofEntries(),
|
||||
Map.ofEntries(),
|
||||
Map.ofEntries()
|
||||
),
|
||||
Arguments.of(
|
||||
arr(a("ger:ger")), a("ger:ger"),
|
||||
List.of(AUDIO_GER, SUB_GER, withName(AUDIO_GER, "SDH"), withName(AUDIO_GER, "commentary"), withName(SUB_GER, "Forced")),
|
||||
Map.ofEntries(on(AUDIO_GER), on(SUB_GER)),
|
||||
Map.ofEntries(on(withName(SUB_GER, "Forced"))),
|
||||
Map.ofEntries(on(withName(AUDIO_GER, "commentary"))),
|
||||
Map.ofEntries(on(withName(AUDIO_GER, "SDH")))
|
||||
),
|
||||
Arguments.of(
|
||||
arr(a("ger:OFF")), a("ger:OFF"),
|
||||
List.of(AUDIO_GER, SUB_GER, withName(AUDIO_GER, "SDH"), withName(AUDIO_GER, "commentary"), withName(SUB_GER, "Forced")),
|
||||
Map.ofEntries(on(AUDIO_GER), on(withName(SUB_GER, "Forced"))),
|
||||
Map.ofEntries(on(withName(SUB_GER, "Forced"))),
|
||||
Map.ofEntries(on(withName(AUDIO_GER, "commentary"))),
|
||||
Map.ofEntries(on(withName(AUDIO_GER, "SDH")))
|
||||
),
|
||||
Arguments.of(
|
||||
arr(a("ger:eng")), null,
|
||||
List.of(AUDIO_GER, SUB_GER, withName(AUDIO_GER, "SDH"), withName(AUDIO_GER, "commentary"), withName(SUB_GER, "Forced")),
|
||||
Map.ofEntries(),
|
||||
Map.ofEntries(on(withName(SUB_GER, "Forced"))),
|
||||
Map.ofEntries(on(withName(AUDIO_GER, "commentary"))),
|
||||
Map.ofEntries(on(withName(AUDIO_GER, "SDH")))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("process")
|
||||
void process(AttributeConfig[] attributeConfigs, AttributeConfig expectedMatch,
|
||||
List<TrackAttributes> tracks,
|
||||
Map<TrackAttributes, Boolean> defaultExp,
|
||||
Map<TrackAttributes, Boolean> forcedExp,
|
||||
Map<TrackAttributes, Boolean> commentaryExp,
|
||||
Map<TrackAttributes, Boolean> hearingImpairedExp) {
|
||||
InputConfig config = new InputConfig();
|
||||
config.setThreads(1);
|
||||
config.setSafeMode(true);
|
||||
config.setAttributeConfig(attributeConfigs);
|
||||
FileInfo fileInfo = new FileInfo(new File(TEST_DIR));
|
||||
fileInfo.addTracks(tracks);
|
||||
FileProcessor fileProcessor = spy(FileProcessor.class);
|
||||
doReturn(fileInfo).when(fileProcessor).readAttributes(any());
|
||||
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(new String[]{"pref"}, Set.of("forced"), Set.of("commentary"), Set.of("SDH"));
|
||||
LastExecutionHandler lastExecutionHandler = new LastExecutionHandler("");
|
||||
SingleFileAttributeUpdater underTest = new SingleFileAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler);
|
||||
|
||||
underTest.process(fileInfo.getFile());
|
||||
|
||||
assertEquals(expectedMatch, fileInfo.getMatchedConfig());
|
||||
assertEquals(fileInfo.getChanges().getDefaultTrack().size(), defaultExp.size());
|
||||
defaultExp.forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getDefaultTrack().get(key), "Default track flag"));
|
||||
|
||||
assertEquals(fileInfo.getChanges().getForcedTrack().size(), forcedExp.size());
|
||||
forcedExp.forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getForcedTrack().get(key), "Forced track flag"));
|
||||
|
||||
assertEquals(fileInfo.getChanges().getCommentaryTrack().size(), commentaryExp.size());
|
||||
commentaryExp.forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getCommentaryTrack().get(key), "Commentary track flag"));
|
||||
|
||||
assertEquals(fileInfo.getChanges().getHearingImpairedTrack().size(), hearingImpairedExp.size());
|
||||
hearingImpairedExp.forEach((key, val) -> assertEquals(val, fileInfo.getChanges().getHearingImpairedTrack().get(key), "Hearing Impaired track flag"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.CommandRunner;
|
||||
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.stream.Stream;
|
||||
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ValidationExecutionStrategyTest {
|
||||
|
||||
@Test
|
||||
void validate() {
|
||||
CommandRunner underTest = new CommandRunner();
|
||||
new CommandLine(underTest)
|
||||
.setExecutionStrategy(new ValidationExecutionStrategy())
|
||||
.parseArgs("-a", "ger:ger", "-m", TEST_MKVTOOLNIX_DIR, TEST_FILE);
|
||||
|
||||
assertEquals(TEST_FILE, underTest.getConfig().getLibraryPath()[0].getPath().replace("\\", "/"));
|
||||
assertEquals(TEST_MKVTOOLNIX_DIR, underTest.getConfig().getMkvToolNix().getPath().replace("\\", "/"));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> validateFailure() {
|
||||
return Stream.of(
|
||||
Arguments.of(new String[]{"-a", "jpn:ger"}, "Error: Missing required argument(s): <libraryPath>"),
|
||||
Arguments.of(new String[]{"/arstarstarst"}, "libraryPath does not exist"),
|
||||
Arguments.of(new String[]{TEST_DIR, "/arstarstarst"}, "libraryPath does not exist"),
|
||||
Arguments.of(new String[]{"/arstarstarst", "-a",}, "Missing required parameter for option '--attribute-config' at index 0 (<attributeConfig>)"),
|
||||
Arguments.of(new String[]{"/arstarstarst", "-a", "jpn:ger"}, "libraryPath does not exist"),
|
||||
Arguments.of(new String[]{"/arstarstarst", "-m"}, "Missing required parameter for option '--mkvtoolnix' (<mkvToolNix>)"),
|
||||
Arguments.of(new String[]{"./", "-m", TEST_INVALID_DIR}, "mkvToolNix does not exist"),
|
||||
Arguments.of(new String[]{"./", "-t"}, "Missing required parameter for option '--threads' (<threads>)"),
|
||||
Arguments.of(new String[]{"./", "-t", "0"}, "threads must be greater than or equal to 1"),
|
||||
Arguments.of(new String[]{"./", "-t", "-1"}, "threads must be greater than or equal to 1"),
|
||||
Arguments.of(new String[]{"./", "-c", "-1"}, "coherent must be greater than or equal to 0")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("validateFailure")
|
||||
void validateFailure(String[] args, String expectedMessage) {
|
||||
StringWriter writer = new StringWriter();
|
||||
PrintWriter printWriter = new PrintWriter(writer);
|
||||
|
||||
new CommandLine(CommandRunner.class)
|
||||
.setExecutionStrategy(new ValidationExecutionStrategy())
|
||||
.setErr(printWriter)
|
||||
.execute(args);
|
||||
|
||||
printWriter.flush();
|
||||
assertEquals(expectedMessage, writer.toString().split("[\r\n]")[0]);
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
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.model.FileStatus.*;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.FileInfoTestUtil.*;
|
||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class FileInfoTest {
|
||||
|
||||
private static Stream<Arguments> isAudioDifferent() {
|
||||
return Stream.of(
|
||||
Arguments.of(createFileInfoAudio(Set.of(AUDIO_GER_DEFAULT), AUDIO_GER_DEFAULT, new AttributeConfig("ger", "")), false),
|
||||
Arguments.of(createFileInfoAudio(Set.of(AUDIO_GER_DEFAULT), AUDIO_ENG, new AttributeConfig("eng", "")), true),
|
||||
Arguments.of(createFileInfoAudio(Set.of(AUDIO_GER_DEFAULT, AUDIO_ENG_DEFAULT), AUDIO_GER_DEFAULT, new AttributeConfig("ger", "")), true),
|
||||
Arguments.of(createFileInfoAudio(Set.of(), AUDIO_GER, new AttributeConfig("ger", "")), true),
|
||||
Arguments.of(createFileInfoAudio(null, AUDIO_GER, new AttributeConfig("ger", "")), true),
|
||||
|
||||
Arguments.of(createFileInfoAudio(Set.of(AUDIO_GER_DEFAULT), null, new AttributeConfig("OFF", "")), true),
|
||||
Arguments.of(createFileInfoAudio(Set.of(), null, new AttributeConfig("OFF", "")), false),
|
||||
Arguments.of(createFileInfoAudio(null, null, new AttributeConfig("OFF", "")), false)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void isAudioDifferent(FileInfo underTest, boolean expected) {
|
||||
assertEquals(expected, underTest.isAudioDifferent());
|
||||
}
|
||||
|
||||
private static Stream<Arguments> isSubtitleDifferent() {
|
||||
return Stream.of(
|
||||
Arguments.of(createFileInfoSubs(Set.of(SUB_GER_DEFAULT), SUB_GER_DEFAULT, new AttributeConfig("", "ger")), false),
|
||||
Arguments.of(createFileInfoSubs(Set.of(SUB_GER_DEFAULT), SUB_ENG, new AttributeConfig("", "eng")), true),
|
||||
Arguments.of(createFileInfoSubs(Set.of(SUB_GER_DEFAULT, SUB_ENG_DEFAULT), SUB_ENG, new AttributeConfig("", "eng")), true),
|
||||
Arguments.of(createFileInfoSubs(Set.of(), SUB_ENG, new AttributeConfig("", "ger")), true),
|
||||
Arguments.of(createFileInfoSubs(null, SUB_GER, new AttributeConfig("", "ger")), true),
|
||||
Arguments.of(createFileInfoSubs(null, null, new AttributeConfig("", "OFF")), false),
|
||||
Arguments.of(createFileInfoSubs(Set.of(), null, new AttributeConfig("", "OFF")), false),
|
||||
Arguments.of(createFileInfoSubs(Set.of(SUB_GER_DEFAULT), null, new AttributeConfig("", "OFF")), true)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void isSubtitleDifferent(FileInfo underTest, boolean expected) {
|
||||
assertEquals(expected, underTest.isSubtitleDifferent());
|
||||
}
|
||||
|
||||
private static Stream<Arguments> getStatus() {
|
||||
return Stream.of(
|
||||
Arguments.of(CHANGE_NECESSARY, createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_ENG, Set.of(), null, Set.of(), Set.of(), Set.of(), new AttributeConfig("eng", "OFF"))),
|
||||
Arguments.of(CHANGE_NECESSARY, createFileInfo(Set.of(), null, Set.of(SUB_GER_DEFAULT), SUB_ENG, Set.of(), Set.of(), Set.of(), new AttributeConfig("OFF", "eng"))),
|
||||
Arguments.of(CHANGE_NECESSARY, createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_ENG, Set.of(SUB_GER_DEFAULT), SUB_ENG, Set.of(), Set.of(), Set.of(), new AttributeConfig("OFF", "eng"))),
|
||||
Arguments.of(CHANGE_NECESSARY, createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_GER_DEFAULT, Set.of(SUB_GER_DEFAULT), SUB_GER_DEFAULT, Set.of(AUDIO_ENG_FORCED), Set.of(), Set.of(), new AttributeConfig("ger", "ger"))),
|
||||
Arguments.of(CHANGE_NECESSARY, createFileInfo(Set.of(), null, Set.of(), null, Set.of(AUDIO_ENG_FORCED), Set.of(), Set.of(), new AttributeConfig("OFF", "OFF"))),
|
||||
Arguments.of(CHANGE_NECESSARY, createFileInfo(Set.of(), null, Set.of(), null, Set.of(), Set.of(), Set.of(SUB_GER_FORCED), new AttributeConfig("OFF", "OFF"))),
|
||||
Arguments.of(CHANGE_NECESSARY, createFileInfo(Set.of(), null, Set.of(), null, Set.of(), Set.of(SUB_ENG_FORCED), Set.of(SUB_GER_FORCED), new AttributeConfig("OFF", "OFF"))),
|
||||
Arguments.of(CHANGE_NECESSARY, createFileInfo(Set.of(), null, Set.of(), null, Set.of(AUDIO_ENG_FORCED), Set.of(SUB_ENG_FORCED), Set.of(SUB_GER_FORCED), new AttributeConfig("OFF", "OFF"))),
|
||||
Arguments.of(CHANGE_NECESSARY, createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_ENG, Set.of(SUB_GER_DEFAULT), SUB_ENG, Set.of(AUDIO_ENG_FORCED), Set.of(SUB_ENG_FORCED), Set.of(SUB_GER_FORCED), new AttributeConfig("eng", "eng"))),
|
||||
Arguments.of(CHANGE_NECESSARY, createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_GER_DEFAULT, Set.of(SUB_GER_DEFAULT), SUB_GER_DEFAULT, Set.of(), Set.of(SUB_ENG_FORCED), Set.of(SUB_ENG_FORCED, SUB_GER), new AttributeConfig("ger", "ger"))),
|
||||
|
||||
Arguments.of(NO_SUITABLE_CONFIG, createFileInfo(Set.of(AUDIO_ENG_DEFAULT), null, Set.of(SUB_GER_DEFAULT), null, Set.of(), Set.of(), Set.of(), new AttributeConfig("eng", "ger"))),
|
||||
Arguments.of(NO_SUITABLE_CONFIG, createFileInfo(Set.of(AUDIO_ENG_DEFAULT), null, Set.of(), null, Set.of(), Set.of(), Set.of(), new AttributeConfig("eng", "ger"))),
|
||||
Arguments.of(NO_SUITABLE_CONFIG, createFileInfo(Set.of(), null, Set.of(), null, Set.of(), Set.of(), Set.of(), new AttributeConfig("eng", "ger"))),
|
||||
|
||||
Arguments.of(ALREADY_SUITED, createFileInfo(Set.of(), null, Set.of(), null, Set.of(), Set.of(), Set.of(), new AttributeConfig("OFF", "OFF"))),
|
||||
Arguments.of(ALREADY_SUITED, createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_GER_DEFAULT, Set.of(), null, Set.of(), Set.of(), Set.of(), new AttributeConfig("ger", "OFF"))),
|
||||
Arguments.of(ALREADY_SUITED, createFileInfo(Set.of(), null, Set.of(SUB_ENG_DEFAULT), SUB_ENG_DEFAULT, Set.of(), Set.of(), Set.of(), new AttributeConfig("OFF", "ger"))),
|
||||
Arguments.of(ALREADY_SUITED, createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_GER_DEFAULT, Set.of(SUB_ENG_DEFAULT), SUB_ENG_DEFAULT, Set.of(), Set.of(), Set.of(), new AttributeConfig("ger", "eng"))),
|
||||
Arguments.of(ALREADY_SUITED, createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_GER_DEFAULT, Set.of(SUB_ENG_DEFAULT), SUB_ENG_DEFAULT, Set.of(), Set.of(SUB_GER_FORCED), Set.of(SUB_GER_FORCED), new AttributeConfig("ger", "eng")))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void getStatus(FileStatus expected, FileInfo underTest) {
|
||||
FileStatus actual = underTest.getStatus();
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@Disabled
|
||||
class DateUtilsTest {
|
||||
|
||||
@Test
|
||||
void convert() {
|
||||
Date expectedDate = new Date(0);
|
||||
String expectedString = "01.01.1970-01:00:00";
|
||||
|
||||
assertEquals(expectedDate, DateUtils.convert(0));
|
||||
assertEquals(expectedDate, DateUtils.convert(expectedString, expectedDate));
|
||||
assertEquals(expectedDate, DateUtils.convert("1234;15", expectedDate));
|
||||
assertEquals(expectedString, DateUtils.convert(expectedDate));
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.LaneType;
|
||||
|
||||
public class FileInfoTestUtil {
|
||||
public static final FileAttribute AUDIO_GER_DEFAULT = new FileAttribute(0, "ger", "", true, false, LaneType.AUDIO);
|
||||
public static final FileAttribute AUDIO_ENG_DEFAULT = new FileAttribute(1, "eng", "", true, false, LaneType.AUDIO);
|
||||
public static final FileAttribute AUDIO_GER = new FileAttribute(0, "ger", "", false, false, LaneType.AUDIO);
|
||||
public static final FileAttribute AUDIO_ENG = new FileAttribute(1, "eng", "", false, false, LaneType.AUDIO);
|
||||
public static final FileAttribute AUDIO_GER_FORCED = new FileAttribute(0, "ger", "", false, true, LaneType.AUDIO);
|
||||
public static final FileAttribute AUDIO_ENG_FORCED = new FileAttribute(1, "eng", "", false, true, LaneType.AUDIO);
|
||||
|
||||
public static final FileAttribute SUB_GER_DEFAULT = new FileAttribute(0, "ger", "", true, false, LaneType.SUBTITLES);
|
||||
public static final FileAttribute SUB_ENG_DEFAULT = new FileAttribute(1, "eng", "", true, false, LaneType.SUBTITLES);
|
||||
public static final FileAttribute SUB_GER = new FileAttribute(0, "ger", "", false, false, LaneType.SUBTITLES);
|
||||
public static final FileAttribute SUB_ENG = new FileAttribute(1, "eng", "", false, false, LaneType.SUBTITLES);
|
||||
public static final FileAttribute SUB_GER_FORCED = new FileAttribute(0, "ger", "", false, true, LaneType.SUBTITLES);
|
||||
public static final FileAttribute SUB_ENG_FORCED = new FileAttribute(1, "eng", "", false, true, LaneType.SUBTITLES);
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
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";
|
||||
public static final String TEST_INVALID_DIR = "src/test/resources/test-dir";
|
||||
public static final String TEST_MKVTOOLNIX_DIR = SystemUtils.IS_OS_WINDOWS ? "src/test/resources/mkvtoolnix_exe" : "src/test/resources/mkvtoolnix";
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -1,72 +1,12 @@
|
||||
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.FileInfo;
|
||||
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 FileInfo createFileInfoAudio(Set<FileAttribute> defaultAudio, FileAttribute desiredAudio, AttributeConfig config) {
|
||||
FileInfo fileInfo = new FileInfo(null);
|
||||
fileInfo.setExistingDefaultAudioLanes(defaultAudio);
|
||||
fileInfo.setDesiredDefaultAudioLane(desiredAudio);
|
||||
fileInfo.setMatchedConfig(config);
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
public static FileInfo createFileInfoSubs(Set<FileAttribute> defaultSubtitle, FileAttribute desiredSubtitle, AttributeConfig config) {
|
||||
FileInfo fileInfo = new FileInfo(null);
|
||||
fileInfo.setExistingDefaultSubtitleLanes(defaultSubtitle);
|
||||
fileInfo.setDesiredDefaultSubtitleLane(desiredSubtitle);
|
||||
fileInfo.setMatchedConfig(config);
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
public static FileInfo createFileInfo(Set<FileAttribute> defaultAudio, FileAttribute desiredAudio,
|
||||
Set<FileAttribute> defaultSubtitle, FileAttribute desiredSubtitle,
|
||||
Set<FileAttribute> existingForcedAudioLanes,
|
||||
Set<FileAttribute> existingForcedSubs, Set<FileAttribute> desiredForcedSubs,
|
||||
AttributeConfig matchedConfig) {
|
||||
FileInfo fileInfo = new FileInfo(null);
|
||||
fileInfo.setExistingDefaultAudioLanes(defaultAudio);
|
||||
fileInfo.setDesiredDefaultAudioLane(desiredAudio);
|
||||
fileInfo.setExistingDefaultSubtitleLanes(defaultSubtitle);
|
||||
fileInfo.setDesiredDefaultSubtitleLane(desiredSubtitle);
|
||||
fileInfo.setExistingForcedAudioLanes(existingForcedAudioLanes);
|
||||
fileInfo.setExistingForcedSubtitleLanes(existingForcedSubs);
|
||||
fileInfo.setDesiredForcedSubtitleLanes(desiredForcedSubs);
|
||||
fileInfo.setMatchedConfig(matchedConfig);
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
public static String[] args(String... args) {
|
||||
String[] staticArray = new String[]{"-l", "/", "-a", "jpn:ger"};
|
||||
String[] staticArray = new String[]{"-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);
|
||||
System.arraycopy(args, 0, result, 0, args.length);
|
||||
System.arraycopy(staticArray, 0, result, args.length, staticArray.length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes;
|
||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackType;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class TrackAttributeUtil {
|
||||
public static final TrackAttributes AUDIO_GER = new TrackAttributes(0, "ger", "", false, false, false, false, TrackType.AUDIO);
|
||||
public static final TrackAttributes AUDIO_ENG = new TrackAttributes(1, "eng", "", false, false, false, false, TrackType.AUDIO);
|
||||
public static final TrackAttributes AUDIO_GER_DEFAULT = new TrackAttributes(0, "ger", "", true, false, false, false, TrackType.AUDIO);
|
||||
public static final TrackAttributes AUDIO_ENG_DEFAULT = new TrackAttributes(1, "eng", "", true, false, false, false, TrackType.AUDIO);
|
||||
public static final TrackAttributes AUDIO_GER_FORCED = new TrackAttributes(0, "ger", "", false, true, false, false, TrackType.AUDIO);
|
||||
public static final TrackAttributes AUDIO_ENG_FORCED = new TrackAttributes(1, "eng", "", false, true, false, false, TrackType.AUDIO);
|
||||
public static final TrackAttributes AUDIO_GER_COMMENTARY = new TrackAttributes(0, "ger", "", false, false, true, false, TrackType.AUDIO);
|
||||
public static final TrackAttributes AUDIO_ENG_COMMENTARY = new TrackAttributes(1, "eng", "", false, false, true, false, TrackType.AUDIO);
|
||||
public static final TrackAttributes AUDIO_GER_HEARING = new TrackAttributes(0, "ger", "", false, false, false, true, TrackType.AUDIO);
|
||||
public static final TrackAttributes AUDIO_ENG_HEARING = new TrackAttributes(1, "eng", "", false, false, false, true, TrackType.AUDIO);
|
||||
|
||||
public static final TrackAttributes SUB_GER = new TrackAttributes(0, "ger", "", false, false, false, false, TrackType.SUBTITLES);
|
||||
public static final TrackAttributes SUB_ENG = new TrackAttributes(1, "eng", "", false, false, false, false, TrackType.SUBTITLES);
|
||||
public static final TrackAttributes SUB_GER_DEFAULT = new TrackAttributes(0, "ger", "", true, false, false, false, TrackType.SUBTITLES);
|
||||
public static final TrackAttributes SUB_ENG_DEFAULT = new TrackAttributes(1, "eng", "", true, false, false, false, TrackType.SUBTITLES);
|
||||
public static final TrackAttributes SUB_GER_FORCED = new TrackAttributes(0, "ger", "", false, true, false, false, TrackType.SUBTITLES);
|
||||
public static final TrackAttributes SUB_ENG_FORCED = new TrackAttributes(1, "eng", "", false, true, false, false, TrackType.SUBTITLES);
|
||||
public static final TrackAttributes SUB_GER_HEARING = new TrackAttributes(0, "ger", "", false, false, false, true, TrackType.SUBTITLES);
|
||||
public static final TrackAttributes SUB_ENG_HEARING = new TrackAttributes(1, "eng", "", false, false, false, true, TrackType.SUBTITLES);
|
||||
|
||||
public static TrackAttributes withName(TrackAttributes track, String trackName) {
|
||||
return new TrackAttributes(track.id(), track.language(), trackName, track.defaultt(), track.forced(), track.commentary(), track.hearingImpaired(), track.type());
|
||||
}
|
||||
|
||||
public static AttributeConfig[] arr(AttributeConfig... configs) {
|
||||
return configs;
|
||||
}
|
||||
|
||||
public static AttributeConfig a(String config) {
|
||||
String[] split = config.split(":");
|
||||
return new AttributeConfig(split[0], split[1]);
|
||||
}
|
||||
|
||||
public static Map.Entry<TrackAttributes, Boolean> on(TrackAttributes track) {
|
||||
return Map.entry(track, true);
|
||||
}
|
||||
|
||||
public static Map.Entry<TrackAttributes, Boolean> off(TrackAttributes track) {
|
||||
return Map.entry(track, false);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user