Init
This commit is contained in:
commit
2f80222293
44
.gitignore
vendored
Normal file
44
.gitignore
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
**/.idea/*
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
42
CommonCode/.gitignore
vendored
Normal file
42
CommonCode/.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
26
CommonCode/build.gradle.kts
Normal file
26
CommonCode/build.gradle.kts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "1.8.21"
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "no.iktdev.streamit.content"
|
||||||
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("com.github.pgreze:kotlin-process:1.3.1")
|
||||||
|
implementation("io.github.microutils:kotlin-logging-jvm:2.0.11")
|
||||||
|
|
||||||
|
|
||||||
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter-params:5.8.1")
|
||||||
|
testImplementation("org.assertj:assertj-core:3.4.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
BIN
CommonCode/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
CommonCode/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
CommonCode/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
CommonCode/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Sat Jul 15 17:55:49 CEST 2023
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
234
CommonCode/gradlew
vendored
Normal file
234
CommonCode/gradlew
vendored
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
89
CommonCode/gradlew.bat
vendored
Normal file
89
CommonCode/gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
2
CommonCode/settings.gradle.kts
Normal file
2
CommonCode/settings.gradle.kts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rootProject.name = "CommonCode"
|
||||||
|
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package no.iktdev.streamit.content.common
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object CommonConfig {
|
||||||
|
var kafkaConsumerId: String = System.getenv("KAFKA_TOPIC") ?: "contentEvents"
|
||||||
|
var incomingContent: File? = if (!System.getenv("DIRECTORY_CONTENT_INCOMING").isNullOrEmpty()) File(System.getenv("DIRECTORY_CONTENT_INCOMING")) else null
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package no.iktdev.streamit.content.common
|
||||||
|
|
||||||
|
import mu.KotlinLogging
|
||||||
|
import java.io.File
|
||||||
|
import java.io.RandomAccessFile
|
||||||
|
|
||||||
|
private val logger = KotlinLogging.logger {}
|
||||||
|
class FileAccess {
|
||||||
|
companion object {
|
||||||
|
fun isFileAvailable(file: File): Boolean {
|
||||||
|
if (!file.exists()) return false
|
||||||
|
var stream: RandomAccessFile? = null
|
||||||
|
try {
|
||||||
|
stream = RandomAccessFile(file, "rw")
|
||||||
|
stream.close()
|
||||||
|
logger.info { "File ${file.name} is read and writable" }
|
||||||
|
return true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
stream?.close()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,112 @@
|
|||||||
|
package no.iktdev.streamit.content.common
|
||||||
|
|
||||||
|
class Naming(val fileName: String) {
|
||||||
|
var cleanedFileName: String
|
||||||
|
private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
cleanedFileName = fileName.apply {
|
||||||
|
removeBracketedText(this)
|
||||||
|
removeParenthesizedText(this)
|
||||||
|
removeResolutionAndTags(this)
|
||||||
|
removeInBetweenCharacters(this)
|
||||||
|
|
||||||
|
removeExtraWhiteSpace(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun guessDesiredFileName(): String {
|
||||||
|
val parts = fileName.split(" - ")
|
||||||
|
return when {
|
||||||
|
parts.size == 2 && parts[1].matches(Regex("\\d{4}")) -> {
|
||||||
|
val title = parts[0]
|
||||||
|
val year = parts[1]
|
||||||
|
"$title ($year)"
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.size >= 3 && parts[1].matches(Regex("S\\d+")) && parts[2].matches(Regex("\\d+[vV]\\d+")) -> {
|
||||||
|
val title = parts[0]
|
||||||
|
val episodeWithRevision = parts[2]
|
||||||
|
val episodeParts = episodeWithRevision.split("v", "V")
|
||||||
|
val episodeNumber = episodeParts[0].toInt()
|
||||||
|
val revisionNumber = episodeParts[1].toInt()
|
||||||
|
val seasonEpisode =
|
||||||
|
"S${episodeNumber.toString().padStart(2, '0')}E${revisionNumber.toString().padStart(2, '0')}"
|
||||||
|
val episodeTitle = if (parts.size > 3) parts[3] else ""
|
||||||
|
"$title - $seasonEpisode - $episodeTitle"
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> fileName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun guessDesiredTitle(): String {
|
||||||
|
val desiredFileName = guessDesiredFileName()
|
||||||
|
return if (desiredFileName.contains(" - ")) {
|
||||||
|
return desiredFileName.split(" - ").firstOrNull() ?: desiredFileName
|
||||||
|
} else desiredFileName
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the filename contains the keyword movie, if so, default to movie
|
||||||
|
*/
|
||||||
|
fun doesContainMovieKeywords(): Boolean {
|
||||||
|
return getMatch("[(](?<=\\()movie(?=\\))[)]")?.isBlank() ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return not null if matches "S01E01"
|
||||||
|
*/
|
||||||
|
fun isSeasonEpisodeDefined(): String? {
|
||||||
|
return getMatch("(?i)S[0-9]+E[0-9]+(?i)")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return not null if matches " 2020 " or ".2020."
|
||||||
|
*/
|
||||||
|
fun isDefinedWithYear(): String? {
|
||||||
|
return getMatch("[ .][0-9]{4}[ .]")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the input value and removes "[Text]"
|
||||||
|
* @param text "[TEST] Dummy - 01 [AZ 1080p] "
|
||||||
|
*/
|
||||||
|
fun removeBracketedText(text: String): String {
|
||||||
|
return Regex("\\[.*?]").replace(text, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun removeParenthesizedText(text: String): String {
|
||||||
|
return Regex("\\(.*?\\)").replace(text, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun removeResolutionAndTags(text: String): String {
|
||||||
|
return Regex("(.*?)(?=\\d+[pk]\\b)").replace(text, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeInBetweenCharacters(text: String): String {
|
||||||
|
return Regex("[.]").replace(text, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param text "example text with extra spaces"
|
||||||
|
* @return example text with extra spaces
|
||||||
|
*/
|
||||||
|
fun removeExtraWhiteSpace(text: String): String {
|
||||||
|
return Regex("\\s{2,}").replace(text, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getMatch(regex: String): String? {
|
||||||
|
return Regex(regex).find(fileName)?.value ?: return null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package no.iktdev.streamit.content.common.deamon
|
||||||
|
|
||||||
|
import com.github.pgreze.process.ProcessResult
|
||||||
|
import com.github.pgreze.process.Redirect
|
||||||
|
import com.github.pgreze.process.process
|
||||||
|
import mu.KotlinLogging
|
||||||
|
|
||||||
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
class Daemon(open val executable: String, val parameters: List<String>, val daemonInterface: IDaemon) {
|
||||||
|
var executor: ProcessResult? = null
|
||||||
|
suspend fun run(): Int {
|
||||||
|
daemonInterface.onStarted()
|
||||||
|
executor = process(executable, *parameters.toTypedArray(),
|
||||||
|
stdout = Redirect.CAPTURE,
|
||||||
|
stderr = Redirect.CAPTURE,
|
||||||
|
consumer = {
|
||||||
|
daemonInterface.onOutputChanged(it)
|
||||||
|
})
|
||||||
|
val resultCode = executor?.resultCode ?: -1
|
||||||
|
if (resultCode == 0) {
|
||||||
|
daemonInterface.onEnded()
|
||||||
|
} else daemonInterface.onError()
|
||||||
|
return resultCode
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package no.iktdev.streamit.content.common.deamon
|
||||||
|
|
||||||
|
interface IDaemon {
|
||||||
|
|
||||||
|
fun onStarted() {}
|
||||||
|
|
||||||
|
fun onOutputChanged(line: String) {}
|
||||||
|
|
||||||
|
fun onEnded() {}
|
||||||
|
|
||||||
|
fun onError()
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,184 @@
|
|||||||
|
package no.iktdev.streamit.content.common.streams
|
||||||
|
|
||||||
|
data class MediaStreams(
|
||||||
|
val streams: List<Stream>
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed class Stream(
|
||||||
|
@Transient open val index: Int,
|
||||||
|
@Transient open val codec_name: String,
|
||||||
|
@Transient open val codec_long_name: String,
|
||||||
|
@Transient open val codec_type: String,
|
||||||
|
@Transient open val codec_tag_string: String,
|
||||||
|
@Transient open val codec_tag: String,
|
||||||
|
@Transient open val r_frame_rate: String,
|
||||||
|
@Transient open val avg_frame_rate: String,
|
||||||
|
@Transient open val time_base: String,
|
||||||
|
@Transient open val start_pts: Long,
|
||||||
|
@Transient open val start_time: String,
|
||||||
|
@Transient open val duration_ts: Long? = null,
|
||||||
|
@Transient open val duration: String? = null,
|
||||||
|
@Transient open val disposition: Disposition,
|
||||||
|
@Transient open val tags: Tags
|
||||||
|
)
|
||||||
|
|
||||||
|
data class VideoStream(
|
||||||
|
override val index: Int,
|
||||||
|
override val codec_name: String,
|
||||||
|
override val codec_long_name: String,
|
||||||
|
override val codec_type: String,
|
||||||
|
override val codec_tag_string: String,
|
||||||
|
override val codec_tag: String,
|
||||||
|
override val r_frame_rate: String,
|
||||||
|
override val avg_frame_rate: String,
|
||||||
|
override val time_base: String,
|
||||||
|
override val start_pts: Long,
|
||||||
|
override val start_time: String,
|
||||||
|
override val disposition: Disposition,
|
||||||
|
override val tags: Tags,
|
||||||
|
override val duration: String?,
|
||||||
|
override val duration_ts: Long?,
|
||||||
|
val profile: String,
|
||||||
|
val width: Int,
|
||||||
|
val height: Int,
|
||||||
|
val coded_width: Int,
|
||||||
|
val coded_height: Int,
|
||||||
|
val closed_captions: Int,
|
||||||
|
val has_b_frames: Int,
|
||||||
|
val sample_aspect_ratio: String,
|
||||||
|
val display_aspect_ratio: String,
|
||||||
|
val pix_fmt: String,
|
||||||
|
val level: Int,
|
||||||
|
val color_range: String,
|
||||||
|
val color_space: String,
|
||||||
|
val color_transfer: String,
|
||||||
|
val color_primaries: String,
|
||||||
|
val chroma_location: String,
|
||||||
|
val refs: Int
|
||||||
|
) : Stream(
|
||||||
|
index,
|
||||||
|
codec_name,
|
||||||
|
codec_long_name,
|
||||||
|
codec_type,
|
||||||
|
codec_tag_string,
|
||||||
|
codec_tag,
|
||||||
|
r_frame_rate,
|
||||||
|
avg_frame_rate,
|
||||||
|
time_base,
|
||||||
|
start_pts,
|
||||||
|
start_time,
|
||||||
|
duration_ts,
|
||||||
|
duration,
|
||||||
|
disposition,
|
||||||
|
tags
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AudioStream(
|
||||||
|
override val index: Int,
|
||||||
|
override val codec_name: String,
|
||||||
|
override val codec_long_name: String,
|
||||||
|
override val codec_type: String,
|
||||||
|
override val codec_tag_string: String,
|
||||||
|
override val codec_tag: String,
|
||||||
|
override val r_frame_rate: String,
|
||||||
|
override val avg_frame_rate: String,
|
||||||
|
override val time_base: String,
|
||||||
|
override val start_pts: Long,
|
||||||
|
override val start_time: String,
|
||||||
|
override val duration: String?,
|
||||||
|
override val duration_ts: Long?,
|
||||||
|
override val disposition: Disposition,
|
||||||
|
override val tags: Tags,
|
||||||
|
val profile: String,
|
||||||
|
val sample_fmt: String,
|
||||||
|
val sample_rate: String,
|
||||||
|
val channels: Int,
|
||||||
|
val channel_layout: String,
|
||||||
|
val bits_per_sample: Int
|
||||||
|
) : Stream(
|
||||||
|
index,
|
||||||
|
codec_name,
|
||||||
|
codec_long_name,
|
||||||
|
codec_type,
|
||||||
|
codec_tag_string,
|
||||||
|
codec_tag,
|
||||||
|
r_frame_rate,
|
||||||
|
avg_frame_rate,
|
||||||
|
time_base,
|
||||||
|
start_pts,
|
||||||
|
start_time,
|
||||||
|
duration_ts,
|
||||||
|
duration,
|
||||||
|
disposition,
|
||||||
|
tags
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SubtitleStream(
|
||||||
|
override val index: Int,
|
||||||
|
override val codec_name: String,
|
||||||
|
override val codec_long_name: String,
|
||||||
|
override val codec_type: String,
|
||||||
|
override val codec_tag_string: String,
|
||||||
|
override val codec_tag: String,
|
||||||
|
override val r_frame_rate: String,
|
||||||
|
override val avg_frame_rate: String,
|
||||||
|
override val time_base: String,
|
||||||
|
override val start_pts: Long,
|
||||||
|
override val start_time: String,
|
||||||
|
override val duration: String?,
|
||||||
|
override val duration_ts: Long?,
|
||||||
|
override val disposition: Disposition,
|
||||||
|
override val tags: Tags,
|
||||||
|
val subtitle_tags: SubtitleTags
|
||||||
|
) : Stream(
|
||||||
|
index,
|
||||||
|
codec_name,
|
||||||
|
codec_long_name,
|
||||||
|
codec_type,
|
||||||
|
codec_tag_string,
|
||||||
|
codec_tag,
|
||||||
|
r_frame_rate,
|
||||||
|
avg_frame_rate,
|
||||||
|
time_base,
|
||||||
|
start_pts,
|
||||||
|
start_time,
|
||||||
|
duration_ts,
|
||||||
|
duration,
|
||||||
|
disposition,
|
||||||
|
tags
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Disposition(
|
||||||
|
val default: Int,
|
||||||
|
val dub: Int,
|
||||||
|
val original: Int,
|
||||||
|
val comment: Int,
|
||||||
|
val lyrics: Int,
|
||||||
|
val karaoke: Int,
|
||||||
|
val forced: Int,
|
||||||
|
val hearing_impaired: Int,
|
||||||
|
val visual_impaired: Int,
|
||||||
|
val clean_effects: Int,
|
||||||
|
val attached_pic: Int,
|
||||||
|
val timed_thumbnails: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Tags(
|
||||||
|
val title: String?,
|
||||||
|
val BPS: String?,
|
||||||
|
val DURATION: String?,
|
||||||
|
val NUMBER_OF_FRAMES: String?,
|
||||||
|
val NUMBER_OF_BYTES: String?,
|
||||||
|
val _STATISTICS_WRITING_APP: String?,
|
||||||
|
val _STATISTICS_WRITING_DATE_UTC: String?,
|
||||||
|
val _STATISTICS_TAGS: String?,
|
||||||
|
val language: String?,
|
||||||
|
val filename: String?,
|
||||||
|
val mimetype: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SubtitleTags(
|
||||||
|
val language: String?,
|
||||||
|
val filename: String?,
|
||||||
|
val mimetype: String?
|
||||||
|
)
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package no.iktdev.streamit.content.common
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Named
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
|
|
||||||
|
class NamingTest {
|
||||||
|
/*
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("serieOnlyTest")
|
||||||
|
fun ensureOnlySerieAndDecodedCorrectly(testData: TestData) {
|
||||||
|
val naming = Naming(testData.input).getName() ?: throw NullPointerException("Named is null")
|
||||||
|
assertThat(naming.type).isEqualTo("serie")
|
||||||
|
assertThat(naming.season).isEqualTo(testData.expected.season)
|
||||||
|
assertThat(naming.episode).isEqualTo(testData.expected.episode)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTest() {
|
||||||
|
val tmp = TestData(Naming.Name(title = "Demo", season = 1, episode = 1, type = "serie"), "[Kametsu] Ghost in the Shell Arise - 05 - Pyrophoric Cult (BD 1080p Hi10 FLAC) [13FF85A7]")
|
||||||
|
val naming = Naming(tmp.input).getName()
|
||||||
|
assertThat(naming).isNotNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun serieOnlyTest(): List<Named<TestData>> {
|
||||||
|
return listOf(
|
||||||
|
Named.of("Is defined", TestData(Naming.Name(title = "Demo", season = 1, episode = 1, type = "serie"), "Demo - S01E01")),
|
||||||
|
Named.of("Is decoded", TestData(Naming.Name("Demo!", "serie", season = 1, episode = 1), "[TMP] Demo! - 03")),
|
||||||
|
Named.of("Is only Episode", TestData(Naming.Name("Demo", "serie", 1, 1), "Demo E1"))
|
||||||
|
)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
data class TestData(
|
||||||
|
val expected: Naming.Name,
|
||||||
|
val input: String
|
||||||
|
)*/
|
||||||
|
}
|
||||||
42
Encode/.gitignore
vendored
Normal file
42
Encode/.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
19
Encode/build.gradle.kts
Normal file
19
Encode/build.gradle.kts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "1.8.21"
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "no.iktdev.streamit.content"
|
||||||
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation(platform("org.junit:junit-bom:5.9.1"))
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
BIN
Encode/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
Encode/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
Encode/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
Encode/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Tue Jul 11 02:14:45 CEST 2023
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
234
Encode/gradlew
vendored
Normal file
234
Encode/gradlew
vendored
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
89
Encode/gradlew.bat
vendored
Normal file
89
Encode/gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
2
Encode/settings.gradle.kts
Normal file
2
Encode/settings.gradle.kts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rootProject.name = "Encode"
|
||||||
|
|
||||||
42
Reader/.gitignore
vendored
Normal file
42
Reader/.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
49
Reader/build.gradle.kts
Normal file
49
Reader/build.gradle.kts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "1.8.21"
|
||||||
|
id("org.springframework.boot") version "2.5.5"
|
||||||
|
id("io.spring.dependency-management") version "1.0.11.RELEASE"
|
||||||
|
kotlin("plugin.spring") version "1.5.31"
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "no.iktdev.streamit.content"
|
||||||
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven("https://jitpack.io")
|
||||||
|
maven {
|
||||||
|
url = uri("https://reposilite.iktdev.no/releases")
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
url = uri("https://reposilite.iktdev.no/snapshots")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("no.iktdev.streamit.library:streamit-library-kafka:0.0.2-alpha10")
|
||||||
|
implementation("no.iktdev:exfl:0.0.4-SNAPSHOT")
|
||||||
|
|
||||||
|
implementation("com.github.pgreze:kotlin-process:1.3.1")
|
||||||
|
implementation("com.github.vishna:watchservice-ktx:master-SNAPSHOT")
|
||||||
|
implementation("io.github.microutils:kotlin-logging-jvm:2.0.11")
|
||||||
|
|
||||||
|
implementation("com.google.code.gson:gson:2.8.9")
|
||||||
|
implementation("org.json:json:20210307")
|
||||||
|
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter:2.7.0")
|
||||||
|
implementation("org.springframework.kafka:spring-kafka:2.8.5")
|
||||||
|
|
||||||
|
implementation(project(":CommonCode"))
|
||||||
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter-params:5.8.1")
|
||||||
|
testImplementation("org.assertj:assertj-core:3.4.1")
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
BIN
Reader/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
Reader/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
Reader/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
Reader/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Tue Jul 11 02:16:45 CEST 2023
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
234
Reader/gradlew
vendored
Normal file
234
Reader/gradlew
vendored
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
89
Reader/gradlew.bat
vendored
Normal file
89
Reader/gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
5
Reader/settings.gradle.kts
Normal file
5
Reader/settings.gradle.kts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
rootProject.name = "Reader"
|
||||||
|
|
||||||
|
include(":CommonCode")
|
||||||
|
project(":CommonCode").projectDir = File("../CommonCode")
|
||||||
|
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package no.iktdev.streamit.content.reader
|
||||||
|
|
||||||
|
import no.iktdev.streamit.content.reader.analyzer.PreferenceReader
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
class ReaderApplication
|
||||||
|
|
||||||
|
val preference = PreferenceReader().getPreference()
|
||||||
|
|
||||||
|
fun main(array: Array<String>) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package no.iktdev.streamit.content.reader
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class ReaderEnv {
|
||||||
|
companion object {
|
||||||
|
val ffprobe: String = System.getenv("SUPPORTING_EXECUTABLE_FFPROBE") ?: "ffprobe"
|
||||||
|
val encodePreference: String? = System.getenv("ENCODE_PREFERENCE") ?: null
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.analyzer
|
||||||
|
|
||||||
|
import no.iktdev.streamit.content.common.streams.AudioStream
|
||||||
|
import no.iktdev.streamit.content.common.streams.MediaStreams
|
||||||
|
import no.iktdev.streamit.content.common.streams.SubtitleStream
|
||||||
|
import no.iktdev.streamit.content.common.streams.VideoStream
|
||||||
|
import no.iktdev.streamit.content.reader.analyzer.encoding.AudioEncodeArguments
|
||||||
|
import no.iktdev.streamit.content.reader.analyzer.encoding.EncodeInformation
|
||||||
|
import no.iktdev.streamit.content.reader.analyzer.encoding.SubtitleEncodeArguments
|
||||||
|
import no.iktdev.streamit.content.reader.analyzer.encoding.VideoEncodeArguments
|
||||||
|
import no.iktdev.streamit.content.reader.preference
|
||||||
|
|
||||||
|
class EncodeArgumentSelector(val inputFile: String, val streams: MediaStreams, val outFileName: String) {
|
||||||
|
var defaultSelectedVideo: VideoStream? = getDefaultSelectedVideo()
|
||||||
|
var defaultSelectedAudio: AudioStream? = getDefaultSelectedAudio()
|
||||||
|
|
||||||
|
private fun getAudioStreams() = streams.streams.filterIsInstance<AudioStream>()
|
||||||
|
private fun getVideoStreams() = streams.streams.filterIsInstance<VideoStream>()
|
||||||
|
|
||||||
|
|
||||||
|
private fun getDefaultSelectedVideo(): VideoStream? {
|
||||||
|
return getVideoStreams().filter { (it.duration_ts ?: 0) > 0 }.maxByOrNull { it.duration_ts!! } ?: getVideoStreams().minByOrNull { it.index }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDefaultSelectedAudio(): AudioStream? {
|
||||||
|
return getAudioStreams().filter { (it.duration_ts ?: 0) > 0 }.maxByOrNull { it.duration_ts!! } ?: getAudioStreams().minByOrNull { it.index }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return VideoStream based on preference or defaultSelectedVideo
|
||||||
|
*/
|
||||||
|
/*private fun getSelectedVideoBasedOnPreference(): VideoStream {
|
||||||
|
val
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return AudioStrem based on preference or defaultSelectedAudio
|
||||||
|
*/
|
||||||
|
private fun getSelectedAudioBasedOnPreference(): AudioStream? {
|
||||||
|
val languageFiltered = getAudioStreams().filter { it.tags.language == preference.audio.language }
|
||||||
|
val channeledAndCodec = languageFiltered.find { it.channels >= (preference.audio.channels ?: 2) && it.codec_name == preference.audio.codec.lowercase() }
|
||||||
|
return channeledAndCodec ?: return languageFiltered.minByOrNull { it.index } ?: defaultSelectedAudio
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getVideoAndAudioArguments(): EncodeInformation? {
|
||||||
|
val selectedVideo = defaultSelectedVideo
|
||||||
|
val selectedAudio = getSelectedAudioBasedOnPreference() ?: defaultSelectedAudio
|
||||||
|
return if (selectedVideo == null || selectedAudio == null) return null
|
||||||
|
else {
|
||||||
|
EncodeInformation(
|
||||||
|
inputFile = inputFile,
|
||||||
|
outFileName = "$outFileName.mp4",
|
||||||
|
language = selectedAudio.tags.language ?: "eng",
|
||||||
|
arguments = VideoEncodeArguments(selectedVideo).getVideoArguments() +
|
||||||
|
AudioEncodeArguments(selectedAudio).getAudioArguments()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSubtitleArguments(): List<EncodeInformation> {
|
||||||
|
return streams.streams.filterIsInstance<SubtitleStream>().map {
|
||||||
|
val subArgs = SubtitleEncodeArguments(it)
|
||||||
|
EncodeInformation(
|
||||||
|
inputFile = inputFile,
|
||||||
|
outFileName = "$outFileName.${subArgs.getFormatToCodec()}",
|
||||||
|
language = it.tags.language ?: "eng",
|
||||||
|
arguments = subArgs.getSubtitleArguments()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.analyzer
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import no.iktdev.streamit.content.common.streams.*
|
||||||
|
import no.iktdev.streamit.content.reader.fileWatcher.FileWatcher
|
||||||
|
import no.iktdev.streamit.library.kafka.KnownEvents
|
||||||
|
import no.iktdev.streamit.library.kafka.Message
|
||||||
|
import no.iktdev.streamit.library.kafka.StatusType
|
||||||
|
import org.apache.kafka.clients.consumer.ConsumerRecord
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class EncodeStreamsMessageParser {
|
||||||
|
fun getFileNameFromEvent(records: MutableList<ConsumerRecord<String, Message>>): FileWatcher.FileResult? {
|
||||||
|
val file = records.find { it.key() == KnownEvents.EVENT_READER_RECEIVED_FILE.event } ?: return null
|
||||||
|
if (file.value().status.statusType != StatusType.SUCCESS) return null
|
||||||
|
return if (file.value().data is String) {
|
||||||
|
return Gson().fromJson(file.value().data as String, FileWatcher.FileResult::class.java)
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMediaStreamsFromEvent(records: MutableList<ConsumerRecord<String, Message>>): MediaStreams? {
|
||||||
|
val streams = records.find { it.key() == KnownEvents.EVENT_READER_RECEIVED_STREAMS.event } ?: return null
|
||||||
|
if (streams.value().status.statusType != StatusType.SUCCESS || streams.value().data !is String) return null
|
||||||
|
val json = streams.value().data as String
|
||||||
|
val gson = Gson()
|
||||||
|
/*return gson.fromJson(streams.value().data as String, MediaStreams::class.java)*/
|
||||||
|
|
||||||
|
val jsonObject = gson.fromJson(json, JsonObject::class.java)
|
||||||
|
|
||||||
|
val streamsJsonArray = jsonObject.getAsJsonArray("streams")
|
||||||
|
|
||||||
|
val rstreams = streamsJsonArray.mapNotNull { streamJson ->
|
||||||
|
val streamObject = streamJson.asJsonObject
|
||||||
|
|
||||||
|
val codecType = streamObject.get("codec_type").asString
|
||||||
|
if (streamObject.get("codec_name").asString == "mjpeg") {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
when (codecType) {
|
||||||
|
"video" -> gson.fromJson(streamObject, VideoStream::class.java)
|
||||||
|
"audio" -> gson.fromJson(streamObject, AudioStream::class.java)
|
||||||
|
"subtitle" -> gson.fromJson(streamObject, SubtitleStream::class.java)
|
||||||
|
else -> null //throw IllegalArgumentException("Unknown stream type: $codecType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MediaStreams(rstreams)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.analyzer
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import no.iktdev.streamit.content.common.CommonConfig
|
||||||
|
import no.iktdev.streamit.content.common.streams.MediaStreams
|
||||||
|
import no.iktdev.streamit.content.reader.analyzer.encoding.EncodeInformation
|
||||||
|
import no.iktdev.streamit.content.reader.fileWatcher.FileWatcher
|
||||||
|
import no.iktdev.streamit.library.kafka.KnownEvents
|
||||||
|
import no.iktdev.streamit.library.kafka.Message
|
||||||
|
import no.iktdev.streamit.library.kafka.Status
|
||||||
|
import no.iktdev.streamit.library.kafka.StatusType
|
||||||
|
import no.iktdev.streamit.library.kafka.consumers.DefaultConsumer
|
||||||
|
import no.iktdev.streamit.library.kafka.listener.pooled.IPooledEvents
|
||||||
|
import no.iktdev.streamit.library.kafka.listener.pooled.PooledEventMessageListener
|
||||||
|
import no.iktdev.streamit.library.kafka.producer.DefaultProducer
|
||||||
|
import org.apache.kafka.clients.consumer.ConsumerRecord
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class EncodeStreamsProducer: IPooledEvents.OnEventsReceived {
|
||||||
|
|
||||||
|
val messageProducer = DefaultProducer(CommonConfig.kafkaConsumerId)
|
||||||
|
|
||||||
|
val defaultConsumer = DefaultConsumer().apply {
|
||||||
|
autoCommit = false
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
val ackListener = PooledEventMessageListener(
|
||||||
|
topic = CommonConfig.kafkaConsumerId, consumer = defaultConsumer,
|
||||||
|
mainFilter = KnownEvents.EVENT_READER_RECEIVED_FILE.event,
|
||||||
|
subFilter = listOf(KnownEvents.EVENT_READER_RECEIVED_STREAMS.event),
|
||||||
|
event = this
|
||||||
|
)
|
||||||
|
ackListener.listen()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areAllMessagesReceived(recordedEvents: MutableMap<String, StatusType>): Boolean {
|
||||||
|
val expected = listOf(KnownEvents.EVENT_READER_RECEIVED_FILE.event, KnownEvents.EVENT_READER_RECEIVED_STREAMS.event)
|
||||||
|
return expected.containsAll(recordedEvents.keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun produceErrorMessage(referenceId: String, reason: String) {
|
||||||
|
val message = Message(referenceId = referenceId,
|
||||||
|
Status(statusType = StatusType.ERROR, errorMessage = reason)
|
||||||
|
)
|
||||||
|
messageProducer.sendMessage(KnownEvents.EVENT_READER_ENCODE_GENERATED.event, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun produceEncodeMessage(referenceId: String, data: EncodeInformation?) {
|
||||||
|
val message = Message(referenceId = referenceId,
|
||||||
|
Status(statusType = if (data != null) StatusType.SUCCESS else StatusType.IGNORED),
|
||||||
|
data = data
|
||||||
|
)
|
||||||
|
messageProducer.sendMessage(KnownEvents.EVENT_READER_ENCODE_GENERATED.event, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAllEventsConsumed(referenceId: String, records: MutableList<ConsumerRecord<String, Message>>) {
|
||||||
|
val parser = EncodeStreamsMessageParser()
|
||||||
|
val fileResult = parser.getFileNameFromEvent(records)
|
||||||
|
if (fileResult == null) {
|
||||||
|
produceErrorMessage(referenceId, "FileResult is either null or not deserializable!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val outFileName = fileResult.desiredNewName.ifBlank { File(fileResult.file).nameWithoutExtension }
|
||||||
|
val streams = parser.getMediaStreamsFromEvent(records)
|
||||||
|
if (streams == null) {
|
||||||
|
produceErrorMessage(referenceId, "No streams received!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val encodeInformation = EncodeArgumentSelector(inputFile = fileResult.file, streams = streams, outFileName = outFileName)
|
||||||
|
produceEncodeMessage(referenceId, encodeInformation.getVideoAndAudioArguments())
|
||||||
|
encodeInformation.getSubtitleArguments().forEach { s ->
|
||||||
|
produceEncodeMessage(referenceId, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.analyzer
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import no.iktdev.streamit.content.reader.ReaderEnv
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
data class EncodingPreference(
|
||||||
|
val video: VideoPreference,
|
||||||
|
val audio: AudioPreference
|
||||||
|
)
|
||||||
|
|
||||||
|
data class VideoPreference(
|
||||||
|
val codec: String = "h264",
|
||||||
|
val pixelFormat: String = "yuv420p",
|
||||||
|
val pixelFormatPassthrough: List<String> = listOf<String>("yuv420p", "yuv420p10le"),
|
||||||
|
val threshold: Int = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AudioPreference(
|
||||||
|
val codec: String = "aac",
|
||||||
|
val sample_rate: Int? = null,
|
||||||
|
val channels: Int? = null,
|
||||||
|
val language: String = "eng", //ISO3 format
|
||||||
|
val preserveChannels: Boolean = true,
|
||||||
|
val defaultToEAC3OnSurroundDetected: Boolean = true,
|
||||||
|
val forceStereo: Boolean = false
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PreferenceReader {
|
||||||
|
fun getPreference(): EncodingPreference {
|
||||||
|
val defaultPreference = EncodingPreference(video = VideoPreference(), audio = AudioPreference())
|
||||||
|
val preferenceText = readPreference() ?: return defaultPreference
|
||||||
|
val configured = deserialize(preferenceText)
|
||||||
|
|
||||||
|
printConfiguration("Audio", "Codec", configured?.audio?.codec, defaultPreference.audio.codec)
|
||||||
|
printConfiguration("Audio", "Language", configured?.audio?.language, defaultPreference.audio.language)
|
||||||
|
printConfiguration("Audio", "Channels", configured?.audio?.channels.toString(), defaultPreference.audio.channels.toString())
|
||||||
|
printConfiguration("Audio", "Sample rate", configured?.audio?.sample_rate.toString(), defaultPreference.audio.sample_rate.toString())
|
||||||
|
printConfiguration("Audio", "Override to EAC3 for surround", configured?.audio?.defaultToEAC3OnSurroundDetected.toString(), defaultPreference.audio.defaultToEAC3OnSurroundDetected.toString())
|
||||||
|
|
||||||
|
|
||||||
|
printConfiguration("Video", "Codec", configured?.video?.codec, defaultPreference.video.codec)
|
||||||
|
printConfiguration("Video", "Pixel format", configured?.video?.pixelFormat, defaultPreference.video.pixelFormat)
|
||||||
|
printConfiguration("Video", "Threshold", configured?.video?.threshold.toString(), defaultPreference.video.threshold.toString())
|
||||||
|
|
||||||
|
|
||||||
|
return configured ?: defaultPreference
|
||||||
|
}
|
||||||
|
|
||||||
|
fun printConfiguration(sourceType: String, key: String, value: String?, default: String?) {
|
||||||
|
val usedValue = if (!value.isNullOrEmpty()) value else if (!default.isNullOrEmpty()) "$default (default)" else "no changes will be made"
|
||||||
|
LoggerFactory.getLogger(javaClass.simpleName).info("$sourceType: $key => $usedValue")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun readPreference(): String? {
|
||||||
|
val prefFile = File(ReaderEnv.encodePreference)
|
||||||
|
if (!prefFile.exists()) {
|
||||||
|
LoggerFactory.getLogger(javaClass.simpleName).info("Preference file: ${prefFile.absolutePath} does not exists...")
|
||||||
|
LoggerFactory.getLogger(javaClass.simpleName).info("Using default configuration")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LoggerFactory.getLogger(javaClass.simpleName).info("Preference file: ${prefFile.absolutePath} found")
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val instr = prefFile.inputStream()
|
||||||
|
return instr.bufferedReader().use { it.readText() }
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
LoggerFactory.getLogger(javaClass.simpleName).error("Failed to read preference file: ${prefFile.absolutePath}.. Will use default configuration")
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deserialize(value: String?): EncodingPreference? {
|
||||||
|
value ?: return null
|
||||||
|
return Gson().fromJson(value, EncodingPreference::class.java) ?: null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.analyzer.encoding
|
||||||
|
|
||||||
|
import no.iktdev.streamit.content.common.streams.AudioStream
|
||||||
|
import no.iktdev.streamit.content.reader.preference
|
||||||
|
|
||||||
|
class AudioEncodeArguments(val audio: AudioStream) {
|
||||||
|
|
||||||
|
fun isAudioCodecEqual() = audio.codec_name.lowercase() == preference.audio.codec.lowercase()
|
||||||
|
|
||||||
|
fun shouldUseEAC3(): Boolean {
|
||||||
|
return (preference.audio.defaultToEAC3OnSurroundDetected && audio.channels > 2 && audio.codec_name.lowercase() != "eac3")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAudioArguments(): MutableList<String> {
|
||||||
|
val result = mutableListOf<String>()
|
||||||
|
if (shouldUseEAC3()) {
|
||||||
|
result.addAll(listOf("-c:a", "eac3"))
|
||||||
|
} else if (!isAudioCodecEqual()) {
|
||||||
|
result.addAll(listOf("-c:a", preference.audio.codec))
|
||||||
|
} else result.addAll(listOf("-acodec", "copy"))
|
||||||
|
result.addAll(listOf("-map", "0:a:${audio.index}"))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.analyzer.encoding
|
||||||
|
|
||||||
|
data class EncodeInformation(
|
||||||
|
val inputFile: String,
|
||||||
|
val outFileName: String,
|
||||||
|
val language: String,
|
||||||
|
val arguments: List<String>
|
||||||
|
)
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.analyzer.encoding
|
||||||
|
|
||||||
|
import no.iktdev.streamit.content.common.streams.SubtitleStream
|
||||||
|
|
||||||
|
class SubtitleEncodeArguments(val subtitle: SubtitleStream) {
|
||||||
|
|
||||||
|
fun getSubtitleArguments(): List<String> {
|
||||||
|
val result = mutableListOf<String>()
|
||||||
|
result.addAll(listOf("-c:s", "copy"))
|
||||||
|
result.addAll(listOf("-map", "0:s:${subtitle.index}"))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFormatToCodec(): String? {
|
||||||
|
return when(subtitle.codec_name) {
|
||||||
|
"ass" -> "ass"
|
||||||
|
"subrip" -> "srt"
|
||||||
|
"webvtt", "vtt" -> "vtt"
|
||||||
|
"smi" -> "smi"
|
||||||
|
"hdmv_pgs_subtitle" -> null
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.analyzer.encoding
|
||||||
|
|
||||||
|
import no.iktdev.streamit.content.common.streams.AudioStream
|
||||||
|
import no.iktdev.streamit.content.common.streams.VideoStream
|
||||||
|
import no.iktdev.streamit.content.reader.preference
|
||||||
|
|
||||||
|
class VideoEncodeArguments(val video: VideoStream) {
|
||||||
|
|
||||||
|
fun isVideoCodecEqual() = video.codec_name == getCorrectCodec()
|
||||||
|
|
||||||
|
|
||||||
|
fun getVideoArguments(): List<String> {
|
||||||
|
val result = mutableListOf<String>()
|
||||||
|
if (isVideoCodecEqual()) result.addAll(listOf(
|
||||||
|
"-vcodec", "copy"
|
||||||
|
)) else {
|
||||||
|
result.addAll(listOf("-c:v", getCorrectCodec()))
|
||||||
|
result.addAll(listOf("-crf", preference.video.threshold.toString()))
|
||||||
|
}
|
||||||
|
if (preference.video.pixelFormatPassthrough.none { it == video.pix_fmt }) {
|
||||||
|
result.addAll(listOf("-pix_fmt", preference.video.pixelFormat))
|
||||||
|
}
|
||||||
|
result.addAll(listOf("-map", "0:v:${video.index}"))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected fun getCorrectCodec(): String {
|
||||||
|
return when(preference.video.codec.lowercase()) {
|
||||||
|
"hevc" -> "libx265"
|
||||||
|
"h265" -> "libx265"
|
||||||
|
"h.265" -> "libx265"
|
||||||
|
|
||||||
|
"h.264" -> "libx264"
|
||||||
|
"h264" -> "libx264"
|
||||||
|
|
||||||
|
else -> preference.video.codec.lowercase()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.fileWatcher
|
||||||
|
|
||||||
|
import dev.vishna.watchservice.KWatchEvent
|
||||||
|
import dev.vishna.watchservice.asWatchChannel
|
||||||
|
import kotlinx.coroutines.channels.consumeEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import mu.KotlinLogging
|
||||||
|
import no.iktdev.exfl.coroutines.Coroutines
|
||||||
|
import no.iktdev.streamit.content.common.CommonConfig
|
||||||
|
import no.iktdev.streamit.content.common.Naming
|
||||||
|
|
||||||
|
import no.iktdev.streamit.content.reader.ReaderEnv
|
||||||
|
import no.iktdev.streamit.library.kafka.KnownEvents
|
||||||
|
import no.iktdev.streamit.library.kafka.Message
|
||||||
|
import no.iktdev.streamit.library.kafka.Status
|
||||||
|
import no.iktdev.streamit.library.kafka.StatusType
|
||||||
|
import no.iktdev.streamit.library.kafka.producer.DefaultProducer
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
private val logger = KotlinLogging.logger {}
|
||||||
|
@Service
|
||||||
|
class FileWatcher: FileWatcherEvents {
|
||||||
|
val messageProducer = DefaultProducer(CommonConfig.kafkaConsumerId)
|
||||||
|
|
||||||
|
val queue = FileWatcherQueue()
|
||||||
|
|
||||||
|
|
||||||
|
val watcherChannel = CommonConfig.incomingContent?.asWatchChannel()
|
||||||
|
init {
|
||||||
|
Coroutines.io().launch {
|
||||||
|
if (watcherChannel == null) {
|
||||||
|
logger.error { "Can't start watcherChannel on null!" }
|
||||||
|
}
|
||||||
|
watcherChannel?.consumeEach {
|
||||||
|
when (it.kind) {
|
||||||
|
KWatchEvent.Kind.Deleted -> {
|
||||||
|
queue.removeFromQueue(it.file, this@FileWatcher::onFileRemoved)
|
||||||
|
}
|
||||||
|
KWatchEvent.Kind.Created, KWatchEvent.Kind.Initialized -> {
|
||||||
|
queue.addToQueue(it.file, this@FileWatcher::onFilePending, this@FileWatcher::onFileAvailable)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
logger.info { "Ignoring event kind: ${it.kind.name} for file ${it.file.name}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onFileAvailable(file: PendingFile) {
|
||||||
|
val naming = Naming(file.file.nameWithoutExtension)
|
||||||
|
val message = Message(
|
||||||
|
referenceId = file.id,
|
||||||
|
status = Status(StatusType.SUCCESS),
|
||||||
|
data = FileResult(file = file.file.absolutePath, title = naming.guessDesiredTitle(), desiredNewName = naming.guessDesiredFileName())
|
||||||
|
)
|
||||||
|
messageProducer.sendMessage(KnownEvents.EVENT_READER_RECEIVED_FILE.event, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFilePending(file: PendingFile) {
|
||||||
|
val message = Message(
|
||||||
|
status = Status(StatusType.PENDING),
|
||||||
|
data = FileResult(file = file.file.absolutePath)
|
||||||
|
)
|
||||||
|
messageProducer.sendMessage(KnownEvents.EVENT_READER_RECEIVED_FILE.event , message)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFileFailed(file: PendingFile) {
|
||||||
|
val message = Message(
|
||||||
|
status = Status(StatusType.ERROR),
|
||||||
|
data = file.file.absolutePath
|
||||||
|
)
|
||||||
|
messageProducer.sendMessage(KnownEvents.EVENT_READER_RECEIVED_FILE.event , message)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFileRemoved(file: PendingFile) {
|
||||||
|
val message = Message(
|
||||||
|
status = Status(StatusType.IGNORED),
|
||||||
|
data = file.file.absolutePath
|
||||||
|
)
|
||||||
|
messageProducer.sendMessage(KnownEvents.EVENT_READER_RECEIVED_FILE.event , message)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FileResult(
|
||||||
|
val file: String,
|
||||||
|
val title: String = "",
|
||||||
|
val desiredNewName: String = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.fileWatcher
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
interface FileWatcherEvents {
|
||||||
|
fun onFileAvailable(file: PendingFile)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the file is being copied or incomplete, or in case a process currently owns the file, pending should be issued
|
||||||
|
*/
|
||||||
|
fun onFilePending(file: PendingFile)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the file is either removed or is not a valid file
|
||||||
|
*/
|
||||||
|
fun onFileFailed(file: PendingFile)
|
||||||
|
|
||||||
|
|
||||||
|
fun onFileRemoved(file: PendingFile)
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.fileWatcher
|
||||||
|
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import no.iktdev.exfl.coroutines.Coroutines
|
||||||
|
import no.iktdev.streamit.content.common.FileAccess
|
||||||
|
import java.io.File
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
data class PendingFile(val id: String = UUID.randomUUID().toString(), val file: File, var time: Long = 0)
|
||||||
|
class FileWatcherQueue {
|
||||||
|
private val fileChannel = Channel<PendingFile>()
|
||||||
|
|
||||||
|
fun addToQueue(file: File, onFilePending: (PendingFile) -> Unit, onFileAccessible: (PendingFile) -> Unit) {
|
||||||
|
// Check if the file is accessible
|
||||||
|
if (FileAccess.isFileAvailable(file)) {
|
||||||
|
// If accessible, run the function immediately and return
|
||||||
|
onFileAccessible(PendingFile(file = file))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the file to the channel for processing
|
||||||
|
fileChannel.trySend(PendingFile(file = file))
|
||||||
|
|
||||||
|
// Coroutine to process the file and remove it from the queue when accessible
|
||||||
|
Coroutines.default().launch {
|
||||||
|
while (true) {
|
||||||
|
delay(500)
|
||||||
|
val currentFile = fileChannel.receive()
|
||||||
|
if (FileAccess.isFileAvailable(currentFile.file)) {
|
||||||
|
onFileAccessible(currentFile)
|
||||||
|
// File is accessible, remove it from the queue
|
||||||
|
removeFromQueue(currentFile.file) { /* Do nothing here as the operation is not intended to be performed here */ }
|
||||||
|
} else {
|
||||||
|
// File is not accessible, put it back in the channel for later processing
|
||||||
|
fileChannel.send(currentFile.apply { time += 500 })
|
||||||
|
onFilePending(currentFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // https://chat.openai.com/share/f3c8f6ea-603a-40d6-a811-f8fea5067501
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeFromQueue(file: File, onFileRemoved: (PendingFile) -> Unit) {
|
||||||
|
val removedFile = fileChannel.findAndRemove { it.file == file }
|
||||||
|
removedFile?.let {
|
||||||
|
onFileRemoved(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension function to find and remove an element from the channel
|
||||||
|
fun <T> Channel<T>.findAndRemove(predicate: (T) -> Boolean): T? {
|
||||||
|
val items = mutableListOf<T>()
|
||||||
|
while (true) {
|
||||||
|
val item = poll() ?: break
|
||||||
|
if (predicate(item)) {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
items.add(item)
|
||||||
|
}
|
||||||
|
for (item in items) {
|
||||||
|
offer(item)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.streams
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import mu.KotlinLogging
|
||||||
|
import no.iktdev.streamit.content.common.CommonConfig
|
||||||
|
import no.iktdev.streamit.content.common.deamon.Daemon
|
||||||
|
import no.iktdev.streamit.content.common.deamon.IDaemon
|
||||||
|
import no.iktdev.streamit.content.reader.ReaderEnv
|
||||||
|
import no.iktdev.streamit.library.kafka.KnownEvents
|
||||||
|
import no.iktdev.streamit.library.kafka.KnownEvents.EVENT_READER_RECEIVED_FILE
|
||||||
|
import no.iktdev.streamit.library.kafka.Message
|
||||||
|
import no.iktdev.streamit.library.kafka.Status
|
||||||
|
import no.iktdev.streamit.library.kafka.StatusType
|
||||||
|
import no.iktdev.streamit.library.kafka.consumers.DefaultConsumer
|
||||||
|
import no.iktdev.streamit.library.kafka.listener.EventMessageListener
|
||||||
|
import no.iktdev.streamit.library.kafka.producer.DefaultProducer
|
||||||
|
import org.apache.kafka.clients.consumer.ConsumerRecord
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
private val logger = KotlinLogging.logger {}
|
||||||
|
@Service
|
||||||
|
class StreamsReader {
|
||||||
|
|
||||||
|
val messageProducer = DefaultProducer(CommonConfig.kafkaConsumerId)
|
||||||
|
val defaultConsumer = DefaultConsumer().apply {
|
||||||
|
// autoCommit = false
|
||||||
|
}
|
||||||
|
init {
|
||||||
|
object: EventMessageListener(CommonConfig.kafkaConsumerId, defaultConsumer, listOf(EVENT_READER_RECEIVED_FILE.event)) {
|
||||||
|
override fun onMessage(data: ConsumerRecord<String, Message>) {
|
||||||
|
if (data.value().status.statusType != StatusType.SUCCESS) {
|
||||||
|
logger.info { "Ignoring event: ${data.key()} as status is not Success!" }
|
||||||
|
return
|
||||||
|
} else if (data.value().data !is String) {
|
||||||
|
logger.info { "Ignoring event: ${data.key()} as values is not of expected type!" }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.info { "Preparing Probe for ${data.value().data}" }
|
||||||
|
val output = mutableListOf<String>()
|
||||||
|
val d = Daemon(executable = ReaderEnv.ffprobe, parameters = listOf("-v", "quiet", "-print_format", "json", "-show_streams", data.value().data as String), daemonInterface = object:
|
||||||
|
IDaemon {
|
||||||
|
override fun onOutputChanged(line: String) {
|
||||||
|
output.add(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStarted() {
|
||||||
|
logger.info { "Probe started for ${data.value().data}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError() {
|
||||||
|
logger.error { "An error occurred for ${data.value().data}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEnded() {
|
||||||
|
logger.info { "Probe ended for ${data.value().data}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
val resultCode = runBlocking {
|
||||||
|
d.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
val message = Message(status = Status( statusType = if (resultCode == 0) StatusType.SUCCESS else StatusType.ERROR), data = output.joinToString("\n"))
|
||||||
|
messageProducer.sendMessage(KnownEvents.EVENT_READER_RECEIVED_STREAMS.event, message)
|
||||||
|
}
|
||||||
|
}.listen()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package no.iktdev.streamit.content.reader
|
||||||
|
|
||||||
|
import org.apache.kafka.clients.consumer.ConsumerRecord
|
||||||
|
|
||||||
|
open class Resources {
|
||||||
|
|
||||||
|
fun getText(path: String): String? {
|
||||||
|
return this.javaClass.classLoader.getResource(path)?.readText()
|
||||||
|
}
|
||||||
|
|
||||||
|
open class Streams(): Resources() {
|
||||||
|
fun all(): List<String> {
|
||||||
|
return listOf<String>(
|
||||||
|
getSample(0),
|
||||||
|
getSample(1),
|
||||||
|
getSample(2),
|
||||||
|
getSample(3),
|
||||||
|
getSample(4),
|
||||||
|
getSample(5),
|
||||||
|
getSample(6),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSample(number: Int): String {
|
||||||
|
return getText("streams/sample$number.json")!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Any?> getConsumerRecord(event: String, data: T): ConsumerRecord<String, T> {
|
||||||
|
return ConsumerRecord("testTopic", 0, 0L, event, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package no.iktdev.streamit.content.reader.analyzer
|
||||||
|
|
||||||
|
import no.iktdev.streamit.content.reader.Resources
|
||||||
|
import no.iktdev.streamit.library.kafka.KnownEvents
|
||||||
|
import no.iktdev.streamit.library.kafka.Message
|
||||||
|
import no.iktdev.streamit.library.kafka.Status
|
||||||
|
import no.iktdev.streamit.library.kafka.StatusType
|
||||||
|
import org.apache.kafka.clients.consumer.ConsumerRecord
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.*
|
||||||
|
|
||||||
|
class EncodeStreamsMessageParserTest {
|
||||||
|
val parser = EncodeStreamsMessageParser()
|
||||||
|
val baseEvent = Message(status = Status( statusType = StatusType.SUCCESS))
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getFileNameFromEvent() {
|
||||||
|
val payload = Resources.Streams().getSample(3)
|
||||||
|
assertDoesNotThrow {
|
||||||
|
val msg = baseEvent.copy(data = payload)
|
||||||
|
val result = parser.getMediaStreamsFromEvent(mutableListOf(
|
||||||
|
Resources().getConsumerRecord(
|
||||||
|
KnownEvents.EVENT_READER_RECEIVED_STREAMS.event,
|
||||||
|
msg
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getMediaStreamsFromEvent() {
|
||||||
|
}
|
||||||
|
}
|
||||||
97
Reader/src/test/resources/streams/sample1.json
Normal file
97
Reader/src/test/resources/streams/sample1.json
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
{
|
||||||
|
"streams": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"codec_name": "hevc",
|
||||||
|
"codec_long_name": "H.265 / HEVC (High Efficiency Video Coding)",
|
||||||
|
"profile": "Main 10",
|
||||||
|
"codec_type": "video",
|
||||||
|
"codec_time_base": "1/25",
|
||||||
|
"codec_tag_string": "hev1",
|
||||||
|
"codec_tag": "0x31766568",
|
||||||
|
"width": 1920,
|
||||||
|
"height": 960,
|
||||||
|
"coded_width": 1920,
|
||||||
|
"coded_height": 960,
|
||||||
|
"has_b_frames": 2,
|
||||||
|
"sample_aspect_ratio": "1:1",
|
||||||
|
"display_aspect_ratio": "2:1",
|
||||||
|
"pix_fmt": "yuv420p10le",
|
||||||
|
"level": 120,
|
||||||
|
"color_range": "tv",
|
||||||
|
"refs": 1,
|
||||||
|
"r_frame_rate": "25/1",
|
||||||
|
"avg_frame_rate": "25/1",
|
||||||
|
"time_base": "1/25000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 70902000,
|
||||||
|
"duration": "2836.080000",
|
||||||
|
"bit_rate": "1999184",
|
||||||
|
"nb_frames": "70902",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"creation_time": "2022-01-04T07:01:48.000000Z",
|
||||||
|
"language": "und",
|
||||||
|
"handler_name": "VideoHandler"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"codec_name": "aac",
|
||||||
|
"codec_long_name": "AAC (Advanced Audio Coding)",
|
||||||
|
"profile": "LC",
|
||||||
|
"codec_type": "audio",
|
||||||
|
"codec_time_base": "1/48000",
|
||||||
|
"codec_tag_string": "mp4a",
|
||||||
|
"codec_tag": "0x6134706d",
|
||||||
|
"sample_fmt": "fltp",
|
||||||
|
"sample_rate": "48000",
|
||||||
|
"channels": 6,
|
||||||
|
"channel_layout": "5.1",
|
||||||
|
"bits_per_sample": 0,
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/48000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 136131024,
|
||||||
|
"duration": "2836.063000",
|
||||||
|
"bit_rate": "224000",
|
||||||
|
"max_bit_rate": "224000",
|
||||||
|
"nb_frames": "132943",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"creation_time": "2022-01-04T07:01:48.000000Z",
|
||||||
|
"language": "nor",
|
||||||
|
"handler_name": "SoundHandler"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
118
Reader/src/test/resources/streams/sample2.json
Normal file
118
Reader/src/test/resources/streams/sample2.json
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
{
|
||||||
|
"streams": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"codec_name": "hevc",
|
||||||
|
"codec_long_name": "H.265 / HEVC (High Efficiency Video Coding)",
|
||||||
|
"profile": "Main 10",
|
||||||
|
"codec_type": "video",
|
||||||
|
"codec_time_base": "1/24",
|
||||||
|
"codec_tag_string": "hev1",
|
||||||
|
"codec_tag": "0x31766568",
|
||||||
|
"width": 1920,
|
||||||
|
"height": 960,
|
||||||
|
"coded_width": 1920,
|
||||||
|
"coded_height": 960,
|
||||||
|
"has_b_frames": 2,
|
||||||
|
"sample_aspect_ratio": "1:1",
|
||||||
|
"display_aspect_ratio": "2:1",
|
||||||
|
"pix_fmt": "yuv420p10le",
|
||||||
|
"level": 120,
|
||||||
|
"color_range": "tv",
|
||||||
|
"refs": 1,
|
||||||
|
"r_frame_rate": "24/1",
|
||||||
|
"avg_frame_rate": "24/1",
|
||||||
|
"time_base": "1/24000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 58857000,
|
||||||
|
"duration": "2452.375000",
|
||||||
|
"bit_rate": "1999262",
|
||||||
|
"nb_frames": "58857",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"creation_time": "2021-12-03T08:59:16.000000Z",
|
||||||
|
"language": "und",
|
||||||
|
"handler_name": "VideoHandler"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"codec_name": "aac",
|
||||||
|
"codec_long_name": "AAC (Advanced Audio Coding)",
|
||||||
|
"profile": "LC",
|
||||||
|
"codec_type": "audio",
|
||||||
|
"codec_time_base": "1/48000",
|
||||||
|
"codec_tag_string": "mp4a",
|
||||||
|
"codec_tag": "0x6134706d",
|
||||||
|
"sample_fmt": "fltp",
|
||||||
|
"sample_rate": "48000",
|
||||||
|
"channels": 6,
|
||||||
|
"channel_layout": "5.1",
|
||||||
|
"bits_per_sample": 0,
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/48000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 117714384,
|
||||||
|
"duration": "2452.383000",
|
||||||
|
"bit_rate": "224003",
|
||||||
|
"max_bit_rate": "224003",
|
||||||
|
"nb_frames": "114958",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"creation_time": "2021-12-03T08:59:16.000000Z",
|
||||||
|
"language": "eng",
|
||||||
|
"handler_name": "SoundHandler"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"format": {
|
||||||
|
"filename": "Alex.Rider.S02E01.1080p.WEBRip.x265-RARBG.mp4",
|
||||||
|
"nb_streams": 2,
|
||||||
|
"nb_programs": 0,
|
||||||
|
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
|
||||||
|
"format_long_name": "QuickTime / MOV",
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration": "2452.426000",
|
||||||
|
"size": "683226674",
|
||||||
|
"bit_rate": "2228737",
|
||||||
|
"probe_score": 100,
|
||||||
|
"tags": {
|
||||||
|
"major_brand": "isom",
|
||||||
|
"minor_version": "512",
|
||||||
|
"compatible_brands": "isomiso2mp41",
|
||||||
|
"creation_time": "2021-12-03T08:59:16.000000Z",
|
||||||
|
"title": "Alex.Rider.S02E01.1080p.WEBRip.x265-RARBG",
|
||||||
|
"encoder": "Lavf58.20.100",
|
||||||
|
"comment": "Alex.Rider.S02E01.1080p.WEBRip.x265-RARBG"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
550
Reader/src/test/resources/streams/sample3.json
Normal file
550
Reader/src/test/resources/streams/sample3.json
Normal file
@ -0,0 +1,550 @@
|
|||||||
|
{
|
||||||
|
"streams": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"codec_name": "hevc",
|
||||||
|
"codec_long_name": "H.265 / HEVC (High Efficiency Video Coding)",
|
||||||
|
"profile": "Main 10",
|
||||||
|
"codec_type": "video",
|
||||||
|
"codec_time_base": "1001/24000",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"width": 1920,
|
||||||
|
"height": 804,
|
||||||
|
"coded_width": 1920,
|
||||||
|
"coded_height": 808,
|
||||||
|
"has_b_frames": 2,
|
||||||
|
"sample_aspect_ratio": "1:1",
|
||||||
|
"display_aspect_ratio": "160:67",
|
||||||
|
"pix_fmt": "yuv420p10le",
|
||||||
|
"level": 123,
|
||||||
|
"color_range": "tv",
|
||||||
|
"color_space": "bt709",
|
||||||
|
"color_transfer": "bt709",
|
||||||
|
"color_primaries": "bt709",
|
||||||
|
"refs": 1,
|
||||||
|
"r_frame_rate": "24000/1001",
|
||||||
|
"avg_frame_rate": "24000/1001",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"title": "Presented By EMBER",
|
||||||
|
"BPS": "3796879",
|
||||||
|
"DURATION": "02:01:28.782000000",
|
||||||
|
"NUMBER_OF_FRAMES": "174756",
|
||||||
|
"NUMBER_OF_BYTES": "3459328516",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v65.0.0 ('Too Much') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2022-05-24 07:43:44",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"codec_name": "ac3",
|
||||||
|
"codec_long_name": "ATSC A/52A (AC-3)",
|
||||||
|
"codec_type": "audio",
|
||||||
|
"codec_time_base": "1/48000",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"sample_fmt": "fltp",
|
||||||
|
"sample_rate": "48000",
|
||||||
|
"channels": 6,
|
||||||
|
"channel_layout": "5.1(side)",
|
||||||
|
"bits_per_sample": 0,
|
||||||
|
"dmix_mode": "-1",
|
||||||
|
"ltrt_cmixlev": "-1.000000",
|
||||||
|
"ltrt_surmixlev": "-1.000000",
|
||||||
|
"loro_cmixlev": "-1.000000",
|
||||||
|
"loro_surmixlev": "-1.000000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"bit_rate": "448000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "eng",
|
||||||
|
"BPS": "448000",
|
||||||
|
"DURATION": "02:01:28.832000000",
|
||||||
|
"NUMBER_OF_FRAMES": "227776",
|
||||||
|
"NUMBER_OF_BYTES": "408174592",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v65.0.0 ('Too Much') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2022-05-24 07:43:44",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"codec_name": "ac3",
|
||||||
|
"codec_long_name": "ATSC A/52A (AC-3)",
|
||||||
|
"codec_type": "audio",
|
||||||
|
"codec_time_base": "1/48000",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"sample_fmt": "fltp",
|
||||||
|
"sample_rate": "48000",
|
||||||
|
"channels": 6,
|
||||||
|
"channel_layout": "5.1(side)",
|
||||||
|
"bits_per_sample": 0,
|
||||||
|
"dmix_mode": "-1",
|
||||||
|
"ltrt_cmixlev": "-1.000000",
|
||||||
|
"ltrt_surmixlev": "-1.000000",
|
||||||
|
"loro_cmixlev": "-1.000000",
|
||||||
|
"loro_surmixlev": "-1.000000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"bit_rate": "448000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "jpn",
|
||||||
|
"BPS": "448000",
|
||||||
|
"DURATION": "02:01:28.832000000",
|
||||||
|
"NUMBER_OF_FRAMES": "227776",
|
||||||
|
"NUMBER_OF_BYTES": "408174592",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v65.0.0 ('Too Much') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2022-05-24 07:43:44",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"codec_name": "ass",
|
||||||
|
"codec_long_name": "ASS (Advanced SSA) subtitle",
|
||||||
|
"codec_type": "subtitle",
|
||||||
|
"codec_time_base": "0/1",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 7288832,
|
||||||
|
"duration": "7288.832000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "eng",
|
||||||
|
"title": "Signs & Songs@EMBER",
|
||||||
|
"BPS": "5",
|
||||||
|
"DURATION": "01:54:41.630000000",
|
||||||
|
"NUMBER_OF_FRAMES": "90",
|
||||||
|
"NUMBER_OF_BYTES": "4696",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v65.0.0 ('Too Much') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2022-05-24 07:43:44",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"codec_name": "ass",
|
||||||
|
"codec_long_name": "ASS (Advanced SSA) subtitle",
|
||||||
|
"codec_type": "subtitle",
|
||||||
|
"codec_time_base": "0/1",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 7288832,
|
||||||
|
"duration": "7288.832000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "eng",
|
||||||
|
"title": "Dialogue@EMBER",
|
||||||
|
"BPS": "78",
|
||||||
|
"DURATION": "01:56:48.150000000",
|
||||||
|
"NUMBER_OF_FRAMES": "1434",
|
||||||
|
"NUMBER_OF_BYTES": "69001",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v65.0.0 ('Too Much') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2022-05-24 07:43:44",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"codec_name": "hdmv_pgs_subtitle",
|
||||||
|
"codec_long_name": "HDMV Presentation Graphic Stream subtitles",
|
||||||
|
"codec_type": "subtitle",
|
||||||
|
"codec_time_base": "0/1",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 7288832,
|
||||||
|
"duration": "7288.832000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "eng",
|
||||||
|
"title": "Signs & Songs@USBD",
|
||||||
|
"BPS": "402",
|
||||||
|
"DURATION": "01:50:49.111000000",
|
||||||
|
"NUMBER_OF_FRAMES": "56",
|
||||||
|
"NUMBER_OF_BYTES": "334551",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v65.0.0 ('Too Much') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2022-05-24 07:43:44",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 6,
|
||||||
|
"codec_name": "hdmv_pgs_subtitle",
|
||||||
|
"codec_long_name": "HDMV Presentation Graphic Stream subtitles",
|
||||||
|
"codec_type": "subtitle",
|
||||||
|
"codec_time_base": "0/1",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 7288832,
|
||||||
|
"duration": "7288.832000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "eng",
|
||||||
|
"title": "Dialogue@USBD",
|
||||||
|
"BPS": "21019",
|
||||||
|
"DURATION": "02:00:56.802000000",
|
||||||
|
"NUMBER_OF_FRAMES": "2829",
|
||||||
|
"NUMBER_OF_BYTES": "19067149",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v65.0.0 ('Too Much') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2022-05-24 07:43:44",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 7,
|
||||||
|
"codec_name": "hdmv_pgs_subtitle",
|
||||||
|
"codec_long_name": "HDMV Presentation Graphic Stream subtitles",
|
||||||
|
"codec_type": "subtitle",
|
||||||
|
"codec_time_base": "0/1",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 7288832,
|
||||||
|
"duration": "7288.832000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "eng",
|
||||||
|
"title": "CC@USBD",
|
||||||
|
"BPS": "34179",
|
||||||
|
"DURATION": "01:58:57.850000000",
|
||||||
|
"NUMBER_OF_FRAMES": "4338",
|
||||||
|
"NUMBER_OF_BYTES": "30495881",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v65.0.0 ('Too Much') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2022-05-24 07:43:44",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 8,
|
||||||
|
"codec_type": "attachment",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/90000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 655994880,
|
||||||
|
"duration": "7288.832000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"filename": "GandhiSans-BoldItalic.otf",
|
||||||
|
"mimetype": "font/otf"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 9,
|
||||||
|
"codec_type": "attachment",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/90000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 655994880,
|
||||||
|
"duration": "7288.832000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"filename": "HAPPYHELL.TTF",
|
||||||
|
"mimetype": "font/ttf"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 10,
|
||||||
|
"codec_type": "attachment",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/90000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 655994880,
|
||||||
|
"duration": "7288.832000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"filename": "AVERIALIBRE-BOLD.TTF",
|
||||||
|
"mimetype": "font/ttf"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 11,
|
||||||
|
"codec_type": "attachment",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/90000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 655994880,
|
||||||
|
"duration": "7288.832000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"filename": "GandhiSans-Bold.otf",
|
||||||
|
"mimetype": "font/otf"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 12,
|
||||||
|
"codec_name": "mjpeg",
|
||||||
|
"codec_long_name": "Motion JPEG",
|
||||||
|
"profile": "Baseline",
|
||||||
|
"codec_type": "video",
|
||||||
|
"codec_time_base": "0/1",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"width": 640,
|
||||||
|
"height": 360,
|
||||||
|
"coded_width": 640,
|
||||||
|
"coded_height": 360,
|
||||||
|
"has_b_frames": 0,
|
||||||
|
"sample_aspect_ratio": "1:1",
|
||||||
|
"display_aspect_ratio": "16:9",
|
||||||
|
"pix_fmt": "yuvj420p",
|
||||||
|
"level": -99,
|
||||||
|
"color_range": "pc",
|
||||||
|
"color_space": "bt470bg",
|
||||||
|
"chroma_location": "center",
|
||||||
|
"refs": 1,
|
||||||
|
"r_frame_rate": "90000/1",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/90000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 655994880,
|
||||||
|
"duration": "7288.832000",
|
||||||
|
"bits_per_raw_sample": "8",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 1,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"filename": "cover.jpg",
|
||||||
|
"mimetype": "image/jpeg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"format": {
|
||||||
|
"filename": "[EMBER] Belle - Ryuu to Sobakasu no Hime (2021) (Movie) [BDRip] [804p Dual Audio HEVC 10 bits DD].mkv",
|
||||||
|
"nb_streams": 13,
|
||||||
|
"nb_programs": 0,
|
||||||
|
"format_name": "matroska,webm",
|
||||||
|
"format_long_name": "Matroska / WebM",
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration": "7288.832000",
|
||||||
|
"size": "4333518626",
|
||||||
|
"bit_rate": "4756338",
|
||||||
|
"probe_score": 100,
|
||||||
|
"tags": {
|
||||||
|
"title": "Belle.1080p.Dual.Audio.BDRip.10.bits.DD.x265-EMBER",
|
||||||
|
"encoder": "libebml v1.4.2 + libmatroska v1.6.4",
|
||||||
|
"creation_time": "2022-05-24T07:43:44.000000Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1093
Reader/src/test/resources/streams/sample4.json
Normal file
1093
Reader/src/test/resources/streams/sample4.json
Normal file
File diff suppressed because it is too large
Load Diff
98
Reader/src/test/resources/streams/sample5.json
Normal file
98
Reader/src/test/resources/streams/sample5.json
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"streams": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"codec_name": "hevc",
|
||||||
|
"codec_long_name": "H.265 / HEVC (High Efficiency Video Coding)",
|
||||||
|
"profile": "Main 10",
|
||||||
|
"codec_type": "video",
|
||||||
|
"codec_tag_string": "hev1",
|
||||||
|
"codec_tag": "0x31766568",
|
||||||
|
"width": 1920,
|
||||||
|
"height": 960,
|
||||||
|
"coded_width": 1920,
|
||||||
|
"coded_height": 960,
|
||||||
|
"closed_captions": 0,
|
||||||
|
"has_b_frames": 2,
|
||||||
|
"sample_aspect_ratio": "1:1",
|
||||||
|
"display_aspect_ratio": "2:1",
|
||||||
|
"pix_fmt": "yuv420p10le",
|
||||||
|
"level": 120,
|
||||||
|
"color_range": "tv",
|
||||||
|
"chroma_location": "left",
|
||||||
|
"refs": 1,
|
||||||
|
"r_frame_rate": "24/1",
|
||||||
|
"avg_frame_rate": "24/1",
|
||||||
|
"time_base": "1/24000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 84883000,
|
||||||
|
"duration": "3536.791667",
|
||||||
|
"bit_rate": "1998078",
|
||||||
|
"nb_frames": "84883",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"creation_time": "2022-05-19T19:59:17.000000Z",
|
||||||
|
"language": "und",
|
||||||
|
"handler_name": "VideoHandler",
|
||||||
|
"vendor_id": "[0][0][0][0]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"codec_name": "aac",
|
||||||
|
"codec_long_name": "AAC (Advanced Audio Coding)",
|
||||||
|
"profile": "LC",
|
||||||
|
"codec_type": "audio",
|
||||||
|
"codec_tag_string": "mp4a",
|
||||||
|
"codec_tag": "0x6134706d",
|
||||||
|
"sample_fmt": "fltp",
|
||||||
|
"sample_rate": "48000",
|
||||||
|
"channels": 6,
|
||||||
|
"channel_layout": "5.1",
|
||||||
|
"bits_per_sample": 0,
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/48000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 169766400,
|
||||||
|
"duration": "3536.800000",
|
||||||
|
"bit_rate": "224001",
|
||||||
|
"nb_frames": "165790",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"creation_time": "2022-05-19T19:59:17.000000Z",
|
||||||
|
"language": "eng",
|
||||||
|
"handler_name": "SoundHandler",
|
||||||
|
"vendor_id": "[0][0][0][0]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
193
Reader/src/test/resources/streams/sample6.json
Normal file
193
Reader/src/test/resources/streams/sample6.json
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
{
|
||||||
|
"streams": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"codec_name": "hevc",
|
||||||
|
"codec_long_name": "H.265 / HEVC (High Efficiency Video Coding)",
|
||||||
|
"profile": "Main 10",
|
||||||
|
"codec_type": "video",
|
||||||
|
"codec_time_base": "1001/24000",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"width": 1920,
|
||||||
|
"height": 1080,
|
||||||
|
"coded_width": 1920,
|
||||||
|
"coded_height": 1080,
|
||||||
|
"has_b_frames": 2,
|
||||||
|
"sample_aspect_ratio": "1:1",
|
||||||
|
"display_aspect_ratio": "16:9",
|
||||||
|
"pix_fmt": "yuv420p10le",
|
||||||
|
"level": 120,
|
||||||
|
"color_range": "tv",
|
||||||
|
"color_space": "bt709",
|
||||||
|
"color_transfer": "bt709",
|
||||||
|
"color_primaries": "bt709",
|
||||||
|
"refs": 1,
|
||||||
|
"r_frame_rate": "24000/1001",
|
||||||
|
"avg_frame_rate": "24000/1001",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"title": "Presented By EMBER",
|
||||||
|
"BPS": "2438576",
|
||||||
|
"DURATION": "00:23:42.004000000",
|
||||||
|
"NUMBER_OF_FRAMES": "34094",
|
||||||
|
"NUMBER_OF_BYTES": "433458227",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v65.0.0 ('Too Much') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2022-07-06 20:30:37",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"codec_name": "eac3",
|
||||||
|
"codec_long_name": "ATSC A/52B (AC-3, E-AC-3)",
|
||||||
|
"codec_type": "audio",
|
||||||
|
"codec_time_base": "1/48000",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"sample_fmt": "fltp",
|
||||||
|
"sample_rate": "48000",
|
||||||
|
"channels": 2,
|
||||||
|
"bits_per_sample": 0,
|
||||||
|
"dmix_mode": "-1",
|
||||||
|
"ltrt_cmixlev": "-1.000000",
|
||||||
|
"ltrt_surmixlev": "-1.000000",
|
||||||
|
"loro_cmixlev": "-1.000000",
|
||||||
|
"loro_surmixlev": "-1.000000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "jpn",
|
||||||
|
"BPS": "128000",
|
||||||
|
"DURATION": "00:23:42.112000000",
|
||||||
|
"NUMBER_OF_FRAMES": "44441",
|
||||||
|
"NUMBER_OF_BYTES": "22753792",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v65.0.0 ('Too Much') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2022-07-06 20:30:37",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"codec_name": "subrip",
|
||||||
|
"codec_long_name": "SubRip subtitle",
|
||||||
|
"codec_type": "subtitle",
|
||||||
|
"codec_time_base": "0/1",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 1422112,
|
||||||
|
"duration": "1422.112000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "eng",
|
||||||
|
"BPS": "65",
|
||||||
|
"DURATION": "00:23:25.487000000",
|
||||||
|
"NUMBER_OF_FRAMES": "342",
|
||||||
|
"NUMBER_OF_BYTES": "11595",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v65.0.0 ('Too Much') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2022-07-06 20:30:37",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"codec_name": "mjpeg",
|
||||||
|
"codec_long_name": "Motion JPEG",
|
||||||
|
"profile": "Progressive",
|
||||||
|
"codec_type": "video",
|
||||||
|
"codec_time_base": "0/1",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"width": 400,
|
||||||
|
"height": 564,
|
||||||
|
"coded_width": 400,
|
||||||
|
"coded_height": 564,
|
||||||
|
"has_b_frames": 0,
|
||||||
|
"sample_aspect_ratio": "1:1",
|
||||||
|
"display_aspect_ratio": "100:141",
|
||||||
|
"pix_fmt": "yuvj420p",
|
||||||
|
"level": -99,
|
||||||
|
"color_range": "pc",
|
||||||
|
"color_space": "bt470bg",
|
||||||
|
"chroma_location": "center",
|
||||||
|
"refs": 1,
|
||||||
|
"r_frame_rate": "90000/1",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/90000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 127990080,
|
||||||
|
"duration": "1422.112000",
|
||||||
|
"bits_per_raw_sample": "8",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 1,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"filename": "cover.jpg",
|
||||||
|
"mimetype": "image/jpeg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
205
Reader/src/test/resources/streams/sample7.json
Normal file
205
Reader/src/test/resources/streams/sample7.json
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
{
|
||||||
|
"streams": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"codec_name": "hevc",
|
||||||
|
"codec_long_name": "H.265 / HEVC (High Efficiency Video Coding)",
|
||||||
|
"profile": "Main 10",
|
||||||
|
"codec_type": "video",
|
||||||
|
"codec_time_base": "1/25",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"width": 1920,
|
||||||
|
"height": 952,
|
||||||
|
"coded_width": 1920,
|
||||||
|
"coded_height": 952,
|
||||||
|
"has_b_frames": 2,
|
||||||
|
"sample_aspect_ratio": "1:1",
|
||||||
|
"display_aspect_ratio": "240:119",
|
||||||
|
"pix_fmt": "yuv420p10le",
|
||||||
|
"level": 120,
|
||||||
|
"color_range": "tv",
|
||||||
|
"refs": 1,
|
||||||
|
"r_frame_rate": "25/1",
|
||||||
|
"avg_frame_rate": "25/1",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"BPS": "3698552",
|
||||||
|
"BPS-eng": "3698552",
|
||||||
|
"DURATION": "00:43:59.240000000",
|
||||||
|
"DURATION-eng": "00:43:59.240000000",
|
||||||
|
"NUMBER_OF_FRAMES": "65981",
|
||||||
|
"NUMBER_OF_FRAMES-eng": "65981",
|
||||||
|
"NUMBER_OF_BYTES": "1220170846",
|
||||||
|
"NUMBER_OF_BYTES-eng": "1220170846",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v17.0.0 ('Be Ur Friend') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_APP-eng": "mkvmerge v17.0.0 ('Be Ur Friend') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2019-05-21 18:17:28",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC-eng": "2019-05-21 18:17:28",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES",
|
||||||
|
"_STATISTICS_TAGS-eng": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"codec_name": "aac",
|
||||||
|
"codec_long_name": "AAC (Advanced Audio Coding)",
|
||||||
|
"profile": "LC",
|
||||||
|
"codec_type": "audio",
|
||||||
|
"codec_time_base": "1/48000",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"sample_fmt": "fltp",
|
||||||
|
"sample_rate": "48000",
|
||||||
|
"channels": 2,
|
||||||
|
"channel_layout": "stereo",
|
||||||
|
"bits_per_sample": 0,
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 20,
|
||||||
|
"start_time": "0.020000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 1,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "nor",
|
||||||
|
"BPS": "152584",
|
||||||
|
"BPS-eng": "152584",
|
||||||
|
"DURATION": "00:43:58.250000000",
|
||||||
|
"DURATION-eng": "00:43:58.250000000",
|
||||||
|
"NUMBER_OF_FRAMES": "123668",
|
||||||
|
"NUMBER_OF_FRAMES-eng": "123668",
|
||||||
|
"NUMBER_OF_BYTES": "50319602",
|
||||||
|
"NUMBER_OF_BYTES-eng": "50319602",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v17.0.0 ('Be Ur Friend') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_APP-eng": "mkvmerge v17.0.0 ('Be Ur Friend') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2019-05-21 18:17:28",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC-eng": "2019-05-21 18:17:28",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES",
|
||||||
|
"_STATISTICS_TAGS-eng": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"codec_name": "subrip",
|
||||||
|
"codec_long_name": "SubRip subtitle",
|
||||||
|
"codec_type": "subtitle",
|
||||||
|
"codec_time_base": "0/1",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 2639240,
|
||||||
|
"duration": "2639.240000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "eng",
|
||||||
|
"BPS": "21",
|
||||||
|
"BPS-eng": "21",
|
||||||
|
"DURATION": "00:43:00.840000000",
|
||||||
|
"DURATION-eng": "00:43:00.840000000",
|
||||||
|
"NUMBER_OF_FRAMES": "197",
|
||||||
|
"NUMBER_OF_FRAMES-eng": "197",
|
||||||
|
"NUMBER_OF_BYTES": "6798",
|
||||||
|
"NUMBER_OF_BYTES-eng": "6798",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v17.0.0 ('Be Ur Friend') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_APP-eng": "mkvmerge v17.0.0 ('Be Ur Friend') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2019-05-21 18:17:28",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC-eng": "2019-05-21 18:17:28",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES",
|
||||||
|
"_STATISTICS_TAGS-eng": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"codec_name": "subrip",
|
||||||
|
"codec_long_name": "SubRip subtitle",
|
||||||
|
"codec_type": "subtitle",
|
||||||
|
"codec_time_base": "0/1",
|
||||||
|
"codec_tag_string": "[0][0][0][0]",
|
||||||
|
"codec_tag": "0x0000",
|
||||||
|
"r_frame_rate": "0/0",
|
||||||
|
"avg_frame_rate": "0/0",
|
||||||
|
"time_base": "1/1000",
|
||||||
|
"start_pts": 0,
|
||||||
|
"start_time": "0.000000",
|
||||||
|
"duration_ts": 2639240,
|
||||||
|
"duration": "2639.240000",
|
||||||
|
"disposition": {
|
||||||
|
"default": 0,
|
||||||
|
"dub": 0,
|
||||||
|
"original": 0,
|
||||||
|
"comment": 0,
|
||||||
|
"lyrics": 0,
|
||||||
|
"karaoke": 0,
|
||||||
|
"forced": 0,
|
||||||
|
"hearing_impaired": 0,
|
||||||
|
"visual_impaired": 0,
|
||||||
|
"clean_effects": 0,
|
||||||
|
"attached_pic": 0,
|
||||||
|
"timed_thumbnails": 0
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"language": "dan",
|
||||||
|
"BPS": "37",
|
||||||
|
"BPS-eng": "37",
|
||||||
|
"DURATION": "00:43:20.306000000",
|
||||||
|
"DURATION-eng": "00:43:20.306000000",
|
||||||
|
"NUMBER_OF_FRAMES": "276",
|
||||||
|
"NUMBER_OF_FRAMES-eng": "276",
|
||||||
|
"NUMBER_OF_BYTES": "12172",
|
||||||
|
"NUMBER_OF_BYTES-eng": "12172",
|
||||||
|
"_STATISTICS_WRITING_APP": "mkvmerge v17.0.0 ('Be Ur Friend') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_APP-eng": "mkvmerge v17.0.0 ('Be Ur Friend') 64-bit",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC": "2019-05-21 18:17:28",
|
||||||
|
"_STATISTICS_WRITING_DATE_UTC-eng": "2019-05-21 18:17:28",
|
||||||
|
"_STATISTICS_TAGS": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES",
|
||||||
|
"_STATISTICS_TAGS-eng": "BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
42
UI/.gitignore
vendored
Normal file
42
UI/.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
19
UI/build.gradle.kts
Normal file
19
UI/build.gradle.kts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "no.iktdev.streamit.content"
|
||||||
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation(platform("org.junit:junit-bom:5.9.1"))
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
BIN
UI/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
UI/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
UI/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
UI/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Sat Jul 15 22:33:37 CEST 2023
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
234
UI/gradlew
vendored
Normal file
234
UI/gradlew
vendored
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
89
UI/gradlew.bat
vendored
Normal file
89
UI/gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
2
UI/settings.gradle.kts
Normal file
2
UI/settings.gradle.kts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rootProject.name = "UI"
|
||||||
|
|
||||||
7
UI/src/main/java/no/iktdev/streamit/content/Main.java
Normal file
7
UI/src/main/java/no/iktdev/streamit/content/Main.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package no.iktdev.streamit.content;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("Hello world!");
|
||||||
|
}
|
||||||
|
}
|
||||||
178
pyMetadata/app.py
Normal file
178
pyMetadata/app.py
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import signal
|
||||||
|
import sys, os, uuid
|
||||||
|
import threading
|
||||||
|
import json
|
||||||
|
from kafka import KafkaConsumer, KafkaProducer
|
||||||
|
from fuzzywuzzy import fuzz
|
||||||
|
from sources.result import Result, Metadata
|
||||||
|
from sources.anii import metadata as AniiMetadata
|
||||||
|
from sources.imdb import metadata as ImdbMetadata
|
||||||
|
|
||||||
|
# Konfigurer Kafka-forbindelsen
|
||||||
|
bootstrap_servers = os.environ.get("KAFKA_BOOTSTRAP_SERVER") if os.environ.get("KAFKA_BOOTSTRAP_SERVER") != None else "127.0.0.1:9092"
|
||||||
|
consumer_group = os.environ.get("KAFKA_CONSUMER_ID") if os.environ.get("KAFKA_CONSUMER_ID") != None else f"Metadata-{uuid.uuid4()}"
|
||||||
|
kafka_topic = os.environ.get("KAFKA_BOOTSTRAP_SERVER") if os.environ.get("KAFKA_BOOTSTRAP_SERVER") != None else "127.0.0.1:9092"
|
||||||
|
|
||||||
|
class ProducerDataValueSchema:
|
||||||
|
def __init__(self, referenceId, statusType, errorMessage, data):
|
||||||
|
self.referenceId = referenceId
|
||||||
|
self.statusType = statusType
|
||||||
|
self.errorMessage = errorMessage
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
'referenceId': self.referenceId,
|
||||||
|
'status': {
|
||||||
|
'statusType': self.statusType,
|
||||||
|
'errorMessage': self.errorMessage
|
||||||
|
},
|
||||||
|
'data': self.data.to_dict() if self.data else None
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
data_dict = self.to_dict()
|
||||||
|
return json.dumps(data_dict)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data_dict):
|
||||||
|
referenceId = data_dict.get('referenceId')
|
||||||
|
statusType = data_dict['status'].get('statusType')
|
||||||
|
errorMessage = data_dict['status'].get('errorMessage')
|
||||||
|
data = data_dict.get('data')
|
||||||
|
|
||||||
|
return cls(referenceId, statusType, errorMessage, data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Kafka consumer-klasse
|
||||||
|
class KafkaConsumerThread(threading.Thread):
|
||||||
|
def __init__(self, bootstrap_servers, topic):
|
||||||
|
super().__init__()
|
||||||
|
self.bootstrap_servers = bootstrap_servers
|
||||||
|
self.topic = topic
|
||||||
|
self.shutdown = threading.Event()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
consumer = KafkaConsumer(self.topic, bootstrap_servers=self.bootstrap_servers)
|
||||||
|
|
||||||
|
while not self.shutdown.is_set():
|
||||||
|
for message in consumer:
|
||||||
|
if self.shutdown.is_set():
|
||||||
|
break
|
||||||
|
|
||||||
|
# Sjekk om meldingen har målnøkkelen
|
||||||
|
if message.key == "request:metadata:obtain" or message.key == "event:reader:received-file":
|
||||||
|
# Opprett en ny tråd for å håndtere meldingen
|
||||||
|
handler_thread = MessageHandlerThread(message)
|
||||||
|
handler_thread.start()
|
||||||
|
|
||||||
|
consumer.close()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.shutdown.set()
|
||||||
|
|
||||||
|
# Kafka message handler-klasse
|
||||||
|
class MessageHandlerThread(threading.Thread):
|
||||||
|
def __init__(self, message):
|
||||||
|
super().__init__()
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# Deserialiser meldingsverdien fra JSON til et Python-dictionary
|
||||||
|
message_value = json.loads(self.message.value)
|
||||||
|
|
||||||
|
# Sjekk om meldingen har en Status
|
||||||
|
if 'status' in message_value:
|
||||||
|
status_type = message_value['status']['statusType']
|
||||||
|
|
||||||
|
# Sjekk om statusen er SUCCESS
|
||||||
|
if status_type == 'SUCCESS':
|
||||||
|
data_value = message_value['data']["title"]
|
||||||
|
|
||||||
|
# Utfør handlingen basert på verdien
|
||||||
|
result = self.perform_action(title=data_value)
|
||||||
|
|
||||||
|
producerMessage = self.compose_message(referenceId=message_value["referenceId"], result=result)
|
||||||
|
|
||||||
|
# Serialiser resultatet til JSON
|
||||||
|
result_json = json.dumps(producerMessage.to_json())
|
||||||
|
|
||||||
|
# Send resultatet tilbake ved hjelp av Kafka-producer
|
||||||
|
producer = KafkaProducer(bootstrap_servers=bootstrap_servers)
|
||||||
|
producer.send(kafka_topic, key="event:metadata:obtained", value=result_json)
|
||||||
|
producer.close()
|
||||||
|
|
||||||
|
def perform_action(self, title) -> Result:
|
||||||
|
anii = AniiMetadata(title)
|
||||||
|
imdb = ImdbMetadata(title)
|
||||||
|
|
||||||
|
anii_result = anii.lookup()
|
||||||
|
imdb_result = imdb.lookup()
|
||||||
|
|
||||||
|
# Sammenlign resultater basert på likheter og sammenhenger med tittelen
|
||||||
|
if anii_result.statusType == "SUCCESS" and imdb_result.statusType == "SUCCESS":
|
||||||
|
# Begge registrene ga suksessresultater, bruk fuzzy matching for å gjøre en vurdering
|
||||||
|
title_similarity_anii = fuzz.ratio(title.lower(), anii_result.data.title.lower())
|
||||||
|
title_similarity_imdb = fuzz.ratio(title.lower(), imdb_result.data.title.lower())
|
||||||
|
|
||||||
|
# Sammenlign likheter mellom tittel og registertitler
|
||||||
|
if title_similarity_anii > title_similarity_imdb:
|
||||||
|
most_likely_result = anii_result
|
||||||
|
else:
|
||||||
|
most_likely_result = imdb_result
|
||||||
|
|
||||||
|
elif anii_result.statusType == "SUCCESS":
|
||||||
|
# AniList ga suksessresultat, bruk det som det mest sannsynlige
|
||||||
|
most_likely_result = anii_result
|
||||||
|
|
||||||
|
elif imdb_result.statusType == "SUCCESS":
|
||||||
|
# IMDb ga suksessresultat, bruk det som det mest sannsynlige
|
||||||
|
most_likely_result = imdb_result
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Begge registrene feilet, håndter etter eget behov
|
||||||
|
most_likely_result = Result(statusType="ERROR", errorMessage="No Result")
|
||||||
|
|
||||||
|
# Returner det mest sannsynlige resultatet
|
||||||
|
return most_likely_result
|
||||||
|
|
||||||
|
|
||||||
|
def compose_message(self, referenceId: str, result: Result) -> ProducerDataValueSchema:
|
||||||
|
""""""
|
||||||
|
return ProducerDataValueSchema(
|
||||||
|
referenceId=referenceId,
|
||||||
|
statusType=result.statusType,
|
||||||
|
errorMessage=result.errorMessage,
|
||||||
|
data=result.data
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Global variabel for å indikere om applikasjonen skal avsluttes
|
||||||
|
should_stop = False
|
||||||
|
|
||||||
|
# Signalhåndteringsfunksjon
|
||||||
|
def signal_handler(sig, frame):
|
||||||
|
global should_stop
|
||||||
|
should_stop = True
|
||||||
|
|
||||||
|
# Hovedprogrammet
|
||||||
|
def main():
|
||||||
|
# Angi signalhåndterer for å fange opp SIGINT (Ctrl+C)
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
|
# Opprett og start consumer-tråden
|
||||||
|
consumer_thread = KafkaConsumerThread(bootstrap_servers, kafka_topic)
|
||||||
|
consumer_thread.start()
|
||||||
|
|
||||||
|
# Vent til should_stop er satt til True for å avslutte applikasjonen
|
||||||
|
while not should_stop:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Stopp consumer-tråden
|
||||||
|
consumer_thread.stop()
|
||||||
|
consumer_thread.join()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
4
pyMetadata/requirments.txt
Normal file
4
pyMetadata/requirments.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
cinemagoer>=2023.5.1
|
||||||
|
AnilistPython>=0.1.3
|
||||||
|
kafka-python>=2.0.2
|
||||||
|
fuzzywuzzy>=0.18.0
|
||||||
33
pyMetadata/sources/anii.py
Normal file
33
pyMetadata/sources/anii.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
from AnilistPython import Anilist
|
||||||
|
from result import Metadata, Result
|
||||||
|
|
||||||
|
class metadata():
|
||||||
|
name: str = None
|
||||||
|
anilist = Anilist()
|
||||||
|
|
||||||
|
def __init__(self, name) -> None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def lookup(self) -> Result:
|
||||||
|
""""""
|
||||||
|
try:
|
||||||
|
result = self.anilist.get_anime(self.name)
|
||||||
|
meta = Metadata()
|
||||||
|
meta.title = result.get("name_english", None)
|
||||||
|
meta.altTitle = result.get("name_romaji", None)
|
||||||
|
meta.cover = result.get("cover_image", None)
|
||||||
|
meta.summary = result.get("desc", None)
|
||||||
|
|
||||||
|
airing_format = result.get('airing_format', '').lower()
|
||||||
|
if airing_format == 'movie':
|
||||||
|
meta.type = 'movie'
|
||||||
|
else:
|
||||||
|
meta.type = 'serie'
|
||||||
|
meta.genres = result.get('genres', [])
|
||||||
|
return Result("SUCCESS", None, meta)
|
||||||
|
|
||||||
|
except IndexError as ingore:
|
||||||
|
return Result(statusType="IGNORE", errorMessage=f"No result for {self.name}")
|
||||||
|
except Exception as e:
|
||||||
|
return Result(statusType="ERROR", errorMessage=str(e))
|
||||||
|
|
||||||
34
pyMetadata/sources/imdb.py
Normal file
34
pyMetadata/sources/imdb.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import imdb
|
||||||
|
from result import Metadata, Result
|
||||||
|
|
||||||
|
class metadata():
|
||||||
|
name: str = None
|
||||||
|
imdb = imdb.Cinemagoer()
|
||||||
|
|
||||||
|
def __init__(self, name) -> None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
|
||||||
|
def lookup(self) -> Result:
|
||||||
|
""""""
|
||||||
|
try:
|
||||||
|
query = self.imdb.search_movie(self.name)
|
||||||
|
imdbId = query[0].movieID
|
||||||
|
result = self.imdb.get_movie(imdbId)
|
||||||
|
meta = Metadata()
|
||||||
|
meta.title = result.get("title", None)
|
||||||
|
meta.altTitle = result.get("localized title", None)
|
||||||
|
meta.cover = result.get("cover url", None)
|
||||||
|
meta.summary = result.get("plot outline", None)
|
||||||
|
|
||||||
|
airing_format = result.get('kind', '').lower()
|
||||||
|
if airing_format == 'movie':
|
||||||
|
meta.type = 'movie'
|
||||||
|
else:
|
||||||
|
meta.type = 'serie'
|
||||||
|
|
||||||
|
meta.genres = result.get('genres', [])
|
||||||
|
|
||||||
|
return Result("SUCCESS", None, meta)
|
||||||
|
except Exception as e:
|
||||||
|
return Result(statusType="ERROR", errorMessage=str(e))
|
||||||
30
pyMetadata/sources/result.py
Normal file
30
pyMetadata/sources/result.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
from typing import List, Optional
|
||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Metadata:
|
||||||
|
title: str
|
||||||
|
altTitle: str
|
||||||
|
cover: str
|
||||||
|
type: str # Serie/Movie
|
||||||
|
summary: str
|
||||||
|
genres: List[str]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Result:
|
||||||
|
statusType: str
|
||||||
|
errorMessage: str
|
||||||
|
data: Metadata
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data_dict):
|
||||||
|
metadata_dict = data_dict.get('data')
|
||||||
|
metadata = Metadata(**metadata_dict) if metadata_dict else None
|
||||||
|
return cls(
|
||||||
|
statusType=data_dict['statusType'],
|
||||||
|
errorMessage=data_dict['errorMessage'],
|
||||||
|
data=metadata
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue
Block a user