mirror of
https://github.com/RatzzFatzz/MKVAudioSubtitleChanger.git
synced 2026-02-11 10:05:58 +01:00
Compare commits
115 Commits
bugfix/fil
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a965e4084 | ||
|
|
e80331beef | ||
|
|
a5fce22b95 | ||
|
|
a97ed89d08 | ||
|
|
cce84f5c15 | ||
|
|
ff38457af1 | ||
|
|
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 | ||
|
|
b0f927dfa8 | ||
|
|
37c65df60c | ||
| 0e9d008c7e | |||
| 3205969d3b | |||
| d24aedb0af | |||
| b86c7b98a5 | |||
|
|
69c192c08b | ||
|
|
7dd01234b6 | ||
|
|
8f38abcf3a | ||
|
|
fc4e80ead0 | ||
|
|
e81b06f6fa | ||
|
|
dc770c9325 | ||
|
|
471255a09b | ||
|
|
9c8315aec7 | ||
|
|
c33777b038 | ||
|
|
6c08ce69ea | ||
|
|
7f8c14e3a9 | ||
|
|
553c672e4d | ||
|
|
d98c4cd49e | ||
|
|
21f244ff3f | ||
|
|
ffac36ac27 | ||
|
|
0813744148 | ||
|
|
44d2601d3e | ||
|
|
36bd93bb50 | ||
|
|
ecc5c56c8c | ||
|
|
f6310c71ee | ||
|
|
bb4a686dfc | ||
|
|
c63fcd4f37 | ||
|
|
9f15b542bd | ||
|
|
76321bb904 | ||
|
|
895597b91f | ||
|
|
4fa5448e1c | ||
|
|
f3accd77d6 | ||
|
|
2710ea2602 | ||
|
|
547b5ad86c | ||
|
|
1863432dc6 | ||
|
|
7ea0ab17b0 | ||
|
|
47b4cdc896 | ||
|
|
b638d93358 | ||
|
|
939f6053dd | ||
|
|
4714ef8db1 | ||
|
|
321115b9ca | ||
|
|
a075dfb27c | ||
|
|
ed8e592963 | ||
|
|
0a7996f049 | ||
|
|
dd60ca93da |
131
.github/workflows/release.yml
vendored
131
.github/workflows/release.yml
vendored
@@ -1,29 +1,48 @@
|
|||||||
# 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
|
name: Build and release
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [ created ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
portable-build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up JDK 11
|
permissions:
|
||||||
uses: actions/setup-java@v2
|
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
|
||||||
|
with:
|
||||||
|
timezoneLinux: "Europe/Berlin"
|
||||||
|
|
||||||
|
- name: Set up JDK 17
|
||||||
|
uses: actions/setup-java@v4.7.0
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 11
|
java-version: 17
|
||||||
|
|
||||||
- name: Setup workspace
|
- name: Setup workspace
|
||||||
run: mkdir artifacts
|
run: mkdir artifacts
|
||||||
|
|
||||||
- name: Build with Maven
|
- name: Build with Maven
|
||||||
run: |
|
run: |
|
||||||
mvn clean package --file pom.xml
|
mvn clean package --file pom.xml -P portable -Drevision="${{ steps.get_version.outputs.VERSION }}"
|
||||||
cp target/M*.{zip,tar} artifacts/
|
cp target/M*.{zip,tar} artifacts/
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
@@ -32,4 +51,94 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
args: 'artifacts/M*'
|
args: 'artifacts/M*'
|
||||||
|
|
||||||
|
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
|
||||||
|
with:
|
||||||
|
timezoneLinux: "Europe/Berlin"
|
||||||
|
|
||||||
|
- name: Set up JDK 17
|
||||||
|
uses: actions/setup-java@v4.7.0
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 17
|
||||||
|
|
||||||
|
- name: Setup workspace
|
||||||
|
run: mkdir artifacts
|
||||||
|
|
||||||
|
- name: Build with Maven
|
||||||
|
run: |
|
||||||
|
mvn clean package --file pom.xml -P debian -Drevision="${{ steps.get_version.outputs.VERSION }}"
|
||||||
|
cp target/M*.deb artifacts/
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: skx/github-action-publish-binaries@master
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
args: 'artifacts/M*'
|
||||||
|
|
||||||
|
windows-installer-build:
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Install mkvtoolnix
|
||||||
|
uses: crazy-max/ghaction-chocolatey@v3
|
||||||
|
with:
|
||||||
|
args: install mkvtoolnix -y
|
||||||
|
|
||||||
|
- 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
|
||||||
|
with:
|
||||||
|
timezoneWindows: "Berlin Standard Time"
|
||||||
|
|
||||||
|
- name: Set up JDK 21
|
||||||
|
uses: actions/setup-java@v4.7.0
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 21
|
||||||
|
|
||||||
|
- name: Setup workspace
|
||||||
|
run: mkdir artifacts
|
||||||
|
|
||||||
|
- name: Build with Maven
|
||||||
|
run: mvn clean package --file pom.xml -P windows -Drevision="${{ steps.get_version.outputs.VERSION }}"
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: AButler/upload-release-assets@v3.0
|
||||||
|
with:
|
||||||
|
files: 'target/installer/*'
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
# Log file
|
# Log file
|
||||||
*.log
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
# BlueJ files
|
# BlueJ files
|
||||||
*.ctxt
|
*.ctxt
|
||||||
|
|||||||
93
README.md
93
README.md
@@ -1,49 +1,72 @@
|
|||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
This program helps to change audio and subtitle tracks of mkv files without rewriting the file. Only track properties will be updated.
|
This 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
|
## Requirements
|
||||||
|
|
||||||
- Java 11 or higher
|
- Java 21 or newer
|
||||||
- mkvtoolnix installation
|
- mkvtoolnix installation
|
||||||
|
|
||||||
## Execution
|
## Execution
|
||||||
**Minimal usage:**
|
```shell
|
||||||
`java -jar mkvaudiosubtitlechanger.jar --library "X:/Files" --attribute-config eng:ger eng:OFF`
|
# Portable
|
||||||
|
java -jar mkvaudiosubtitlechanger.jar --attribute-config eng:ger -s ./videos
|
||||||
**Safe usage (best for testing before applying to whole library):**
|
# Installed
|
||||||
`java -jar mkvaudiosubtitlechanger.jar --library "X:/Files" --attribute-config eng:ger eng:OFF --safe-mode`
|
mkvaudiosubtitlechanger.jar --attribute-config eng:ger -s ./videos
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
## Available parameters
|
|
||||||
```
|
```
|
||||||
-l,--library-path <arg> Path to library
|
Remove `--safemode` or `-s` to actually apply the changes. Using safemode for the first execution is recommended.
|
||||||
-a,--attribute-config <arg> Attribute config to decide which tracks to choose when
|
|
||||||
-p,--config-path <arg> Path to config file
|
|
||||||
-m,--mkvtoolnix <arg> Path to mkv tool nix installation
|
|
||||||
-s,--safe-mode Test run (no files will be changes)
|
|
||||||
-c,--coherent <arg> Try to match all files in dir of depth with the same config
|
|
||||||
-cf,--force-coherent Force coherent and don't update anything if config fits not whole config (default: false)
|
|
||||||
-n,--only-new-files Sets filter-date to last successful execution (Overwrites input of filter-date)
|
|
||||||
-d,--filter-date <arg> Only consider files created newer than entered date (format: "dd.MM.yyyy-HH:mm:ss")
|
|
||||||
-t,--threads <arg> Thread count (default: 2)
|
|
||||||
-i,--include-pattern <arg> Include files matching pattern (default: ".*")
|
|
||||||
-e,--excluded-directories <arg> Directories to be excluded, combines with config file
|
|
||||||
-fk,--forced-keywords <arg> Additional keywords to identify forced tracks
|
|
||||||
-ck,--commentary-keywords <arg> Additional keywords to identify commentary tracks
|
|
||||||
-ps,--preferred-subtitles <arg> Additional keywords to prefer specific subtitle tracks
|
|
||||||
-v,--version Display version
|
|
||||||
-h,--help "For help this is" - Yoda
|
|
||||||
```
|
|
||||||
If you need more information about how each parameter works, check out [this wiki page](https://github.com/RatzzFatzz/MKVAudioSubtitleChanger/wiki/Parameters).
|
|
||||||
|
|
||||||
All parameters can also be defined in a [config file](https://github.com/RatzzFatzz/MKVAudioSubtitleChanger/wiki/How-to-config-file).
|
### 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.
|
||||||
|
|
||||||
|
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 for matching defaults in order (e.g. jpn:eng jpn:ger)
|
||||||
|
-m, --mkvtoolnix=<mkvToolNix>
|
||||||
|
path to mkvtoolnix installation
|
||||||
|
-s, --safemode test run (no files will be changes)
|
||||||
|
-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).
|
||||||
|
|
||||||
|
All parameters can also be defined in a [config file](https://picocli.info/#_argument_files_for_long_command_lines).
|
||||||
|
|
||||||
## Build requirements
|
## Build requirements
|
||||||
- JDK 11 or higher
|
- JDK 21 or newer
|
||||||
- Maven 3
|
- Maven 3
|
||||||
- Git
|
- Git
|
||||||
|
|
||||||
@@ -51,5 +74,5 @@ All parameters can also be defined in a [config file](https://github.com/RatzzFa
|
|||||||
```shell
|
```shell
|
||||||
git clone https://github.com/RatzzFatzz/MKVAudioSubtitleChanger.git
|
git clone https://github.com/RatzzFatzz/MKVAudioSubtitleChanger.git
|
||||||
cd MKVAudioSubtitleChanger
|
cd MKVAudioSubtitleChanger
|
||||||
mvn package
|
mvn clean package -Pportable
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
mkvtoolnix: C:\Program Files\MKVToolNix
|
|
||||||
library: X:/Files
|
|
||||||
|
|
||||||
attribute-config:
|
|
||||||
1:
|
|
||||||
audio: ger
|
|
||||||
subtitle: OFF
|
|
||||||
2:
|
|
||||||
audio: eng
|
|
||||||
subtitle: ger
|
|
||||||
|
|
||||||
# Recommendations for data stored on HDDs, increase when using SSDs
|
|
||||||
#threads: 2
|
|
||||||
|
|
||||||
#forced-keywords: ["forced", "signs"]
|
|
||||||
#commentary-keywords: ["commentary", "director"]
|
|
||||||
#preferred-subtitles: ["unstyled"]
|
|
||||||
|
|
||||||
#exclude-directories:
|
|
||||||
# - "D:/Path/To/File.mkv"
|
|
||||||
# - "D:/Path/To/Directory"
|
|
||||||
|
|
||||||
# If pattern is negated, can be used to exclude files
|
|
||||||
#include-pattern: "regex"
|
|
||||||
|
|
||||||
# Only files newer than
|
|
||||||
#filter-date: 20.03.2021-10:11:12
|
|
||||||
|
|
||||||
safe-mode:
|
|
||||||
#coherent:
|
|
||||||
#force-coherent:
|
|
||||||
#only-new-files:
|
|
||||||
|
|
||||||
|
|
||||||
BIN
example.gif
BIN
example.gif
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 145 KiB |
@@ -14,10 +14,6 @@
|
|||||||
<destName>${project.artifactId}.jar</destName>
|
<destName>${project.artifactId}.jar</destName>
|
||||||
<outputDirectory>/</outputDirectory>
|
<outputDirectory>/</outputDirectory>
|
||||||
</file>
|
</file>
|
||||||
<file>
|
|
||||||
<source>${project.basedir}/config-template.yaml</source>
|
|
||||||
<outputDirectory>/</outputDirectory>
|
|
||||||
</file>
|
|
||||||
<file>
|
<file>
|
||||||
<source></source>
|
<source></source>
|
||||||
</file>
|
</file>
|
||||||
|
|||||||
379
pom.xml
379
pom.xml
@@ -6,21 +6,241 @@
|
|||||||
|
|
||||||
<groupId>at.pcgamingfreaks</groupId>
|
<groupId>at.pcgamingfreaks</groupId>
|
||||||
<artifactId>MKVAudioSubtitleChanger</artifactId>
|
<artifactId>MKVAudioSubtitleChanger</artifactId>
|
||||||
<version>3.0.1</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>
|
||||||
|
|
||||||
|
<project.maintainer>RatzzFatzz</project.maintainer>
|
||||||
|
<project.maintainer.mail>github.contact@ratzloeffel.de</project.maintainer.mail>
|
||||||
|
<project.description>Command-line utility for batch-managing default audio and subtitle tracks in MKV files.</project.description>
|
||||||
|
|
||||||
|
<java-version>17</java-version>
|
||||||
|
<lombok-version>1.18.42</lombok-version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>portable</id>
|
||||||
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<includes>
|
||||||
|
<include>log4j2.yml</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<version>3.3.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>archive</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<appendAssemblyId>false</appendAssemblyId>
|
||||||
|
<descriptors>
|
||||||
|
<descriptor>maven/assembly.xml</descriptor>
|
||||||
|
</descriptors>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>windows</id>
|
||||||
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<includes>
|
||||||
|
<include>log4j2-windows.yml</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>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-jpackage-input</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/jpackage-input</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${project.build.directory}</directory>
|
||||||
|
<includes>
|
||||||
|
<include>${project.artifactId}.jar</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>filter-windows-installer-info</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/wix-resources</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${project.basedir}/src/wix/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.panteleyev</groupId>
|
||||||
|
<artifactId>jpackage-maven-plugin</artifactId>
|
||||||
|
<version>1.7.1</version>
|
||||||
|
<configuration>
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<vendor>RatzzFatzz</vendor>
|
||||||
|
<appVersion>${project.version}</appVersion>
|
||||||
|
|
||||||
|
<destination>target/installer</destination>
|
||||||
|
|
||||||
|
<input>target/jpackage-input</input>
|
||||||
|
<mainClass>at.pcgamingfreaks.mkvaudiosubtitlechanger.Main</mainClass>
|
||||||
|
<mainJar>${project.artifactId}.jar</mainJar>
|
||||||
|
|
||||||
|
<resourceDir>${project.build.directory}/wix-resources/</resourceDir>
|
||||||
|
<type>EXE</type>
|
||||||
|
<winConsole>true</winConsole>
|
||||||
|
<winShortcut>false</winShortcut>
|
||||||
|
<winMenu>false</winMenu>
|
||||||
|
<javaOptions>
|
||||||
|
<javaOption>-Dlog4j2.configurationFile=log4j2-windows.yml</javaOption>
|
||||||
|
</javaOptions>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>windows</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>jpackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>debian</id>
|
||||||
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<includes>
|
||||||
|
<include>log4j2-debian.yml</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>3.3.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>filter-linux-package-info</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/debian-package-info</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${project.basedir}/src/deb</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>jdeb</artifactId>
|
||||||
|
<groupId>org.vafer</groupId>
|
||||||
|
<version>1.13</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>jdeb</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<dataSet>
|
||||||
|
<!-- JAR file -->
|
||||||
|
<data>
|
||||||
|
<src>${project.build.directory}/${project.build.finalName}.jar</src>
|
||||||
|
<type>file</type>
|
||||||
|
<mapper>
|
||||||
|
<type>perm</type>
|
||||||
|
<prefix>/usr/lib/${project.artifactId}</prefix>
|
||||||
|
</mapper>
|
||||||
|
</data>
|
||||||
|
<!-- Launcher script -->
|
||||||
|
<data>
|
||||||
|
<src>${project.build.directory}/debian-package-info/bin/mkvaudiosubtitlechanger</src>
|
||||||
|
<type>file</type>
|
||||||
|
<mapper>
|
||||||
|
<type>perm</type>
|
||||||
|
<prefix>/usr/bin</prefix>
|
||||||
|
<filemode>755</filemode>
|
||||||
|
</mapper>
|
||||||
|
</data>
|
||||||
|
</dataSet>
|
||||||
|
<controlDir>${project.build.directory}/debian-package-info/control</controlDir>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<defaultGoal>clean package</defaultGoal>
|
<defaultGoal>clean package</defaultGoal>
|
||||||
<sourceDirectory>src/main/java</sourceDirectory>
|
<sourceDirectory>src/main/java</sourceDirectory>
|
||||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||||
<resources>
|
<resources>
|
||||||
<resource>
|
|
||||||
<directory>src/main/resources</directory>
|
|
||||||
</resource>
|
|
||||||
<resource>
|
<resource>
|
||||||
<directory>./</directory>
|
<directory>./</directory>
|
||||||
<includes>
|
<includes>
|
||||||
<include>language-codes</include>
|
<include>language-codes</include>
|
||||||
<include>project.properties</include>
|
<include>project.properties</include>
|
||||||
|
<include>LICENSE</include>
|
||||||
</includes>
|
</includes>
|
||||||
<filtering>true</filtering>
|
<filtering>true</filtering>
|
||||||
</resource>
|
</resource>
|
||||||
@@ -35,11 +255,11 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
<version>3.2.2</version>
|
<version>3.4.2</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<archive>
|
<archive>
|
||||||
<manifestEntries>
|
<manifestEntries>
|
||||||
<Main-Class>at/pcgamingfreaks/mkvaudiosubtitlechanger/Main</Main-Class>
|
<Main-Class>${mainClass}</Main-Class>
|
||||||
</manifestEntries>
|
</manifestEntries>
|
||||||
</archive>
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
@@ -47,7 +267,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
<version>3.4.1</version>
|
<version>3.6.0</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>package</phase>
|
<phase>package</phase>
|
||||||
@@ -76,35 +296,33 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>2.22.2</version>
|
<version>3.5.2</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.13.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>11</source>
|
<source>${java-version}</source>
|
||||||
<target>11</target>
|
<target>${java-version}</target>
|
||||||
|
<compilerArgs>
|
||||||
|
<arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
|
||||||
|
</compilerArgs>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok-version}</version>
|
||||||
|
</path>
|
||||||
|
<path>
|
||||||
|
<groupId>info.picocli</groupId>
|
||||||
|
<artifactId>picocli-codegen</artifactId>
|
||||||
|
<version>4.7.7</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
|
||||||
<version>3.3.0</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>single</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<appendAssemblyId>false</appendAssemblyId>
|
|
||||||
<descriptors>
|
|
||||||
<descriptor>maven/assembly.xml</descriptor>
|
|
||||||
</descriptors>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
@@ -116,86 +334,143 @@
|
|||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||||
<groupId>com.intellij</groupId>
|
|
||||||
<artifactId>forms_rt</artifactId>
|
|
||||||
<version>7.0.3</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>1.18.24</version>
|
<version>${lombok-version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/info.picocli/picocli -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>info.picocli</groupId>
|
||||||
|
<artifactId>picocli</artifactId>
|
||||||
|
<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>9.1.0.Final</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Expression Language Implementation -->
|
||||||
|
<!-- https://mvnrepository.com/artifact/jakarta.el/jakarta.el-api -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.el</groupId>
|
||||||
|
<artifactId>jakarta.el-api</artifactId>
|
||||||
|
<version>6.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish</groupId>
|
||||||
|
<artifactId>jakarta.el</artifactId>
|
||||||
|
<version>5.0.0-M1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- region logging -->
|
<!-- region logging -->
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-api</artifactId>
|
<artifactId>log4j-api</artifactId>
|
||||||
<version>2.18.0</version>
|
<version>2.25.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-core</artifactId>
|
<artifactId>log4j-core</artifactId>
|
||||||
<version>2.18.0</version>
|
<version>2.25.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j18-impl -->
|
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j2-impl -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-slf4j18-impl</artifactId>
|
<artifactId>log4j-slf4j2-impl</artifactId>
|
||||||
<version>2.18.0</version>
|
<version>2.25.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||||
<version>2.13.4</version>
|
<version>2.20.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<version>2.13.4.2</version>
|
<version>2.20.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/commons-cli/commons-cli -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-cli</groupId>
|
<groupId>commons-cli</groupId>
|
||||||
<artifactId>commons-cli</artifactId>
|
<artifactId>commons-cli</artifactId>
|
||||||
<version>1.5.0</version>
|
<version>1.11.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
<version>3.12.0</version>
|
<version>3.20.0</version>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>me.tongfei</groupId>
|
<groupId>me.tongfei</groupId>
|
||||||
<artifactId>progressbar</artifactId>
|
<artifactId>progressbar</artifactId>
|
||||||
<version>0.9.5</version>
|
<version>0.10.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- endregion -->
|
<!-- endregion -->
|
||||||
<!-- region unit-tests -->
|
<!-- region unit-tests -->
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
<version>5.9.0</version>
|
<version>6.0.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<version>5.9.0</version>
|
<version>6.0.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
|
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mockito</groupId>
|
<groupId>org.mockito</groupId>
|
||||||
<artifactId>mockito-all</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
<version>1.10.19</version>
|
<version>5.21.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-params</artifactId>
|
<artifactId>junit-jupiter-params</artifactId>
|
||||||
<version>5.9.0</version>
|
<version>6.0.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- endregion -->
|
<!-- endregion -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>at.pcgamingfreaks</groupId>
|
<groupId>at.pcgamingfreaks</groupId>
|
||||||
@@ -206,7 +481,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.harawata</groupId>
|
<groupId>net.harawata</groupId>
|
||||||
<artifactId>appdirs</artifactId>
|
<artifactId>appdirs</artifactId>
|
||||||
<version>1.2.1</version>
|
<version>1.5.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
1
src/deb/bin/mkvaudiosubtitlechanger
Normal file
1
src/deb/bin/mkvaudiosubtitlechanger
Normal file
@@ -0,0 +1 @@
|
|||||||
|
java -Dlog4j2.configurationFile=log4j2-debian.yml -jar /usr/lib/${project.artifactId}/${project.artifactId}-${project.version}.jar "$@"
|
||||||
8
src/deb/control/control
Normal file
8
src/deb/control/control
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Package: [[artifactId]]
|
||||||
|
Version: [[version]]
|
||||||
|
Section: misc
|
||||||
|
Priority: optional
|
||||||
|
Architecture: all
|
||||||
|
Depends: java-runtime (>=${java-version}), mkvtoolnix
|
||||||
|
Maintainer: ${project.maintainer} <${project.maintainer.mail}>
|
||||||
|
Description: ${project.description}
|
||||||
@@ -1,21 +1,18 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger;
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger;
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.CommandRunner;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ConfigLoader;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.validation.ValidationExecutionStrategy;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.impl.CachedMkvFileProcessor;
|
import picocli.CommandLine;
|
||||||
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 lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
ConfigLoader.initConfig(args);
|
if (args.length == 0) {
|
||||||
AttributeUpdaterKernel kernel = Config.getInstance().getCoherent() != null
|
CommandLine.usage(CommandRunner.class, System.out);
|
||||||
? new CoherentAttributeUpdaterKernel(new MkvFileCollector(), new CachedMkvFileProcessor())
|
return;
|
||||||
: new DefaultAttributeUpdaterKernel(new MkvFileCollector(), new CachedMkvFileProcessor());
|
}
|
||||||
kernel.execute();
|
|
||||||
|
new CommandLine(CommandRunner.class)
|
||||||
|
.setExecutionStrategy(new ValidationExecutionStrategy())
|
||||||
|
.execute(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.MkvToolNix;
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.commons.cli.CommandLineParser;
|
|
||||||
import org.apache.commons.cli.DefaultParser;
|
|
||||||
import org.apache.commons.cli.HelpFormatter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
||||||
public class Config {
|
|
||||||
@Getter(AccessLevel.NONE)
|
|
||||||
@Setter(AccessLevel.NONE)
|
|
||||||
private static Config config = null;
|
|
||||||
@Getter(AccessLevel.NONE)
|
|
||||||
CommandLineParser parser = new DefaultParser();
|
|
||||||
@Getter(AccessLevel.NONE)
|
|
||||||
HelpFormatter formatter = new HelpFormatter();
|
|
||||||
private File configPath;
|
|
||||||
private File libraryPath;
|
|
||||||
@Getter(AccessLevel.NONE)
|
|
||||||
private File mkvToolNix;
|
|
||||||
|
|
||||||
private int threads;
|
|
||||||
private Pattern includePattern;
|
|
||||||
private boolean safeMode;
|
|
||||||
|
|
||||||
private Integer coherent;
|
|
||||||
private boolean forceCoherent;
|
|
||||||
private boolean onlyNewFiles;
|
|
||||||
private Date filterDate;
|
|
||||||
|
|
||||||
private Set<String> forcedKeywords = new HashSet<>(Arrays.asList("forced", "signs", "songs"));
|
|
||||||
private Set<String> commentaryKeywords = new HashSet<>(Arrays.asList("commentary", "director"));
|
|
||||||
private Set<String> excludedDirectories = new HashSet<>();
|
|
||||||
private Set<String> preferredSubtitles = new HashSet<>(Arrays.asList("unstyled"));
|
|
||||||
|
|
||||||
private List<AttributeConfig> attributeConfig;
|
|
||||||
|
|
||||||
public static Config getInstance() {
|
|
||||||
return getInstance(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Config getInstance(boolean reset) {
|
|
||||||
if (config == null || reset) {
|
|
||||||
config = new Config();
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 getNormalizedLibraryPath() {
|
|
||||||
return this.getLibraryPath().getAbsolutePath().replace("\\", "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new StringJoiner(", ", Config.class.getSimpleName() + "[", "]")
|
|
||||||
.add("parser=" + parser)
|
|
||||||
.add("formatter=" + formatter)
|
|
||||||
.add("configPath=" + configPath)
|
|
||||||
.add("libraryPath=" + libraryPath)
|
|
||||||
.add("mkvToolNix=" + mkvToolNix)
|
|
||||||
.add("threads=" + threads)
|
|
||||||
.add("includePattern=" + includePattern)
|
|
||||||
.add("safeMode=" + safeMode)
|
|
||||||
.add("coherent=" + coherent)
|
|
||||||
.add("forceCoherent=" + forceCoherent)
|
|
||||||
.add("onlyNewFiles=" + onlyNewFiles)
|
|
||||||
.add("filterDate=" + filterDate)
|
|
||||||
.add("forcedKeywords=" + forcedKeywords)
|
|
||||||
.add("commentaryKeywords=" + commentaryKeywords)
|
|
||||||
.add("excludedDirectories=" + excludedDirectories)
|
|
||||||
.add("preferredSubtitles=" + preferredSubtitles)
|
|
||||||
.add("attributeConfig=" + attributeConfig)
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator.*;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.ProjectUtil;
|
|
||||||
import at.pcgamingfreaks.yaml.YAML;
|
|
||||||
import at.pcgamingfreaks.yaml.YamlInvalidContentException;
|
|
||||||
import org.apache.commons.cli.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty.*;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.CommandLineOptionsUtil.optionOf;
|
|
||||||
|
|
||||||
public class ConfigLoader {
|
|
||||||
private static final List<ConfigValidator<?>> VALIDATORS = Stream.of(
|
|
||||||
new ConfigPathValidator(CONFIG_PATH, false),
|
|
||||||
new PathValidator(LIBRARY, true, null),
|
|
||||||
new ThreadValidator(THREADS, false, 2),
|
|
||||||
new MkvToolNixPathValidator(MKV_TOOL_NIX, true, Path.of("C:\\Program Files\\MKVToolNix").toFile()),
|
|
||||||
new BooleanValidator(SAFE_MODE, false),
|
|
||||||
new BooleanValidator(ONLY_NEW_FILES, false),
|
|
||||||
new DateValidator(FILTER_DATE, false),
|
|
||||||
new PatternValidator(INCLUDE_PATTERN, false, Pattern.compile(".*")),
|
|
||||||
new SetValidator(FORCED_KEYWORDS, false, true),
|
|
||||||
new SetValidator(COMMENTARY_KEYWORDS, false, true),
|
|
||||||
new SetValidator(EXCLUDED_DIRECTORY, false, true),
|
|
||||||
new SetValidator(PREFERRED_SUBTITLES, false, true),
|
|
||||||
new AttributeConfigValidator(),
|
|
||||||
new CoherentConfigValidator(COHERENT, false),
|
|
||||||
new BooleanValidator(FORCE_COHERENT, false)
|
|
||||||
).sorted(Comparator.comparing((ConfigValidator<?> validator) -> validator.getWeight()).reversed()).collect(Collectors.toList());
|
|
||||||
|
|
||||||
public static void initConfig(String[] args) {
|
|
||||||
HelpFormatter formatter = new HelpFormatter();
|
|
||||||
formatter.setOptionComparator(null);
|
|
||||||
YAML yamlConfig = null;
|
|
||||||
|
|
||||||
Options options = initOptions();
|
|
||||||
CommandLine cmd = parseCommandLineArgs(formatter, options, args);
|
|
||||||
|
|
||||||
exitIfHelp(cmd, options, formatter);
|
|
||||||
exitIfVersion(cmd);
|
|
||||||
|
|
||||||
List<ValidationResult> results = new ArrayList<>();
|
|
||||||
|
|
||||||
for (ConfigValidator<?> validator: VALIDATORS) {
|
|
||||||
results.add(validator.validate(yamlConfig, cmd));
|
|
||||||
if (yamlConfig == null) {
|
|
||||||
try {
|
|
||||||
yamlConfig = Config.getInstance().getConfigPath() != null
|
|
||||||
? new YAML(Config.getInstance().getConfigPath())
|
|
||||||
: new YAML("");
|
|
||||||
} catch (IOException | YamlInvalidContentException ignored) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results.contains(ValidationResult.INVALID) || results.contains(ValidationResult.MISSING)) System.exit(1);
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Options initOptions() {
|
|
||||||
Options options = new Options();
|
|
||||||
Arrays.stream(ConfigProperty.values())
|
|
||||||
.filter(prop -> prop.abrv() != null)
|
|
||||||
.map(prop -> optionOf(prop, prop.abrv(), prop.args()))
|
|
||||||
.forEach(options::addOption);
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static CommandLine parseCommandLineArgs(HelpFormatter formatter, Options options, String[] args) {
|
|
||||||
CommandLineParser parser = new DefaultParser();
|
|
||||||
|
|
||||||
try {
|
|
||||||
CommandLine cmd = parser.parse(options, args);
|
|
||||||
if (cmd == null) throw new NullPointerException();
|
|
||||||
return cmd;
|
|
||||||
} catch (ParseException | NullPointerException e) {
|
|
||||||
formatter.printHelp(130, "java -jar MKVAudioSubtitlesChanger.jar -l <path_to_library>",
|
|
||||||
"\nParameters:", options,
|
|
||||||
"\nFeature requests and bug reports: https://github.com/RatzzFatzz/MKVAudioSubtitleChanger/issues");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
return null; // can't be reached
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void exitIfHelp(CommandLine cmd, Options options, HelpFormatter formatter) {
|
|
||||||
if (cmd.hasOption(HELP.prop())) {
|
|
||||||
formatter.printHelp(130, "java -jar MKVAudioSubtitlesChanger.jar -l <path_to_library>",
|
|
||||||
"\nParameters:", options,
|
|
||||||
"\nFeature requests and bug reports: https://github.com/RatzzFatzz/MKVAudioSubtitleChanger/issues");
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void exitIfVersion(CommandLine cmd) {
|
|
||||||
if (cmd.hasOption(VERSION.prop())) {
|
|
||||||
System.out.printf("MKV Audio Subtitle Changer Version %s%n", ProjectUtil.getVersion());
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
|
||||||
|
|
||||||
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.isAudioLanguageValid;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.LanguageValidatorUtil.isLanguageValid;
|
|
||||||
|
|
||||||
public class AttributeConfigValidator extends ConfigValidator<List<AttributeConfig>> {
|
|
||||||
private static final String SEPARATOR = ":";
|
|
||||||
|
|
||||||
public AttributeConfigValidator() {
|
|
||||||
super(ConfigProperty.ATTRIBUTE_CONFIG, true, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public ValidationResult validate(YAML yaml, CommandLine cmd) {
|
|
||||||
System.out.printf("%s: ", property.prop());
|
|
||||||
List<AttributeConfig> result;
|
|
||||||
|
|
||||||
|
|
||||||
if (cmd.hasOption(property.prop())) {
|
|
||||||
List<String> values = List.of(cmd.getOptionValues(property.prop()));
|
|
||||||
result = values.stream().anyMatch(pair -> !pair.contains(SEPARATOR))
|
|
||||||
? List.of()
|
|
||||||
: values.stream().map(pair -> pair.split(SEPARATOR))
|
|
||||||
.map(split -> new AttributeConfig(split[0], split[1]))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
} else if(yaml.getKeysFiltered(property.prop() + ".*").size() > 0) {
|
|
||||||
Function<String, String> audio = key -> yaml.getString(key + ".audio", null);
|
|
||||||
Function<String, String> subtitle = key -> yaml.getString(key + ".subtitle", null);
|
|
||||||
|
|
||||||
result = yaml.getKeysFiltered(".*audio.*").stream()
|
|
||||||
.sorted()
|
|
||||||
.map(key -> key.replace(".audio", ""))
|
|
||||||
.map(key -> new AttributeConfig(audio.apply(key), subtitle.apply(key)))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
} else if (required) {
|
|
||||||
System.out.println("missing");
|
|
||||||
return ValidationResult.MISSING;
|
|
||||||
} else {
|
|
||||||
System.out.println("ok");
|
|
||||||
return ValidationResult.NOT_PRESENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isValid(result) || !setValue(result)) {
|
|
||||||
System.out.println("invalid");
|
|
||||||
return ValidationResult.INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("ok");
|
|
||||||
return ValidationResult.VALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
List<AttributeConfig> parse(String value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
boolean isValid(List<AttributeConfig> result) {
|
|
||||||
if (result.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
boolean isValid;
|
|
||||||
for (AttributeConfig attributeConfig : result) {
|
|
||||||
isValid = isAudioLanguageValid(attributeConfig.getAudioLanguage())
|
|
||||||
&& isLanguageValid(attributeConfig.getSubtitleLanguage());
|
|
||||||
if (!isValid) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +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;
|
|
||||||
|
|
||||||
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,22 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
|
||||||
import org.apache.commons.lang3.math.NumberUtils;
|
|
||||||
|
|
||||||
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,35 +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;
|
|
||||||
|
|
||||||
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,176 +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
|
|
||||||
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,61 +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
|
|
||||||
public class DateValidator extends ConfigValidator<Date> {
|
|
||||||
private static final Date INVALID_DATE = new Date(0);
|
|
||||||
private static final Date DEFAULT_DATE = new Date(1000);
|
|
||||||
|
|
||||||
public DateValidator(ConfigProperty property, boolean required) {
|
|
||||||
super(property, required, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isOverwritingNecessary() {
|
|
||||||
return Config.getInstance().isOnlyNewFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Date overwriteValue() {
|
|
||||||
try {
|
|
||||||
String filePath = AppDirsFactory.getInstance().getUserConfigDir(ProjectUtil.getProjectName(), null, null);
|
|
||||||
File lastExecutionFile = Path.of(filePath + "/last-execution.yml").toFile();
|
|
||||||
if (!lastExecutionFile.exists()) {
|
|
||||||
return DEFAULT_DATE;
|
|
||||||
}
|
|
||||||
YAML yaml = new YAML(lastExecutionFile);
|
|
||||||
return parse(yaml.getString(Config.getInstance().getNormalizedLibraryPath(), DateUtils.convert(DEFAULT_DATE)));
|
|
||||||
} catch (YamlInvalidContentException | IOException e) {
|
|
||||||
log.error("Couldn't open last-execution.properties");
|
|
||||||
return INVALID_DATE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Date parse(String value) {
|
|
||||||
return DateUtils.convert(value, INVALID_DATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean isValid(Date result) {
|
|
||||||
return !result.equals(INVALID_DATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getWeight() {
|
|
||||||
return 40;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +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_MERGER;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.MkvToolNix.MKV_PROP_EDIT;
|
|
||||||
|
|
||||||
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_MERGER + EXE).toFile().isFile()
|
|
||||||
&& Path.of(result.getAbsolutePath() + "/" + MKV_PROP_EDIT + EXE).toFile().isFile())
|
|
||||||
|| (Path.of(result.getAbsolutePath() + "/" + MKV_MERGER).toFile().isFile()
|
|
||||||
&& Path.of(result.getAbsolutePath() + "/" + MKV_PROP_EDIT).toFile().isFile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
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,34 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.regex.PatternSyntaxException;
|
|
||||||
|
|
||||||
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,111 +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;
|
|
||||||
|
|
||||||
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,26 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config.validator;
|
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
|
||||||
import org.apache.commons.lang3.math.NumberUtils;
|
|
||||||
|
|
||||||
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,73 @@
|
|||||||
|
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.LogManager;
|
||||||
|
import org.apache.logging.log4j.core.LoggerContext;
|
||||||
|
import org.apache.logging.log4j.core.appender.RollingFileAppender;
|
||||||
|
import org.apache.logging.log4j.core.config.Configuration;
|
||||||
|
import org.apache.logging.log4j.core.config.Configurator;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
|
||||||
|
@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(getApplicationHome()) : null;
|
||||||
|
FileFilter fileFilter = new FileFilter(config.getExcluded(), config.getIncludePattern(), config.getFilterDate(), lastExecutionHandler);
|
||||||
|
FileProcessor fileProcessor = new CachedFileProcessor(new MkvFileProcessor(config.getMkvToolNix(), fileFilter));
|
||||||
|
AttributeChangeProcessor attributeChangeProcessor = new AttributeChangeProcessor(config.getPreferredSubtitles().toArray(new String[0]), config.getForcedKeywords(), config.getCommentaryKeywords(), config.getHearingImpaired());
|
||||||
|
|
||||||
|
AttributeUpdater kernel = config.getCoherent() != null
|
||||||
|
? new CoherentAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler)
|
||||||
|
: new SingleFileAttributeUpdater(config, fileProcessor, attributeChangeProcessor, lastExecutionHandler);
|
||||||
|
kernel.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApplicationHome() {
|
||||||
|
LoggerContext context = (LoggerContext) LogManager.getContext(false);
|
||||||
|
Configuration config = context.getConfiguration();
|
||||||
|
|
||||||
|
for (org.apache.logging.log4j.core.Appender appender : config.getAppenders().values()) {
|
||||||
|
if (appender instanceof RollingFileAppender rollingFileAppender) {
|
||||||
|
String fileName = rollingFileAppender.getFileName();
|
||||||
|
return new java.io.File(fileName).getParentFile().getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.error("Could not load log4j2 path info");
|
||||||
|
System.out.println("Could not load log4j2 path info");
|
||||||
|
System.exit(1);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.Config;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.DateUtils;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
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
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class FileFilter {
|
public class FileFilter {
|
||||||
static boolean accept(File pathName, String[] fileExtensions) {
|
private final Set<String> excluded;
|
||||||
if (hasProperFileExtension(pathName, fileExtensions)
|
private final Pattern includePattern;
|
||||||
&& hasMatchingPattern(pathName)
|
private final Instant filterDate;
|
||||||
&& isNewer(pathName)) {
|
private final LastExecutionHandler lastExecutionHandler;
|
||||||
return true;
|
|
||||||
|
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();
|
if (!hasMatchingPattern(pathName)
|
||||||
ResultStatistic.getInstance().excluded();
|
|| isExcluded(pathName, new HashSet<>(excluded))
|
||||||
return false;
|
|| 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) {
|
private boolean hasProperFileExtension(File pathName, Set<String> fileExtensions) {
|
||||||
return StringUtils.endsWithAny(pathName.getAbsolutePath().toLowerCase(), fileExtensions);
|
Matcher matcher = extensionPattern.matcher(pathName.getName());
|
||||||
|
return matcher.find() && fileExtensions.contains(matcher.group(EXTENSION_GROUP));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasMatchingPattern(File pathName) {
|
private boolean hasMatchingPattern(File pathName) {
|
||||||
return Config.getInstance().getIncludePattern().matcher(pathName.getName()).matches();
|
return includePattern.matcher(pathName.getName()).matches();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isNewer(File pathName) {
|
private boolean isNewer(File pathName, Instant date) {
|
||||||
Config config = Config.getInstance();
|
if (date == null) return true;
|
||||||
if (config.getFilterDate() == null) return true;
|
|
||||||
try {
|
try {
|
||||||
BasicFileAttributes attributes = Files.readAttributes(pathName.toPath(), BasicFileAttributes.class);
|
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) {
|
} catch (IOException e) {
|
||||||
log.warn("File attributes could not be read.", e);
|
log.warn("File attributes could not be read", e);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isNewer(Date creationDate) {
|
private boolean isExcluded(File pathName, Set<String> excludedDirs) {
|
||||||
return creationDate.toInstant().isAfter(Config.getInstance().getFilterDate().toInstant());
|
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.FileInfoDto;
|
|
||||||
|
|
||||||
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(FileInfoDto 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(FileInfoDto info, List<FileAttribute> nonForcedTracks, List<FileAttribute> nonCommentaryTracks,
|
|
||||||
AttributeConfig... configs);
|
|
||||||
|
|
||||||
List<FileAttribute> retrieveNonForcedTracks(List<FileAttribute> attributes);
|
|
||||||
|
|
||||||
List<FileAttribute> retrieveNonCommentaryTracks(List<FileAttribute> attributes);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the file.
|
|
||||||
* @param file to be updated
|
|
||||||
* @param fileInfo information to update file
|
|
||||||
* @throws IOException when error occurs accessing file retrieving information
|
|
||||||
* @throws MkvToolNixException when error occurs while sending query to mkvpropedit
|
|
||||||
*/
|
|
||||||
void update(File file, FileInfoDto fileInfo) throws IOException, MkvToolNixException;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
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 static final String FILE_NAME = "last-execution.properties";
|
||||||
|
private final File file;
|
||||||
|
private final Properties lastFileExecution;
|
||||||
|
|
||||||
|
public LastExecutionHandler(String path) {
|
||||||
|
file = new File(path + "/" + FILE_NAME);
|
||||||
|
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.info("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,190 +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 ENABLE_FORCED_TRACK = "--edit track:%s --set flag-forced=1 ";
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public List<FileAttribute> loadAttributes(File file) {
|
|
||||||
Map<String, Object> jsonMap;
|
|
||||||
List<FileAttribute> fileAttributes = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
String command = format("\"%s\"", Config.getInstance().getPathFor(MkvToolNix.MKV_MERGER));
|
|
||||||
String[] arguments = new String[]{
|
|
||||||
command,
|
|
||||||
"--identify",
|
|
||||||
"--identification-format",
|
|
||||||
"json",
|
|
||||||
file.getAbsoluteFile().toString()
|
|
||||||
};
|
|
||||||
|
|
||||||
InputStream inputStream = Runtime.getRuntime().exec(arguments).getInputStream();
|
|
||||||
jsonMap = mapper.readValue(inputStream, Map.class);
|
|
||||||
List<Map<String, Object>> tracks = (List<Map<String, Object>>) jsonMap.get("tracks");
|
|
||||||
if (tracks == null) {
|
|
||||||
log.warn("Couldn't retrieve information of {}", file.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(fileAttributes.toString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
log.error("File could not be found or loaded!");
|
|
||||||
}
|
|
||||||
return fileAttributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void detectDefaultTracks(FileInfoDto info, List<FileAttribute> attributes, List<FileAttribute> nonForcedTracks) {
|
|
||||||
Set<FileAttribute> detectedForcedSubtitleLanes = new HashSet<>();
|
|
||||||
for (FileAttribute attribute : attributes) {
|
|
||||||
if (attribute.isDefaultTrack() && AUDIO.equals(attribute.getType()))
|
|
||||||
info.getDefaultAudioLanes().add(attribute);
|
|
||||||
if (attribute.isDefaultTrack() && SUBTITLES.equals(attribute.getType()))
|
|
||||||
info.getDefaultSubtitleLanes().add(attribute);
|
|
||||||
if (attribute.isForcedTrack() && SUBTITLES.equals(attribute.getType()))
|
|
||||||
detectedForcedSubtitleLanes.add(attribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
info.setDesiredForcedSubtitleLanes(attributes.stream()
|
|
||||||
.filter(e -> !nonForcedTracks.contains(e))
|
|
||||||
.filter(e -> !detectedForcedSubtitleLanes.contains(e))
|
|
||||||
.collect(Collectors.toSet())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void detectDesiredTracks(FileInfoDto info, List<FileAttribute> nonForcedTracks, List<FileAttribute> nonCommentaryTracks,
|
|
||||||
AttributeConfig... configs) {
|
|
||||||
Set<FileAttribute> tracks = SetUtils.retainOf(nonForcedTracks, nonCommentaryTracks);
|
|
||||||
Set<FileAttribute> audioTracks = tracks.stream().filter(a -> AUDIO.equals(a.getType())).collect(Collectors.toSet());
|
|
||||||
Set<FileAttribute> subtitleTracks = tracks.stream().filter(a -> SUBTITLES.equals(a.getType())).collect(Collectors.toSet());
|
|
||||||
|
|
||||||
for (AttributeConfig config : configs) {
|
|
||||||
Optional<FileAttribute> desiredAudio = detectDesiredTrack(config.getAudioLanguage(), audioTracks).findFirst();
|
|
||||||
Optional<FileAttribute> desiredSubtitle = detectDesiredSubtitleTrack(config.getSubtitleLanguage(), subtitleTracks).findFirst();
|
|
||||||
|
|
||||||
if (desiredAudio.isPresent() && ("OFF".equals(config.getSubtitleLanguage()) || desiredSubtitle.isPresent())) {
|
|
||||||
info.setMatchedConfig(config);
|
|
||||||
info.setDesiredAudioLane(desiredAudio.get());
|
|
||||||
info.setDesiredSubtitleLane(desiredSubtitle.orElse(null));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Stream<FileAttribute> detectDesiredTrack(String language, Set<FileAttribute> tracks) {
|
|
||||||
return tracks.stream().filter(track -> language.equals(track.getLanguage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Stream<FileAttribute> detectDesiredSubtitleTrack(String language, Set<FileAttribute> tracks) {
|
|
||||||
return detectDesiredTrack(language, tracks)
|
|
||||||
.sorted(subtitleTrackComparator.reversed());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<FileAttribute> retrieveNonForcedTracks(List<FileAttribute> attributes) {
|
|
||||||
return attributes.stream()
|
|
||||||
.filter(elem -> !StringUtils.containsAnyIgnoreCase(elem.getTrackName(),
|
|
||||||
Config.getInstance().getForcedKeywords().toArray(new CharSequence[0])))
|
|
||||||
.filter(elem -> !elem.isForcedTrack())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<FileAttribute> retrieveNonCommentaryTracks(List<FileAttribute> attributes) {
|
|
||||||
return attributes.stream()
|
|
||||||
.filter(elem -> !StringUtils.containsAnyIgnoreCase(elem.getTrackName(),
|
|
||||||
Config.getInstance().getCommentaryKeywords().toArray(new CharSequence[0])))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void update(File file, FileInfoDto fileInfo) throws IOException, MkvToolNixException {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(format("\"%s\" ", Config.getInstance().getPathFor(MkvToolNix.MKV_PROP_EDIT)));
|
|
||||||
sb.append(format("\"%s\" ", file.getAbsolutePath()));
|
|
||||||
|
|
||||||
if (fileInfo.isAudioDifferent()) {
|
|
||||||
if (fileInfo.getDefaultAudioLanes() != null && !fileInfo.getDefaultAudioLanes().isEmpty()) {
|
|
||||||
for (FileAttribute track: fileInfo.getDefaultAudioLanes()) {
|
|
||||||
sb.append(format(DISABLE_DEFAULT_TRACK, track.getId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.append(format(ENABLE_DEFAULT_TRACK, fileInfo.getDesiredAudioLane().getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileInfo.isSubtitleDifferent()) {
|
|
||||||
if (fileInfo.getDefaultSubtitleLanes() != null && !fileInfo.getDefaultSubtitleLanes().isEmpty()) {
|
|
||||||
for (FileAttribute track: fileInfo.getDefaultSubtitleLanes()) {
|
|
||||||
sb.append(format(DISABLE_DEFAULT_TRACK, track.getId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fileInfo.getDesiredSubtitleLane() != null) {
|
|
||||||
sb.append(format(ENABLE_DEFAULT_TRACK, fileInfo.getDesiredSubtitleLane().getId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileInfo.areForcedTracksDifferent()) {
|
|
||||||
for (FileAttribute attribute : fileInfo.getDesiredForcedSubtitleLanes()) {
|
|
||||||
sb.append(format(ENABLE_FORCED_TRACK, attribute.getId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info(sb.toString());
|
|
||||||
InputStream inputstream = Runtime.getRuntime().exec(sb.toString()).getInputStream();
|
|
||||||
String output = IOUtils.toString(new InputStreamReader(inputstream));
|
|
||||||
log.debug(output);
|
|
||||||
if (output.contains("Error")) throw new MkvToolNixException(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +1,49 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes;
|
||||||
import lombok.RequiredArgsConstructor;
|
import org.apache.logging.log4j.util.Strings;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
public class SubtitleTrackComparator implements Comparator<TrackAttributes> {
|
||||||
public class SubtitleTrackComparator implements Comparator<FileAttribute> {
|
private final Set<String> preferredSubtitles;
|
||||||
private final 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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int compare(FileAttribute track1, FileAttribute track2) {
|
public int compare(TrackAttributes track1, TrackAttributes track2) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
if (StringUtils.containsAnyIgnoreCase(track1.getTrackName(), preferredSubtitles)) {
|
String track1Name = Strings.isNotBlank(track1.trackName()) ? track1.trackName().toLowerCase() : "";
|
||||||
result++;
|
String track2Name = Strings.isNotBlank(track2.trackName()) ? track2.trackName().toLowerCase() : "";
|
||||||
}
|
|
||||||
if (StringUtils.containsAnyIgnoreCase(track2.getTrackName(), preferredSubtitles)) {
|
if (preferredSubtitles.contains(track1Name)) result++;
|
||||||
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 (result == 0) {
|
||||||
if (track1.isDefaultTrack()) result++;
|
if (track1.defaultt()) result++;
|
||||||
if (track2.isDefaultTrack()) result--;
|
if (track2.defaultt()) result--;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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,173 +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.FileInfoDto;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ResultStatistic;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.DateUtils;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.util.ProjectUtil;
|
|
||||||
import at.pcgamingfreaks.yaml.YAML;
|
|
||||||
import at.pcgamingfreaks.yaml.YamlInvalidContentException;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import me.tongfei.progressbar.ProgressBar;
|
|
||||||
import me.tongfei.progressbar.ProgressBarBuilder;
|
|
||||||
import me.tongfei.progressbar.ProgressBarStyle;
|
|
||||||
import net.harawata.appdirs.AppDirsFactory;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public abstract class AttributeUpdaterKernel {
|
|
||||||
|
|
||||||
protected final FileCollector collector;
|
|
||||||
protected final FileProcessor processor;
|
|
||||||
protected final ResultStatistic statistic = ResultStatistic.getInstance();
|
|
||||||
private final ExecutorService executor = Executors.newFixedThreadPool(Config.getInstance().getThreads());
|
|
||||||
|
|
||||||
protected ProgressBarBuilder pbBuilder() {
|
|
||||||
return new ProgressBarBuilder()
|
|
||||||
.setStyle(ProgressBarStyle.ASCII)
|
|
||||||
.setUpdateIntervalMillis(250)
|
|
||||||
.setMaxRenderedLength(75);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
public void execute() {
|
|
||||||
statistic.startTimer();
|
|
||||||
|
|
||||||
try (ProgressBar progressBar = pbBuilder().build()) {
|
|
||||||
List<File> files = loadFiles(Config.getInstance().getLibraryPath().getAbsolutePath());
|
|
||||||
progressBar.maxHint(files.size());
|
|
||||||
|
|
||||||
files.forEach(file -> executor.submit(() -> {
|
|
||||||
process(file);
|
|
||||||
progressBar.step();
|
|
||||||
}));
|
|
||||||
|
|
||||||
executor.shutdown();
|
|
||||||
executor.awaitTermination(1, TimeUnit.DAYS);
|
|
||||||
}
|
|
||||||
|
|
||||||
endProcess();
|
|
||||||
|
|
||||||
statistic.stopTimer();
|
|
||||||
statistic.printResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<File> loadExcludedFiles() {
|
|
||||||
List<File> excludedFiles = Config.getInstance().getExcludedDirectories().stream()
|
|
||||||
.map(collector::loadFiles)
|
|
||||||
.flatMap(Collection::stream)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
statistic.increaseTotalBy(excludedFiles.size());
|
|
||||||
statistic.increaseExcludedBy(excludedFiles.size());
|
|
||||||
return excludedFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load files or directories to update.
|
|
||||||
* Remove excluded directories.
|
|
||||||
*
|
|
||||||
* @param path Path to library
|
|
||||||
* @return List of files to update.
|
|
||||||
*/
|
|
||||||
abstract List<File> loadFiles(String path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start of the file updating process.
|
|
||||||
* This method is called by the executor and its contents are executed in parallel.
|
|
||||||
*
|
|
||||||
* @param file file or directory to update
|
|
||||||
*/
|
|
||||||
void process(File file) {
|
|
||||||
FileInfoDto fileInfo = new FileInfoDto(file);
|
|
||||||
List<FileAttribute> attributes = processor.loadAttributes(file);
|
|
||||||
|
|
||||||
List<FileAttribute> nonForcedTracks = processor.retrieveNonForcedTracks(attributes);
|
|
||||||
List<FileAttribute> nonCommentaryTracks = processor.retrieveNonCommentaryTracks(attributes);
|
|
||||||
|
|
||||||
processor.detectDefaultTracks(fileInfo, attributes, nonForcedTracks);
|
|
||||||
processor.detectDesiredTracks(fileInfo, nonForcedTracks, nonCommentaryTracks,
|
|
||||||
Config.getInstance().getAttributeConfig().toArray(new AttributeConfig[]{}));
|
|
||||||
|
|
||||||
updateFile(fileInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Persist file changes.
|
|
||||||
*
|
|
||||||
* @param fileInfoDto contains information about file and desired configuration.
|
|
||||||
*/
|
|
||||||
protected void updateFile(FileInfoDto fileInfoDto) {
|
|
||||||
statistic.total();
|
|
||||||
switch (fileInfoDto.getStatus()) {
|
|
||||||
case CHANGE_NECESSARY:
|
|
||||||
statistic.shouldChange();
|
|
||||||
commitChange(fileInfoDto);
|
|
||||||
break;
|
|
||||||
case UNABLE_TO_APPLY:
|
|
||||||
statistic.noSuitableConfigFound();
|
|
||||||
break;
|
|
||||||
case ALREADY_SUITED:
|
|
||||||
statistic.alreadyFits();
|
|
||||||
break;
|
|
||||||
case UNKNOWN:
|
|
||||||
default:
|
|
||||||
statistic.failure();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void commitChange(FileInfoDto fileInfo) {
|
|
||||||
if (Config.getInstance().isSafeMode()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
processor.update(fileInfo.getFile(), fileInfo);
|
|
||||||
statistic.success();
|
|
||||||
log.info("Updated {}", fileInfo.getFile().getAbsolutePath());
|
|
||||||
} catch (IOException | MkvToolNixException e) {
|
|
||||||
statistic.failedChanging();
|
|
||||||
log.warn("File couldn't be updated: '{}', Error: {}", fileInfo.getFile().getAbsoluteFile(), e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void endProcess() {
|
|
||||||
if (Config.getInstance().isSafeMode()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String filePath = AppDirsFactory.getInstance().getUserConfigDir(ProjectUtil.getProjectName(), null, null);
|
|
||||||
|
|
||||||
File configDir = Path.of(filePath).toFile();
|
|
||||||
if (!configDir.exists()) configDir.mkdirs();
|
|
||||||
|
|
||||||
File lastExecutionFile = Path.of(filePath + "/last-execution.yml").toFile();
|
|
||||||
if (!lastExecutionFile.exists()) lastExecutionFile.createNewFile();
|
|
||||||
|
|
||||||
YAML yaml = new YAML(lastExecutionFile);
|
|
||||||
yaml.set(Config.getInstance().getNormalizedLibraryPath(), DateUtils.convert(new Date()));
|
|
||||||
yaml.save(lastExecutionFile);
|
|
||||||
} catch (IOException | YamlInvalidContentException e) {
|
|
||||||
log.error("last-execution.yml could not be created or read.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,111 +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.FileInfoDto;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import me.tongfei.progressbar.ProgressBarBuilder;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class CoherentAttributeUpdaterKernel extends AttributeUpdaterKernel {
|
|
||||||
|
|
||||||
public CoherentAttributeUpdaterKernel(FileCollector collector, FileProcessor processor) {
|
|
||||||
super(collector, processor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ProgressBarBuilder pbBuilder() {
|
|
||||||
return super.pbBuilder()
|
|
||||||
.setUnit(" directories", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
List<File> loadFiles(String path) {
|
|
||||||
return loadFiles(path, Config.getInstance().getCoherent());
|
|
||||||
}
|
|
||||||
|
|
||||||
List<File> loadFiles(String path, int depth) {
|
|
||||||
List<File> excludedFiles = loadExcludedFiles();
|
|
||||||
List<File> directories = collector.loadDirectories(path, depth)
|
|
||||||
.stream().filter(file -> !excludedFiles.contains(file))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
return directories.stream()
|
|
||||||
.filter(dir -> isParentDirectory(dir, directories))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isParentDirectory(File directory, List<File> directories) {
|
|
||||||
String path = directory.getAbsolutePath();
|
|
||||||
return directories.stream()
|
|
||||||
.noneMatch(dir -> dir.getAbsolutePath().contains(path) && !StringUtils.equals(path, dir.getAbsolutePath()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update files in directory, if possible, with the same {@link AttributeConfig}.
|
|
||||||
* If {@link Config#isForceCoherent()} then there will be no changes to the file if they don't match the same config.
|
|
||||||
* Otherwise, the default behaviour is executed.
|
|
||||||
* This method is called by the executor and is run in parallel.
|
|
||||||
*
|
|
||||||
* @param file directory containing files
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
void process(File file) {
|
|
||||||
process(file, Config.getInstance().getCoherent());
|
|
||||||
}
|
|
||||||
|
|
||||||
void process(File file, int depth) {
|
|
||||||
// TODO: Implement level crawl if coherence is not possible on user entered depth
|
|
||||||
// IMPL idea: recursive method call, cache needs to be implemented
|
|
||||||
List<FileInfoDto> fileInfos = collector.loadFiles(file.getAbsolutePath()).stream()
|
|
||||||
.map(FileInfoDto::new)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
for (AttributeConfig config : Config.getInstance().getAttributeConfig()) {
|
|
||||||
|
|
||||||
for (FileInfoDto fileInfo : fileInfos) {
|
|
||||||
List<FileAttribute> attributes = processor.loadAttributes(fileInfo.getFile());
|
|
||||||
|
|
||||||
List<FileAttribute> nonForcedTracks = processor.retrieveNonForcedTracks(attributes);
|
|
||||||
List<FileAttribute> nonCommentaryTracks = processor.retrieveNonCommentaryTracks(attributes);
|
|
||||||
|
|
||||||
processor.detectDefaultTracks(fileInfo, attributes, nonForcedTracks);
|
|
||||||
processor.detectDesiredTracks(fileInfo, nonForcedTracks, nonCommentaryTracks, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileInfos.stream().allMatch(elem -> ("OFF".equals(config.getSubtitleLanguage()) || elem.getDesiredSubtitleLane() != null)
|
|
||||||
&& elem.getDesiredAudioLane() != null)) {
|
|
||||||
log.info("Found {}/{} match for {}", config.getAudioLanguage(), config.getSubtitleLanguage(), file.getAbsolutePath());
|
|
||||||
fileInfos.forEach(this::updateFile);
|
|
||||||
return; // match found, end process here
|
|
||||||
}
|
|
||||||
|
|
||||||
fileInfos.forEach(f -> {
|
|
||||||
f.setDesiredAudioLane(null);
|
|
||||||
f.setDesiredSubtitleLane(null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("No coherent match found for {}", file.getAbsoluteFile());
|
|
||||||
|
|
||||||
for (FileInfoDto fileInfo : fileInfos) {
|
|
||||||
if (!Config.getInstance().isForceCoherent()) {
|
|
||||||
super.process(fileInfo.getFile());
|
|
||||||
} else {
|
|
||||||
statistic.excluded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastExecutionHandler != null) 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 com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
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,27 +10,35 @@ import java.util.Objects;
|
|||||||
@Getter
|
@Getter
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class AttributeConfig {
|
public class AttributeConfig {
|
||||||
private final String audioLanguage;
|
private final String audioLang;
|
||||||
private final String subtitleLanguage;
|
private final String subLang;
|
||||||
|
|
||||||
|
public static AttributeConfig of(String audioLanguage, String subtitleLanguage) {
|
||||||
|
return new AttributeConfig(audioLanguage, subtitleLanguage);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
AttributeConfig that = (AttributeConfig) o;
|
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
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(audioLanguage, subtitleLanguage);
|
return Objects.hash(audioLang, subLang);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toStringShort() {
|
||||||
|
return audioLang + ":" + subLang;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "AttributeConfig{"
|
return "AttributeConfig{"
|
||||||
+ "audioLanguage='" + audioLanguage + '\''
|
+ "audioLanguage='" + audioLang + '\''
|
||||||
+ ", subtitleLanguage='" + subtitleLanguage + '\'' +
|
+ ", 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +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.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class FileInfo {
|
||||||
|
private final File file;
|
||||||
|
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
private final List<TrackAttributes> tracks = new ArrayList<>();
|
||||||
|
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
private final List<TrackAttributes> audioTracks = new ArrayList<>();
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
private final List<TrackAttributes> subtitleTracks = new ArrayList<>();
|
||||||
|
|
||||||
|
private PlannedChange changes = new PlannedChange();
|
||||||
|
@Setter
|
||||||
|
private AttributeConfig matchedConfig;
|
||||||
|
|
||||||
|
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 void addTracks(Collection<TrackAttributes> tracks) {
|
||||||
|
for (TrackAttributes track : tracks) addTrack(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TrackAttributes> getTracks() {
|
||||||
|
return Collections.unmodifiableList(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TrackAttributes> getAudioTracks() {
|
||||||
|
return Collections.unmodifiableList(audioTracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TrackAttributes> getSubtitleTracks() {
|
||||||
|
return Collections.unmodifiableList(subtitleTracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetChanges() {
|
||||||
|
changes = new PlannedChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class FileInfoDto {
|
|
||||||
private final File file;
|
|
||||||
private Set<FileAttribute> defaultAudioLanes = new HashSet<>();
|
|
||||||
private Set<FileAttribute> defaultSubtitleLanes = new HashSet<>();
|
|
||||||
private Set<FileAttribute> desiredForcedSubtitleLanes;
|
|
||||||
private FileAttribute desiredAudioLane;
|
|
||||||
private FileAttribute desiredSubtitleLane;
|
|
||||||
private AttributeConfig matchedConfig;
|
|
||||||
|
|
||||||
public boolean isAudioDifferent() {
|
|
||||||
return desiredAudioLane != null &&
|
|
||||||
(defaultAudioLanes == null || !defaultAudioLanes.contains(desiredAudioLane) || defaultAudioLanes.size() > 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSubtitleDifferent() {
|
|
||||||
return isSubtitleMatchDifferent() || isSubtitleOFF();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSubtitleMatchDifferent() {
|
|
||||||
return desiredSubtitleLane != null
|
|
||||||
&& (defaultSubtitleLanes == null || !defaultSubtitleLanes.contains(desiredSubtitleLane) || defaultSubtitleLanes.size() > 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSubtitleOFF() {
|
|
||||||
return desiredSubtitleLane == null && "OFF".equals(matchedConfig.getSubtitleLanguage()) &&
|
|
||||||
(defaultSubtitleLanes != null && !defaultSubtitleLanes.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean areForcedTracksDifferent() {
|
|
||||||
return desiredForcedSubtitleLanes.size() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileStatus getStatus() {
|
|
||||||
if (isChangeNecessary()) return FileStatus.CHANGE_NECESSARY;
|
|
||||||
if (isUnableToApplyConfig()) return FileStatus.UNABLE_TO_APPLY;
|
|
||||||
if (isAlreadySuitable()) return FileStatus.ALREADY_SUITED;
|
|
||||||
return FileStatus.UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isUnableToApplyConfig() {
|
|
||||||
return desiredAudioLane == null && desiredSubtitleLane == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isAlreadySuitable() {
|
|
||||||
return defaultAudioLanes.contains(desiredAudioLane) && defaultSubtitleLanes.contains(desiredSubtitleLane);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isChangeNecessary() {
|
|
||||||
return isAudioDifferent() || isSubtitleDifferent() || areForcedTracksDifferent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "[" + "defaultAudioLanes=" + defaultAudioLanes +
|
|
||||||
", defaultSubtitleLanes=" + defaultSubtitleLanes +
|
|
||||||
", desiredForcedSubtitleLanes=" + desiredForcedSubtitleLanes +
|
|
||||||
", desiredAudioLane=" + desiredAudioLane +
|
|
||||||
", desiredSubtitleLane=" + desiredSubtitleLane +
|
|
||||||
']';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
|
||||||
|
|
||||||
public enum FileStatus {
|
|
||||||
CHANGE_NECESSARY,
|
|
||||||
UNABLE_TO_APPLY,
|
|
||||||
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
|
|||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public enum MkvToolNix {
|
public enum MkvToolNix {
|
||||||
MKV_MERGER("mkvmerge"),
|
MKV_MERGE("mkvmerge"),
|
||||||
MKV_PROP_EDIT("mkvpropedit");
|
MKV_PROP_EDIT("mkvpropedit");
|
||||||
|
|
||||||
private final String file;
|
private final String file;
|
||||||
|
|||||||
@@ -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;
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ResultStatistic {
|
public class ResultStatistic {
|
||||||
private static final String result = "Total files: %s%n" +
|
private static final String PRINT_TEMPLATE = "Total: %s, Changing: %s (Successful: %s, Failed %s), Unchanged: %s, Excluded: %s, Unknown/Failed: %s\nRuntime: %s";
|
||||||
"├─ 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 ResultStatistic instance;
|
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 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 startTime = 0;
|
||||||
private long runtime = 0;
|
private long runtime = 0;
|
||||||
|
|
||||||
public static ResultStatistic getInstance() {
|
public static ResultStatistic getInstance() {
|
||||||
if (instance == null) {
|
return getInstance(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResultStatistic getInstance(boolean reset) {
|
||||||
|
if (instance == null || reset) {
|
||||||
instance = new ResultStatistic();
|
instance = new ResultStatistic();
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void increaseTotalBy(int amount) {
|
public int total() {
|
||||||
filesTotal += amount;
|
return changePlanned + unchanged + excluded + unknownFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void total() {
|
public synchronized void changePlanned() {
|
||||||
filesTotal++;
|
changePlanned++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void increaseExcludedBy(int amount) {
|
public synchronized void changeSuccessful() {
|
||||||
excluded += amount;
|
changeSuccessful++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void changeFailed() {
|
||||||
|
changeFailed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void unchanged() {
|
||||||
|
unchanged++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void increaseUnchangedBy(int amount) {
|
||||||
|
unchanged += amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void excluded() {
|
public synchronized void excluded() {
|
||||||
excluded++;
|
excluded++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void shouldChange() {
|
public synchronized void unknownFailed() {
|
||||||
shouldChange++;
|
unknownFailed++;
|
||||||
}
|
|
||||||
|
|
||||||
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 void startTimer() {
|
public void startTimer() {
|
||||||
@@ -87,11 +70,6 @@ public class ResultStatistic {
|
|||||||
runtime = System.currentTimeMillis() - startTime;
|
runtime = System.currentTimeMillis() - startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void printResult() {
|
|
||||||
System.out.println(prettyPrint());
|
|
||||||
log.info(this.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String formatTimer() {
|
private String formatTimer() {
|
||||||
int seconds = (int) (runtime / 1000);
|
int seconds = (int) (runtime / 1000);
|
||||||
int minutes = seconds / 60;
|
int minutes = seconds / 60;
|
||||||
@@ -109,21 +87,14 @@ public class ResultStatistic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String prettyPrint() {
|
public void print() {
|
||||||
return String.format(result, filesTotal, excluded, shouldChange, failedChanging, successfullyChanged,
|
String result = this.toString();
|
||||||
noSuitableConfigFound, alreadyFits, failed, formatTimer());
|
System.out.println(result);
|
||||||
|
log.info(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ResultStatistic: " + "filesTotal=" + filesTotal +
|
return String.format(PRINT_TEMPLATE, total(), changePlanned, changeSuccessful, changeFailed, unchanged, excluded, unknownFailed, formatTimer());
|
||||||
", excluded=" + excluded +
|
|
||||||
", shouldChange=" + shouldChange +
|
|
||||||
" (failedChanging=" + failedChanging +
|
|
||||||
", successfullyChanged=" + successfullyChanged +
|
|
||||||
"), noSuitableConfigFound=" + noSuitableConfigFound +
|
|
||||||
", alreadyFits=" + alreadyFits +
|
|
||||||
", failed=" + failed +
|
|
||||||
", runtime=" + formatTimer();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,37 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Getter
|
public record TrackAttributes(int id, String language, String trackName,
|
||||||
@AllArgsConstructor
|
boolean defaultt, boolean forced, boolean commentary, boolean hearingImpaired,
|
||||||
public class FileAttribute {
|
TrackType type) {
|
||||||
private final int id;
|
|
||||||
private final String language;
|
|
||||||
private final String trackName;
|
|
||||||
private final boolean defaultTrack;
|
|
||||||
private final boolean forcedTrack;
|
|
||||||
private final LaneType type;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
FileAttribute attribute = (FileAttribute) o;
|
TrackAttributes attribute = (TrackAttributes) o;
|
||||||
return id == attribute.id
|
return id == attribute.id
|
||||||
&& defaultTrack == attribute.defaultTrack
|
&& defaultt == attribute.defaultt
|
||||||
&& forcedTrack == attribute.forcedTrack
|
&& forced == attribute.forced
|
||||||
|
&& commentary == attribute.commentary
|
||||||
|
&& hearingImpaired == attribute.hearingImpaired
|
||||||
&& Objects.equals(language, attribute.language)
|
&& Objects.equals(language, attribute.language)
|
||||||
&& Objects.equals(trackName, attribute.trackName)
|
&& Objects.equals(trackName, attribute.trackName)
|
||||||
&& type == attribute.type;
|
&& type == attribute.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(id, language, trackName, defaultTrack, forcedTrack, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "[" + "id=" + id +
|
return "[" + "id=" + id +
|
||||||
", language='" + language + '\'' +
|
", language='" + language + '\'' +
|
||||||
", trackName='" + trackName + '\'' +
|
", trackName='" + trackName + '\'' +
|
||||||
", defaultTrack=" + defaultTrack +
|
", defaultt=" + defaultt +
|
||||||
", forcedTrack=" + forcedTrack +
|
", forced=" + forced +
|
||||||
|
", commentary=" + commentary +
|
||||||
|
", hearingImpaired=" + hearingImpaired +
|
||||||
", type=" + type +
|
", type=" + type +
|
||||||
']';
|
']';
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.model;
|
||||||
|
|
||||||
public enum LaneType {
|
public enum TrackType {
|
||||||
AUDIO,
|
AUDIO,
|
||||||
SUBTITLES;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,10 +23,6 @@ public class LanguageValidatorUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isAudioLanguageValid(String language) {
|
|
||||||
return !language.equals("OFF") && ISO3_LANGUAGES.contains(language);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isLanguageValid(String language) {
|
public static boolean isLanguageValid(String language) {
|
||||||
return ISO3_LANGUAGES.contains(language);
|
return ISO3_LANGUAGES.contains(language);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
public class ProjectUtil {
|
public class ProjectUtil implements CommandLine.IVersionProvider {
|
||||||
private static final Properties PROJECT_PROPERTIES = new Properties();
|
private static final Properties PROJECT_PROPERTIES = new Properties();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -15,11 +21,32 @@ public class ProjectUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getVersion() {
|
|
||||||
return PROJECT_PROPERTIES.getProperty("version");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getProjectName() {
|
public static String getProjectName() {
|
||||||
return PROJECT_PROPERTIES.getProperty("project_name");
|
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();
|
||||||
|
}
|
||||||
42
src/main/resources/log4j2-debian.yml
Normal file
42
src/main/resources/log4j2-debian.yml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
Configuration:
|
||||||
|
name: MainConfig
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
Property:
|
||||||
|
- name: logDir
|
||||||
|
value: ${sys:user.home}/.local/mkvaudiosubtitlechanger/logs
|
||||||
|
- name: logPattern
|
||||||
|
value: "%d{DEFAULT} | %-5level | %msg %n %throwable"
|
||||||
|
|
||||||
|
Appenders:
|
||||||
|
RollingFile:
|
||||||
|
name: FileAppender
|
||||||
|
fileName: ${logDir}/application.log
|
||||||
|
filePattern: ${logDir}/archive/application-%d{yyyy-MM-dd}-%i.log.gz
|
||||||
|
PatternLayout:
|
||||||
|
Pattern: ${logPattern}
|
||||||
|
ThresholdFilter:
|
||||||
|
level: debug
|
||||||
|
Policies:
|
||||||
|
OnStartupTriggeringPolicy:
|
||||||
|
minSize: 0
|
||||||
|
DefaultRolloverStrategy:
|
||||||
|
max: 30
|
||||||
|
Delete:
|
||||||
|
basePath: logs/archive
|
||||||
|
maxDepth: 1
|
||||||
|
IfLastModified:
|
||||||
|
age: 30d
|
||||||
|
IfAccumulatedFileSize:
|
||||||
|
exceeds: 1GB
|
||||||
|
|
||||||
|
Loggers:
|
||||||
|
Root:
|
||||||
|
level: info
|
||||||
|
AppenderRef:
|
||||||
|
- ref: FileAppender
|
||||||
|
Logger:
|
||||||
|
name: "com.zaxxer.hikari.HikariConfig"
|
||||||
|
level: info
|
||||||
|
AppenderRef:
|
||||||
|
- ref: FileAppender
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
Configuration:
|
|
||||||
name: DefaultLogger
|
|
||||||
Appenders:
|
|
||||||
Console:
|
|
||||||
name: Console_Out
|
|
||||||
PatternLayout:
|
|
||||||
Pattern: "%d{DEFAULT} | %-5level | %thread | %msg %n %throwable"
|
|
||||||
ThresholdFilter:
|
|
||||||
level: debug
|
|
||||||
File:
|
|
||||||
name: FileAppender
|
|
||||||
fileName: default.log
|
|
||||||
PatternLayout:
|
|
||||||
Pattern: "%d{DEFAULT} | %-5level | %thread | %msg %n %throwable"
|
|
||||||
ThresholdFilter:
|
|
||||||
level: debug
|
|
||||||
Loggers:
|
|
||||||
Root:
|
|
||||||
level: debug
|
|
||||||
AppenderRef:
|
|
||||||
- ref: Console_Out
|
|
||||||
- ref: FileAppender
|
|
||||||
Logger:
|
|
||||||
name: "com.zaxxer.hikari.HikariConfig"
|
|
||||||
level: info
|
|
||||||
AppenderRef:
|
|
||||||
- ref: Console_Out
|
|
||||||
- ref: FileAppender
|
|
||||||
49
src/main/resources/log4j2-dev.yml
Normal file
49
src/main/resources/log4j2-dev.yml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
Configuration:
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
Property:
|
||||||
|
- name: logDir
|
||||||
|
value: "./logs"
|
||||||
|
- name: log_pattern
|
||||||
|
value: "%d{DEFAULT} | %-5level | %thread | %C{1} | %msg %n %throwable"
|
||||||
|
|
||||||
|
Appenders:
|
||||||
|
Console:
|
||||||
|
name: Console_Out
|
||||||
|
PatternLayout:
|
||||||
|
Pattern: ${log_pattern}
|
||||||
|
ThresholdFilter:
|
||||||
|
level: debug
|
||||||
|
RollingFile:
|
||||||
|
name: FileAppender
|
||||||
|
fileName: ${logDir}/application.log
|
||||||
|
filePattern: ${logDir}/archive/application-%d{yyyy-MM-dd}-%i.log.gz
|
||||||
|
PatternLayout:
|
||||||
|
Pattern: ${logPattern}
|
||||||
|
ThresholdFilter:
|
||||||
|
level: debug
|
||||||
|
Policies:
|
||||||
|
OnStartupTriggeringPolicy:
|
||||||
|
minSize: 0
|
||||||
|
DefaultRolloverStrategy:
|
||||||
|
max: 30
|
||||||
|
Delete:
|
||||||
|
basePath: logs/archive
|
||||||
|
maxDepth: 1
|
||||||
|
IfLastModified:
|
||||||
|
age: 30d
|
||||||
|
IfAccumulatedFileSize:
|
||||||
|
exceeds: 1GB
|
||||||
|
|
||||||
|
Loggers:
|
||||||
|
Root:
|
||||||
|
level: debug
|
||||||
|
AppenderRef:
|
||||||
|
- ref: Console_Out
|
||||||
|
- ref: FileAppender
|
||||||
|
Logger:
|
||||||
|
name: "com.zaxxer.hikari.HikariConfig"
|
||||||
|
level: info
|
||||||
|
AppenderRef:
|
||||||
|
- ref: Console_Out
|
||||||
|
- ref: FileAppender
|
||||||
42
src/main/resources/log4j2-windows.yml
Normal file
42
src/main/resources/log4j2-windows.yml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
Configuration:
|
||||||
|
name: WindowsConfig
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
Property:
|
||||||
|
- name: logDir
|
||||||
|
value: ${sys:user.home}/AppData/Local/MKVAudioSubtitleChanger/logs
|
||||||
|
- name: logPattern
|
||||||
|
value: "%d{DEFAULT} | %-5level | %msg %n %throwable"
|
||||||
|
|
||||||
|
Appenders:
|
||||||
|
RollingFile:
|
||||||
|
name: FileAppender
|
||||||
|
fileName: ${logDir}/application.log
|
||||||
|
filePattern: ${logDir}/archive/application-%d{yyyy-MM-dd}-%i.log.gz
|
||||||
|
PatternLayout:
|
||||||
|
Pattern: ${logPattern}
|
||||||
|
ThresholdFilter:
|
||||||
|
level: debug
|
||||||
|
Policies:
|
||||||
|
OnStartupTriggeringPolicy:
|
||||||
|
minSize: 0
|
||||||
|
DefaultRolloverStrategy:
|
||||||
|
max: 30
|
||||||
|
Delete:
|
||||||
|
basePath: logs/archive
|
||||||
|
maxDepth: 1
|
||||||
|
IfLastModified:
|
||||||
|
age: 30d
|
||||||
|
IfAccumulatedFileSize:
|
||||||
|
exceeds: 1GB
|
||||||
|
|
||||||
|
Loggers:
|
||||||
|
Root:
|
||||||
|
level: info
|
||||||
|
AppenderRef:
|
||||||
|
- ref: FileAppender
|
||||||
|
Logger:
|
||||||
|
name: "com.zaxxer.hikari.HikariConfig"
|
||||||
|
level: info
|
||||||
|
AppenderRef:
|
||||||
|
- ref: FileAppender
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
Configuration:
|
|
||||||
name: DefaultLogger
|
|
||||||
Appenders:
|
|
||||||
File:
|
|
||||||
name: FileAppender
|
|
||||||
fileName: default.log
|
|
||||||
PatternLayout:
|
|
||||||
Pattern: "%d{DEFAULT} | %-5level | %thread | %msg %n %throwable"
|
|
||||||
ThresholdFilter:
|
|
||||||
level: info
|
|
||||||
Loggers:
|
|
||||||
Root:
|
|
||||||
level: info
|
|
||||||
AppenderRef:
|
|
||||||
- ref: FileAppender
|
|
||||||
Logger:
|
|
||||||
name: "com.zaxxer.hikari.HikariConfig"
|
|
||||||
level: info
|
|
||||||
AppenderRef:
|
|
||||||
- ref: FileAppender
|
|
||||||
42
src/main/resources/log4j2.yml
Normal file
42
src/main/resources/log4j2.yml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
Configuration:
|
||||||
|
name: MainConfig
|
||||||
|
|
||||||
|
Properties:
|
||||||
|
Property:
|
||||||
|
- name: logDir
|
||||||
|
value: ./logs
|
||||||
|
- name: logPattern
|
||||||
|
value: "%d{DEFAULT} | %-5level | %msg %n %throwable"
|
||||||
|
|
||||||
|
Appenders:
|
||||||
|
RollingFile:
|
||||||
|
name: FileAppender
|
||||||
|
fileName: ${logDir}/application.log
|
||||||
|
filePattern: ${logDir}/archive/application-%d{yyyy-MM-dd}-%i.log.gz
|
||||||
|
PatternLayout:
|
||||||
|
Pattern: ${logPattern}
|
||||||
|
ThresholdFilter:
|
||||||
|
level: debug
|
||||||
|
Policies:
|
||||||
|
OnStartupTriggeringPolicy:
|
||||||
|
minSize: 0
|
||||||
|
DefaultRolloverStrategy:
|
||||||
|
max: 30
|
||||||
|
Delete:
|
||||||
|
basePath: logs/archive
|
||||||
|
maxDepth: 1
|
||||||
|
IfLastModified:
|
||||||
|
age: 30d
|
||||||
|
IfAccumulatedFileSize:
|
||||||
|
exceeds: 1GB
|
||||||
|
|
||||||
|
Loggers:
|
||||||
|
Root:
|
||||||
|
level: info
|
||||||
|
AppenderRef:
|
||||||
|
- ref: FileAppender
|
||||||
|
Logger:
|
||||||
|
name: "com.zaxxer.hikari.HikariConfig"
|
||||||
|
level: info
|
||||||
|
AppenderRef:
|
||||||
|
- ref: FileAppender
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.config;
|
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_FILE;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class ConfigLoaderTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void initConfig() {
|
|
||||||
String[] sut = new String[]{"-a", "ger:ger", "-l", TEST_FILE,
|
|
||||||
"-s", "-cf", "-n",
|
|
||||||
"-c", "2",
|
|
||||||
"-t", "4",
|
|
||||||
"-i", ".*[abc].*",
|
|
||||||
"-fk", "testForced",
|
|
||||||
"-ck", "testCommentary",
|
|
||||||
"-ps", "testPreferred"
|
|
||||||
};
|
|
||||||
ConfigLoader.initConfig(sut);
|
|
||||||
|
|
||||||
assertTrue(Config.getInstance().getLibraryPath().exists());
|
|
||||||
assertEquals(List.of(new AttributeConfig("ger", "ger")), Config.getInstance().getAttributeConfig());
|
|
||||||
|
|
||||||
assertTrue(Config.getInstance().isSafeMode());
|
|
||||||
assertTrue(Config.getInstance().isForceCoherent());
|
|
||||||
assertTrue(Config.getInstance().isOnlyNewFiles());
|
|
||||||
assertNotNull(Config.getInstance().getFilterDate());
|
|
||||||
|
|
||||||
assertEquals(2, Config.getInstance().getCoherent());
|
|
||||||
assertEquals(4, Config.getInstance().getThreads());
|
|
||||||
assertEquals(".*[abc].*", Config.getInstance().getIncludePattern().pattern());
|
|
||||||
assertTrue(Config.getInstance().getForcedKeywords().contains("testForced"));
|
|
||||||
assertTrue(Config.getInstance().getCommentaryKeywords().contains("testCommentary"));
|
|
||||||
assertTrue(Config.getInstance().getPreferredSubtitles().contains("testPreferred"));
|
|
||||||
|
|
||||||
assertNull(Config.getInstance().getConfigPath());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,85 +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.AttributeConfig;
|
|
||||||
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.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult.*;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty.*;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.CommandLineOptionsUtil.optionOf;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class AttributeConfigValidatorTest {
|
|
||||||
private static CommandLineParser parser;
|
|
||||||
private static Options options;
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
static void before() {
|
|
||||||
parser = new DefaultParser();
|
|
||||||
options = new Options();
|
|
||||||
options.addOption(optionOf(ATTRIBUTE_CONFIG, ATTRIBUTE_CONFIG.abrv(), ATTRIBUTE_CONFIG.args()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void beforeEach() {
|
|
||||||
Config.getInstance(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<Arguments> provideTestCases() {
|
|
||||||
return Stream.of(
|
|
||||||
Arguments.of(attrConfYaml("jpn", "ger"), new String[]{}, VALID, attrConf("jpn", "ger")),
|
|
||||||
Arguments.of("", new String[]{"-a", "jpn:ger"}, VALID, attrConf("jpn", "ger")),
|
|
||||||
Arguments.of(attrConfYaml("jpn", "ger", "jpn", "eng"), new String[]{}, VALID, attrConf("jpn", "ger", "jpn", "eng")),
|
|
||||||
Arguments.of("", new String[]{"-a", "jpn:ger", "jpn:eng"}, VALID, attrConf("jpn", "ger", "jpn", "eng")),
|
|
||||||
Arguments.of(attrConfYaml("jpn", "ger", "jpn", "OFF"), new String[]{}, VALID, attrConf("jpn", "ger", "jpn", "OFF")),
|
|
||||||
Arguments.of("", new String[]{"-a", "jpn:ger", "jpn:OFF"}, VALID, attrConf("jpn", "ger", "jpn", "OFF")),
|
|
||||||
Arguments.of(attrConfYaml("jpn", "invalid"), new String[]{}, INVALID, null),
|
|
||||||
Arguments.of("", new String[]{}, MISSING, null)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestCases")
|
|
||||||
void validate(String yamlArgs, String[] cmdArgs, ValidationResult expectedResult, List<AttributeConfig> expectedConfig)
|
|
||||||
throws ParseException, YamlInvalidContentException {
|
|
||||||
AttributeConfigValidator underTest = new AttributeConfigValidator();
|
|
||||||
|
|
||||||
ValidationResult result = underTest.validate(new YAML(yamlArgs), parser.parse(options, cmdArgs));
|
|
||||||
|
|
||||||
assertEquals(expectedResult, result);
|
|
||||||
assertIterableEquals(expectedConfig, Config.getInstance().getAttributeConfig());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String attrConfYaml(String... languages) {
|
|
||||||
StringBuilder yaml = new StringBuilder("attribute-config: ");
|
|
||||||
int counter = 0;
|
|
||||||
for (int i = 0; i < languages.length; i += 2) {
|
|
||||||
counter++;
|
|
||||||
yaml.append(String.format("\n %s:\n audio: %s\n subtitle: %s", counter, languages[i], languages[i+1]));
|
|
||||||
}
|
|
||||||
return yaml.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
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,58 +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.model.ConfigProperty.*;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.CommandLineOptionsUtil.optionOf;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.yamlList;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
class BooleanValidatorTest {
|
|
||||||
private static CommandLineParser parser;
|
|
||||||
private static Options options;
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
static void before() {
|
|
||||||
parser = new DefaultParser();
|
|
||||||
options = new Options();
|
|
||||||
options.addOption(optionOf(SAFE_MODE, SAFE_MODE.abrv(), SAFE_MODE.args()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<Arguments> provideTestCases() {
|
|
||||||
return Stream.of(
|
|
||||||
Arguments.of(SAFE_MODE, false, "", new String[]{"-safe-mode"}, VALID),
|
|
||||||
Arguments.of(SAFE_MODE, true, "", new String[]{"-safe-mode"}, VALID),
|
|
||||||
Arguments.of(SAFE_MODE, false, "", new String[]{""}, NOT_PRESENT),
|
|
||||||
Arguments.of(SAFE_MODE, true, "", new String[]{""}, MISSING),
|
|
||||||
Arguments.of(SAFE_MODE, false, yamlList(ARGUMENTS, SAFE_MODE), new String[]{""}, VALID),
|
|
||||||
Arguments.of(SAFE_MODE, true, yamlList(ARGUMENTS, SAFE_MODE), new String[]{""}, VALID),
|
|
||||||
Arguments.of(SAFE_MODE, false, yamlList(ARGUMENTS, WINDOWS), new String[]{""}, NOT_PRESENT),
|
|
||||||
Arguments.of(SAFE_MODE, true, yamlList(ARGUMENTS, WINDOWS), new String[]{""}, MISSING)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestCases")
|
|
||||||
void validate(ConfigProperty property, boolean required, String yamlArgs, String[] cmdArgs,
|
|
||||||
ValidationResult expectedResult) throws ParseException, YamlInvalidContentException {
|
|
||||||
BooleanValidator underTest = new BooleanValidator(property, required);
|
|
||||||
|
|
||||||
ValidationResult result = underTest.validate(new YAML(yamlArgs), parser.parse(options, cmdArgs));
|
|
||||||
|
|
||||||
assertEquals(expectedResult, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +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.io.File;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult.*;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult.MISSING;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty.*;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.CommandLineOptionsUtil.optionOf;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class MkvToolNixPathValidatorTest {
|
|
||||||
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 CommandLineParser parser;
|
|
||||||
private static Options options;
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
static void before() {
|
|
||||||
parser = new DefaultParser();
|
|
||||||
options = new Options();
|
|
||||||
options.addOption(optionOf(MKV_TOOL_NIX, MKV_TOOL_NIX.abrv(), MKV_TOOL_NIX.args()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<Arguments> provideTestCases() {
|
|
||||||
return Stream.of(
|
|
||||||
Arguments.of(MKV_TOOL_NIX, false, null, "", new String[]{"-m", TEST_MKVTOOLNIX_DIR}, VALID),
|
|
||||||
Arguments.of(MKV_TOOL_NIX, true, null, "", new String[]{"-m", TEST_MKVTOOLNIX_EXE_DIR}, VALID),
|
|
||||||
Arguments.of(MKV_TOOL_NIX, false, null, "mkvtoolnix: " + TEST_MKVTOOLNIX_EXE_DIR, new String[]{}, VALID),
|
|
||||||
Arguments.of(MKV_TOOL_NIX, true, null, "mkvtoolnix: " + TEST_MKVTOOLNIX_DIR, new String[]{}, VALID),
|
|
||||||
Arguments.of(MKV_TOOL_NIX, false, Path.of(TEST_MKVTOOLNIX_EXE_DIR).toFile(), "", new String[]{}, DEFAULT),
|
|
||||||
Arguments.of(MKV_TOOL_NIX, false, null, "", new String[]{}, NOT_PRESENT),
|
|
||||||
Arguments.of(MKV_TOOL_NIX, true, null, "", new String[]{}, MISSING),
|
|
||||||
Arguments.of(MKV_TOOL_NIX, true, null, "", new String[]{"-m", TEST_INVALID_DIR}, INVALID)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestCases")
|
|
||||||
void validate(ConfigProperty property, boolean required, File defaultValue, String yamlArgs, String[] cmdArgs,
|
|
||||||
ValidationResult expectedResult) throws ParseException, YamlInvalidContentException {
|
|
||||||
MkvToolNixPathValidator underTest = new MkvToolNixPathValidator(property, required, defaultValue);
|
|
||||||
|
|
||||||
ValidationResult result = underTest.validate(new YAML(yamlArgs), parser.parse(options, cmdArgs));
|
|
||||||
|
|
||||||
assertEquals(expectedResult, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,65 +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.io.File;
|
|
||||||
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.LIBRARY;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.CommandLineOptionsUtil.optionOf;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_DIR;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.PathUtils.TEST_FILE;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.argumentsOf;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class PathValidatorTest {
|
|
||||||
private static CommandLineParser parser;
|
|
||||||
private static Options options;
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
static void before() {
|
|
||||||
parser = new DefaultParser();
|
|
||||||
options = new Options();
|
|
||||||
options.addOption(optionOf(LIBRARY, LIBRARY.abrv(), LIBRARY.args()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<Arguments> provideTestCases() {
|
|
||||||
return Stream.of(
|
|
||||||
argumentsOf(LIBRARY, false, null, "library-path: " + TEST_DIR, new String[]{}, VALID),
|
|
||||||
argumentsOf(LIBRARY, true, null, "", new String[]{"-l", TEST_FILE}, VALID),
|
|
||||||
|
|
||||||
argumentsOf(LIBRARY, false, TEST_DIR, "", new String[]{}, DEFAULT),
|
|
||||||
argumentsOf(LIBRARY, true, TEST_FILE, "", new String[]{}, DEFAULT),
|
|
||||||
argumentsOf(LIBRARY, true, null, "", new String[]{}, MISSING),
|
|
||||||
argumentsOf(LIBRARY, false, null, "", new String[]{}, NOT_PRESENT),
|
|
||||||
|
|
||||||
argumentsOf(LIBRARY, true, null, "", new String[]{"-l", TEST_DIR + "/invalid"}, INVALID),
|
|
||||||
argumentsOf(LIBRARY, false, null, "library-path: " + TEST_DIR + "/invalid", new String[]{}, INVALID),
|
|
||||||
argumentsOf(LIBRARY, true, TEST_DIR, "", new String[]{"-l", TEST_DIR + "/invalid"}, INVALID)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestCases")
|
|
||||||
void validate(ConfigProperty property, boolean required, File defaultValue, String yamlArgs, String[] cmdArgs,
|
|
||||||
ValidationResult expectedResult) throws ParseException, YamlInvalidContentException {
|
|
||||||
PathValidator underTest = new PathValidator(property, required, defaultValue);
|
|
||||||
|
|
||||||
ValidationResult result = underTest.validate(new YAML(yamlArgs), parser.parse(options, cmdArgs));
|
|
||||||
|
|
||||||
assertEquals(expectedResult, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.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.regex.Pattern;
|
|
||||||
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.INCLUDE_PATTERN;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.CommandLineOptionsUtil.optionOf;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.argumentsOf;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class PatternValidatorTest {
|
|
||||||
private static CommandLineParser parser;
|
|
||||||
private static Options options;
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
static void before() {
|
|
||||||
parser = new DefaultParser();
|
|
||||||
options = new Options();
|
|
||||||
options.addOption(optionOf(INCLUDE_PATTERN, INCLUDE_PATTERN.abrv(), INCLUDE_PATTERN.args()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<Arguments> provideTestCases() {
|
|
||||||
return Stream.of(
|
|
||||||
argumentsOf(INCLUDE_PATTERN, false, null, "include-pattern: \"[abd]?.*\"", new String[]{}, VALID),
|
|
||||||
argumentsOf(INCLUDE_PATTERN, true, null, "", new String[]{"-i", "[abd]?.*"}, VALID),
|
|
||||||
|
|
||||||
argumentsOf(INCLUDE_PATTERN, false, Pattern.compile(".*"), "", new String[]{}, DEFAULT),
|
|
||||||
argumentsOf(INCLUDE_PATTERN, true, Pattern.compile(".*"), "", new String[]{}, DEFAULT),
|
|
||||||
|
|
||||||
argumentsOf(INCLUDE_PATTERN, true, null, "", new String[]{}, MISSING),
|
|
||||||
argumentsOf(INCLUDE_PATTERN, false, null, "", new String[]{}, NOT_PRESENT),
|
|
||||||
|
|
||||||
argumentsOf(INCLUDE_PATTERN, true, null, "", new String[]{"-i", "?."}, INVALID),
|
|
||||||
argumentsOf(INCLUDE_PATTERN, false, null, "include-pattern: \"[arst*\"", new String[]{}, INVALID),
|
|
||||||
argumentsOf(INCLUDE_PATTERN, true, Pattern.compile(".?"), "", new String[]{"-i", "?."}, INVALID)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestCases")
|
|
||||||
void validate(ConfigProperty property, boolean required, Pattern defaultValue, String yamlArgs, String[] cmdArgs,
|
|
||||||
ValidationResult expectedResult) throws ParseException, YamlInvalidContentException {
|
|
||||||
PatternValidator underTest = new PatternValidator(property, required, defaultValue);
|
|
||||||
|
|
||||||
ValidationResult result = underTest.validate(new YAML(yamlArgs), parser.parse(options, cmdArgs));
|
|
||||||
|
|
||||||
assertEquals(expectedResult, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +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.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.api.BeforeEach;
|
|
||||||
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.model.ConfigProperty.COMMENTARY_KEYWORDS;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.CommandLineOptionsUtil.optionOf;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.argumentsOf;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
class SetValidatorTest {
|
|
||||||
|
|
||||||
private static CommandLineParser parser;
|
|
||||||
private static Options options;
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
static void before() {
|
|
||||||
parser = new DefaultParser();
|
|
||||||
options = new Options();
|
|
||||||
options.addOption(optionOf(COMMENTARY_KEYWORDS, COMMENTARY_KEYWORDS.abrv(), COMMENTARY_KEYWORDS.args()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void beforeEach() {
|
|
||||||
Config.getInstance(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<Arguments> provideTestCases() {
|
|
||||||
return Stream.of(
|
|
||||||
argumentsOf(COMMENTARY_KEYWORDS, true, true, "", new String[]{"-ck", "test"}, VALID, 3),
|
|
||||||
argumentsOf(COMMENTARY_KEYWORDS, true, false, COMMENTARY_KEYWORDS.prop() + ": [test]", new String[]{}, VALID, 1),
|
|
||||||
argumentsOf(COMMENTARY_KEYWORDS, true, false, COMMENTARY_KEYWORDS.prop() + ":\n - test\n - test2", new String[]{}, VALID, 2),
|
|
||||||
argumentsOf(COMMENTARY_KEYWORDS, false, true, COMMENTARY_KEYWORDS.prop() + ": [test]", new String[]{}, VALID, 3),
|
|
||||||
argumentsOf(COMMENTARY_KEYWORDS, false, false, "", new String[]{"-ck", "test"}, VALID, 1),
|
|
||||||
argumentsOf(COMMENTARY_KEYWORDS, true, true, COMMENTARY_KEYWORDS.prop() + ": [commentary]", new String[]{}, VALID, 2),
|
|
||||||
|
|
||||||
argumentsOf(COMMENTARY_KEYWORDS, true, true, "", new String[]{}, MISSING, 2),
|
|
||||||
argumentsOf(COMMENTARY_KEYWORDS, false, true, "", new String[]{}, NOT_PRESENT, 2)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestCases")
|
|
||||||
void validate(ConfigProperty property, boolean required, boolean append, String yamlArgs, String[] cmdArgs,
|
|
||||||
ValidationResult expectedResult, int expectedSize) throws ParseException, YamlInvalidContentException {
|
|
||||||
SetValidator underTest = new SetValidator(property, required, append);
|
|
||||||
|
|
||||||
ValidationResult result = underTest.validate(new YAML(yamlArgs), parser.parse(options, cmdArgs));
|
|
||||||
|
|
||||||
assertEquals(expectedResult, result);
|
|
||||||
assertEquals(expectedSize, Config.getInstance().getCommentaryKeywords().size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +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.model.ConfigProperty.THREADS;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.CommandLineOptionsUtil.optionOf;
|
|
||||||
import static at.pcgamingfreaks.mkvaudiosubtitlechanger.util.TestUtil.argumentsOf;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
class ThreadValidatorTest {
|
|
||||||
|
|
||||||
private static CommandLineParser parser;
|
|
||||||
private static Options options;
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
static void before() {
|
|
||||||
parser = new DefaultParser();
|
|
||||||
options = new Options();
|
|
||||||
options.addOption(optionOf(THREADS, "t", THREADS.args()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<Arguments> provideTestCases() {
|
|
||||||
return Stream.of(
|
|
||||||
argumentsOf(THREADS, false, null, "", new String[]{"-t", "10"}, VALID),
|
|
||||||
argumentsOf(THREADS, true, null, "", new String[]{"-t", "10"}, VALID),
|
|
||||||
argumentsOf(THREADS, false, null, "threads: 10", new String[]{}, VALID),
|
|
||||||
argumentsOf(THREADS, true, null, "threads: 10", new String[]{}, VALID),
|
|
||||||
argumentsOf(THREADS, false, 2, "", new String[]{}, DEFAULT),
|
|
||||||
argumentsOf(THREADS, true, null, "", new String[]{}, MISSING),
|
|
||||||
argumentsOf(THREADS, false, null, "", new String[]{}, NOT_PRESENT),
|
|
||||||
argumentsOf(THREADS, true, null, "", new String[]{"-t", "-1"}, INVALID),
|
|
||||||
argumentsOf(THREADS, true, null, "threads: 0", new String[]{}, INVALID),
|
|
||||||
argumentsOf(THREADS, true, 2, "", new String[]{"-t", "0"}, INVALID)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource("provideTestCases")
|
|
||||||
void validate(ConfigProperty property, boolean required, Integer defaultValue, String yamlArgs, String[] cmdArgs,
|
|
||||||
ValidationResult expectedResult) throws ParseException, YamlInvalidContentException {
|
|
||||||
ThreadValidator underTest = new ThreadValidator(property, required, defaultValue);
|
|
||||||
|
|
||||||
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,74 @@
|
|||||||
|
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_PATH = ".";
|
||||||
|
private static final String LAST_EXECUTION_FILE = "./last-execution.properties";
|
||||||
|
private static final String TEST_MKV_FILE = "/arst/file.mkv";
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void destruct() {
|
||||||
|
File file = new File(LAST_EXECUTION_FILE);
|
||||||
|
if (file.exists()) file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void missingFile() throws IOException {
|
||||||
|
LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_PATH);
|
||||||
|
assertNull(underTest.get(TEST_MKV_FILE));
|
||||||
|
underTest.update(TEST_MKV_FILE);
|
||||||
|
assertNotNull(underTest.get(TEST_MKV_FILE));
|
||||||
|
underTest.persist();
|
||||||
|
File file = new File(LAST_EXECUTION_FILE);
|
||||||
|
assertTrue(file.exists());
|
||||||
|
assertTrue(Files.readString(file.toPath()).contains(TEST_MKV_FILE + "="));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void emptyFile() throws IOException {
|
||||||
|
File file = new File(LAST_EXECUTION_FILE);
|
||||||
|
file.createNewFile();
|
||||||
|
missingFile(); // does the checks needed for empty file case
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void existingFileNoChanges() throws IOException {
|
||||||
|
File file = new File(LAST_EXECUTION_FILE);
|
||||||
|
file.createNewFile();
|
||||||
|
Files.writeString(file.toPath(), TEST_MKV_FILE + "=" + Instant.now());
|
||||||
|
String expected = Files.readString(file.toPath()).replace(":", "\\:");
|
||||||
|
|
||||||
|
LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_PATH);
|
||||||
|
assertNotNull(underTest.get(TEST_MKV_FILE));
|
||||||
|
underTest.persist();
|
||||||
|
File file1 = new File(LAST_EXECUTION_FILE);
|
||||||
|
assertTrue(file1.exists());
|
||||||
|
assertTrue(Files.readString(file.toPath()).contains(expected), "File contains expected value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void existingFileWithChanges() throws IOException {
|
||||||
|
File file = new File(LAST_EXECUTION_FILE);
|
||||||
|
file.createNewFile();
|
||||||
|
Files.writeString(file.toPath(), TEST_MKV_FILE + "=" + Instant.now());
|
||||||
|
String expected = Files.readString(file.toPath());
|
||||||
|
|
||||||
|
LastExecutionHandler underTest = new LastExecutionHandler(LAST_EXECUTION_PATH);
|
||||||
|
assertNotNull(underTest.get(TEST_MKV_FILE));
|
||||||
|
underTest.update(TEST_MKV_FILE);
|
||||||
|
assertNotNull(underTest.get(TEST_MKV_FILE));
|
||||||
|
underTest.persist();
|
||||||
|
File file1 = new File(LAST_EXECUTION_FILE);
|
||||||
|
assertTrue(file1.exists());
|
||||||
|
assertNotEquals(expected, Files.readString(file.toPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,46 +1,59 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.impl;
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackAttributes;
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.LaneType;
|
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.TrackType;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
class SubtitleTrackComparatorTest {
|
class SubtitleTrackComparatorTest {
|
||||||
private static final SubtitleTrackComparator comparator = new SubtitleTrackComparator(new String[]{"unstyled"});
|
|
||||||
|
|
||||||
private static Stream<Arguments> compareArguments() {
|
private static Stream<Arguments> compareArguments() {
|
||||||
return Stream.of(
|
return Stream.of(
|
||||||
Arguments.of(List.of(attr("unstyled sub", false), attr("styled sub", false)),
|
Arguments.of(attr(""), attr(""), 0),
|
||||||
List.of(attr("unstyled sub", false), attr("styled sub", false))),
|
Arguments.of(attr("pref"), attr(""), 1),
|
||||||
Arguments.of(List.of(attr("styled sub", false), attr("unstyled sub", false)),
|
Arguments.of(attr(""), attr("pref"), -1),
|
||||||
List.of(attr("unstyled sub", false), attr("styled sub", false))),
|
Arguments.of(attr("pref"), attr("pref"), 0),
|
||||||
|
|
||||||
Arguments.of(List.of(attr("unstyled sub", true), attr("styled sub", false)),
|
Arguments.of(attr("", true), attr("", true), 0),
|
||||||
List.of(attr("unstyled sub", true), attr("styled sub", false))),
|
Arguments.of(attr("", true), attr(""), -1),
|
||||||
Arguments.of(List.of(attr("styled sub", true), attr("unstyled sub", false)),
|
Arguments.of(attr("CC", true), attr(""), -1),
|
||||||
List.of(attr("unstyled sub", false), attr("styled sub", true))),
|
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)),
|
Arguments.of(attr("pref", true), attr("pref"), -1),
|
||||||
List.of(attr("unstyled sub", true), attr("unstyled sub", false)))
|
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
|
@ParameterizedTest
|
||||||
@MethodSource("compareArguments")
|
@MethodSource("compareArguments")
|
||||||
void compare(List<FileAttribute> input, List<FileAttribute> expected) {
|
void compare(TrackAttributes track1, TrackAttributes track2, int expected) {
|
||||||
List<FileAttribute> result = input.stream().sorted(comparator.reversed()).collect(Collectors.toList());
|
SubtitleTrackComparator comparator = new SubtitleTrackComparator(List.of("pref"), List.of("CC", "SDH"));
|
||||||
|
int actual = comparator.compare(track1, track2);
|
||||||
assertArrayEquals(expected.toArray(new FileAttribute[0]), result.toArray(new FileAttribute[0]));
|
assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FileAttribute attr(String trackName, boolean defaultTrack) {
|
private static TrackAttributes attr(String trackname) {
|
||||||
return new FileAttribute(0, "", trackName, defaultTrack, false, LaneType.SUBTITLES);
|
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,59 +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.util.TestUtil.createFileInfo;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class FileInfoDtoTest {
|
|
||||||
private static final FileAttribute AUDIO_GER_DEFAULT = new FileAttribute(0, "ger", "", true, false, LaneType.AUDIO);
|
|
||||||
private static final FileAttribute AUDIO_GER = new FileAttribute(0, "ger", "", false, false, LaneType.AUDIO);
|
|
||||||
private static final FileAttribute AUDIO_ENG_DEFAULT = new FileAttribute(1, "eng", "", true, false, LaneType.AUDIO);
|
|
||||||
private static final FileAttribute AUDIO_ENG = new FileAttribute(1, "eng", "", false, false, LaneType.AUDIO);
|
|
||||||
|
|
||||||
private static final FileAttribute SUB_GER_DEFAULT = new FileAttribute(0, "ger", "", true, false, LaneType.SUBTITLES);
|
|
||||||
private static final FileAttribute SUB_GER = new FileAttribute(0, "ger", "", false, false, LaneType.SUBTITLES);
|
|
||||||
private static final FileAttribute SUB_ENG_DEFAULT = new FileAttribute(1, "eng", "", true, false, LaneType.SUBTITLES);
|
|
||||||
private static final FileAttribute SUB_ENG = new FileAttribute(1, "eng", "", false, false, LaneType.SUBTITLES);
|
|
||||||
|
|
||||||
|
|
||||||
private static Stream<Arguments> isAudioDifferent() {
|
|
||||||
return Stream.of(
|
|
||||||
Arguments.of(createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_GER_DEFAULT), false),
|
|
||||||
Arguments.of(createFileInfo(Set.of(AUDIO_GER_DEFAULT), AUDIO_ENG), true),
|
|
||||||
Arguments.of(createFileInfo(Set.of(AUDIO_GER_DEFAULT, AUDIO_ENG_DEFAULT), AUDIO_GER_DEFAULT), true),
|
|
||||||
Arguments.of(createFileInfo(Set.of(), AUDIO_GER), true),
|
|
||||||
Arguments.of(createFileInfo(null, AUDIO_GER), true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource
|
|
||||||
void isAudioDifferent(FileInfoDto underTest, boolean expected) {
|
|
||||||
assertEquals(expected, underTest.isAudioDifferent());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream<Arguments> isSubtitleDifferent() {
|
|
||||||
return Stream.of(
|
|
||||||
Arguments.of(createFileInfo(Set.of(SUB_GER_DEFAULT), SUB_GER_DEFAULT, new AttributeConfig("", "ger")), false),
|
|
||||||
Arguments.of(createFileInfo(Set.of(SUB_GER_DEFAULT), SUB_ENG, new AttributeConfig("", "eng")), true),
|
|
||||||
Arguments.of(createFileInfo(Set.of(SUB_GER_DEFAULT, SUB_ENG_DEFAULT), SUB_ENG, new AttributeConfig("", "eng")), true),
|
|
||||||
Arguments.of(createFileInfo(Set.of(), SUB_ENG, new AttributeConfig("", "ger")), true),
|
|
||||||
Arguments.of(createFileInfo(null, SUB_GER, new AttributeConfig("", "ger")), true),
|
|
||||||
Arguments.of(createFileInfo(null, null, new AttributeConfig("", "OFF")), false),
|
|
||||||
Arguments.of(createFileInfo(Set.of(), null, new AttributeConfig("", "OFF")), false),
|
|
||||||
Arguments.of(createFileInfo(Set.of(SUB_GER_DEFAULT), null, new AttributeConfig("", "OFF")), true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource
|
|
||||||
void isSubtitleDifferent(FileInfoDto underTest, boolean expected) {
|
|
||||||
assertEquals(expected, underTest.isSubtitleDifferent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
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,6 +1,10 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
|
||||||
public class PathUtils {
|
public class PathUtils {
|
||||||
public static final String TEST_DIR = "src/test/resources/test-dir";
|
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_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,46 +1,12 @@
|
|||||||
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
package at.pcgamingfreaks.mkvaudiosubtitlechanger.util;
|
||||||
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.config.ValidationResult;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.AttributeConfig;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.ConfigProperty;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileAttribute;
|
|
||||||
import at.pcgamingfreaks.mkvaudiosubtitlechanger.model.FileInfoDto;
|
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static java.util.stream.Collectors.joining;
|
|
||||||
|
|
||||||
public class TestUtil {
|
public 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,
|
public static String[] args(String... args) {
|
||||||
ValidationResult result) {
|
String[] staticArray = new String[]{"-a", "jpn:ger", "/"};
|
||||||
return Arguments.of(property, required, defaultValue, yaml, cmd, result);
|
String[] result = new String[staticArray.length + args.length];
|
||||||
}
|
System.arraycopy(args, 0, result, 0, args.length);
|
||||||
|
System.arraycopy(staticArray, 0, result, args.length, staticArray.length);
|
||||||
public static Arguments argumentsOf(ConfigProperty property, boolean required, boolean append, String yaml, String[] cmd,
|
return result;
|
||||||
ValidationResult result, int expectedSize) {
|
|
||||||
return Arguments.of(property, required, append, yaml, cmd, result, expectedSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FileInfoDto createFileInfo(Set<FileAttribute> defaultAudio, FileAttribute desiredAudio) {
|
|
||||||
FileInfoDto fileInfoDto = new FileInfoDto(null);
|
|
||||||
fileInfoDto.setDefaultAudioLanes(defaultAudio);
|
|
||||||
fileInfoDto.setDesiredAudioLane(desiredAudio);
|
|
||||||
return fileInfoDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FileInfoDto createFileInfo(Set<FileAttribute> defaultSubtitle, FileAttribute desiredSubtitle, AttributeConfig config) {
|
|
||||||
FileInfoDto fileInfoDto = new FileInfoDto(null);
|
|
||||||
fileInfoDto.setDefaultSubtitleLanes(defaultSubtitle);
|
|
||||||
fileInfoDto.setDesiredSubtitleLane(desiredSubtitle);
|
|
||||||
fileInfoDto.setMatchedConfig(config);
|
|
||||||
return fileInfoDto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user