Compare commits

..

10 Commits

Author SHA1 Message Date
Guillaume Tâche
aab2e2a486 Fixes ASS converter, adds some doc 2024-10-06 17:47:36 +02:00
Guillaume Tâche
c58a8b0588 Reworks default tools, moves some files 2024-10-04 21:54:55 +02:00
Guillaume Tâche
df58cf4585 Allows choosing and managing each tool 2024-10-03 21:55:14 +02:00
Guillaume Tâche
0a2f9e0c31 Adds context menu for subtitles table 2024-09-26 21:41:02 +02:00
Guillaume Tâche
c59619da2d Rework to avoid using preferences object to retrieve options 2024-09-22 21:59:10 +02:00
Guillaume Tâche
7f99c48e2c Adds tests for FFmpeg 2024-09-21 21:22:30 +02:00
Guillaume Tâche
703a4c71ae Adds some tests, cleanup a bit 2024-09-20 08:36:52 +02:00
Guillaume Tâche
17086a87ef Moves some classes 2024-09-16 20:01:12 +02:00
Guillaume Tâche
233b4bdb77 Adds tests for whisperx 2024-09-16 17:41:58 +02:00
Guillaume Tâche
3cc2f7a0c9 Adds tests for common whisper 2024-09-16 14:01:57 +02:00
319 changed files with 10936 additions and 2467 deletions

8
.idea/.gitignore generated vendored
View File

@@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -1,7 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<ScalaCodeStyleSettings>
<option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
</ScalaCodeStyleSettings>
</code_scheme>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@@ -1,30 +1,53 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AbsoluteAlignmentInUserInterface" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractClassExtendsConcreteClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractClassNeverImplemented" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractMethodCallInConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractMethodOverridesAbstractMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractMethodOverridesConcreteMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractMethodWithMissingImplementations" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AccessToNonThreadSafeStaticFieldFromInstance" enabled="true" level="WARNING" enabled_by_default="true">
<option name="nonThreadSafeClasses">
<value />
</option>
<option name="nonThreadSafeTypes" value="" />
</inspection_tool>
<inspection_tool class="AccessToStaticFieldLockedOnInstance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlphaUnsortedPropertiesFile" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="AmbiguousFieldAccess" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AmbiguousMethodCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AnonymousClassMethodCount" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="1" />
</inspection_tool>
<inspection_tool class="AnonymousClassVariableHidesContainingMethodVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AnonymousInnerClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AnonymousInnerClassMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ArrayEquality" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssertEqualsCalledOnArray" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssertMessageNotString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssertsWithoutMessages" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentOrReturnOfFieldWithMutableType" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToForLoopParameter" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_checkForeachParameters" value="true" />
</inspection_tool>
<inspection_tool class="AssignmentToForLoopParameterJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToFunctionParameterJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToLambdaParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToMethodParameter" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreTransformationOfOriginalParameter" value="false" />
</inspection_tool>
<inspection_tool class="AssignmentToNull" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreAssignmentsToFields" value="true" />
</inspection_tool>
<inspection_tool class="AssignmentToStaticFieldFromInstanceMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToSuperclassField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,ch.bger.common.dbhelper.impl.AbstractDatabaseQuerier,connection|internalConnection" />
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,ch.bger.common.dbhelper.impl.AbstractDatabaseQuerier,connection|internalConnection,ch.bger.ajonline.client.AbstractClient,client" />
</inspection_tool>
<inspection_tool class="AutoTupling" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AwaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AwaitWithoutCorrespondingSignal" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BadExceptionCaught" enabled="true" level="WARNING" enabled_by_default="true">
<option name="exceptionsString" value="" />
@@ -59,12 +82,17 @@
<option name="questionString" value="add,are,can,check,contains,could,endsWith,equals,has,is,matches,must,put,remove,shall,should,startsWith,was,were,will,would" />
</inspection_tool>
<inspection_tool class="BooleanParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BoundedWildcard" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BreakStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BreakStatementJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BreakStatementWithLabel" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BreakStatementWithLabelJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BulkFileAttributesRead" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CallToNativeMethodWhileLocked" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CallToSimpleGetterInClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreGetterCallsOnOtherObjects" value="false" />
<option name="onlyReportPrivateGetter" value="false" />
</inspection_tool>
<inspection_tool class="CallToSimpleSetterInClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreSetterCallsOnOtherObjects" value="false" />
<option name="onlyReportPrivateSetter" value="false" />
@@ -98,7 +126,6 @@
<inspection_tool class="ComparableImplementedButEqualsNotOverridden" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ComparatorNotSerializable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CompareToUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ComparisonOfShortAndChar" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConditionSignal" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConditionalExpressionWithIdenticalBranchesJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConfusingFloatingPointLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
@@ -107,6 +134,7 @@
<inspection_tool class="ConnectionResource" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantAssertCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantDeclaredInInterface" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantJUnitAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantMathCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ContinueStatement" enabled="true" level="WARNING" enabled_by_default="true" />
@@ -115,12 +143,13 @@
<inspection_tool class="ContinueStatementWithLabelJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CovariantEquals" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CyclicClassDependency" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CyclicPackageDependency" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DateToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DeclareCollectionAsInterface" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreLocalVariables" value="false" />
<option name="ignorePrivateMethodsAndFields" value="false" />
</inspection_tool>
<inspection_tool class="DeconstructionCanBeUsed" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="DefaultNotLastCaseInSwitch" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DefaultNotLastCaseInSwitchJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DisjointPackage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DivideByZeroJS" enabled="true" level="WARNING" enabled_by_default="true" />
@@ -131,6 +160,7 @@
<inspection_tool class="DuplicateBooleanBranch" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DuplicateConditionJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DuplicatePropertyInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DynamicRegexReplaceableByCompiledPattern" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DynamicallyGeneratedCodeJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EmptyCatchBlockJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EmptyClass" enabled="true" level="WARNING" enabled_by_default="true">
@@ -141,6 +171,9 @@
<option name="ignoreThrowables" value="true" />
<option name="commentsAreContent" value="true" />
</inspection_tool>
<inspection_tool class="EmptyDirectory" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES">
<option name="onlyReportDirectoriesUnderSourceRoots" value="true" />
</inspection_tool>
<inspection_tool class="EmptyTryBlockJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EnhancedSwitchBackwardMigration" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="EnumerationCanBeIteration" enabled="true" level="WARNING" enabled_by_default="true" />
@@ -188,6 +221,7 @@
<inspection_tool class="HibernateResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="insideTryAllowed" value="false" />
</inspection_tool>
<inspection_tool class="HtmlTagCanBeJavadocTag" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IOResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredTypesString" value="java.io.ByteArrayOutputStream,java.io.ByteArrayInputStream,java.io.StringBufferInputStream,java.io.CharArrayWriter,java.io.CharArrayReader,java.io.StringWriter,java.io.StringReader" />
<option name="insideTryAllowed" value="false" />
@@ -195,6 +229,7 @@
<inspection_tool class="IfMayBeConditional" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IgnoredJUnitTest" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IllegalDependencyOnInternalPackage" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ImplicitDefaultCharsetUsage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InconsistentLanguageLevel" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IncorrectFormatting" enabled="false" level="WEAK WARNING" enabled_by_default="false">
@@ -203,6 +238,9 @@
</scope>
<option name="reportPerFile" value="true" />
</inspection_tool>
<inspection_tool class="InnerClassOnInterface" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInnerInterfaces" value="true" />
</inspection_tool>
<inspection_tool class="InnerClassReferencedViaSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InnerClassVariableHidesOuterClassVariable" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInvisibleFields" value="true" />
@@ -215,6 +253,7 @@
<inspection_tool class="InstanceofIncompatibleInterface" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InstanceofThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IntLiteralMayBeLongLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InterfaceMayBeAnnotatedFunctional" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InterfaceNeverImplemented" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreInterfacesThatOnlyDeclareConstants" value="false" />
</inspection_tool>
@@ -328,6 +367,10 @@
<option name="REPORT_VARIABLES" value="true" />
<option name="REPORT_PARAMETERS" value="true" />
</inspection_tool>
<inspection_tool class="LocalVariableHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInvisibleFields" value="true" />
<option name="m_ignoreStaticMethods" value="true" />
</inspection_tool>
<inspection_tool class="LoggerInitializedWithForeignClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
</inspection_tool>
@@ -349,9 +392,11 @@
<option name="ignoreStaticMethodsFromNonStaticInnerClass" value="false" />
<option name="onlyReportStaticMethods" value="false" />
</inspection_tool>
<inspection_tool class="MethodOverloadsParentMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodOverridesInaccessibleMethodOfSuper" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodOverridesStaticMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MigrateAssertToMatcherAssert" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MisorderedAssertEqualsArguments" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MisorderedAssertEqualsParameters" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MissingDeprecatedAnnotation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MissingJavadoc" enabled="true" level="WARNING" enabled_by_default="true">
@@ -408,6 +453,7 @@
<inspection_tool class="MisspelledEquals" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MisspelledMethodName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MultipleExceptionsDeclaredOnTestMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MultipleTopLevelClassesInFile" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NativeMethods" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NegatedConditional" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreNegatedNullComparison" value="false" />
@@ -415,6 +461,7 @@
</inspection_tool>
<inspection_tool class="NegatedConditionalExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NegatedConditionalExpressionJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NegatedEqualityExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NegatedIfElse" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreNegatedNullComparison" value="false" />
<option name="m_ignoreNegatedZeroComparison" value="false" />
@@ -441,6 +488,7 @@
</inspection_tool>
<inspection_tool class="NonSerializableObjectBoundToHttpSession" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonSerializableObjectPassedToObjectStream" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonShortCircuitBoolean" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonShortCircuitBooleanExpressionJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonStaticFinalLogger" enabled="true" level="WARNING" enabled_by_default="true">
<option name="loggerClassName" value="java.util.logging.Logger,org.slf4j.Logger,org.apache.commons.logging.Log,org.apache.log4j.Logger,org.apache.logging.log4j.Logger" />
@@ -448,6 +496,8 @@
<inspection_tool class="NonSynchronizedMethodOverridesSynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonThreadSafeLazyInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NullThrown" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NumericToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ObjectAllocationInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ObjectInstantiationInEqualsHashCode" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ObjectNotify" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ObsoleteCollection" enabled="true" level="WARNING" enabled_by_default="true">
@@ -460,8 +510,11 @@
<inspection_tool class="OverlyStrongTypeCast" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreInMatchingInstanceof" value="true" />
</inspection_tool>
<inspection_tool class="OverridableMethodCallDuringObjectConstruction" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OverriddenMethodCallDuringObjectConstruction" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PackageDotHtmlMayBePackageInfo" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PackageInMultipleModules" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PackageVisibleField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ParameterHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInvisibleFields" value="true" />
<option name="m_ignoreStaticMethodParametersHidingInstanceFields" value="true" />
@@ -476,10 +529,14 @@
<inspection_tool class="ParameterTypePreventsOverriding" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ParameterizedParametersStaticCollection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PlatformDetectionJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PointlessIndexOfComparison" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PostfixMethodCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProblematicVarargsMethodOverride" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="false">
<scope name="Project Files" level="WARNING" enabled="true" />
</inspection_tool>
<inspection_tool class="PropertyValueSetToItself" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProtectedField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PublicConstructorInNonPublicClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PublicFieldAccessedInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PublicMethodNotExposedInInterface" enabled="true" level="WARNING" enabled_by_default="true">
@@ -488,6 +545,20 @@
</option>
<option name="onlyWarnIfContainingClassImplementsAnInterface" value="false" />
</inspection_tool>
<inspection_tool class="PyAugmentAssignmentInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PyClassicStyleClassInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ourVersions">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="3.13" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="QodanaJavaSanity" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="QodanaKotlinSanity" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="QsUndeclaredPathMimeTypesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="RandomDoubleForRandomInteger" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ReadObjectAndWriteObjectPrivate" enabled="true" level="WARNING" enabled_by_default="true" />
@@ -507,6 +578,9 @@
<option name="ignoreObscureOperators" value="false" />
</inspection_tool>
<inspection_tool class="ReplaceAssignmentWithOperatorAssignmentJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ResultOfObjectAllocationIgnored" enabled="true" level="WARNING" enabled_by_default="true">
<scope name="Tests" level="WARNING" enabled="false" />
</inspection_tool>
<inspection_tool class="ResultSetIndexZero" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ReturnOfInnerClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ReuseOfLocalVariableJS" enabled="true" level="WARNING" enabled_by_default="true" />
@@ -524,15 +598,20 @@
<inspection_tool class="SideEffectsInMonadicTransformation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SimplifiableAnnotation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SimplifiableEqualsExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Singleton" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SleepWhileHoldingLock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SocketResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="insideTryAllowed" value="false" />
</inspection_tool>
<inspection_tool class="SqlGotoInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StandardJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="StaticCallOnSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticFieldReferenceOnSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticGuardedByInstance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticInheritance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticNonFinalField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticPseudoFunctionalStyleMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringBufferField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringConcatenationArgumentToLogCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringConcatenationInFormatCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringConcatenationInMessageFormatCall" enabled="true" level="WARNING" enabled_by_default="true" />
@@ -544,13 +623,20 @@
<inspection_tool class="SubtractionInCompareTo" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuperTearDownInFinally" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuppressionAnnotation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousArrayCast" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousGetterSetter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousLiteralUnderscore" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousLocalesLanguages" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SynchronizationOnStaticField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SynchronizeOnLock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SynchronizeOnThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_includeNativeMethods" value="true" />
<option name="ignoreSynchronizedSuperMethods" value="true" />
</inspection_tool>
<inspection_tool class="SynchronizedOnLiteralObject" enabled="true" level="WARNING" enabled_by_default="true">
<option name="warnOnAllPossiblyLiterals" value="true" />
</inspection_tool>
<inspection_tool class="SystemExit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreInMainMethod" value="true" />
</inspection_tool>
@@ -571,9 +657,12 @@
<inspection_tool class="ThreadLocalNotStaticFinal" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadPriority" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadStartInConstruction" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadStopSuspendResume" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadYield" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThrowCaughtLocally" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreRethrownExceptions" value="false" />
</inspection_tool>
<inspection_tool class="TimeToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TooBroadThrows" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TransientFieldNotInitialized" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
@@ -586,6 +675,12 @@
</inspection_tool>
<inspection_tool class="TypeParameterExtendsFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnconditionalWait" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessarilyQualifiedStaticUsage" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreStaticFieldAccesses" value="false" />
<option name="m_ignoreStaticMethodCalls" value="false" />
<option name="m_ignoreStaticAccessFromStaticContext" value="false" />
</inspection_tool>
<inspection_tool class="UnnecessarilyQualifiedStaticallyImportedElement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryConstantArrayCreationExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryExplicitNumericCast" enabled="true" level="WARNING" enabled_by_default="true" />
@@ -593,6 +688,9 @@
<inspection_tool class="UnnecessaryJavaDocLink" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreInlineLinkToSuper" value="false" />
</inspection_tool>
<inspection_tool class="UnnecessarySuperConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessarySuperQualifier" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnreachableCode" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnstableTypeUsedInSignature" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedCatchParameterJS" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreCatchBlocksWithComments" value="false" />
@@ -601,6 +699,10 @@
<inspection_tool class="UpperCaseFieldNameNotConstant" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseEllipsisInPropertyInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfAWTPeerClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfAnotherObjectsPrivateField" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreSameClass" value="false" />
<option name="ignoreEquals" value="false" />
</inspection_tool>
<inspection_tool class="UseOfClone" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfJDBCDriverClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfObsoleteAssert" enabled="true" level="WARNING" enabled_by_default="true" />
@@ -622,6 +724,7 @@
<inspection_tool class="WaitCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitNotifyNotInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitOrAwaitWithoutTimeout" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitWithoutCorrespondingNotify" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WeakerAccess" enabled="true" level="WARNING" enabled_by_default="true">
<option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="true" />

4
.idea/misc.xml generated
View File

@@ -11,7 +11,5 @@
<component name="NodePackageJsonFileManager">
<packageJsonPaths />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK" />
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ScalaCompilerConfiguration">
<option name="separateProdTestSources" value="false" />
</component>
</project>

11
.idea/sonarlint.xml generated
View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SonarLintProjectSettings">
<option name="moduleMapping">
<map>
<entry key="autosubtitle-gui-fx" value="autosubtitle-fx" />
<entry key="whisper-base" value="whisperbase" />
</map>
</option>
</component>
</project>

2
.idea/vcs.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="" vcs="Git" />
</component>
</project>

337
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,337 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b9d7e333-acad-414e-8be1-4685e71ee4fe" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core/src/main/java/com/github/gtache/autosubtitle/subtitle/converter/impl/ASSSubtitleConverter.java" beforeDir="false" afterPath="$PROJECT_DIR$/core/src/main/java/com/github/gtache/autosubtitle/subtitle/converter/impl/ASSSubtitleConverter.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/core/src/test/java/com/github/gtache/autosubtitle/subtitle/converter/impl/TestASSSubtitleConverter.java" beforeDir="false" afterPath="$PROJECT_DIR$/core/src/test/java/com/github/gtache/autosubtitle/subtitle/converter/impl/TestASSSubtitleConverter.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/gui/fx/src/main/java/com/github/gtache/autosubtitle/gui/parameters/fx/FXParametersModel.java" beforeDir="false" afterPath="$PROJECT_DIR$/gui/fx/src/main/java/com/github/gtache/autosubtitle/gui/parameters/fx/FXParametersModel.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/gui/fx/src/main/resources/com/github/gtache/autosubtitle/gui/setup/fx/setupView.fxml" beforeDir="false" afterPath="$PROJECT_DIR$/gui/fx/src/main/resources/com/github/gtache/autosubtitle/gui/setup/fx/setupView.fxml" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="CoverageOptionsProvider">
<option name="myAddOrReplace" value="0" />
</component>
<component name="FormatOnSaveOptions">
<option name="myRunOnSave" value="true" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GradleLocalSettings">
<option name="myGradleUserHome" value="D:/.gradle" />
<option name="projectSyncType">
<map>
<entry key="D:/Projects/Android/Rooster" value="PREVIEW" />
<entry key="D:/Projects/Scala/scalajs-gradle/scalajs-plugin" value="PREVIEW" />
</map>
</option>
</component>
<component name="MavenImportPreferences">
<option name="generalSettings">
<MavenGeneralSettings>
<option name="alwaysUpdateSnapshots" value="true" />
<option name="localRepository" value="D:\caches\.m2\" />
</MavenGeneralSettings>
</option>
<option name="importingSettings">
<MavenImportingSettings>
<option name="downloadAnnotationsAutomatically" value="true" />
<option name="downloadDocsAutomatically" value="true" />
<option name="downloadSourcesAutomatically" value="true" />
</MavenImportingSettings>
</option>
</component>
<component name="OptimizeOnSaveOptions">
<option name="myRunOnSave" value="true" />
</component>
<component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;&quot;,
&quot;associatedIndex&quot;: 4
}</component>
<component name="ProjectId" id="2mwO59xISTvRVmoGPPwWN1oEXCt" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Application.Main.executor": "Run",
"JUnit.All in autosubtitle-core.executor": "Run",
"JUnit.TestASSSubtitleConverter.executor": "Coverage",
"JUnit.TestASSSubtitleConverter.testFormatDefaultFont.executor": "Run",
"JUnit.TestASSSubtitleConverter.testParseFormat.executor": "Debug",
"Maven.autosubtitle [clean,package,-DskipTests].executor": "Run",
"Maven.autosubtitle [clean,package].executor": "Run",
"Maven.autosubtitle [clean].executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
"com.codeium.enabled": "true",
"git-widget-placeholder": "master",
"ignore.virus.scanning.warn.message": "true",
"kotlin-language-version-configured": "true",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"project.structure.last.edited": "Modules",
"project.structure.proportion": "0.0",
"project.structure.side.proportion": "0.0",
"settings.editor.selected.configurable": "reference.projectsettings.compiler.annotationProcessors",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="I:\Projects\Java\auto-subtitle\gui\fx\src\main\resources\com\github\gtache\autosubtitle\gui\work\fx" />
<recent name="I:\Projects\Java\auto-subtitle\gui\fx\src\main\resources\com\github\gtache\autosubtitle\gui\subtitles\fx" />
<recent name="I:\Projects\Java\auto-subtitle\gui\fx\src\main\resources\com\github\gtache\autosubtitle\gui\setup\fx" />
<recent name="I:\Projects\Java\auto-subtitle\gui\fx\src\main\resources\com\github\gtache\autosubtitle\gui\parameters\fx" />
<recent name="I:\Projects\Java\auto-subtitle\gui\core\src\main\resources\com\github\gtache\autosubtitle\gui\work\impl" />
</key>
<key name="MoveClassesOrPackagesDialog.RECENTS_KEY">
<recent name="com.github.gtache.autosubtitle.gui.work.impl.spi" />
<recent name="com.github.gtache.autosubtitle.gui.subtitles.impl.spi" />
<recent name="com.github.gtache.autosubtitle.gui.setup.impl.spi" />
<recent name="com.github.gtache.autosubtitle.gui.parameters.impl.spi" />
<recent name="com.github.gtache.autosubtitle.gui.fx" />
</key>
<key name="CopyClassDialog.RECENTS_KEY">
<recent name="com.github.gtache.autosubtitle.modules.setup.impl" />
<recent name="com.github.gtache.autosubtitle.modules.impl" />
</key>
</component>
<component name="RunAnythingCache">
<option name="myCommands">
<command value="mvn clean package" />
<command value="mvn clean" />
<command value="mvn clean package -DskipTests" />
</option>
</component>
<component name="RunManager" selected="JUnit.TestASSSubtitleConverter">
<configuration default="true" type="Applet">
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType">
<module name="" />
<option name="VM_PARAMETERS" value="-Xmx512m -Xms256m -XX:MaxPermSize=250m -ea" />
<option name="PROGRAM_PARAMETERS" />
<predefined_log_file enabled="true" id="idea.log" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="Main" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="com.github.gtache.autosubtitle.gui.run.Main" />
<module name="autosubtitle-gui-run" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="com.github.gtache.autosubtitle.gui.run.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="Application" factoryName="Application">
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="All in autosubtitle-core" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
<module name="autosubtitle-core" />
<option name="PACKAGE_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="WORKING_DIRECTORY" value="%MODULE_WORKING_DIR%" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="TestASSSubtitleConverter" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
<module name="autosubtitle-core" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="com.github.gtache.autosubtitle.subtitle.converter.impl.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="PACKAGE_NAME" value="com.github.gtache.autosubtitle.subtitle.converter.impl" />
<option name="MAIN_CLASS_NAME" value="com.github.gtache.autosubtitle.subtitle.converter.impl.TestASSSubtitleConverter" />
<option name="TEST_OBJECT" value="class" />
<option name="WORKING_DIRECTORY" value="%MODULE_WORKING_DIR%" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="TestASSSubtitleConverter.testFormatDefaultFont" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
<module name="autosubtitle-core" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="com.github.gtache.autosubtitle.subtitle.converter.impl.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="PACKAGE_NAME" value="com.github.gtache.autosubtitle.subtitle.converter.impl" />
<option name="MAIN_CLASS_NAME" value="com.github.gtache.autosubtitle.subtitle.converter.impl.TestASSSubtitleConverter" />
<option name="METHOD_NAME" value="testFormatDefaultFont" />
<option name="TEST_OBJECT" value="method" />
<option name="WORKING_DIRECTORY" value="%MODULE_WORKING_DIR%" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="TestASSSubtitleConverter.testParseFormat" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
<module name="autosubtitle-core" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="com.github.gtache.autosubtitle.subtitle.converter.impl.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="PACKAGE_NAME" value="com.github.gtache.autosubtitle.subtitle.converter.impl" />
<option name="MAIN_CLASS_NAME" value="com.github.gtache.autosubtitle.subtitle.converter.impl.TestASSSubtitleConverter" />
<option name="METHOD_NAME" value="testParseFormat" />
<option name="TEST_OBJECT" value="method" />
<option name="WORKING_DIRECTORY" value="%MODULE_WORKING_DIR%" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="JUnit" factoryName="JUnit">
<option name="TEST_OBJECT" value="class" />
<option name="WORKING_DIRECTORY" value="%MODULE_WORKING_DIR%" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="JetRunConfigurationType">
<module name="auto-subtitle" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="KotlinStandaloneScriptRunConfigurationType">
<module name="auto-subtitle" />
<option name="filePath" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="PythonConfigurationType" factoryName="Python">
<module name="auto-subtitle" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration default="true" type="Python.FlaskServer">
<module name="auto-subtitle" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="launchJavascriptDebuger" value="false" />
<method v="2" />
</configuration>
<configuration default="true" type="TestNG">
<option name="TEST_OBJECT" value="CLASS" />
<option name="WORKING_DIRECTORY" value="%MODULE_WORKING_DIR%" />
<properties />
<listeners />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="Tox" factoryName="Tox">
<module name="auto-subtitle" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="JUnit.TestASSSubtitleConverter" />
<item itemvalue="JUnit.TestASSSubtitleConverter.testFormatDefaultFont" />
<item itemvalue="JUnit.TestASSSubtitleConverter.testParseFormat" />
<item itemvalue="Application.Main" />
<item itemvalue="JUnit.All in autosubtitle-core" />
</list>
</recent_temporary>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-9823dce3aa75-28b599e66164-intellij.indexing.shared.core-IU-242.23339.11" />
<option value="bundled-js-predefined-d6986cc7102b-5c90d61e3bab-JavaScript-IU-242.23339.11" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="b9d7e333-acad-414e-8be1-4685e71ee4fe" name="Changes" comment="" />
<created>1727983986841</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1727983986841</updated>
<workItem from="1727983987865" duration="45000" />
<workItem from="1727984039387" duration="1480000" />
<workItem from="1728066754302" duration="4620000" />
<workItem from="1728153813144" duration="6710000" />
<workItem from="1728207088557" duration="675000" />
<workItem from="1728227281845" duration="2280000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/auto_subtitle$TestASSSubtitleConverter.ic" NAME="TestASSSubtitleConverter Coverage Results" MODIFIED="1728229568706" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="idea" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="true">
<FILTER>com.github.gtache.autosubtitle.subtitle.converter.impl.*</FILTER>
</SUITE>
<SUITE FILE_PATH="coverage/auto_subtitle$All_in_autosubtitle_core.ic" NAME="All in autosubtitle-core Coverage Results" MODIFIED="1728070687799" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="idea" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="true" />
</component>
</project>

View File

@@ -9,7 +9,7 @@ import java.io.InputStream;
public interface Audio {
/**
* @return The audio input stream
* @return The input stream
* @throws IOException If an I/O error occurs
*/
InputStream getInputStream() throws IOException;

View File

@@ -4,13 +4,14 @@ package com.github.gtache.autosubtitle;
* Represents info about an audio
*/
public interface AudioInfo {
/**
* @return The audio extension (mp3, etc.)
*/
String audioFormat();
/**
* @return The audio duration in milliseconds
* @return The extension (mp3, etc.)
*/
String format();
/**
* @return The duration in milliseconds
*/
long duration();
}

View File

@@ -8,6 +8,7 @@ import java.nio.file.Path;
/**
* Represents a file
*/
@FunctionalInterface
public interface File {
/**
* @return The file input stream

View File

@@ -54,7 +54,7 @@ public enum Language {
static {
final Map<String, Language> map = new java.util.HashMap<>();
for (final var language : Language.values()) {
for (final var language : values()) {
map.put(language.name().toLowerCase(), language);
map.put(language.englishName.toLowerCase(), language);
map.put(language.iso2, language);

View File

@@ -0,0 +1,23 @@
package com.github.gtache.autosubtitle;
/**
* Type of a tool
*/
public enum ToolType {
/**
* The subtitle extractor (used to extract subtitles from a video)
*/
SUBTITLE_EXTRACTOR,
/**
* The subtitle translator (used to translate subtitles from one language to another)
*/
TRANSLATOR,
/**
* The video converter (used to add subtitles)
*/
VIDEO_CONVERTER,
/**
* The video loader (used to load videos)w
*/
VIDEO_LOADER
}

View File

@@ -7,9 +7,8 @@ import java.io.InputStream;
* Represents a video
*/
public interface Video {
/**
* @return The video input stream
* @return The input stream
* @throws IOException If an I/O error occurs
*/
InputStream getInputStream() throws IOException;
@@ -18,5 +17,4 @@ public interface Video {
* @return The video info
*/
VideoInfo info();
}

View File

@@ -1,5 +1,6 @@
package com.github.gtache.autosubtitle;
import com.github.gtache.autosubtitle.subtitle.ExportOptions;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import java.io.IOException;
@@ -16,40 +17,44 @@ public interface VideoConverter {
*
* @param video The video
* @param subtitles The subtitles collections to add
* @param options The export options for the subtitles
* @return The modified video
* @throws IOException If an I/O error occurs
*/
Video addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles) throws IOException;
Video addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles, final ExportOptions options) throws IOException;
/**
* Adds soft subtitles to the given video
*
* @param video The video
* @param subtitles The subtitles collections to add
* @param options The export options for the subtitles
* @param path The output path
* @throws IOException If an I/O error occurs
*/
void addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles, final Path path) throws IOException;
void addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles, final ExportOptions options, final Path path) throws IOException;
/**
* Adds hard subtitles to the given video
*
* @param video The video
* @param subtitles The subtitle collection to add
* @param options The export options for the subtitles
* @return The modified video
* @throws IOException If an I/O error occurs
*/
Video addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles) throws IOException;
Video addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles, final ExportOptions options) throws IOException;
/**
* Adds hard subtitles to the given video
*
* @param video The video
* @param subtitles The subtitle collection to add
* @param options The export options for the subtitles
* @param path The output path
* @throws IOException If an I/O error occurs
*/
void addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles, final Path path) throws IOException;
void addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles, final ExportOptions options, final Path path) throws IOException;
/**
* Extracts the audio from the given video

View File

@@ -3,11 +3,7 @@ package com.github.gtache.autosubtitle;
/**
* Info about a video
*/
public interface VideoInfo {
/**
* @return The video extension (mp4, etc.)
*/
String videoFormat();
public interface VideoInfo extends AudioInfo {
/**
* @return The video width in pixels
@@ -19,11 +15,6 @@ public interface VideoInfo {
*/
int height();
/**
* @return The video duration in milliseconds
*/
long duration();
/**
* @return The aspect ratio of the video
*/

View File

@@ -14,9 +14,8 @@ public interface Archiver {
*
* @param files The files to zip
* @param destination The zipped file
* @throws IOException if an error occurs
*/
void compress(final List<Path> files, final Path destination) throws IOException;
void compress(final List<Path> files, final Path destination);
/**
* Unzips an archive to the given destination

View File

@@ -26,7 +26,6 @@ public interface ProcessListener {
*
* @param duration The maximum time to wait
* @return The process result
* @throws IOException if an error occurs
*/
ProcessResult join(final Duration duration) throws IOException;
ProcessResult join(final Duration duration);
}

View File

@@ -1,5 +1,7 @@
package com.github.gtache.autosubtitle.setup;
import com.github.gtache.autosubtitle.ToolType;
/**
* Manages the setup of a component
*/
@@ -10,6 +12,11 @@ public interface SetupManager {
*/
String name();
/**
* @return the type of the component
*/
ToolType type();
/**
* @return the status of the component setup
*/
@@ -17,9 +24,8 @@ public interface SetupManager {
/**
* @return whether the component is installed
* @throws SetupException if an error occurred
*/
boolean isInstalled() throws SetupException;
boolean isInstalled();
/**
* Installs the component
@@ -46,9 +52,8 @@ public interface SetupManager {
* Checks if an update is available for the component
*
* @return whether an update is available
* @throws SetupException if an error occurred during the check
*/
boolean isUpdateAvailable() throws SetupException;
boolean isUpdateAvailable();
/**
* Updates the component

View File

@@ -0,0 +1,19 @@
package com.github.gtache.autosubtitle.subtitle;
import com.github.gtache.autosubtitle.subtitle.converter.FormatOptions;
/**
* Represents the subtitles export options
*/
public interface ExportOptions {
/**
* @return The output format
*/
OutputFormat outputFormat();
/**
* @return The subtitles format options
*/
FormatOptions formatOptions();
}

View File

@@ -0,0 +1,14 @@
package com.github.gtache.autosubtitle.subtitle;
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
/**
* Represents the subtitles import options
*/
public interface ImportOptions {
/**
* @return The options for subtitle parsing
*/
ParseOptions parseOptions();
}

View File

@@ -1,7 +1,6 @@
package com.github.gtache.autosubtitle.subtitle;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.VideoInfo;
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
import java.io.IOException;
@@ -19,32 +18,33 @@ public interface SubtitleImporterExporter<T extends Subtitle> {
* Imports subtitles from a file
*
* @param file The path to the file
* @return A mapping of langauge to collection
* @param options The import options
* @return A mapping of language to collection
* @throws IOException If an error occurred
* @throws ParseException If an error occurred while parsing a subtitle
*/
Map<Language, SubtitleCollection<T>> importSubtitles(final Path file) throws IOException, ParseException;
Map<Language, SubtitleCollection<T>> importSubtitles(final Path file, final ImportOptions options) throws IOException, ParseException;
/**
* Exports multiple collections to a file
*
* @param collections The subtitle collections
* @param videoInfo The video info (e.g. ASS format uses it)
* @param options The export options
* @param file The path to the file
* @throws IOException If an error occurred
*/
void exportSubtitles(final Collection<? extends SubtitleCollection<?>> collections, final VideoInfo videoInfo, final Path file) throws IOException;
void exportSubtitles(final Collection<? extends SubtitleCollection<?>> collections, final ExportOptions options, final Path file) throws IOException;
/**
* Exports a single collection to a file
*
* @param collection The subtitle collection
* @param videoInfo The video info (e.g. ASS format uses it)
* @param options The export options
* @param file The path to the file
* @throws IOException If an error occurred
*/
default void exportSubtitles(final SubtitleCollection<?> collection, final VideoInfo videoInfo, final Path file) throws IOException {
exportSubtitles(List.of(collection), videoInfo, file);
default void exportSubtitles(final SubtitleCollection<?> collection, final ExportOptions options, final Path file) throws IOException {
exportSubtitles(List.of(collection), options, file);
}
/**

View File

@@ -0,0 +1,19 @@
package com.github.gtache.autosubtitle.subtitle.converter;
/**
* Exception thrown when an error occurs during subtitle formatting
*/
public class FormatException extends Exception {
public FormatException(final String message) {
super(message);
}
public FormatException(final String message, final Throwable cause) {
super(message, cause);
}
public FormatException(final Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,20 @@
package com.github.gtache.autosubtitle.subtitle.converter;
import com.github.gtache.autosubtitle.VideoInfo;
import com.github.gtache.autosubtitle.subtitle.Font;
/**
* Represents the subtitle format options
*/
public interface FormatOptions {
/**
* @return the current video info
*/
VideoInfo videoInfo();
/**
* @return The default font to use when not specified
*/
Font defaultFont();
}

View File

@@ -0,0 +1,24 @@
package com.github.gtache.autosubtitle.subtitle.converter;
import com.github.gtache.autosubtitle.subtitle.Font;
/**
* Represents the parse options
*/
public interface ParseOptions {
/**
* @return The max length in characters for a subtitle line
*/
int maxLineLength();
/**
* @return The max number of lines for a subtitle
*/
int maxLines();
/**
* @return The default font to use when not specified
*/
Font defaultFont();
}

View File

@@ -1,6 +1,5 @@
package com.github.gtache.autosubtitle.subtitle.converter;
import com.github.gtache.autosubtitle.VideoInfo;
import com.github.gtache.autosubtitle.subtitle.Subtitle;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
@@ -9,7 +8,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
/**
* Converts subtitles to a specific format (e.g. srt, ssa, ass, ...) and vice-versa
* Converts subtitles to a specific format (e.g. srt, ssa, ass, ...) and vice versa
*/
public interface SubtitleConverter<T extends Subtitle> {
@@ -17,22 +16,24 @@ public interface SubtitleConverter<T extends Subtitle> {
* Converts the subtitle collection
*
* @param collection The collection
* @param videoInfo The video info (e.g. ASS format uses it)
* @param options The format options
* @return The converted subtitles as the content of a file
* @throws FormatException If an error occurred
*/
String format(final SubtitleCollection<?> collection, final VideoInfo videoInfo);
String format(final SubtitleCollection<?> collection, final FormatOptions options) throws FormatException;
/**
* Parses a subtitle collection
*
* @param file The path to the file
* @param options The parse options
* @return The subtitle collection
* @throws ParseException If an error occurred
*/
default SubtitleCollection<T> parse(final Path file) throws ParseException {
default SubtitleCollection<T> parse(final Path file, final ParseOptions options) throws ParseException {
try {
final var content = Files.readString(file);
return parse(content);
return parse(content, options);
} catch (final IOException e) {
throw new ParseException(e);
}
@@ -42,10 +43,11 @@ public interface SubtitleConverter<T extends Subtitle> {
* Parses a subtitle collection
*
* @param content The content of the file
* @param options The conversion options
* @return The subtitle collection
* @throws ParseException If an error occurred
*/
SubtitleCollection<T> parse(String content) throws ParseException;
SubtitleCollection<T> parse(String content, ParseOptions options) throws ParseException;
/**
* Check if the parser can parse the given file

View File

@@ -1,5 +1,7 @@
package com.github.gtache.autosubtitle.subtitle.converter;
import com.github.gtache.autosubtitle.subtitle.OutputFormat;
import java.util.Collection;
/**
@@ -19,4 +21,14 @@ public interface SubtitleConverterProvider {
* @return The converter (or null if not found)
*/
SubtitleConverter<?> getConverter(final String format);
/**
* Returns the converter for the given format
*
* @param format The format
* @return The converter (or null if not found)
*/
default SubtitleConverter<?> getConverter(final OutputFormat format) {
return getConverter(format.name());
}
}

View File

@@ -0,0 +1,25 @@
package com.github.gtache.autosubtitle.subtitle.extractor;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
/**
* Options for subtitle extraction
*/
public interface ExtractOptions {
/**
* @return The language of the subtitles
*/
Language language();
/**
* @return The extraction model
*/
ExtractionModel model();
/**
* @return The subtitles parsing options
*/
ParseOptions parseOptions();
}

View File

@@ -3,6 +3,7 @@ package com.github.gtache.autosubtitle.subtitle.extractor;
/**
* An extraction model
*/
@FunctionalInterface
public interface ExtractionModel {
/**
* @return the name of the model

View File

@@ -1,7 +1,6 @@
package com.github.gtache.autosubtitle.subtitle.extractor;
import com.github.gtache.autosubtitle.Audio;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.Video;
import com.github.gtache.autosubtitle.subtitle.Subtitle;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
@@ -34,45 +33,19 @@ public interface SubtitleExtractor<T extends Subtitle> {
* Extracts the subtitles from a video
*
* @param video The video
* @param model The model to use
* @param options The language of the audio
* @return The extracted subtitle collection
* @throws ExtractException If an error occurs
*/
default SubtitleCollection<T> extract(final Video video, final ExtractionModel model) throws ExtractException {
return extract(video, Language.AUTO, model);
}
/**
* Extracts the subtitles from a video
*
* @param video The video
* @param language The language of the video
* @param model The model to use
* @return The extracted subtitle collection
* @throws ExtractException If an error occurs
*/
SubtitleCollection<T> extract(final Video video, final Language language, final ExtractionModel model) throws ExtractException;
SubtitleCollection<T> extract(final Video video, final ExtractOptions options) throws ExtractException;
/**
* Extracts the subtitles from an audio
*
* @param audio The audio
* @param model The model to use
* @param options The extraction options
* @return The extracted subtitle collection
* @throws ExtractException If an error occurs
*/
default SubtitleCollection<T> extract(final Audio audio, final ExtractionModel model) throws ExtractException {
return extract(audio, Language.AUTO, model);
}
/**
* Extracts the subtitles from an audio
*
* @param audio The audio
* @param language The language of the audio
* @param model The model to use
* @return The extracted subtitle collection
* @throws ExtractException If an error occurs
*/
SubtitleCollection<T> extract(final Audio audio, final Language language, final ExtractionModel model) throws ExtractException;
SubtitleCollection<T> extract(final Audio audio, final ExtractOptions options) throws ExtractException;
}

View File

@@ -1,22 +1,17 @@
package com.github.gtache.autosubtitle;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestVideoInfo {
private final VideoInfo videoInfo;
TestVideoInfo(@Mock final VideoInfo videoInfo) {
this.videoInfo = Objects.requireNonNull(videoInfo);
TestVideoInfo() {
this.videoInfo = spy(VideoInfo.class);
}
@Test

View File

@@ -11,9 +11,14 @@ import static org.mockito.Mockito.when;
class TestArchiver {
private final Archiver archiver;
TestArchiver() {
this.archiver = spy(Archiver.class);
}
@Test
void testIsSupported() {
final var archiver = spy(Archiver.class);
when(archiver.archiveExtension()).thenReturn("test");
assertTrue(archiver.isPathSupported(Paths.get("x.test")));
assertFalse(archiver.isPathSupported(Paths.get("tes")));

View File

@@ -1,26 +1,36 @@
package com.github.gtache.autosubtitle.subtitle;
import com.github.gtache.autosubtitle.VideoInfo;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import static org.mockito.Mockito.*;
import static java.util.Objects.requireNonNull;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class TestSubtitleImporterExporter {
private final SubtitleImporterExporter<Subtitle> importerExporter;
private final SubtitleCollection<Subtitle> subtitleCollection;
private final ExportOptions options;
TestSubtitleImporterExporter(@Mock final SubtitleCollection<Subtitle> subtitleCollection, @Mock final ExportOptions options) {
this.subtitleCollection = requireNonNull(subtitleCollection);
this.options = requireNonNull(options);
this.importerExporter = spy(SubtitleImporterExporter.class);
}
@Test
void testExportSubtitleCollection() throws IOException {
final var importExporter = spy(SubtitleImporterExporter.class);
final var collection = mock(SubtitleCollection.class);
final var videoInfo = Mockito.mock(VideoInfo.class);
final var file = mock(Path.class);
final var file = Paths.get("path");
importExporter.exportSubtitles(collection, videoInfo, file);
verify(importExporter).exportSubtitles(List.of(collection), videoInfo, file);
importerExporter.exportSubtitles(subtitleCollection, options, file);
verify(importerExporter).exportSubtitles(List.of(subtitleCollection), options, file);
}
}

View File

@@ -11,23 +11,25 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestSubtitleConverter {
private final SubtitleConverter subtitleConverter;
private final SubtitleConverter<Subtitle> subtitleConverter;
private final SubtitleCollection<Subtitle> subtitleCollection;
private final ParseOptions options;
TestSubtitleConverter(@Mock final SubtitleConverter subtitleConverter,
@Mock final SubtitleCollection<Subtitle> subtitleCollection) {
this.subtitleConverter = Objects.requireNonNull(subtitleConverter);
this.subtitleCollection = Objects.requireNonNull(subtitleCollection);
TestSubtitleConverter(@Mock final SubtitleConverter<Subtitle> subtitleConverter,
@Mock final SubtitleCollection<Subtitle> subtitleCollection,
@Mock final ParseOptions options) {
this.subtitleConverter = requireNonNull(subtitleConverter);
this.subtitleCollection = requireNonNull(subtitleCollection);
this.options = requireNonNull(options);
}
@Test
@@ -35,19 +37,19 @@ class TestSubtitleConverter {
final var file = tempDir.resolve("test.srt");
final var string = "test";
Files.writeString(file, string);
when(subtitleConverter.parse(file)).thenCallRealMethod();
when(subtitleConverter.parse(string)).thenReturn(subtitleCollection);
when(subtitleConverter.parse(file, options)).thenCallRealMethod();
when(subtitleConverter.parse(string, options)).thenReturn(subtitleCollection);
assertEquals(subtitleCollection, subtitleConverter.parse(file));
assertEquals(subtitleCollection, subtitleConverter.parse(file, options));
}
@Test
void testParseException(@TempDir final Path tempDir) throws ParseException {
final var file = tempDir.resolve("test.srt");
when(subtitleConverter.parse(file)).thenCallRealMethod();
when(subtitleConverter.parse(anyString())).thenReturn(subtitleCollection);
when(subtitleConverter.parse(file, options)).thenCallRealMethod();
when(subtitleConverter.parse(anyString(), eq(options))).thenReturn(subtitleCollection);
assertThrows(ParseException.class, () -> subtitleConverter.parse(file));
assertThrows(ParseException.class, () -> subtitleConverter.parse(file, options));
}
@Test

View File

@@ -0,0 +1,32 @@
package com.github.gtache.autosubtitle.subtitle.converter;
import com.github.gtache.autosubtitle.subtitle.OutputFormat;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class TestSubtitleConverterProvider {
private final SubtitleConverter<?> converter;
private final SubtitleConverterProvider provider;
TestSubtitleConverterProvider(@Mock final SubtitleConverter<?> converter) {
this.converter = Objects.requireNonNull(converter);
this.provider = spy(SubtitleConverterProvider.class);
}
@Test
void testGetConverter() {
final var format = OutputFormat.SRT;
when(provider.getConverter("SRT")).thenReturn((SubtitleConverter) converter);
assertEquals(converter, provider.getConverter(format));
verify(provider).getConverter("SRT");
}
}

View File

@@ -1,54 +0,0 @@
package com.github.gtache.autosubtitle.subtitle.extractor;
import com.github.gtache.autosubtitle.Audio;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.Video;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class TestSubtitleExtractor {
private final SubtitleExtractor subtitleExtractor;
private final SubtitleCollection subtitleCollection;
private final Audio audio;
private final Video video;
private final ExtractionModel extractionModel;
TestSubtitleExtractor(@Mock final SubtitleExtractor subtitleExtractor,
@Mock final SubtitleCollection subtitleCollection,
@Mock final Audio audio,
@Mock final Video video,
@Mock final ExtractionModel extractionModel) {
this.subtitleExtractor = Objects.requireNonNull(subtitleExtractor);
this.subtitleCollection = Objects.requireNonNull(subtitleCollection);
this.audio = Objects.requireNonNull(audio);
this.video = Objects.requireNonNull(video);
this.extractionModel = Objects.requireNonNull(extractionModel);
}
@Test
void testExtractVideo() throws ExtractException {
when(subtitleExtractor.extract(any(Video.class), any())).thenCallRealMethod();
when(subtitleExtractor.extract(video, Language.AUTO, extractionModel)).thenReturn(subtitleCollection);
assertEquals(subtitleCollection, subtitleExtractor.extract(video, extractionModel));
}
@Test
void testExtractAudio() throws ExtractException {
when(subtitleExtractor.extract(any(Audio.class), any())).thenCallRealMethod();
when(subtitleExtractor.extract(audio, Language.AUTO, extractionModel)).thenReturn(subtitleCollection);
assertEquals(subtitleCollection, subtitleExtractor.extract(audio, extractionModel));
}
}

View File

@@ -6,12 +6,15 @@ import picocli.CommandLine;
import java.util.Set;
/**
* Main class for CLI
*/
@CommandLine.Command(name = "autosubtitle", mixinStandardHelpOptions = true, version = "autosubtitle 1.0-SNAPSHOT", description = "CLI for auto-subtitle")
public final class Cli implements Runnable {
@CommandLine.Option(names = {"-b", "--burn"}, description = "Burn the subtitles. Otherwise, adds them to the video", defaultValue = "false")
@CommandLine.Option(names = {"-b", "--burn"}, description = "Burns the subtitles. Otherwise, adds them to the video", defaultValue = "false")
private boolean burn;
@CommandLine.Option(names = {"-e", "--extractor"}, description = "The subtitle extractor to use [whisper]", defaultValue = "whisper")
@CommandLine.Option(names = {"-e", "--extractor"}, description = "The subtitle extractor to use [whisperx]", defaultValue = "whisperx")
private String extractor;
@CommandLine.Option(names = {"-i", "--input"}, description = "The input file", required = true)
private String input;
@@ -23,7 +26,7 @@ public final class Cli implements Runnable {
private String subtitleConverter;
@CommandLine.Option(names = {"-c", "--video-converter"}, description = "The video converter to use [ffmpeg]", defaultValue = "ffmpeg")
private String videoConverter;
@CommandLine.Option(names = {"--translations"}, description = "The list of translations to create. Ignored if burn is specified", split = ",", arity = "0..*")
@CommandLine.Option(names = "--translations", description = "The list of translations to create. Ignored if burn is specified", split = ",", arity = "0..*")
private Set<String> translations;
@CommandLine.Option(names = {"-t", "--translator"}, description = "The translator to use [deepl]. Ignored if burn is specified", defaultValue = "deepl")
private String translator;

View File

@@ -6,4 +6,5 @@ module com.github.gtache.autosubtitle.cli {
requires com.github.gtache.autosubtitle.ffmpeg;
requires com.github.gtache.autosubtitle.whisperx;
requires info.picocli;
requires guava;
}

View File

@@ -1,27 +0,0 @@
package com.github.gtache.autosubtitle.archive.client;
import com.github.gtache.autosubtitle.archive.Archiver;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
/**
* {@link Archiver} using a remote API
*/
public class RemoteArchiver implements Archiver {
@Override
public void compress(final List<Path> files, final Path destination) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void decompress(final Path archive, final Path destination) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public String archiveExtension() {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,4 @@
package com.github.gtache.autosubtitle.client;
public abstract class AbstractClient {
}

View File

@@ -3,9 +3,9 @@ package com.github.gtache.autosubtitle.client;
import com.github.gtache.autosubtitle.Audio;
import com.github.gtache.autosubtitle.Video;
import com.github.gtache.autosubtitle.VideoConverter;
import com.github.gtache.autosubtitle.subtitle.ExportOptions;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
@@ -14,27 +14,28 @@ import java.util.Collection;
*/
public class RemoteVideoConverter implements VideoConverter {
@Override
public Video addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles) throws IOException {
public Video addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles, final ExportOptions options) {
throw new UnsupportedOperationException();
}
@Override
public void addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles, final Path path) throws IOException {
public void addSoftSubtitles(final Video video, final Collection<? extends SubtitleCollection<?>> subtitles, final ExportOptions options, final Path path) {
throw new UnsupportedOperationException();
}
@Override
public Video addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles) throws IOException {
public Video addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles, final ExportOptions options) {
throw new UnsupportedOperationException();
}
@Override
public void addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles, final ExportOptions options, final Path path) {
throw new UnsupportedOperationException();
}
@Override
public void addHardSubtitles(final Video video, final SubtitleCollection<?> subtitles, final Path path) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public Audio getAudio(final Video video) throws IOException {
public Audio getAudio(final Video video) {
throw new UnsupportedOperationException();
}
}

View File

@@ -3,7 +3,6 @@ package com.github.gtache.autosubtitle.client;
import com.github.gtache.autosubtitle.Video;
import com.github.gtache.autosubtitle.VideoLoader;
import java.io.IOException;
import java.nio.file.Path;
/**
@@ -11,7 +10,7 @@ import java.nio.file.Path;
*/
public class RemoteVideoLoader implements VideoLoader {
@Override
public Video loadVideo(final Path path) throws IOException {
public Video loadVideo(final Path path) {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,9 +1,9 @@
package com.github.gtache.autosubtitle.subtitle.converter.client;
import com.github.gtache.autosubtitle.VideoInfo;
import com.github.gtache.autosubtitle.subtitle.Subtitle;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
import com.github.gtache.autosubtitle.subtitle.converter.FormatOptions;
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
/**
@@ -13,12 +13,12 @@ import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
*/
public class RemoteSubtitleConverter<T extends Subtitle> implements SubtitleConverter<T> {
@Override
public String format(final SubtitleCollection<?> collection, final VideoInfo videoInfo) {
public String format(final SubtitleCollection<?> collection, final FormatOptions options) {
throw new UnsupportedOperationException();
}
@Override
public SubtitleCollection<T> parse(final String content) throws ParseException {
public SubtitleCollection<T> parse(final String content, final ParseOptions options) {
throw new UnsupportedOperationException();
}

View File

@@ -1,26 +1,25 @@
package com.github.gtache.autosubtitle.subtitle.extractor.client;
import com.github.gtache.autosubtitle.Audio;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.Video;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractException;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractOptions;
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
import com.github.gtache.autosubtitle.subtitle.extractor.impl.AbstractSubtitleExtractor;
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImpl;
/**
* {@link SubtitleExtractor} using a remote API
*/
public class RemoteSubtitleExtractor extends AbstractSubtitleExtractor {
public class RemoteSubtitleExtractor extends AbstractSubtitleExtractor<SubtitleImpl> {
@Override
public SubtitleCollection extract(final Video video, final Language language, final ExtractionModel model) throws ExtractException {
public SubtitleCollection<SubtitleImpl> extract(final Video video, final ExtractOptions options) {
throw new UnsupportedOperationException();
}
@Override
public SubtitleCollection extract(final Audio audio, final Language language, final ExtractionModel model) throws ExtractException {
public SubtitleCollection<SubtitleImpl> extract(final Audio audio, final ExtractOptions options) {
throw new UnsupportedOperationException();
}
}

View File

@@ -3,7 +3,6 @@ package com.github.gtache.autosubtitle.translation.client;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.subtitle.Subtitle;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import com.github.gtache.autosubtitle.translation.TranslationException;
import com.github.gtache.autosubtitle.translation.Translator;
/**
@@ -18,17 +17,17 @@ public class RemoteTranslator<T extends Subtitle> implements Translator<T> {
}
@Override
public String translate(final String text, final Language from, final Language to) throws TranslationException {
public String translate(final String text, final Language from, final Language to) {
throw new UnsupportedOperationException();
}
@Override
public T translate(final Subtitle subtitle, final Language from, final Language to) throws TranslationException {
public T translate(final Subtitle subtitle, final Language from, final Language to) {
throw new UnsupportedOperationException();
}
@Override
public SubtitleCollection<T> translate(final SubtitleCollection<?> collection, final Language from, final Language to) throws TranslationException {
public SubtitleCollection<T> translate(final SubtitleCollection<?> collection, final Language from, final Language to) {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.setup.conda;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.setup.conda;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.setup.conda;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.setup.conda;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.setup.conda;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.setup.conda;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,15 +1,18 @@
package com.github.gtache.autosubtitle.setup.conda;
import com.github.gtache.autosubtitle.ToolType;
import com.github.gtache.autosubtitle.impl.OS;
import com.github.gtache.autosubtitle.process.ProcessRunner;
import com.github.gtache.autosubtitle.setup.SetupException;
import com.github.gtache.autosubtitle.setup.SetupStatus;
import com.github.gtache.autosubtitle.setup.impl.AbstractSetupManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.net.http.HttpClient;
import java.nio.file.Files;
@@ -42,6 +45,11 @@ public class CondaSetupManager extends AbstractSetupManager {
return "conda";
}
@Override
public ToolType type() {
return null; //Just an intermediary tool
}
@Override
protected SetupStatus getStatus() throws SetupException {
if (isSystemCondaInstalled()) {
@@ -70,8 +78,8 @@ public class CondaSetupManager extends AbstractSetupManager {
logger.info("Conda downloaded");
}
switch (configuration.os()) {
case OS.WINDOWS -> installWindows();
case OS.MAC, OS.LINUX -> installLinux();
case WINDOWS -> installWindows();
case MAC, LINUX -> installLinux();
default -> throw new SetupException("Unsupported OS: " + configuration.os());
}
}
@@ -106,9 +114,9 @@ public class CondaSetupManager extends AbstractSetupManager {
private void downloadConda() throws SetupException {
switch (configuration.os()) {
case OS.WINDOWS -> downloadCondaWindows();
case OS.MAC -> downloadCondaMac();
case OS.LINUX -> downloadCondaLinux();
case WINDOWS -> downloadCondaWindows();
case MAC -> downloadCondaMac();
case LINUX -> downloadCondaLinux();
default -> throw new SetupException("Unsupported OS: " + configuration.os());
}
logger.info("Downloaded conda to {}", configuration.condaInstallerPath());

View File

@@ -6,6 +6,7 @@ import com.github.gtache.autosubtitle.process.ProcessResult;
import com.github.gtache.autosubtitle.process.ProcessRunner;
import com.github.gtache.autosubtitle.setup.SetupException;
import com.github.gtache.autosubtitle.setup.SetupStatus;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
@@ -36,26 +37,30 @@ class TestCondaSetupManager {
private final CondaSetupConfiguration configuration;
private final ProcessRunner processRunner;
private final HttpClient httpClient;
private final HttpResponse<Path> response;
private final CondaSetupManager condaSetupManager;
private final ProcessResult systemProcessResult;
private final Path systemPath;
private final HttpResponse<Path> response;
TestCondaSetupManager(@Mock final CondaSetupConfiguration configuration, @Mock final ProcessRunner processRunner,
@Mock final HttpClient httpClient, @Mock final ProcessResult systemProcessResult, @Mock final HttpResponse<Path> response) throws IOException, InterruptedException {
@Mock final HttpClient httpClient, @Mock final ProcessResult systemProcessResult, @Mock final HttpResponse<Path> response) {
this.configuration = requireNonNull(configuration);
this.processRunner = requireNonNull(processRunner);
this.httpClient = requireNonNull(httpClient);
this.condaSetupManager = new CondaSetupManager(configuration, processRunner, httpClient);
this.systemProcessResult = requireNonNull(systemProcessResult);
this.systemPath = Paths.get("system");
this.response = requireNonNull(response);
when(response.statusCode()).thenReturn(200);
this.condaSetupManager = new CondaSetupManager(configuration, processRunner, httpClient);
this.systemPath = Paths.get("system");
}
@BeforeEach
void beforeEach() throws IOException, InterruptedException {
when(configuration.condaSystemPath()).thenReturn(systemPath);
when(systemProcessResult.output()).thenReturn(List.of("conda 99.99.99"));
when(systemProcessResult.exitCode()).thenReturn(0);
when(processRunner.run(List.of(systemPath.toString(), "--version"), Duration.ofSeconds(5))).thenReturn(systemProcessResult);
when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandler.class))).thenReturn(response);
when(response.statusCode()).thenReturn(200);
}
@Test
@@ -63,6 +68,11 @@ class TestCondaSetupManager {
assertEquals("conda", condaSetupManager.name());
}
@Test
void testType() {
assertNull(condaSetupManager.type());
}
@Test
void testGetStatusIsSystemInstalled() throws SetupException {
assertEquals(SetupStatus.SYSTEM_INSTALLED, condaSetupManager.getStatus());
@@ -117,10 +127,7 @@ class TestCondaSetupManager {
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
assertDoesNotThrow(condaSetupManager::install);
final var requestCapture = ArgumentCaptor.forClass(HttpRequest.class);
verify(httpClient).send(requestCapture.capture(), any(HttpResponse.BodyHandler.class));
final var request = requestCapture.getValue();
assertEquals("https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe", request.uri().toString());
checkURLRequested("https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe");
verify(processRunner).run(args, Duration.ofMinutes(15));
}
@@ -140,10 +147,7 @@ class TestCondaSetupManager {
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
assertDoesNotThrow(condaSetupManager::install);
final var requestCapture = ArgumentCaptor.forClass(HttpRequest.class);
verify(httpClient).send(requestCapture.capture(), any(HttpResponse.BodyHandler.class));
final var request = requestCapture.getValue();
assertEquals("https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh", request.uri().toString());
checkURLRequested("https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh");
verify(processRunner).run(args, Duration.ofMinutes(15));
}
@@ -163,15 +167,12 @@ class TestCondaSetupManager {
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
assertDoesNotThrow(condaSetupManager::install);
final var requestCapture = ArgumentCaptor.forClass(HttpRequest.class);
verify(httpClient).send(requestCapture.capture(), any(HttpResponse.BodyHandler.class));
final var request = requestCapture.getValue();
assertEquals("https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh", request.uri().toString());
checkURLRequested("https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh");
verify(processRunner).run(args, Duration.ofMinutes(15));
}
@Test
void testInstallDownloadLinuxUnsupported(@TempDir final Path tempDir) throws IOException {
void testInstallDownloadLinuxUnsupported(@TempDir final Path tempDir) {
final var installerPath = tempDir.resolve("conda");
when(configuration.condaInstallerPath()).thenReturn(installerPath);
when(configuration.condaBundledPath()).thenReturn(tempDir);
@@ -200,10 +201,7 @@ class TestCondaSetupManager {
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
assertDoesNotThrow(condaSetupManager::install);
final var requestCapture = ArgumentCaptor.forClass(HttpRequest.class);
verify(httpClient).send(requestCapture.capture(), any(HttpResponse.BodyHandler.class));
final var request = requestCapture.getValue();
assertEquals("https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh", request.uri().toString());
checkURLRequested("https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh");
verify(processRunner).run(args, Duration.ofMinutes(15));
}
@@ -223,15 +221,12 @@ class TestCondaSetupManager {
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
assertDoesNotThrow(condaSetupManager::install);
final var requestCapture = ArgumentCaptor.forClass(HttpRequest.class);
verify(httpClient).send(requestCapture.capture(), any(HttpResponse.BodyHandler.class));
final var request = requestCapture.getValue();
assertEquals("https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh", request.uri().toString());
checkURLRequested("https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh");
verify(processRunner).run(args, Duration.ofMinutes(15));
}
@Test
void testInstallDownloadMacUnsupported(@TempDir final Path tempDir) throws IOException {
void testInstallDownloadMacUnsupported(@TempDir final Path tempDir) {
final var installerPath = tempDir.resolve("conda");
when(configuration.condaInstallerPath()).thenReturn(installerPath);
when(configuration.condaBundledPath()).thenReturn(tempDir);
@@ -245,7 +240,7 @@ class TestCondaSetupManager {
}
@Test
void testInstallDownloadUnsupported(@TempDir final Path tempDir) throws IOException {
void testInstallDownloadUnsupported(@TempDir final Path tempDir) {
final var installerPath = tempDir.resolve("conda");
when(configuration.condaInstallerPath()).thenReturn(installerPath);
when(configuration.condaBundledPath()).thenReturn(tempDir);
@@ -268,7 +263,7 @@ class TestCondaSetupManager {
when(systemProcessResult.exitCode()).thenReturn(1);
when(configuration.condaRootPath()).thenReturn(tempDir);
final var args = List.of(installerPath.toString(), "/InstallationType=JustMe", "/RegisterPython=0", "/S", "/D=" + tempDir.toString());
final var args = List.of(installerPath.toString(), "/InstallationType=JustMe", "/RegisterPython=0", "/S", "/D=" + tempDir);
final var result = mock(ProcessResult.class);
when(result.exitCode()).thenReturn(0);
when(processRunner.run(args, Duration.ofMinutes(15))).thenReturn(result);
@@ -510,4 +505,11 @@ class TestCondaSetupManager {
assertThrows(NullPointerException.class, () -> new CondaSetupManager(configuration, processRunner, null));
}
private void checkURLRequested(final String url) throws IOException, InterruptedException {
final var requestCapture = ArgumentCaptor.forClass(HttpRequest.class);
verify(httpClient).send(requestCapture.capture(), any(HttpResponse.BodyHandler.class));
final var request = requestCapture.getValue();
assertEquals(url, request.uri().toString());
}
}

View File

@@ -24,6 +24,10 @@
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>

View File

@@ -5,6 +5,7 @@ import com.github.gtache.autosubtitle.archive.Archiver;
import com.github.gtache.autosubtitle.archive.ArchiverProvider;
import javax.inject.Inject;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;

View File

@@ -3,6 +3,7 @@ package com.github.gtache.autosubtitle.archive.impl;
import com.github.gtache.autosubtitle.archive.Archiver;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -14,15 +15,15 @@ import java.util.zip.ZipInputStream;
/**
* Zip implementation of {@link Archiver}
*/
public class ZipDecompresser implements Archiver {
public class ZipArchiver implements Archiver {
@Inject
ZipDecompresser() {
ZipArchiver() {
}
@Override
public void compress(final List<Path> files, final Path destination) throws IOException {
public void compress(final List<Path> files, final Path destination) {
throw new UnsupportedOperationException("Not supported");
}

View File

@@ -7,10 +7,10 @@ import java.util.Objects;
/**
* Implementation of {@link AudioInfo}
*/
public record AudioInfoImpl(String audioFormat, long duration) implements AudioInfo {
public record AudioInfoImpl(String format, long duration) implements AudioInfo {
public AudioInfoImpl {
Objects.requireNonNull(audioFormat);
Objects.requireNonNull(format);
if (duration < 0) {
throw new IllegalArgumentException("Duration must be positive");
}

View File

@@ -7,10 +7,10 @@ import java.util.Objects;
/**
* Implementation of {@link VideoInfo}
*/
public record VideoInfoImpl(String videoFormat, int width, int height, long duration) implements VideoInfo {
public record VideoInfoImpl(String format, int width, int height, long duration) implements VideoInfo {
public VideoInfoImpl {
Objects.requireNonNull(videoFormat);
Objects.requireNonNull(format);
if (width <= 0) {
throw new IllegalArgumentException("Width must be greater than 0 : " + width);
}

View File

@@ -3,7 +3,7 @@ package com.github.gtache.autosubtitle.modules.archive.impl;
import com.github.gtache.autosubtitle.archive.Archiver;
import com.github.gtache.autosubtitle.archive.ArchiverProvider;
import com.github.gtache.autosubtitle.archive.impl.ArchiverProviderImpl;
import com.github.gtache.autosubtitle.archive.impl.ZipDecompresser;
import com.github.gtache.autosubtitle.archive.impl.ZipArchiver;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoMap;
@@ -25,5 +25,5 @@ public abstract class ArchiveModule {
@Binds
@StringKey("zip")
@IntoMap
abstract Archiver bindsZipDecompresser(final ZipDecompresser decompresser);
abstract Archiver bindsZipDecompresser(final ZipArchiver decompresser);
}

View File

@@ -13,6 +13,7 @@ import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
import java.util.prefs.Preferences;
/**

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -0,0 +1,16 @@
package com.github.gtache.autosubtitle.modules.impl;
import com.github.gtache.autosubtitle.ToolType;
import dagger.MapKey;
/**
* Used for injection
*/
@MapKey
public @interface ToolTypeKey {
/**
* @return the tool type
*/
ToolType value();
}

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.setup.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,11 +1,14 @@
package com.github.gtache.autosubtitle.modules.setup.impl;
import com.github.gtache.autosubtitle.ToolType;
import com.github.gtache.autosubtitle.setup.SetupManager;
import dagger.Module;
import dagger.Provides;
import java.net.http.HttpClient;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
/**
* Dagger core module for setup
@@ -32,4 +35,14 @@ public final class SetupModule {
static HttpClient providesHttpClient() {
return HttpClient.newHttpClient();
}
@Provides
static Map<ToolType, Map<String, SetupManager>> providesSetupManagers(@VideoConverterSetup final Map<String, SetupManager> convertersManagers,
@SubtitleExtractorSetup final Map<String, SetupManager> extractorsManagers,
@TranslatorSetup final Map<String, SetupManager> translatorsManagers) {
return Map.of(ToolType.VIDEO_CONVERTER, convertersManagers,
ToolType.SUBTITLE_EXTRACTOR, extractorsManagers,
ToolType.TRANSLATOR, translatorsManagers);
}
}

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.setup.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.setup.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.setup.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,6 +1,7 @@
package com.github.gtache.autosubtitle.modules.setup.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,4 +1,4 @@
package com.github.gtache.autosubtitle.modules.setup.whisper;
package com.github.gtache.autosubtitle.modules.setup.impl;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
@@ -12,5 +12,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Retention(RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
public @interface PythonVersion {
public @interface VideoLoaderSetup {
}

View File

@@ -1,5 +1,6 @@
package com.github.gtache.autosubtitle.modules.subtitle.converter.impl;
import com.github.gtache.autosubtitle.modules.subtitle.converter.json.impl.JSONConverterModule;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
import com.github.gtache.autosubtitle.subtitle.converter.impl.ASSSubtitleConverter;
@@ -13,7 +14,7 @@ import dagger.multibindings.StringKey;
/**
* Dagger module for the subtitle converters
*/
@Module
@Module(includes = JSONConverterModule.class)
public abstract class SubtitleConverterModule {
private SubtitleConverterModule() {

View File

@@ -0,0 +1,33 @@
package com.github.gtache.autosubtitle.modules.subtitle.converter.json.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
import com.github.gtache.autosubtitle.subtitle.converter.json.impl.JSONSubtitleConverter;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;
import javax.inject.Singleton;
/**
* Dagger module for the json subtitle converter
*/
@Module
public abstract class JSONConverterModule {
private JSONConverterModule() {
}
@Binds
@IntoMap
@StringKey("json")
abstract SubtitleConverter bindsJsonSubtitleConverter(final JSONSubtitleConverter converter);
@Provides
@Singleton
static ObjectMapper providesObjectMapper() {
return new ObjectMapper();
}
}

View File

@@ -9,7 +9,7 @@ import dagger.Module;
/**
* Dagger module for subtitles
*/
@Module(includes = {SubtitleConverterModule.class})
@Module(includes = SubtitleConverterModule.class)
public abstract class SubtitleModule {
private SubtitleModule() {

View File

@@ -3,10 +3,12 @@ package com.github.gtache.autosubtitle.process.impl;
import com.github.gtache.autosubtitle.process.ProcessListener;
import com.github.gtache.autosubtitle.process.ProcessResult;
import com.github.gtache.autosubtitle.process.ProcessRunner;
import javax.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.inject.Inject;
import java.io.IOException;
import java.time.Duration;
import java.util.List;

View File

@@ -1,30 +1,32 @@
package com.github.gtache.autosubtitle.subtitle.converter.impl;
import com.github.gtache.autosubtitle.VideoInfo;
import com.github.gtache.autosubtitle.modules.impl.FontName;
import com.github.gtache.autosubtitle.modules.impl.FontSize;
import com.github.gtache.autosubtitle.subtitle.Bounds;
import com.github.gtache.autosubtitle.subtitle.Font;
import com.github.gtache.autosubtitle.subtitle.Subtitle;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import com.github.gtache.autosubtitle.subtitle.converter.FormatOptions;
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
import com.github.gtache.autosubtitle.subtitle.impl.BoundsImpl;
import com.github.gtache.autosubtitle.subtitle.impl.FontImpl;
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleCollectionImpl;
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImpl;
import com.github.gtache.autosubtitle.translation.Translator;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.prefs.Preferences;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static java.util.Objects.requireNonNull;
/**
* Converts subtitles to SRT format
*/
@@ -35,47 +37,71 @@ public class ASSSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
private static final String DIALOGUE = "Dialogue:";
private static final String EVENTS_SECTION = "[Events]";
private static final String STYLES_SECTION = "[V4+ Styles]";
private final Translator<?> translator;
private final Preferences preferences;
private final String defaultFontName;
private final int defaultFontSize;
private static final String START_FIELD = "Start";
private static final String END_FIELD = "End";
private static final String STYLE_FIELD = "Style";
private static final String TEXT_FIELD = "Text";
private static final String NAME_FIELD = "Name";
private static final String FONTNAME_FIELD = "Fontname";
private static final String FONTSIZE_FIELD = "Fontsize";
private static final String MARGIN_L_FIELD = "MarginL";
private static final String MARGIN_R_FIELD = "MarginR";
private static final String MARGIN_V_FIELD = "MarginV";
private static final List<String> STYLE_FIELDS = List.of(NAME_FIELD, FONTNAME_FIELD, FONTSIZE_FIELD, "PrimaryColour",
"SecondaryColour", "OutlineColour", "BackColour", "Bold", "Italic",
"Underline", "StrikeOut", "ScaleX", "ScaleY", "Spacing", "Angle", "BorderStyle", "Outline", "Shadow", "Alignment",
MARGIN_L_FIELD, MARGIN_R_FIELD, MARGIN_V_FIELD, "AlphaLevel", "Encoding");
private static final Set<String> REQUIRED_STYLE_FIELDS = Set.of(NAME_FIELD, FONTNAME_FIELD, FONTSIZE_FIELD);
private static final List<String> EVENT_FIELDS = List.of("Layer", START_FIELD, END_FIELD, STYLE_FIELD, "Name", MARGIN_L_FIELD, MARGIN_R_FIELD, MARGIN_V_FIELD, "Effect", TEXT_FIELD);
private static final Set<String> REQUIRED_EVENT_FIELDS = Set.of(START_FIELD, END_FIELD, STYLE_FIELD, TEXT_FIELD);
private static final Pattern NEWLINE_PATTERN = Pattern.compile("\\n");
private static final Pattern POS_PATTERN = Pattern.compile("^\\{\\\\pos\\((?<x>\\d+),(?<y>\\d+)\\)}");
private static final Pattern START_DIALOG_PATTERN = Pattern.compile("^Dialogue: ");
private static final Pattern START_ZERO_PATTERN = Pattern.compile("^0+");
private static final Pattern PLAY_RES_Y_PATTERN = Pattern.compile("^PlayResY: (?<height>\\d+)", Pattern.MULTILINE);
private final Map<String, Translator<?>> translators;
@Inject
ASSSubtitleConverter(final Translator translator, final Preferences preferences,
@FontName final String defaultFontName, @FontSize final int defaultFontSize) {
this.translator = requireNonNull(translator);
this.preferences = requireNonNull(preferences);
this.defaultFontName = Objects.requireNonNull(defaultFontName);
if (defaultFontSize <= 0) {
throw new IllegalArgumentException("Font size must be positive : " + defaultFontSize);
}
this.defaultFontSize = defaultFontSize;
ASSSubtitleConverter(final Map<String, Translator<?>> translators) {
this.translators = Map.copyOf(translators);
}
@Override
public String format(final SubtitleCollection<?> collection, final VideoInfo videoInfo) {
public String format(final SubtitleCollection<?> collection, final FormatOptions options) {
final var subtitles = collection.subtitles().stream().sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
final var scriptInfo = getScriptInfo(videoInfo);
final var styles = getStyles(subtitles);
final var events = getEvents(subtitles);
final var scriptInfo = writeScriptInfo(options.videoInfo());
final var styles = writeStyles(subtitles, options.defaultFont());
final var events = writeEvents(subtitles, options.defaultFont());
return scriptInfo + "\n\n" + styles + "\n\n" + events + "\n";
}
private String getEvents(final Collection<? extends Subtitle> subtitles) {
return EVENTS_SECTION + "\n" + "Format: Start, End, Style, Text\n" +
subtitles.stream().map(this::getEvent).collect(Collectors.joining("\n"));
private static String writeEvents(final Collection<? extends Subtitle> subtitles, final Font defaultFont) {
return EVENTS_SECTION + "\n" + "Format: " + String.join(", ", EVENT_FIELDS) + "\n" +
subtitles.stream().map(s -> writeEvent(s, defaultFont)).collect(Collectors.joining("\n"));
}
private String getEvent(final Subtitle subtitle) {
return DIALOGUE + " " + formatTime(subtitle.start()) + "," + formatTime(subtitle.end()) + "," + getName(subtitle.font()) + "," + subtitle.content();
private static String writeEvent(final Subtitle subtitle, final Font defaultFont) {
return DIALOGUE + " 0," +
formatTime(subtitle.start()) + "," +
formatTime(subtitle.end()) + "," +
getStyleName(subtitle.font(), defaultFont) + "," +
"X," +
"0000," +
"0000," +
"0000," +
"," +
(subtitle.bounds() == null ? "" : "{\\pos(" + (long) subtitle.bounds().x() + "," + (long) subtitle.bounds().y() + ")}") +
subtitle.content();
}
private String getName(final Font font) {
private static String getStyleName(final Font font, final Font defaultFont) {
final String fontName;
final int fontSize;
if (font == null) {
fontName = preferences.get("fontName", defaultFontName);
fontSize = preferences.getInt("fontSize", defaultFontSize);
fontName = defaultFont.name();
fontSize = defaultFont.size();
} else {
fontName = font.name();
fontSize = font.size();
@@ -83,19 +109,19 @@ public class ASSSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
return fontName + fontSize;
}
private String getStyles(final Collection<? extends Subtitle> subtitles) {
return STYLES_SECTION + "\n" + "Format: Name, Fontname, Fontsize\n" + listStyles(subtitles);
private static String writeStyles(final Collection<? extends Subtitle> subtitles, final Font defaultFont) {
return STYLES_SECTION + "\n" + "Format: Name, Fontname, Fontsize\n" + listStyles(subtitles, defaultFont);
}
private String listStyles(final Collection<? extends Subtitle> subtitles) {
private static String listStyles(final Collection<? extends Subtitle> subtitles, final Font defaultFont) {
final var uniqueStyles = subtitles.stream().map(Subtitle::font).filter(Objects::nonNull).collect(Collectors.toSet());
if (subtitles.stream().anyMatch(s -> s.font() == null)) {
uniqueStyles.add(new FontImpl(preferences.get("fontName", defaultFontName), preferences.getInt("fontSize", defaultFontSize)));
uniqueStyles.add(defaultFont);
}
return uniqueStyles.stream().map(f -> STYLE + " " + getName(f) + ", " + f.name() + ", " + f.size()).collect(Collectors.joining("\n"));
return uniqueStyles.stream().map(f -> STYLE + " " + getStyleName(f, defaultFont) + ", " + f.name() + ", " + f.size()).collect(Collectors.joining("\n"));
}
private static String getScriptInfo(final VideoInfo videoInfo) {
private static String writeScriptInfo(final VideoInfo videoInfo) {
return """
[Script Info]
PlayResX: %d
@@ -103,85 +129,129 @@ public class ASSSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
WrapStyle: 1""".formatted(videoInfo.width(), videoInfo.height());
}
private static String formatTime(final long time) {
final var millisPerHour = 3600000;
final var millisPerMinute = 60000;
final var hours = time / millisPerHour;
final var minutes = (time - hours * millisPerHour) / millisPerMinute;
final var seconds = (time - hours * millisPerHour - minutes * millisPerMinute) / 1000;
final var millis = time - hours * millisPerHour - minutes * millisPerMinute - seconds * 1000;
final var hundredths = millis / 10;
return String.format("%d:%02d:%02d.%02d", hours, minutes, seconds, hundredths);
}
@Override
public SubtitleCollectionImpl<SubtitleImpl> parse(final String content) throws ParseException {
public SubtitleCollectionImpl<SubtitleImpl> parse(final String content, final ParseOptions options) throws ParseException {
final var videoHeight = parseVideoHeight(content);
final var fonts = parseFonts(content);
final var subtitles = parseSubtitles(content, fonts);
final var subtitles = parseSubtitles(content, fonts, options.defaultFont(), videoHeight);
final var text = subtitles.stream().map(Subtitle::content).collect(Collectors.joining());
final var language = translator.getLanguage(text);
final var language = translators.values().iterator().next().getLanguage(text); //Use any translator for now
return new SubtitleCollectionImpl<>(text, subtitles, language);
}
private static List<SubtitleImpl> parseSubtitles(final String content, final Map<String, Font> fonts) throws ParseException {
final var fontIndex = content.indexOf(EVENTS_SECTION);
if (fontIndex == -1) {
private static int parseVideoHeight(final CharSequence content) {
final var matcher = PLAY_RES_Y_PATTERN.matcher(content);
if (matcher.find()) {
return Integer.parseInt(matcher.group("height"));
} else {
return -1;
}
}
private static List<SubtitleImpl> parseSubtitles(final String content, final Map<String, Font> fonts, final Font defaultFont, final int videoHeight) throws ParseException {
final var eventsIndex = content.indexOf(EVENTS_SECTION);
if (eventsIndex == -1) {
throw new ParseException("Events section not found in " + content);
} else {
final var split = content.substring(fontIndex).split("\\n");
final List<String> fields;
if (split[0].startsWith(EVENTS_SECTION)) {
fields = getFields(split[1]);
} else {
throw new ParseException("Couldn't parse events : " + content);
final var split = NEWLINE_PATTERN.split(content.substring(eventsIndex));
final var fields = getFields(split[1]);
final var fieldsMapping = EVENT_FIELDS.stream().collect(Collectors.toMap(e -> e, fields::indexOf));
if (REQUIRED_EVENT_FIELDS.stream().anyMatch(s -> fieldsMapping.get(s) == -1)) {
throw new ParseException("Missing required fields : " + content);
}
final var startIndex = fields.indexOf("Start");
final var endIndex = fields.indexOf("End");
final var styleIndex = fields.indexOf("Style");
final var textIndex = fields.indexOf("Text");
if (startIndex == -1 || endIndex == -1 || styleIndex == -1 || textIndex == -1) {
throw new ParseException("Couldn't parse events : " + content);
if (fieldsMapping.get("Text") != fields.size() - 1) {
throw new ParseException("Text field should be the last field : " + content);
}
//Ignore effect for now
return Arrays.stream(split).filter(s -> s.startsWith(DIALOGUE))
.map(s -> {
final var values = Arrays.stream(s.replace(DIALOGUE, "").split(",")).map(String::trim).filter(w -> !w.isBlank()).toList();
final var start = parseTime(values.get(startIndex));
final var end = parseTime(values.get(endIndex));
final var style = values.get(styleIndex);
final var font = fonts.get(style);
final var text = values.stream().skip(textIndex).collect(Collectors.joining(", "));
return new SubtitleImpl(text, start, end, font, null); //TODO pos style overrides
final var values = parseValues(s, fields.size());
final var start = parseTime(values.get(fieldsMapping.get(START_FIELD)));
final var end = parseTime(values.get(fieldsMapping.get(END_FIELD)));
final var style = values.get(fieldsMapping.get(STYLE_FIELD));
final var marginBounds = getMarginBounds(values, fieldsMapping, videoHeight);
final var font = fonts.getOrDefault(style, defaultFont);
final var rawText = values.get(fieldsMapping.get(TEXT_FIELD));
final var matcher = POS_PATTERN.matcher(rawText);
if (matcher.find()) {
final var x = Integer.parseInt(matcher.group("x"));
final var y = Integer.parseInt(matcher.group("y"));
final var bounds = new BoundsImpl(x, y, 0, 0);
final var text = matcher.replaceAll("");
return new SubtitleImpl(text, start, end, font, bounds);
} else {
return new SubtitleImpl(rawText, start, end, font, marginBounds);
}
}).toList();
}
}
private Map<String, Font> parseFonts(final String content) throws ParseException {
final var fontIndex = content.indexOf(STYLES_SECTION);
if (fontIndex == -1) {
private static List<String> parseValues(final CharSequence value, final int size) {
final var trimmed = START_DIALOG_PATTERN.matcher(value).replaceAll("").trim();
final var values = new ArrayList<String>(EVENT_FIELDS.size());
var substring = trimmed;
while (!substring.isEmpty()) {
final var index = substring.indexOf(',');
if (index == -1 || values.size() == size - 1) {
values.add(substring);
substring = "";
} else {
values.add(substring.substring(0, index));
substring = substring.substring(index + 1);
}
}
return values;
}
private static Bounds getMarginBounds(final List<String> values, final Map<String, Integer> fieldsMapping, final int videoHeight) {
final var marginLIndex = fieldsMapping.get(MARGIN_L_FIELD);
final var marginRIndex = fieldsMapping.get(MARGIN_R_FIELD);
final var marginVIndex = fieldsMapping.get(MARGIN_V_FIELD);
if (marginLIndex == -1 || marginRIndex == -1 || marginVIndex == -1) {
return null;
} else {
final var marginL = parseMargin(values.get(marginLIndex));
final var marginR = parseMargin(values.get(marginRIndex));
final var marginV = parseMargin(values.get(marginVIndex));
if (marginL == -1 || marginR == -1 || marginV == -1) {
return null;
} else {
final var y = videoHeight <= 0 ? 0 : videoHeight - marginV;
return new BoundsImpl(marginL, y, marginR - (double) marginL, 0);
}
}
}
private static int parseMargin(final CharSequence margin) {
final var removedZero = START_ZERO_PATTERN.matcher(margin).replaceAll("");
return removedZero.isEmpty() ? -1 : Integer.parseInt(removedZero);
}
private static Map<String, Font> parseFonts(final String content) throws ParseException {
final var stylesIndex = content.indexOf(STYLES_SECTION);
if (stylesIndex == -1) {
throw new ParseException("Styles section not found in " + content);
} else {
final var split = content.substring(fontIndex).split("\\n");
final List<String> fields;
if (split[0].startsWith(STYLES_SECTION)) {
fields = getFields(split[1]);
} else {
throw new ParseException("Couldn't parse styles : " + content);
}
final var fontNameIndex = fields.indexOf("Fontname");
final var fontSizeIndex = fields.indexOf("Fontsize");
if (fontNameIndex == -1 || fontSizeIndex == -1) {
throw new ParseException("Couldn't parse styles : " + content);
final var split = NEWLINE_PATTERN.split(content.substring(stylesIndex));
final var fields = getFields(split[1]);
final var fieldsMapping = STYLE_FIELDS.stream().collect(Collectors.toMap(e -> e, fields::indexOf));
if (REQUIRED_STYLE_FIELDS.stream().anyMatch(s -> fieldsMapping.get(s) == -1)) {
throw new ParseException("Missing required fields : " + content);
}
return Arrays.stream(split).filter(s -> s.startsWith(STYLE))
.map(s -> {
final var values = Arrays.stream(s.replace(STYLE, "").split(",")).map(String::trim).filter(w -> !w.isBlank()).toList();
final var name = values.get(fontNameIndex);
final var size = Integer.parseInt(values.get(fontSizeIndex));
return new FontImpl(name, size);
}).collect(Collectors.toMap(this::getName, f -> f));
final var name = values.get(fieldsMapping.get(NAME_FIELD));
final var fontName = values.get(fieldsMapping.get(FONTNAME_FIELD));
final var fontSize = Integer.parseInt(values.get(fieldsMapping.get(FONTSIZE_FIELD)));
return new NamedFont(name, new FontImpl(fontName, fontSize));
}).collect(Collectors.toMap(f -> f.name, f -> f.font));
}
}
private record NamedFont(String name, Font font) {
}
private static List<String> getFields(final String string) {
return Arrays.stream(string.replace(FORMAT, "").split(",")).map(String::trim).filter(s -> !s.isBlank()).toList();
}
@@ -196,6 +266,16 @@ public class ASSSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
return (hours * 3600L + minutes * 60L + seconds) * 1000L + hundredths * 10L;
}
private static String formatTime(final long time) {
final var millisPerHour = 3600000;
final var millisPerMinute = 60000;
final var hours = time / millisPerHour;
final var minutes = (time - hours * millisPerHour) / millisPerMinute;
final var seconds = (time - hours * millisPerHour - minutes * millisPerMinute) / 1000;
final var hundredths = (time - hours * millisPerHour - minutes * millisPerMinute - seconds * 1000) / 10;
return String.format("%02d:%02d:%02d.%02d", hours, minutes, seconds, hundredths);
}
@Override
public String formatName() {
return "ass";

View File

@@ -0,0 +1,17 @@
package com.github.gtache.autosubtitle.subtitle.converter.impl;
import com.github.gtache.autosubtitle.VideoInfo;
import com.github.gtache.autosubtitle.subtitle.Font;
import com.github.gtache.autosubtitle.subtitle.converter.FormatOptions;
import java.util.Objects;
/**
* Implementation of {@link FormatOptions}
*/
public record FormatOptionsImpl(VideoInfo videoInfo, Font defaultFont) implements FormatOptions {
public FormatOptionsImpl {
Objects.requireNonNull(videoInfo);
Objects.requireNonNull(defaultFont);
}
}

View File

@@ -0,0 +1,21 @@
package com.github.gtache.autosubtitle.subtitle.converter.impl;
import com.github.gtache.autosubtitle.subtitle.Font;
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
import java.util.Objects;
/**
* Implementation of {@link ParseOptions}
*/
public record ParseOptionsImpl(int maxLineLength, int maxLines, Font defaultFont) implements ParseOptions {
public ParseOptionsImpl {
if (maxLineLength <= 0) {
throw new IllegalArgumentException("maxLineLength must be > 0");
}
if (maxLines <= 0) {
throw new IllegalArgumentException("maxLines must be > 0");
}
Objects.requireNonNull(defaultFont);
}
}

View File

@@ -1,9 +1,10 @@
package com.github.gtache.autosubtitle.subtitle.converter.impl;
import com.github.gtache.autosubtitle.VideoInfo;
import com.github.gtache.autosubtitle.subtitle.Subtitle;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import com.github.gtache.autosubtitle.subtitle.converter.FormatOptions;
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleCollectionImpl;
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImpl;
@@ -12,25 +13,28 @@ import com.github.gtache.autosubtitle.translation.Translator;
import javax.inject.Inject;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static java.util.Objects.requireNonNull;
/**
* Converts subtitles to SRT format
*/
public class SRTSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
private final Translator<?> translator;
private static final Pattern ARROW_PATTERN = Pattern.compile(" --> ");
private static final Pattern DOUBLE_NEWLINE_PATTERN = Pattern.compile("\n\n");
private final Map<String, Translator<?>> translators;
@Inject
SRTSubtitleConverter(final Translator translator) {
this.translator = requireNonNull(translator);
SRTSubtitleConverter(final Map<String, Translator<?>> translators) {
this.translators = Map.copyOf(translators);
}
@Override
public String format(final SubtitleCollection<?> collection, final VideoInfo videoInfo) {
public String format(final SubtitleCollection<?> collection, final FormatOptions options) {
final var subtitles = collection.subtitles().stream().sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
return IntStream.range(0, subtitles.size()).mapToObj(i -> {
final var subtitle = subtitles.get(i);
@@ -40,24 +44,14 @@ public class SRTSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
}).collect(Collectors.joining("\n\n")) + "\n";
}
private static String formatTime(final long time) {
final var millisPerHour = 3600000;
final var millisPerMinute = 60000;
final var hours = time / millisPerHour;
final var minutes = (time - hours * millisPerHour) / millisPerMinute;
final var seconds = (time - hours * millisPerHour - minutes * millisPerMinute) / 1000;
final var millis = time - hours * millisPerHour - minutes * millisPerMinute - seconds * 1000;
return String.format("%02d:%02d:%02d,%03d", hours, minutes, seconds, millis);
}
@Override
public SubtitleCollectionImpl<SubtitleImpl> parse(final String content) throws ParseException {
public SubtitleCollectionImpl<SubtitleImpl> parse(final String content, final ParseOptions options) throws ParseException {
try {
final var elements = content.split("\n\n");
final var elements = DOUBLE_NEWLINE_PATTERN.split(content);
final var subtitles = Arrays.stream(elements).filter(element -> !element.isBlank()).map(element -> {
final var lines = element.split("\n");
final var time = lines[1];
final var timeSplit = time.split(" --> ");
final var timeSplit = ARROW_PATTERN.split(time);
final var startTimeStr = timeSplit[0];
final var endTimeStr = timeSplit[1];
final var start = parseTime(startTimeStr);
@@ -66,7 +60,7 @@ public class SRTSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
return new SubtitleImpl(text, start, end, null, null);
}).toList();
final var text = subtitles.stream().map(Subtitle::content).collect(Collectors.joining(" "));
return new SubtitleCollectionImpl<>(text, subtitles, translator.getLanguage(text));
return new SubtitleCollectionImpl<>(text, subtitles, translators.values().iterator().next().getLanguage(text)); //Use any translator for now
} catch (final Exception e) {
throw new ParseException(e);
}
@@ -82,6 +76,16 @@ public class SRTSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
return (hours * 3600L + minutes * 60L + seconds) * 1000 + millis;
}
private static String formatTime(final long time) {
final var millisPerHour = 3600000;
final var millisPerMinute = 60000;
final var hours = time / millisPerHour;
final var minutes = (time - hours * millisPerHour) / millisPerMinute;
final var seconds = (time - hours * millisPerHour - minutes * millisPerMinute) / 1000;
final var millis = time - hours * millisPerHour - minutes * millisPerMinute - seconds * 1000;
return String.format("%02d:%02d:%02d,%03d", hours, minutes, seconds, millis);
}
@Override
public String formatName() {
return "srt";

View File

@@ -4,6 +4,7 @@ import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
import javax.inject.Inject;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;

View File

@@ -0,0 +1,75 @@
package com.github.gtache.autosubtitle.subtitle.converter.json.impl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.gtache.autosubtitle.subtitle.Subtitle;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import com.github.gtache.autosubtitle.subtitle.converter.FormatException;
import com.github.gtache.autosubtitle.subtitle.converter.FormatOptions;
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleCollectionImpl;
import com.github.gtache.autosubtitle.subtitle.impl.SubtitleImpl;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* {@link SubtitleConverter} implementation for JSON files
*/
@Singleton
public class JSONSubtitleConverter implements SubtitleConverter<SubtitleImpl> {
private final ObjectMapper mapper;
@Inject
JSONSubtitleConverter(final ObjectMapper mapper) {
this.mapper = Objects.requireNonNull(mapper);
}
@Override
public String format(final SubtitleCollection<?> collection, final FormatOptions options) throws FormatException {
final var segments = collection.subtitles().stream().map(s -> new JSONSubtitleSegment(s.start(),
s.end(), s.content())).toList();
final var subtitles = new JSONSubtitles(segments, collection.language());
try {
return mapper.writeValueAsString(subtitles);
} catch (final JsonProcessingException e) {
throw new FormatException(e);
}
}
@Override
public SubtitleCollectionImpl<SubtitleImpl> parse(final String content, final ParseOptions options) throws ParseException {
try {
final var json = mapper.readValue(content, JSONSubtitles.class);
final var subtitles = json.segments().stream().flatMap(s -> {
final var start = s.start();
final var end = s.end();
return Stream.of(new SubtitleImpl(s.text(), start, end, null, null));
}).sorted(Comparator.comparing(Subtitle::start).thenComparing(Subtitle::end)).toList();
final var language = json.language();
final var subtitlesText = subtitles.stream().map(s -> s.content().trim()).collect(Collectors.joining(" "));
return new SubtitleCollectionImpl<>(subtitlesText, subtitles, language);
} catch (final Exception e) {
throw new ParseException(e);
}
}
@Override
public boolean canParse(final Path file) {
return file.getFileName().toString().endsWith(".json");
}
@Override
public String formatName() {
return "json";
}
}

View File

@@ -0,0 +1,26 @@
package com.github.gtache.autosubtitle.subtitle.converter.json.impl;
import static java.util.Objects.requireNonNull;
/**
* A subtitle segment as a JSON
*
* @param start The start in milliseconds
* @param end The end in milliseconds
* @param text The text
*/
public record JSONSubtitleSegment(long start, long end, String text) {
public JSONSubtitleSegment {
if (start < 0) {
throw new IllegalArgumentException("start must be >= 0 : " + start);
}
if (end < 0) {
throw new IllegalArgumentException("end must be >= 0 : " + end);
}
if (start > end) {
throw new IllegalArgumentException("start must be <= end : " + start + " > " + end);
}
requireNonNull(text);
}
}

View File

@@ -0,0 +1,21 @@
package com.github.gtache.autosubtitle.subtitle.converter.json.impl;
import com.github.gtache.autosubtitle.Language;
import java.util.List;
import static java.util.Objects.requireNonNull;
/**
* Root json object for subtitles
*
* @param segments The list of subtitle segments
* @param language The subtitles language
*/
public record JSONSubtitles(List<JSONSubtitleSegment> segments, Language language) {
public JSONSubtitles {
segments = List.copyOf(segments);
requireNonNull(language);
}
}

View File

@@ -1,5 +1,6 @@
package com.github.gtache.autosubtitle.subtitle.extractor.impl;
import com.github.gtache.autosubtitle.subtitle.Subtitle;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractEvent;
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractor;
import com.github.gtache.autosubtitle.subtitle.extractor.SubtitleExtractorListener;
@@ -10,7 +11,7 @@ import java.util.Set;
/**
* Base implementation of {@link SubtitleExtractor}
*/
public abstract class AbstractSubtitleExtractor implements SubtitleExtractor {
public abstract class AbstractSubtitleExtractor<T extends Subtitle> implements SubtitleExtractor<T> {
private final Set<SubtitleExtractorListener> listeners;

View File

@@ -0,0 +1,21 @@
package com.github.gtache.autosubtitle.subtitle.extractor.impl;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractOptions;
import com.github.gtache.autosubtitle.subtitle.extractor.ExtractionModel;
import java.util.Objects;
/**
* Implementation of {@link ExtractOptions}
*/
public record ExtractOptionsImpl(Language language, ExtractionModel model,
ParseOptions parseOptions) implements ExtractOptions {
public ExtractOptionsImpl {
Objects.requireNonNull(language);
Objects.requireNonNull(model);
Objects.requireNonNull(parseOptions);
}
}

View File

@@ -0,0 +1,18 @@
package com.github.gtache.autosubtitle.subtitle.impl;
import com.github.gtache.autosubtitle.subtitle.ExportOptions;
import com.github.gtache.autosubtitle.subtitle.OutputFormat;
import com.github.gtache.autosubtitle.subtitle.converter.FormatOptions;
import java.util.Objects;
/**
* Implementation of {@link ExportOptions}
*/
public record ExportOptionsImpl(OutputFormat outputFormat, FormatOptions formatOptions) implements ExportOptions {
public ExportOptionsImpl {
Objects.requireNonNull(outputFormat);
Objects.requireNonNull(formatOptions);
}
}

View File

@@ -0,0 +1,15 @@
package com.github.gtache.autosubtitle.subtitle.impl;
import com.github.gtache.autosubtitle.subtitle.ImportOptions;
import com.github.gtache.autosubtitle.subtitle.converter.ParseOptions;
import static java.util.Objects.requireNonNull;
/**
* Implementation of {@link ImportOptions}
*/
public record ImportOptionsImpl(ParseOptions parseOptions) implements ImportOptions {
public ImportOptionsImpl {
requireNonNull(parseOptions);
}
}

View File

@@ -6,7 +6,6 @@ import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import static java.util.Objects.requireNonNull;
@@ -17,7 +16,7 @@ public record SubtitleCollectionImpl<T extends Subtitle>(String text, Collection
Language language) implements SubtitleCollection<T> {
public SubtitleCollectionImpl {
Objects.requireNonNull(text);
requireNonNull(text);
subtitles = List.copyOf(subtitles);
requireNonNull(language);
}

View File

@@ -1,18 +1,22 @@
package com.github.gtache.autosubtitle.subtitle.impl;
import com.github.gtache.autosubtitle.Language;
import com.github.gtache.autosubtitle.VideoInfo;
import com.github.gtache.autosubtitle.archive.Archiver;
import com.github.gtache.autosubtitle.archive.ArchiverProvider;
import com.github.gtache.autosubtitle.subtitle.ExportOptions;
import com.github.gtache.autosubtitle.subtitle.ImportOptions;
import com.github.gtache.autosubtitle.subtitle.SubtitleCollection;
import com.github.gtache.autosubtitle.subtitle.SubtitleImporterExporter;
import com.github.gtache.autosubtitle.subtitle.converter.FormatException;
import com.github.gtache.autosubtitle.subtitle.converter.ParseException;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverter;
import com.github.gtache.autosubtitle.subtitle.converter.SubtitleConverterProvider;
import javax.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -40,19 +44,19 @@ public class SubtitleImporterExporterImpl implements SubtitleImporterExporter<Su
}
@Override
public Map<Language, SubtitleCollection<SubtitleImpl>> importSubtitles(final Path file) throws IOException, ParseException {
public Map<Language, SubtitleCollection<SubtitleImpl>> importSubtitles(final Path file, final ImportOptions options) throws IOException, ParseException {
final var fileName = file.getFileName().toString();
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
if (archiverProvider.getArchiver(extension) == null) {
final var loaded = loadSingleFile(file);
final var loaded = loadSingleFile(file, options);
logger.info("Loaded {}", file);
return Map.of(loaded.language(), loaded);
} else {
return loadArchive(file);
return loadArchive(file, options);
}
}
private Map<Language, SubtitleCollection<SubtitleImpl>> loadArchive(final Path file) throws IOException, ParseException {
private Map<Language, SubtitleCollection<SubtitleImpl>> loadArchive(final Path file, final ImportOptions options) throws IOException, ParseException {
final var fileName = file.getFileName().toString();
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
final var archiver = archiverProvider.getArchiver(extension);
@@ -64,7 +68,7 @@ public class SubtitleImporterExporterImpl implements SubtitleImporterExporter<Su
}
final var map = new EnumMap<Language, SubtitleCollection<SubtitleImpl>>(Language.class);
for (final var path : files) {
final var loaded = loadSingleFile(path);
final var loaded = loadSingleFile(path, options);
map.put(loaded.language(), loaded);
Files.deleteIfExists(path);
}
@@ -73,39 +77,39 @@ public class SubtitleImporterExporterImpl implements SubtitleImporterExporter<Su
return map;
}
private SubtitleCollection<SubtitleImpl> loadSingleFile(final Path file) throws ParseException {
private SubtitleCollection<SubtitleImpl> loadSingleFile(final Path file, final ImportOptions options) throws ParseException {
final var fileName = file.getFileName().toString();
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
final var parser = converterProvider.getConverter(extension);
if (parser == null) {
throw new ParseException("No converter found for " + file);
} else {
final var parsed = parser.parse(file);
final var parsed = parser.parse(file, options.parseOptions());
return new SubtitleCollectionImpl<>(parsed.text(), parsed.subtitles().stream().map(SubtitleImpl::new).toList(), parsed.language());
}
}
@Override
public void exportSubtitles(final Collection<? extends SubtitleCollection<?>> collections, final VideoInfo videoInfo, final Path file) throws IOException {
public void exportSubtitles(final Collection<? extends SubtitleCollection<?>> collections, final ExportOptions options, final Path file) throws IOException {
final var fileName = file.getFileName().toString();
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
if (archiverProvider.getArchiver(extension) != null) {
saveArchive(file, collections, videoInfo);
saveArchive(file, collections, options);
} else if (collections.size() == 1) {
saveSingleFile(file, collections.iterator().next(), videoInfo);
saveSingleFile(file, collections.iterator().next(), options);
} else {
throw new IllegalArgumentException("Cannot export multiple collections to a non-archive file : " + file);
}
}
private void saveArchive(final Path file, final Iterable<? extends SubtitleCollection<?>> collections, final VideoInfo videoInfo) throws IOException {
private void saveArchive(final Path file, final Iterable<? extends SubtitleCollection<?>> collections, final ExportOptions options) throws IOException {
final var fileName = file.getFileName().toString();
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
final var archiver = archiverProvider.getArchiver(extension);
final var tempDir = Files.createTempDirectory("autosubtitle");
for (final var collection : collections) {
final var subtitleFile = tempDir.resolve(collection.language().name().toLowerCase() + ".json");
saveSingleFile(subtitleFile, collection, videoInfo);
saveSingleFile(subtitleFile, collection, options);
}
final var files = new ArrayList<Path>();
try (final var stream = Files.list(tempDir)) {
@@ -119,16 +123,20 @@ public class SubtitleImporterExporterImpl implements SubtitleImporterExporter<Su
logger.info("Saved {}", file);
}
private void saveSingleFile(final Path file, final SubtitleCollection<?> collection, final VideoInfo videoInfo) throws IOException {
private void saveSingleFile(final Path file, final SubtitleCollection<?> collection, final ExportOptions options) throws IOException {
final var fileName = file.getFileName().toString();
final var extension = fileName.substring(fileName.lastIndexOf('.') + 1);
final var converter = converterProvider.getConverter(extension);
if (converter == null) {
throw new IOException("No converter found for " + file);
} else {
final var string = converter.format(collection, videoInfo);
try {
final var string = converter.format(collection, options.formatOptions());
Files.writeString(file, string);
logger.info("Saved {}", file);
} catch (final FormatException e) {
throw new IOException(e);
}
}
}

View File

@@ -6,18 +6,22 @@ module com.github.gtache.autosubtitle.core {
requires transitive dagger;
requires transitive java.net.http;
requires transitive javax.inject;
requires transitive java.prefs;
requires transitive com.fasterxml.jackson.databind;
requires org.apache.logging.log4j;
requires java.prefs;
exports com.github.gtache.autosubtitle.impl;
exports com.github.gtache.autosubtitle.archive.impl;
exports com.github.gtache.autosubtitle.process.impl;
exports com.github.gtache.autosubtitle.setup.impl;
exports com.github.gtache.autosubtitle.subtitle.impl;
exports com.github.gtache.autosubtitle.subtitle.converter.impl;
exports com.github.gtache.autosubtitle.subtitle.converter.json.impl;
exports com.github.gtache.autosubtitle.subtitle.extractor.impl;
exports com.github.gtache.autosubtitle.modules.impl;
exports com.github.gtache.autosubtitle.modules.setup.impl;
exports com.github.gtache.autosubtitle.subtitle.extractor.impl;
exports com.github.gtache.autosubtitle.subtitle.converter.impl;
exports com.github.gtache.autosubtitle.modules.subtitle.impl;
exports com.github.gtache.autosubtitle.modules.subtitle.converter.impl;
exports com.github.gtache.autosubtitle.modules.subtitle.converter.json.impl;
}

View File

@@ -2,12 +2,12 @@ package com.github.gtache.autosubtitle.archive.impl;
import com.github.gtache.autosubtitle.archive.Archiver;
import com.github.gtache.autosubtitle.archive.ArchiverProvider;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
@@ -22,16 +22,20 @@ class TestArchiverProviderImpl {
private final ArchiverProvider archiverProvider;
TestArchiverProviderImpl(@Mock final Archiver zipArchiver, @Mock final Archiver rarArchiver) {
when(zipArchiver.archiveExtension()).thenReturn("zip");
when(rarArchiver.archiveExtension()).thenReturn("rar");
this.zipArchiver = Objects.requireNonNull(zipArchiver);
this.rarArchiver = Objects.requireNonNull(rarArchiver);
this.archiverProvider = new ArchiverProviderImpl(Map.of("zip", zipArchiver, "rar", rarArchiver));
}
@BeforeEach
void beforeEach() {
when(zipArchiver.archiveExtension()).thenReturn("zip");
when(rarArchiver.archiveExtension()).thenReturn("rar");
}
@Test
void testAllArchivers() {
final Collection<Archiver> allArchivers = archiverProvider.allArchivers();
final var allArchivers = archiverProvider.allArchivers();
assertEquals(2, allArchivers.size());
assertTrue(allArchivers.contains(zipArchiver));
assertTrue(allArchivers.contains(rarArchiver));

View File

@@ -11,34 +11,39 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class TestZipDecompresser {
class TestZipArchiver {
private final ZipDecompresser zipDecompresser;
private final ZipArchiver zipArchiver;
TestZipDecompresser() {
this.zipDecompresser = new ZipDecompresser();
TestZipArchiver() {
this.zipArchiver = new ZipArchiver();
}
@Test
void testIsPathSupported() {
assertTrue(zipDecompresser.isPathSupported(Path.of("test.zip")));
assertFalse(zipDecompresser.isPathSupported(Path.of("test")));
assertFalse(zipDecompresser.isPathSupported(Path.of("test.txt")));
assertFalse(zipDecompresser.isPathSupported(Path.of("test.zip2")));
assertTrue(zipArchiver.isPathSupported(Path.of("test.zip")));
assertFalse(zipArchiver.isPathSupported(Path.of("test")));
assertFalse(zipArchiver.isPathSupported(Path.of("test.txt")));
assertFalse(zipArchiver.isPathSupported(Path.of("test.zip2")));
}
@Test
void testCompress() {
assertThrows(UnsupportedOperationException.class, () -> zipDecompresser.compress(List.of(Paths.get("file.txt")), Paths.get("target")));
final var paths = List.of(Paths.get("file.txt"));
final var target = Paths.get("target");
assertThrows(UnsupportedOperationException.class, () -> zipArchiver.compress(paths, target));
}
@Test
void testDecompress(@TempDir final Path tempDir) throws IOException {
final var file = tempDir.resolve("test.zip");
try (final var in = getClass().getResourceAsStream("in.zip")) {
if (in == null) {
throw new IOException("in.zip not found");
}
Files.copy(in, file);
}
zipDecompresser.decompress(file, tempDir);
zipArchiver.decompress(file, tempDir);
final var inTxt = tempDir.resolve("in.txt");
final var bin = tempDir.resolve("bin");
final var binTxt = bin.resolve("bin.txt");
@@ -57,6 +62,8 @@ class TestZipDecompresser {
@Test
void testIllegal() {
assertThrows(IllegalArgumentException.class, () -> zipDecompresser.decompress(Paths.get("file.txt"), Paths.get("target")));
final var source = Paths.get("source");
final var target = Paths.get("target");
assertThrows(IllegalArgumentException.class, () -> zipArchiver.decompress(source, target));
}
}

View File

@@ -26,25 +26,25 @@ class TestArchitecture {
@Test
void testGetArchitecture() {
for (final var value : Architecture.values()) {
for (final var value : values()) {
System.setProperty("os.arch", value.name());
assertEquals(value, Architecture.getArchitecture());
assertEquals(value, getArchitecture());
}
System.setProperty("os.arch", "any");
assertEquals(UNKNOWN, Architecture.getArchitecture());
assertEquals(UNKNOWN, getArchitecture());
}
@Test
void testGetArchitectureName() {
assertEquals(I386, Architecture.getArchitecture("i386"));
assertEquals(I386, Architecture.getArchitecture("I386"));
assertEquals(I386, getArchitecture("i386"));
assertEquals(I386, getArchitecture("I386"));
}
@Test
void testIsAMD64() {
final var expectedAMD64 = Set.of(X86, X86_64, AMD64);
expectedAMD64.forEach(a -> assertTrue(a.isAMD64()));
Arrays.stream(Architecture.values()).filter(a -> !expectedAMD64.contains(a))
Arrays.stream(values()).filter(a -> !expectedAMD64.contains(a))
.forEach(a -> assertFalse(a.isAMD64()));
}
@@ -52,7 +52,7 @@ class TestArchitecture {
void testIsARM64() {
final var expectedARM64 = Set.of(ARM64, ARMV8, ARMV9, AARCH64);
expectedARM64.forEach(a -> assertTrue(a.isARM64()));
Arrays.stream(Architecture.values()).filter(a -> !expectedARM64.contains(a))
Arrays.stream(values()).filter(a -> !expectedARM64.contains(a))
.forEach(a -> assertFalse(a.isARM64()));
}
}

View File

@@ -11,14 +11,14 @@ class TestAudioInfoImpl {
private final long duration;
TestAudioInfoImpl() {
this.audioFormat = "audioFormat";
this.audioFormat = "format";
this.duration = 1000L;
}
@Test
void testGetters() {
final var audioInfo = new AudioInfoImpl(audioFormat, duration);
assertEquals(audioFormat, audioInfo.audioFormat());
assertEquals(audioFormat, audioInfo.format());
assertEquals(duration, audioInfo.duration());
}

View File

@@ -21,7 +21,7 @@ class TestFileAudioImpl {
private final Path path;
private final AudioInfo info;
TestFileAudioImpl(@Mock final Path path, @Mock final AudioInfo info) throws IOException {
TestFileAudioImpl(@Mock final Path path, @Mock final AudioInfo info) {
this.path = requireNonNull(path);
this.info = requireNonNull(info);
}

View File

@@ -20,7 +20,7 @@ class TestMemoryAudioImpl {
private final Supplier<InputStream> supplier;
private final AudioInfo info;
TestMemoryAudioImpl(@Mock InputStream inputStream, @Mock AudioInfo info) {
TestMemoryAudioImpl(@Mock final InputStream inputStream, @Mock final AudioInfo info) {
this.supplier = () -> requireNonNull(inputStream);
this.info = requireNonNull(info);
}

View File

@@ -19,7 +19,7 @@ class TestMemoryVideoImpl {
private final Supplier<InputStream> supplier;
private final VideoInfo info;
TestMemoryVideoImpl(@Mock InputStream inputStream, @Mock VideoInfo info) {
TestMemoryVideoImpl(@Mock final InputStream inputStream, @Mock final VideoInfo info) {
this.supplier = () -> requireNonNull(inputStream);
this.info = requireNonNull(info);
}

Some files were not shown because too many files have changed in this diff Show More