Database Setup and usage
This commit is contained in:
parent
69ae0ba5ab
commit
1838b6e4ab
6
.idea/copilot.data.migration.agent.xml
generated
Normal file
6
.idea/copilot.data.migration.agent.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AgentMigrationStateService">
|
||||||
|
<option name="migrationStatus" value="COMPLETED" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/copilot.data.migration.ask.xml
generated
Normal file
6
.idea/copilot.data.migration.ask.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AskMigrationStateService">
|
||||||
|
<option name="migrationStatus" value="COMPLETED" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/copilot.data.migration.ask2agent.xml
generated
Normal file
6
.idea/copilot.data.migration.ask2agent.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Ask2AgentMigrationStateService">
|
||||||
|
<option name="migrationStatus" value="COMPLETED" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/copilot.data.migration.edit.xml
generated
Normal file
6
.idea/copilot.data.migration.edit.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="EditMigrationStateService">
|
||||||
|
<option name="migrationStatus" value="COMPLETED" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
3
.idea/gradle.xml
generated
3
.idea/gradle.xml
generated
@ -16,7 +16,8 @@
|
|||||||
<option value="$PROJECT_DIR$/apps/ui" />
|
<option value="$PROJECT_DIR$/apps/ui" />
|
||||||
<option value="$PROJECT_DIR$/shared" />
|
<option value="$PROJECT_DIR$/shared" />
|
||||||
<option value="$PROJECT_DIR$/shared/common" />
|
<option value="$PROJECT_DIR$/shared/common" />
|
||||||
<option value="$PROJECT_DIR$/shared/eventi" />
|
<option value="$PROJECT_DIR$/shared/event-task-contract" />
|
||||||
|
<option value="$PROJECT_DIR$/shared/ffmpeg" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
|
|||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="azul-17" project-jdk-type="JavaSDK" />
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="azul-17" project-jdk-type="JavaSDK" />
|
||||||
</project>
|
</project>
|
||||||
543
.idea/workspace.xml
generated
543
.idea/workspace.xml
generated
@ -4,194 +4,86 @@
|
|||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="79fc3e25-8083-4c15-b17f-a1e0d61c77a6" name="Changes" comment="Publishing reason for failure">
|
<list default="true" id="79fc3e25-8083-4c15-b17f-a1e0d61c77a6" name="Changes" comment="v5 init">
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/copilot.data.migration.agent.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/copilot.data.migration.ask.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/copilot.data.migration.ask2agent.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/copilot.data.migration.edit.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplication.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/ConverterEnv.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/convert/ConvertListener.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/convert/Converter2.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/listeners/ConvertTaskListener.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/apps/converter/src/main/resources/application.yml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/apps/converter/src/test/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplicationTest.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/DatabaseApplication.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/DatabaseConfig.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/DatabaseConfiguration.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/DatabaseEnv.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/DatabaseUtil.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/tables/EventsTable.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/tables/TasksTable.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/EventRegistry.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/TaskRegistry.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/Util.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/events/ConverterEvents.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/events/CoverDownloadedEvent.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/events/FileEvents.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/events/MediaParsedInfoEvent.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/events/MediaReadyEvent.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/events/MediaStreamParsedEvent.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/events/MediaStreamReadEvent.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/events/MediaStreamReadTaskCreatedEvent.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/events/MetadataEvents.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/events/ProcesserEvents.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/events/StartProcessingEvent.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/tasks/ConvertTask.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/tasks/CoverDownloadTask.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/tasks/EncodeTask.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/tasks/ExtractTask.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/tasks/MediaReadTask.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract/tasks/MetadataSearchTask.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/model/MediaInfo.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/model/MediaStreams.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/parsing/FileNameDeterminate.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/parsing/FileNameParser.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/parsing/NameHelper.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/parsing/Regexes.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/stores/EventStore.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/stores/TaskStore.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/resources/application.yml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/resources/flyway/V1__create_events_table.sql" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/main/resources/flyway/V2__create_tasks_table.sql" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/FlywayMigrationTest.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/ffmpeg/build.gradle.kts" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/ffmpeg/src/main/kotlin/no/iktdev/mediaprocessing/ffmpeg/FFinfo.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/ffmpeg/src/main/kotlin/no/iktdev/mediaprocessing/ffmpeg/FFmpeg.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/ffmpeg/src/main/kotlin/no/iktdev/mediaprocessing/ffmpeg/arguments/MpegArgument.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/ffmpeg/src/main/kotlin/no/iktdev/mediaprocessing/ffmpeg/data/FFOutput.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/ffmpeg/src/main/kotlin/no/iktdev/mediaprocessing/ffmpeg/data/FFinfoOutput.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/ffmpeg/src/main/kotlin/no/iktdev/mediaprocessing/ffmpeg/data/FFmpegOutput.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/ffmpeg/src/main/kotlin/no/iktdev/mediaprocessing/ffmpeg/decoder/FfmpegDecodedProgress.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/shared/ffmpeg/src/main/kotlin/no/iktdev/mediaprocessing/ffmpeg/decoder/FfmpegProgressDecoder.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/gradle.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/gradle.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplication.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/apps/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/build.gradle.kts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/ConverterEnv.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/apps/converter/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/converter/build.gradle.kts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/Implementations.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/resources/application.properties" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/TaskCoordinator.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/apps/coordinator/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/coordinator/build.gradle.kts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/convert/ConvertListener.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/apps/processer/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/processer/build.gradle.kts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/convert/Converter2.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/apps/ui/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/ui/build.gradle.kts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/tasks/ConvertService.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/build.gradle.kts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/CoordinatorApplication.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/settings.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/settings.gradle.kts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/CoordinatorEventCoordinator.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/shared/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/shared/build.gradle.kts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/CoordinatorEventListener.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/shared/common/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/shared/common/build.gradle.kts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/CoordinatorUtils.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/Utils.kt" beforeDir="false" afterPath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/Utils.kt" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/EventsDatabase.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/DatabaseDeserializerTest.kt" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/Implementations.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/H2DataSource.kt" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/controller/ActionEventController.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/H2DataSource2.kt" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/controller/InfoController.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/PersistentMessageFromJsonDump.kt" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/controller/PollController.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/parsing/FileNameDeterminateTest.kt" beforeDir="false" afterPath="$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/parsing/FileNameDeterminateTest.kt" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/controller/RequestEventController.kt" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/tests/PersistentEventMangerTestBase.kt" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/coordination/ProcesserSocketMessageListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/services/UnattendedIndexing.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/implementations/WorkTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/BaseInfoFromFileTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/CompletedTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/ConvertWorkTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/CoverDownloadTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/CoverFromMetadataTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/EncodeWorkArgumentsTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/EncodeWorkTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/ExtractWorkArgumentsTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/ExtractWorkTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/MediaOutInformationTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/MetadataWaitOrDefaultTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/ParseMediaFileStreamsTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/PersistContentTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/listeners/ReadMediaFileStreamsTaskListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/EncodeWorkArgumentsMapping.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/EventsSummaryMapping.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/ExtractWorkArgumentsMapping.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/FFmpegBase.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/store/ContentCatalogStore.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/store/ContentCompletionMover.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/store/ContentGenresStore.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/store/ContentMetadataStore.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/store/ContentSubtitleStore.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/store/ContentTitleStore.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/store/ProcessedFileStore.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/streams/AudioArguments.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/streams/SubtitleArguments.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/mapping/streams/VideoArguments.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/tasksV2/validator/CompletionValidator.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/watcher/FileWatcherQueue.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/watcher/InputDirectoryWatcher.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/Implementations.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/ProcesserApplication.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/ProcesserEnv.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/Reporter.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/TaskCoordinator.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/controller/CancelController.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/ffmpeg/FfmpegArgumentsBuilder.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/ffmpeg/FfmpegListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/ffmpeg/FfmpegRunner.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/ffmpeg/FfmpegTaskService.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/ffmpeg/progress/FfmpegDecodedProgress.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/ffmpeg/progress/FfmpegProgressDecoder.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/services/EncodeService.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/processer/src/main/kotlin/no/iktdev/mediaprocessing/processer/services/ExtractService.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/Configuration.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/UIApplication.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/UIEnv.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/dto/EventChain.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/dto/EventDataDto.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/dto/EventSummary.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/dto/explore/ExplorerAttr.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/dto/explore/ExplorerCursor.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/explorer/ExplorerCore.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/service/CompletedEventsService.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/ChainedEventsTopic.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/ExplorerTopic.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/FileRequestTopic.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/ProcesserTasksTopic.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketClient.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketMessageHandler.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/UnprocessedFilesTopic.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/a2a/ProcesserListenerService.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/Defaults.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/DownloadClient.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/LogHelper.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/Preference.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/SharedConfig.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/TaskCoordinatorBase.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/Events.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/EventsListenerContract.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/EventsManagerContract.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/EventsUtil.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/ProcessType.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/BaseInfoEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/ConvertWorkCreatedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/ConvertWorkPerformed.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/EncodeArgumentCreatedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/EncodeWorkCreatedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/EncodeWorkPerformedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/Event.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/ExtractArgumentCreatedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/ExtractWorkCreatedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/ExtractWorkPerformedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/MediaCoverDownloadedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/MediaCoverInfoReceivedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/MediaFileStreamsParsedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/MediaFileStreamsReadEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/MediaMetadataReceivedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/MediaOutInformationConstructedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/MediaProcessCompletedEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/MediaProcessStartEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/PermitWorkCreationEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/data/PersistedContentEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/dto/Enums.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/dto/EventRequest.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/dto/EventSummary.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/dto/ProcesserEventInfo.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/dto/RequestWorkProceed.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/dto/Requester.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/dto/tasks/TaskData.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/ffmpeg/AudioArgumentsDto.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/ffmpeg/MediaStreams.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/ffmpeg/PreferenceDto.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/ffmpeg/SubtitleArgumentsDto.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/ffmpeg/VideoAndAudioDto.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/ffmpeg/VideoArgumentsDto.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/reader/MetadataDto.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/reader/SubtitlesDto.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/contract/reader/VideoDetails.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/EventsDatabase.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/cal/EventsManager.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/cal/RunnerManager.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/cal/TasksManager.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/tables/allEvents.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/tables/events.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/tables/files.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/tables/filesProcessed.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/tables/runners.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/database/tables/tasks.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/extended/FileExt.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/parsing/FileNameDeterminate.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/parsing/FileNameParser.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/parsing/NameHelper.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/parsing/Regexes.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/runner/ResultRunner.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/services/TaskService.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/socket/SocketImplementation.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/task/Task.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/task/TaskDoz.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/task/TaskType.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/build.gradle.kts" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/LogHelper.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/core/ConsumableEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/core/LocalDateTimeAdapter.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/core/PersistentMessageHelper.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/core/WGson.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/data/EventImpl.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/database/DataSource.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/database/DatabaseConnectionConfig.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/database/MySqlDataSource.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/database/TableDefaultOperations.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/implementations/EventCoordinator.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/implementations/EventListenerImpl.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/main/kotlin/no/iktdev/eventi/implementations/EventsManagerImpl.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/EventiApplication.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/EventiApplicationTests.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/EventiImplementationBase.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/TestConfig.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/MockDataEventListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/MockDataSource.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/MockEventCoordinator.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/MockEventManager.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/data/FirstEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/data/InitEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/data/SecondEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/data/ThirdEvent.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/listeners/FirstEventListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/listeners/ForthEventListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/listeners/SecondEventListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/mock/listeners/ThirdEventListener.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/tests/FirstEventListenerImplTestBase.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/tests/ForthEventListenerImplTestBase.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/tests/SecondEventListenerImplTestBase.kt" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/shared/eventi/src/test/kotlin/no/iktdev/eventi/tests/ThirdEventListenerImplTestBase.kt" beforeDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@ -263,9 +155,10 @@
|
|||||||
<component name="FileTemplateManagerImpl">
|
<component name="FileTemplateManagerImpl">
|
||||||
<option name="RECENT_TEMPLATES">
|
<option name="RECENT_TEMPLATES">
|
||||||
<list>
|
<list>
|
||||||
|
<option value="JUnit5 Test Class" />
|
||||||
|
<option value="Kotlin Interface" />
|
||||||
<option value="Kotlin Object" />
|
<option value="Kotlin Object" />
|
||||||
<option value="Kotlin File" />
|
<option value="Kotlin File" />
|
||||||
<option value="JUnit5 Test Class" />
|
|
||||||
<option value="Kotlin Class" />
|
<option value="Kotlin Class" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
@ -282,6 +175,9 @@
|
|||||||
<component name="MarkdownSettingsMigration">
|
<component name="MarkdownSettingsMigration">
|
||||||
<option name="stateVersion" value="1" />
|
<option name="stateVersion" value="1" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="ProblemsViewState">
|
||||||
|
<option name="selectedTabId" value="DEPENDENCY_CHECKER_PROBLEMS_TAB" />
|
||||||
|
</component>
|
||||||
<component name="ProjectColorInfo">{
|
<component name="ProjectColorInfo">{
|
||||||
"customColor": "",
|
"customColor": "",
|
||||||
"associatedIndex": 8
|
"associatedIndex": 8
|
||||||
@ -303,6 +199,9 @@
|
|||||||
"Gradle.ConvertWorkTaskListenerTest.validate_shouldIProcessAndHandleEvent1.executor": "Run",
|
"Gradle.ConvertWorkTaskListenerTest.validate_shouldIProcessAndHandleEvent1.executor": "Run",
|
||||||
"Gradle.ConvertWorkTaskListenerTest.validate_shouldIProcessAndHandleEvent2.executor": "Run",
|
"Gradle.ConvertWorkTaskListenerTest.validate_shouldIProcessAndHandleEvent2.executor": "Run",
|
||||||
"Gradle.ConvertWorkTaskListenerTest.validate_shouldIProcessAndHandleEvent3.executor": "Run",
|
"Gradle.ConvertWorkTaskListenerTest.validate_shouldIProcessAndHandleEvent3.executor": "Run",
|
||||||
|
"Gradle.ConverterApplicationTest.Verify that we can access TaskStore.executor": "Debug",
|
||||||
|
"Gradle.ConverterApplicationTest.context loads and common configuration is available.executor": "Run",
|
||||||
|
"Gradle.ConverterApplicationTest.executor": "Run",
|
||||||
"Gradle.DatabaseDeserializerTest.executor": "Run",
|
"Gradle.DatabaseDeserializerTest.executor": "Run",
|
||||||
"Gradle.DatabaseDeserializerTest.validateMediaInfo.executor": "Run",
|
"Gradle.DatabaseDeserializerTest.validateMediaInfo.executor": "Run",
|
||||||
"Gradle.DatabaseDeserializerTest.validateMetadataRead.executor": "Debug",
|
"Gradle.DatabaseDeserializerTest.validateMetadataRead.executor": "Debug",
|
||||||
@ -314,6 +213,8 @@
|
|||||||
"Gradle.FileNameParserTest.assertDotRemoval.executor": "Run",
|
"Gradle.FileNameParserTest.assertDotRemoval.executor": "Run",
|
||||||
"Gradle.FileNameParserTest.assertTitleFails.executor": "Run",
|
"Gradle.FileNameParserTest.assertTitleFails.executor": "Run",
|
||||||
"Gradle.FileNameParserTest.executor": "Run",
|
"Gradle.FileNameParserTest.executor": "Run",
|
||||||
|
"Gradle.FlywayMigrationTest.executor": "Run",
|
||||||
|
"Gradle.FlywayMigrationTest.should run flyway migrations and create expected tables.executor": "Debug",
|
||||||
"Gradle.MediaProcessing2 [apps:build].executor": "Run",
|
"Gradle.MediaProcessing2 [apps:build].executor": "Run",
|
||||||
"Gradle.MediaProcessing2 [apps:clean].executor": "Run",
|
"Gradle.MediaProcessing2 [apps:clean].executor": "Run",
|
||||||
"Gradle.MediaProcessing2 [build].executor": "Run",
|
"Gradle.MediaProcessing2 [build].executor": "Run",
|
||||||
@ -354,10 +255,10 @@
|
|||||||
"git-widget-placeholder": "v5",
|
"git-widget-placeholder": "v5",
|
||||||
"ignore.virus.scanning.warn.message": "true",
|
"ignore.virus.scanning.warn.message": "true",
|
||||||
"kotlin-language-version-configured": "true",
|
"kotlin-language-version-configured": "true",
|
||||||
"last_opened_file_path": "/mount/870 EVO 1TB/Workspace/mediaprocesserv5",
|
"last_opened_file_path": "/mount/870 EVO 1TB/Workspace/mediaprocesserv5/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/model",
|
||||||
"project.structure.last.edited": "Modules",
|
"project.structure.last.edited": "Modules",
|
||||||
"project.structure.proportion": "0.0",
|
"project.structure.proportion": "0.15",
|
||||||
"project.structure.side.proportion": "0.0",
|
"project.structure.side.proportion": "0.2",
|
||||||
"settings.editor.selected.configurable": "reference.settingsdialog.project.gradle"
|
"settings.editor.selected.configurable": "reference.settingsdialog.project.gradle"
|
||||||
},
|
},
|
||||||
"keyToStringList": {
|
"keyToStringList": {
|
||||||
@ -368,10 +269,11 @@
|
|||||||
}]]></component>
|
}]]></component>
|
||||||
<component name="RecentsManager">
|
<component name="RecentsManager">
|
||||||
<key name="CopyFile.RECENT_KEYS">
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
<recent name="D:\Workspace\MediaProcessing2\apps\processer\src\test\kotlin\no\iktdev\mediaprocessing\processer" />
|
<recent name="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/model" />
|
||||||
<recent name="D:\Workspace\MediaProcessing2\shared\eventi\src\main\kotlin\no\iktdev\eventi" />
|
<recent name="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract" />
|
||||||
<recent name="D:\Workspace\MediaProcessing2\shared\common\src\main\kotlin\no\iktdev\mediaprocessing\shared\common\contract" />
|
<recent name="$PROJECT_DIR$/shared/event-task-contract/src/main/kotlin/no/iktdev/mediaprocessing/event_task_contract" />
|
||||||
<recent name="D:\Workspace\MediaProcessing2\shared\contract\src\main\kotlin\no\iktdev\mediaprocessing\shared\contract\tables" />
|
<recent name="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter" />
|
||||||
|
<recent name="$PROJECT_DIR$/apps/converter/src/main/resources" />
|
||||||
</key>
|
</key>
|
||||||
<key name="MoveFile.RECENT_KEYS">
|
<key name="MoveFile.RECENT_KEYS">
|
||||||
<recent name="D:\Workspace\MediaProcessing2\shared\common\src\main\kotlin\no\iktdev\mediaprocessing\shared\common\database\cal" />
|
<recent name="D:\Workspace\MediaProcessing2\shared\common\src\main\kotlin\no\iktdev\mediaprocessing\shared\common\database\cal" />
|
||||||
@ -392,11 +294,16 @@
|
|||||||
<recent name="no.iktdev.mediaprocessing.processer.ffmpeg" />
|
<recent name="no.iktdev.mediaprocessing.processer.ffmpeg" />
|
||||||
</key>
|
</key>
|
||||||
<key name="CopyKotlinDeclarationDialog.RECENTS_KEY">
|
<key name="CopyKotlinDeclarationDialog.RECENTS_KEY">
|
||||||
|
<recent name="no.iktdev.mediaprocessing.converter.listeners" />
|
||||||
|
<recent name="no.iktdev.mediaprocessing.converter" />
|
||||||
<recent name="no.iktdev.mediaprocessing.coordinator.tasksV2.listeners" />
|
<recent name="no.iktdev.mediaprocessing.coordinator.tasksV2.listeners" />
|
||||||
<recent name="no.iktdev.mediaprocessing.shared.common.database" />
|
<recent name="no.iktdev.mediaprocessing.shared.common.database" />
|
||||||
<recent name="no.iktdev.mediaprocessing.ui.service" />
|
<recent name="no.iktdev.mediaprocessing.ui.service" />
|
||||||
<recent name="no.iktdev.mediaprocessing.shared.common.database.cal" />
|
</key>
|
||||||
<recent name="no.iktdev.mediaprocessing.shared.common.contract" />
|
<key name="K2MoveDeclarationsDialog.RECENT_PACKAGE_KEY">
|
||||||
|
<recent name="no.iktdev.mediaprocessing.shared.common.event_task_contract" />
|
||||||
|
<recent name="no.iktdev.mediaprocessing.shared.common.database." />
|
||||||
|
<recent name="no.iktdev.mediaprocessing.shared.common.database.tables" />
|
||||||
</key>
|
</key>
|
||||||
</component>
|
</component>
|
||||||
<component name="RunAnythingCache">
|
<component name="RunAnythingCache">
|
||||||
@ -412,30 +319,8 @@
|
|||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="RunManager" selected="Gradle.Tests in 'MediaProcessing.apps.coordinator.test'">
|
<component name="RunManager" selected="Gradle.ConverterApplicationTest">
|
||||||
<configuration name="MediaProcessing2 [clean]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
<configuration name="ConverterApplicationTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="clean" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
<configuration name="Tests in 'MediaProcessing.apps.coordinator.test'" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
|
||||||
<ExternalSystemSettings>
|
<ExternalSystemSettings>
|
||||||
<option name="executionName" />
|
<option name="executionName" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
@ -446,31 +331,9 @@
|
|||||||
</option>
|
</option>
|
||||||
<option name="taskNames">
|
<option name="taskNames">
|
||||||
<list>
|
<list>
|
||||||
<option value=":apps:coordinator:test" />
|
<option value=":apps:converter:test" />
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>true</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
<configuration name="VideoArgumentsTest.hevcStream2" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value=":apps:coordinator:test" />
|
|
||||||
<option value="--tests" />
|
<option value="--tests" />
|
||||||
<option value=""no.iktdev.mediaprocessing.coordinator.tasksV2.mapping.streams.VideoArgumentsTest.hevcStream2"" />
|
<option value=""no.iktdev.mediaprocessing.converter.ConverterApplicationTest"" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
<option name="vmOptions" />
|
<option name="vmOptions" />
|
||||||
@ -481,7 +344,7 @@
|
|||||||
<RunAsTest>true</RunAsTest>
|
<RunAsTest>true</RunAsTest>
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
<configuration name="VideoArgumentsTest.vc1Stream" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
<configuration name="ConverterApplicationTest.Verify that we can access TaskStore" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||||
<ExternalSystemSettings>
|
<ExternalSystemSettings>
|
||||||
<option name="executionName" />
|
<option name="executionName" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
@ -492,9 +355,9 @@
|
|||||||
</option>
|
</option>
|
||||||
<option name="taskNames">
|
<option name="taskNames">
|
||||||
<list>
|
<list>
|
||||||
<option value=":apps:coordinator:test" />
|
<option value=":apps:converter:test" />
|
||||||
<option value="--tests" />
|
<option value="--tests" />
|
||||||
<option value=""no.iktdev.mediaprocessing.coordinator.tasksV2.mapping.streams.VideoArgumentsTest.vc1Stream"" />
|
<option value=""no.iktdev.mediaprocessing.converter.ConverterApplicationTest.Verify that we can access TaskStore"" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
<option name="vmOptions" />
|
<option name="vmOptions" />
|
||||||
@ -505,7 +368,7 @@
|
|||||||
<RunAsTest>true</RunAsTest>
|
<RunAsTest>true</RunAsTest>
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
<configuration name="VideoArgumentsTest.vc1Stream2" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
<configuration name="ConverterApplicationTest.context loads and common configuration is available" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||||
<ExternalSystemSettings>
|
<ExternalSystemSettings>
|
||||||
<option name="executionName" />
|
<option name="executionName" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
@ -516,9 +379,57 @@
|
|||||||
</option>
|
</option>
|
||||||
<option name="taskNames">
|
<option name="taskNames">
|
||||||
<list>
|
<list>
|
||||||
<option value=":apps:coordinator:test" />
|
<option value=":apps:converter:test" />
|
||||||
<option value="--tests" />
|
<option value="--tests" />
|
||||||
<option value=""no.iktdev.mediaprocessing.coordinator.tasksV2.mapping.streams.VideoArgumentsTest.vc1Stream2"" />
|
<option value=""no.iktdev.mediaprocessing.converter.ConverterApplicationTest.context loads and common configuration is available"" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="vmOptions" />
|
||||||
|
</ExternalSystemSettings>
|
||||||
|
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||||
|
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||||
|
<DebugAllEnabled>false</DebugAllEnabled>
|
||||||
|
<RunAsTest>true</RunAsTest>
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<configuration name="FlywayMigrationTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||||
|
<ExternalSystemSettings>
|
||||||
|
<option name="executionName" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="externalSystemIdString" value="GRADLE" />
|
||||||
|
<option name="scriptParameters" value="" />
|
||||||
|
<option name="taskDescriptions">
|
||||||
|
<list />
|
||||||
|
</option>
|
||||||
|
<option name="taskNames">
|
||||||
|
<list>
|
||||||
|
<option value=":shared:common:test" />
|
||||||
|
<option value="--tests" />
|
||||||
|
<option value=""no.iktdev.mediaprocessing.shared.common.FlywayMigrationTest"" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="vmOptions" />
|
||||||
|
</ExternalSystemSettings>
|
||||||
|
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||||
|
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||||
|
<DebugAllEnabled>false</DebugAllEnabled>
|
||||||
|
<RunAsTest>true</RunAsTest>
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<configuration name="FlywayMigrationTest.should run flyway migrations and create expected tables" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||||
|
<ExternalSystemSettings>
|
||||||
|
<option name="executionName" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="externalSystemIdString" value="GRADLE" />
|
||||||
|
<option name="scriptParameters" value="" />
|
||||||
|
<option name="taskDescriptions">
|
||||||
|
<list />
|
||||||
|
</option>
|
||||||
|
<option name="taskNames">
|
||||||
|
<list>
|
||||||
|
<option value=":shared:common:test" />
|
||||||
|
<option value="--tests" />
|
||||||
|
<option value=""no.iktdev.mediaprocessing.shared.common.FlywayMigrationTest.should run flyway migrations and create expected tables"" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
<option name="vmOptions" />
|
<option name="vmOptions" />
|
||||||
@ -530,20 +441,20 @@
|
|||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
<list>
|
<list>
|
||||||
<item itemvalue="Gradle.MediaProcessing2 [clean]" />
|
<item itemvalue="Gradle.ConverterApplicationTest.Verify that we can access TaskStore" />
|
||||||
<item itemvalue="Gradle.Tests in 'MediaProcessing.apps.coordinator.test'" />
|
<item itemvalue="Gradle.ConverterApplicationTest.context loads and common configuration is available" />
|
||||||
<item itemvalue="Gradle.VideoArgumentsTest.hevcStream2" />
|
<item itemvalue="Gradle.ConverterApplicationTest" />
|
||||||
<item itemvalue="Gradle.VideoArgumentsTest.vc1Stream" />
|
<item itemvalue="Gradle.FlywayMigrationTest" />
|
||||||
<item itemvalue="Gradle.VideoArgumentsTest.vc1Stream2" />
|
<item itemvalue="Gradle.FlywayMigrationTest.should run flyway migrations and create expected tables" />
|
||||||
<item itemvalue="Kotlin.UIApplicationKt" />
|
<item itemvalue="Kotlin.UIApplicationKt" />
|
||||||
</list>
|
</list>
|
||||||
<recent_temporary>
|
<recent_temporary>
|
||||||
<list>
|
<list>
|
||||||
<item itemvalue="Gradle.Tests in 'MediaProcessing.apps.coordinator.test'" />
|
<item itemvalue="Gradle.ConverterApplicationTest" />
|
||||||
<item itemvalue="Gradle.MediaProcessing2 [clean]" />
|
<item itemvalue="Gradle.FlywayMigrationTest.should run flyway migrations and create expected tables" />
|
||||||
<item itemvalue="Gradle.VideoArgumentsTest.vc1Stream2" />
|
<item itemvalue="Gradle.ConverterApplicationTest.Verify that we can access TaskStore" />
|
||||||
<item itemvalue="Gradle.VideoArgumentsTest.vc1Stream" />
|
<item itemvalue="Gradle.ConverterApplicationTest.context loads and common configuration is available" />
|
||||||
<item itemvalue="Gradle.VideoArgumentsTest.hevcStream2" />
|
<item itemvalue="Gradle.FlywayMigrationTest" />
|
||||||
</list>
|
</list>
|
||||||
</recent_temporary>
|
</recent_temporary>
|
||||||
</component>
|
</component>
|
||||||
@ -556,13 +467,6 @@
|
|||||||
<option name="presentableId" value="Default" />
|
<option name="presentableId" value="Default" />
|
||||||
<updated>1722029797420</updated>
|
<updated>1722029797420</updated>
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL−00117" summary="Removed SockJS">
|
|
||||||
<option name="closed" value="true" />
|
|
||||||
<created>1742149170986</created>
|
|
||||||
<option name="number" value="LOCAL−00117" />
|
|
||||||
<option name="presentableId" value="LOCAL−00117" />
|
|
||||||
<updated>1742149170987</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL−00118" summary="Updated UI + Indexing">
|
<task id="LOCAL−00118" summary="Updated UI + Indexing">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
<created>1742231127291</created>
|
<created>1742231127291</created>
|
||||||
@ -906,7 +810,15 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1755640931383</updated>
|
<updated>1755640931383</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="166" />
|
<task id="LOCAL-00166" summary="v5 init">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1762600047515</created>
|
||||||
|
<option name="number" value="00166" />
|
||||||
|
<option name="presentableId" value="LOCAL-00166" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1762600047516</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="167" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="Vcs.Log.Tabs.Properties">
|
<component name="Vcs.Log.Tabs.Properties">
|
||||||
@ -933,7 +845,6 @@
|
|||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="VcsManagerConfiguration">
|
<component name="VcsManagerConfiguration">
|
||||||
<MESSAGE value="Fixed created" />
|
|
||||||
<MESSAGE value="Changes to behaviour" />
|
<MESSAGE value="Changes to behaviour" />
|
||||||
<MESSAGE value="Trimming data in meta" />
|
<MESSAGE value="Trimming data in meta" />
|
||||||
<MESSAGE value="Fixes" />
|
<MESSAGE value="Fixes" />
|
||||||
@ -958,7 +869,8 @@
|
|||||||
<MESSAGE value="Updated ignore" />
|
<MESSAGE value="Updated ignore" />
|
||||||
<MESSAGE value="Info controller" />
|
<MESSAGE value="Info controller" />
|
||||||
<MESSAGE value="Publishing reason for failure" />
|
<MESSAGE value="Publishing reason for failure" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="Publishing reason for failure" />
|
<MESSAGE value="v5 init" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="v5 init" />
|
||||||
</component>
|
</component>
|
||||||
<component name="XDebuggerManager">
|
<component name="XDebuggerManager">
|
||||||
<breakpoint-manager>
|
<breakpoint-manager>
|
||||||
@ -1028,16 +940,6 @@
|
|||||||
<line>31</line>
|
<line>31</line>
|
||||||
<option name="timeStamp" value="43" />
|
<option name="timeStamp" value="43" />
|
||||||
</line-breakpoint>
|
</line-breakpoint>
|
||||||
<line-breakpoint enabled="true" type="kotlin-line">
|
|
||||||
<url>file://$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/DatabaseDeserializerTest.kt</url>
|
|
||||||
<line>18</line>
|
|
||||||
<option name="timeStamp" value="48" />
|
|
||||||
</line-breakpoint>
|
|
||||||
<line-breakpoint enabled="true" type="kotlin-line">
|
|
||||||
<url>file://$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/DatabaseDeserializerTest.kt</url>
|
|
||||||
<line>96</line>
|
|
||||||
<option name="timeStamp" value="49" />
|
|
||||||
</line-breakpoint>
|
|
||||||
<line-breakpoint enabled="true" type="kotlin-line">
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
<url>file://$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/explorer/ExplorerCore.kt</url>
|
<url>file://$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/explorer/ExplorerCore.kt</url>
|
||||||
<line>40</line>
|
<line>40</line>
|
||||||
@ -1279,6 +1181,85 @@
|
|||||||
<line>32</line>
|
<line>32</line>
|
||||||
<option name="timeStamp" value="189" />
|
<option name="timeStamp" value="189" />
|
||||||
</line-breakpoint>
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/shared/ffmpeg/src/main/kotlin/no/iktdev/mediaprocessing/ffmpeg/FFinfo.kt</url>
|
||||||
|
<line>20</line>
|
||||||
|
<option name="timeStamp" value="192" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/FlywayMigrationTest.kt</url>
|
||||||
|
<line>32</line>
|
||||||
|
<option name="timeStamp" value="194" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/FlywayMigrationTest.kt</url>
|
||||||
|
<line>41</line>
|
||||||
|
<option name="timeStamp" value="195" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/FlywayMigrationTest.kt</url>
|
||||||
|
<line>45</line>
|
||||||
|
<properties>
|
||||||
|
<option name="lambda-ordinal" value="-1" />
|
||||||
|
</properties>
|
||||||
|
<option name="timeStamp" value="196" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/FlywayMigrationTest.kt</url>
|
||||||
|
<line>52</line>
|
||||||
|
<option name="timeStamp" value="197" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/FlywayMigrationTest.kt</url>
|
||||||
|
<line>53</line>
|
||||||
|
<option name="timeStamp" value="198" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/stores/TaskStore.kt</url>
|
||||||
|
<line>20</line>
|
||||||
|
<option name="timeStamp" value="199" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/stores/TaskStore.kt</url>
|
||||||
|
<line>24</line>
|
||||||
|
<properties>
|
||||||
|
<option name="lambda-ordinal" value="-1" />
|
||||||
|
</properties>
|
||||||
|
<option name="timeStamp" value="201" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/stores/TaskStore.kt</url>
|
||||||
|
<line>31</line>
|
||||||
|
<option name="timeStamp" value="202" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/apps/converter/src/test/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplicationTest.kt</url>
|
||||||
|
<line>42</line>
|
||||||
|
<option name="timeStamp" value="203" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/apps/converter/src/test/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplicationTest.kt</url>
|
||||||
|
<line>40</line>
|
||||||
|
<option name="timeStamp" value="204" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/apps/converter/src/test/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplicationTest.kt</url>
|
||||||
|
<line>38</line>
|
||||||
|
<option name="timeStamp" value="205" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/apps/converter/src/test/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplicationTest.kt</url>
|
||||||
|
<line>37</line>
|
||||||
|
<option name="timeStamp" value="206" />
|
||||||
|
</line-breakpoint>
|
||||||
|
<line-breakpoint enabled="true" type="kotlin-line">
|
||||||
|
<url>file://$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/stores/TaskStore.kt</url>
|
||||||
|
<line>25</line>
|
||||||
|
<properties>
|
||||||
|
<option name="lambda-ordinal" value="-1" />
|
||||||
|
</properties>
|
||||||
|
<option name="timeStamp" value="208" />
|
||||||
|
</line-breakpoint>
|
||||||
</breakpoints>
|
</breakpoints>
|
||||||
</breakpoint-manager>
|
</breakpoint-manager>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@ -20,5 +20,5 @@ tasks.test {
|
|||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(17)
|
jvmToolchain(21)
|
||||||
}
|
}
|
||||||
@ -1,9 +1,8 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
kotlin("plugin.spring") version "1.5.31"
|
id("org.springframework.boot")
|
||||||
id("org.springframework.boot") version "2.5.5"
|
id("io.spring.dependency-management")
|
||||||
id("io.spring.dependency-management") version "1.0.11.RELEASE"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "no.iktdev.mediaprocessing.apps"
|
group = "no.iktdev.mediaprocessing.apps"
|
||||||
@ -27,40 +26,32 @@ repositories {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val exposedVersion = "0.44.0"
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
/*Spring boot*/
|
/*Spring boot*/
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
implementation("org.springframework.boot:spring-boot-starter:2.7.0")
|
implementation("org.springframework.boot:spring-boot-starter:2.7.0")
|
||||||
// implementation("org.springframework.kafka:spring-kafka:3.0.1")
|
|
||||||
implementation("org.springframework.boot:spring-boot-starter-websocket:2.6.3")
|
implementation("org.springframework.boot:spring-boot-starter-websocket:2.6.3")
|
||||||
implementation("org.springframework.kafka:spring-kafka:2.8.5")
|
|
||||||
|
|
||||||
|
|
||||||
implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
|
|
||||||
implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")
|
|
||||||
implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
|
|
||||||
implementation("org.jetbrains.exposed:exposed-java-time:$exposedVersion")
|
|
||||||
implementation ("mysql:mysql-connector-java:8.0.29")
|
|
||||||
|
|
||||||
implementation("io.github.microutils:kotlin-logging-jvm:2.0.11")
|
implementation("io.github.microutils:kotlin-logging-jvm:2.0.11")
|
||||||
implementation("com.google.code.gson:gson:2.8.9")
|
implementation("com.google.code.gson:gson:2.8.9")
|
||||||
implementation("org.json:json:20210307")
|
implementation("org.json:json:20210307")
|
||||||
|
|
||||||
implementation("no.iktdev:exfl:0.0.16-SNAPSHOT")
|
implementation("no.iktdev:exfl:0.0.16-SNAPSHOT")
|
||||||
implementation("no.iktdev.library:subtitle:1.8.1-SNAPSHOT")
|
implementation("no.iktdev.library:subtitle:1.8.1-SNAPSHOT")
|
||||||
|
implementation("no.iktdev:eventi:1.0-rc13")
|
||||||
|
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
||||||
implementation("com.github.vishna:watchservice-ktx:master-SNAPSHOT")
|
implementation("com.github.vishna:watchservice-ktx:master-SNAPSHOT")
|
||||||
implementation("com.github.pgreze:kotlin-process:1.4.1")
|
implementation("com.github.pgreze:kotlin-process:1.4.1")
|
||||||
|
|
||||||
implementation(project(mapOf("path" to ":shared:eventi")))
|
|
||||||
implementation(project(mapOf("path" to ":shared:common")))
|
implementation(project(mapOf("path" to ":shared:common")))
|
||||||
|
|
||||||
|
|
||||||
implementation(kotlin("stdlib-jdk8"))
|
implementation(kotlin("stdlib-jdk8"))
|
||||||
|
|
||||||
|
testImplementation("io.mockk:mockk:1.12.0")
|
||||||
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
@ -78,5 +69,5 @@ tasks.jar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(17)
|
jvmToolchain(21)
|
||||||
}
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
package no.iktdev.mediaprocessing.converter
|
||||||
|
|
||||||
|
import mu.KotlinLogging
|
||||||
|
import no.iktdev.eventi.events.EventTypeRegistry
|
||||||
|
import no.iktdev.eventi.tasks.TaskTypeRegistry
|
||||||
|
import no.iktdev.exfl.coroutines.CoroutinesDefault
|
||||||
|
import no.iktdev.exfl.coroutines.CoroutinesIO
|
||||||
|
import no.iktdev.exfl.observable.Observables
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.EventRegistry
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.TaskRegistry
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.DatabaseApplication
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.getAppVersion
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
import org.springframework.boot.runApplication
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
|
||||||
|
@SpringBootApplication(scanBasePackages = [
|
||||||
|
"no.iktdev.converter",
|
||||||
|
"no.iktdev.mediaprocessing.shared.common"
|
||||||
|
])
|
||||||
|
open class ConverterApplication: DatabaseApplication() {
|
||||||
|
}
|
||||||
|
|
||||||
|
val ioCoroutine = CoroutinesIO()
|
||||||
|
val defaultCoroutine = CoroutinesDefault()
|
||||||
|
|
||||||
|
private val log = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
ioCoroutine.addListener(listener = object: Observables.ObservableValue.ValueListener<Throwable> {
|
||||||
|
override fun onUpdated(value: Throwable) {
|
||||||
|
value.printStackTrace()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defaultCoroutine.addListener(listener = object: Observables.ObservableValue.ValueListener<Throwable> {
|
||||||
|
override fun onUpdated(value: Throwable) {
|
||||||
|
value.printStackTrace()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
runApplication<ConverterApplication>(*args)
|
||||||
|
log.info { "App Version: ${getAppVersion()}" }
|
||||||
|
}
|
||||||
|
//private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
open class ApplicationConfiguration() {
|
||||||
|
init {
|
||||||
|
EventRegistry.getEvents().let {
|
||||||
|
EventTypeRegistry.register(it)
|
||||||
|
}
|
||||||
|
TaskRegistry.getTasks().let {
|
||||||
|
TaskTypeRegistry.register(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package no.iktdev.mediaprocessing.converter
|
||||||
|
|
||||||
|
import no.iktdev.exfl.using
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class ConverterEnv {
|
||||||
|
companion object {
|
||||||
|
val allowOverwrite = System.getenv("ALLOW_OVERWRITE").toBoolean() ?: false
|
||||||
|
val syncDialogs = System.getenv("SYNC_DIALOGS").toBoolean()
|
||||||
|
val outFormats: List<String> = System.getenv("OUT_FORMATS")?.split(",")?.toList() ?: emptyList()
|
||||||
|
|
||||||
|
val logDirectory = if (!System.getenv("LOG_DIR").isNullOrBlank()) File(System.getenv("LOG_DIR")) else
|
||||||
|
File("data").using("logs", "convert")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package no.iktdev.mediaprocessing.converter.convert
|
||||||
|
|
||||||
|
interface ConvertListener {
|
||||||
|
fun onStarted(inputFile: String)
|
||||||
|
fun onCompleted(inputFile: String, outputFiles: List<String>)
|
||||||
|
fun onError(inputFile: String, message: String) {}
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
package no.iktdev.mediaprocessing.converter.convert
|
||||||
|
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks.Data
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks.SubtitleFormats
|
||||||
|
import no.iktdev.library.subtitle.Configuration
|
||||||
|
import no.iktdev.library.subtitle.Syncro
|
||||||
|
import no.iktdev.library.subtitle.classes.Dialog
|
||||||
|
import no.iktdev.library.subtitle.classes.DialogType
|
||||||
|
import no.iktdev.library.subtitle.export.Export
|
||||||
|
import no.iktdev.library.subtitle.reader.BaseReader
|
||||||
|
import no.iktdev.library.subtitle.reader.Reader
|
||||||
|
import no.iktdev.mediaprocessing.converter.ConverterEnv
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.jvm.Throws
|
||||||
|
|
||||||
|
class Converter2(val data: Data,
|
||||||
|
private val listener: ConvertListener) {
|
||||||
|
|
||||||
|
@Throws(FileUnavailableException::class)
|
||||||
|
private fun getReader(): BaseReader? {
|
||||||
|
val file = File(data.inputFile)
|
||||||
|
if (!file.canRead())
|
||||||
|
throw FileUnavailableException("Can't open file for reading..")
|
||||||
|
return Reader(file).getSubtitleReader()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun syncDialogs(input: List<Dialog>): List<Dialog> {
|
||||||
|
return if (ConverterEnv.syncDialogs) Syncro().sync(input) else input
|
||||||
|
}
|
||||||
|
|
||||||
|
fun canRead(): Boolean {
|
||||||
|
try {
|
||||||
|
val reader = getReader()
|
||||||
|
return reader != null
|
||||||
|
} catch (e: FileUnavailableException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(FileUnavailableException::class, FileIsNullOrEmpty::class)
|
||||||
|
fun execute() {
|
||||||
|
val file = File(data.inputFile)
|
||||||
|
listener.onStarted(file.absolutePath)
|
||||||
|
try {
|
||||||
|
Configuration.exportJson = true
|
||||||
|
val read = getReader()?.read() ?: throw FileIsNullOrEmpty()
|
||||||
|
if (read.isEmpty())
|
||||||
|
throw FileIsNullOrEmpty()
|
||||||
|
val filtered = read.filter { !it.ignore && it.type !in listOf(DialogType.SIGN_SONG, DialogType.CAPTION) }
|
||||||
|
val syncOrNotSync = syncDialogs(filtered)
|
||||||
|
|
||||||
|
val exporter = Export(file, File(data.outputDirectory), data.outputFileName)
|
||||||
|
|
||||||
|
val outFiles = if (data.formats.isEmpty()) {
|
||||||
|
exporter.write(syncOrNotSync)
|
||||||
|
} else {
|
||||||
|
val exported = mutableListOf<File>()
|
||||||
|
if (data.formats.contains(SubtitleFormats.SRT)) {
|
||||||
|
exported.add(exporter.writeSrt(syncOrNotSync))
|
||||||
|
}
|
||||||
|
if (data.formats.contains(SubtitleFormats.SMI)) {
|
||||||
|
exported.add(exporter.writeSmi(syncOrNotSync))
|
||||||
|
}
|
||||||
|
if (data.formats.contains(SubtitleFormats.VTT)) {
|
||||||
|
exported.add(exporter.writeVtt(syncOrNotSync))
|
||||||
|
}
|
||||||
|
exported
|
||||||
|
}
|
||||||
|
result = outFiles.map { it.absolutePath }
|
||||||
|
listener.onCompleted(file.absolutePath, result!!)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
listener.onError(file.absolutePath, e.message ?: e.localizedMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var result: List<String>? = null
|
||||||
|
fun getResult(): List<String> {
|
||||||
|
if (result == null) {
|
||||||
|
throw IllegalStateException("Execute must be called before getting result")
|
||||||
|
}
|
||||||
|
return result!!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FileIsNullOrEmpty(override val message: String? = "File read is null or empty"): RuntimeException()
|
||||||
|
class FileUnavailableException(override val message: String): RuntimeException()
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
package no.iktdev.mediaprocessing.converter.listeners
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
import no.iktdev.eventi.models.Task
|
||||||
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
|
import no.iktdev.eventi.tasks.TaskListener
|
||||||
|
import no.iktdev.eventi.tasks.TaskType
|
||||||
|
import no.iktdev.mediaprocessing.converter.convert.ConvertListener
|
||||||
|
import no.iktdev.mediaprocessing.converter.convert.Converter2
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.ConvertTaskPerformedEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.ConvertedData
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks.ConvertTask
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class ConvertTaskListener: TaskListener(TaskType.CPU_INTENSIVE) {
|
||||||
|
|
||||||
|
override fun getWorkerId(): String {
|
||||||
|
return "${this::class.java.simpleName}-${TaskType.CPU_INTENSIVE}-${UUID.randomUUID()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun supports(task: Task): Boolean {
|
||||||
|
return task is ConvertTask
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onTask(task: Task): Event? {
|
||||||
|
if (task !is ConvertTask) {
|
||||||
|
throw IllegalArgumentException("Invalid task type: ${task::class.java.name}")
|
||||||
|
}
|
||||||
|
val converter = Converter2(task.data, object: ConvertListener {
|
||||||
|
override fun onStarted(inputFile: String) {
|
||||||
|
}
|
||||||
|
override fun onCompleted(inputFile: String, outputFiles: List<String>) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
withHeartbeatRunner {
|
||||||
|
reporter?.updateLastSeen(task.taskId)
|
||||||
|
}
|
||||||
|
|
||||||
|
converter.execute()
|
||||||
|
|
||||||
|
return try {
|
||||||
|
val result = converter.getResult()
|
||||||
|
val newEvent = ConvertTaskPerformedEvent(
|
||||||
|
data = ConvertedData(
|
||||||
|
language = task.data.language,
|
||||||
|
outputFiles = result,
|
||||||
|
baseName = task.data.storeFileName
|
||||||
|
),
|
||||||
|
status = TaskStatus.Completed
|
||||||
|
).producedFrom(task)
|
||||||
|
newEvent
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
val newEvent = ConvertTaskPerformedEvent(
|
||||||
|
data = null,
|
||||||
|
status = TaskStatus.Failed
|
||||||
|
).producedFrom(task)
|
||||||
|
newEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,5 +0,0 @@
|
|||||||
spring.output.ansi.enabled=always
|
|
||||||
logging.level.org.apache.kafka=INFO
|
|
||||||
logging.level.root=INFO
|
|
||||||
logging.level.Exposed=OFF
|
|
||||||
logging.level.org.springframework.web.socket.config.WebSocketMessageBrokerStats = WARN
|
|
||||||
21
apps/converter/src/main/resources/application.yml
Normal file
21
apps/converter/src/main/resources/application.yml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
spring:
|
||||||
|
output:
|
||||||
|
ansi:
|
||||||
|
enabled: always
|
||||||
|
flyway:
|
||||||
|
enabled: true
|
||||||
|
locations: classpath:flyway
|
||||||
|
baseline-on-migrate: true
|
||||||
|
datasource:
|
||||||
|
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
|
||||||
|
driver-class-name: org.h2.Driver
|
||||||
|
username: sa
|
||||||
|
password:
|
||||||
|
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
root: INFO
|
||||||
|
org.apache.kafka: INFO
|
||||||
|
Exposed: OFF
|
||||||
|
org.springframework.web.socket.config.WebSocketMessageBrokerStats: WARN
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
package no.iktdev.mediaprocessing.converter
|
||||||
|
|
||||||
|
import io.mockk.junit5.MockKExtension
|
||||||
|
import mu.KotlinLogging
|
||||||
|
import no.iktdev.eventi.models.Task
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.stores.TaskStore
|
||||||
|
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest
|
||||||
|
import org.springframework.test.context.ActiveProfiles
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension
|
||||||
|
|
||||||
|
@SpringBootTest(classes = [ConverterApplication::class])
|
||||||
|
@ExtendWith(SpringExtension::class)
|
||||||
|
class ConverterApplicationTest {
|
||||||
|
private val log = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
data class TestTask(
|
||||||
|
val success: Boolean
|
||||||
|
) : Task()
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `context loads and common configuration is available`() {
|
||||||
|
// Hvis du har beans du vil verifisere, kan du autowire dem her
|
||||||
|
// @Autowired lateinit var database: Database
|
||||||
|
|
||||||
|
// Dummy assertion for å verifisere at konteksten starter
|
||||||
|
assertNotNull(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Verify that we can access TaskStore`() {
|
||||||
|
val tasks = TaskStore.getPendingTasks()
|
||||||
|
assertNotNull(tasks)
|
||||||
|
assert(tasks.isEmpty())
|
||||||
|
|
||||||
|
TaskStore.persist(TestTask(success = true).newReferenceId())
|
||||||
|
|
||||||
|
val tasksAfter = TaskStore.getPendingTasks()
|
||||||
|
assertNotNull(tasksAfter)
|
||||||
|
assert(tasksAfter.isNotEmpty())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +1,10 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
kotlin("plugin.spring") version "1.5.31"
|
kotlin("plugin.spring")
|
||||||
id("org.springframework.boot") version "3.2.0"
|
id("org.springframework.boot")
|
||||||
id("io.spring.dependency-management") version "1.1.4"
|
id("io.spring.dependency-management")
|
||||||
id("org.jetbrains.kotlin.plugin.serialization") version "1.5.0" // Legg til Kotlin Serialization-plugin
|
id("org.jetbrains.kotlin.plugin.serialization")
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "no.iktdev.mediaprocessing"
|
group = "no.iktdev.mediaprocessing"
|
||||||
@ -44,9 +44,8 @@ dependencies {
|
|||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
||||||
implementation("com.github.vishna:watchservice-ktx:master-SNAPSHOT")
|
implementation("com.github.vishna:watchservice-ktx:master-SNAPSHOT")
|
||||||
|
|
||||||
//implementation(project(mapOf("path" to ":shared")))
|
|
||||||
|
|
||||||
implementation(project(mapOf("path" to ":shared:eventi")))
|
implementation(project(mapOf("path" to ":shared:ffmpeg")))
|
||||||
implementation(project(mapOf("path" to ":shared:common")))
|
implementation(project(mapOf("path" to ":shared:common")))
|
||||||
|
|
||||||
implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
|
implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
|
||||||
@ -85,7 +84,7 @@ tasks.withType<Test> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(17)
|
jvmToolchain(21)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.bootJar {
|
tasks.bootJar {
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
kotlin("plugin.spring") version "1.5.31"
|
kotlin("plugin.spring")
|
||||||
id("org.springframework.boot") version "2.5.5"
|
id("org.springframework.boot")
|
||||||
id("io.spring.dependency-management") version "1.0.11.RELEASE"
|
id("io.spring.dependency-management")
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "no.iktdev.mediaprocessing.apps"
|
group = "no.iktdev.mediaprocessing.apps"
|
||||||
@ -49,7 +49,7 @@ dependencies {
|
|||||||
implementation("com.github.pgreze:kotlin-process:1.4.1")
|
implementation("com.github.pgreze:kotlin-process:1.4.1")
|
||||||
|
|
||||||
//implementation(project(mapOf("path" to ":shared")))
|
//implementation(project(mapOf("path" to ":shared")))
|
||||||
implementation(project(mapOf("path" to ":shared:eventi")))
|
implementation(project(mapOf("path" to ":shared:ffmpeg")))
|
||||||
implementation(project(mapOf("path" to ":shared:common")))
|
implementation(project(mapOf("path" to ":shared:common")))
|
||||||
|
|
||||||
|
|
||||||
@ -83,5 +83,5 @@ tasks.jar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(17)
|
jvmToolchain(21)
|
||||||
}
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
kotlin("plugin.spring") version "1.5.31"
|
kotlin("plugin.spring")
|
||||||
id("org.springframework.boot") version "2.5.5"
|
id("org.springframework.boot")
|
||||||
id("io.spring.dependency-management") version "1.0.11.RELEASE"
|
id("io.spring.dependency-management")
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "no.iktdev.mediaprocessing"
|
group = "no.iktdev.mediaprocessing"
|
||||||
@ -44,7 +44,6 @@ dependencies {
|
|||||||
implementation ("mysql:mysql-connector-java:8.0.29")
|
implementation ("mysql:mysql-connector-java:8.0.29")
|
||||||
|
|
||||||
implementation("no.iktdev:exfl:0.0.16-SNAPSHOT")
|
implementation("no.iktdev:exfl:0.0.16-SNAPSHOT")
|
||||||
implementation(project(mapOf("path" to ":shared:eventi")))
|
|
||||||
implementation(project(mapOf("path" to ":shared:common")))
|
implementation(project(mapOf("path" to ":shared:common")))
|
||||||
|
|
||||||
|
|
||||||
@ -56,7 +55,7 @@ tasks.test {
|
|||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(17)
|
jvmToolchain(21)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.bootJar {
|
tasks.bootJar {
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
kotlin("plugin.spring") version "1.5.31"
|
|
||||||
kotlin("jvm") version "2.1.0"
|
kotlin("jvm") version "2.1.0"
|
||||||
|
kotlin("plugin.spring") version "2.1.0"
|
||||||
|
id("org.jetbrains.kotlin.plugin.serialization") version "2.1.0"
|
||||||
|
id("org.springframework.boot") version "3.2.2"
|
||||||
|
id("io.spring.dependency-management") version "1.1.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "no.iktdev.mediaprocessing"
|
group = "no.iktdev.mediaprocessing"
|
||||||
@ -21,5 +24,5 @@ tasks.test {
|
|||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(17)
|
jvmToolchain(21)
|
||||||
}
|
}
|
||||||
@ -10,7 +10,7 @@ findProject(":apps:processer")?.name = "processer"
|
|||||||
|
|
||||||
|
|
||||||
findProject(":shared")?.name = "shared"
|
findProject(":shared")?.name = "shared"
|
||||||
findProject(":shared:eventi")?.name = "eventi"
|
findProject(":shared:ffmpeg")?.name = "ffmpeg"
|
||||||
findProject(":shared:common")?.name = "common"
|
findProject(":shared:common")?.name = "common"
|
||||||
|
|
||||||
include("apps")
|
include("apps")
|
||||||
@ -20,5 +20,7 @@ include("apps:converter")
|
|||||||
include("apps:processer")
|
include("apps:processer")
|
||||||
|
|
||||||
include("shared")
|
include("shared")
|
||||||
include("shared:eventi")
|
|
||||||
include("shared:common")
|
include("shared:common")
|
||||||
|
|
||||||
|
include("shared:ffmpeg")
|
||||||
|
include("shared:event-task-contract")
|
||||||
@ -45,5 +45,5 @@ tasks.test {
|
|||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(17)
|
jvmToolchain(21)
|
||||||
}
|
}
|
||||||
@ -1,7 +1,10 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
id("org.jetbrains.kotlin.plugin.serialization") version "1.5.0" // Legg til Kotlin Serialization-plugin
|
kotlin("plugin.spring")
|
||||||
|
id("org.jetbrains.kotlin.plugin.serialization")
|
||||||
|
id("org.springframework.boot")
|
||||||
|
id("io.spring.dependency-management")
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "no.iktdev.mediaprocessing.shared"
|
group = "no.iktdev.mediaprocessing.shared"
|
||||||
@ -11,14 +14,16 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven("https://jitpack.io")
|
maven("https://jitpack.io")
|
||||||
maven {
|
maven {
|
||||||
|
name = "ReposiliteReleases"
|
||||||
url = uri("https://reposilite.iktdev.no/releases")
|
url = uri("https://reposilite.iktdev.no/releases")
|
||||||
}
|
}
|
||||||
maven {
|
maven {
|
||||||
|
name = "ReposiliteSnapshot"
|
||||||
url = uri("https://reposilite.iktdev.no/snapshots")
|
url = uri("https://reposilite.iktdev.no/snapshots")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val exposedVersion = "0.44.0"
|
val exposedVersion = "0.61.0"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
@ -28,9 +33,11 @@ dependencies {
|
|||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
||||||
implementation("com.google.code.gson:gson:2.8.9")
|
implementation("com.google.code.gson:gson:2.8.9")
|
||||||
implementation("org.json:json:20230227")
|
implementation("org.json:json:20231013")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-websocket:2.6.3")
|
implementation("org.springframework.boot:spring-boot-starter-websocket:2.6.3")
|
||||||
implementation("org.springframework.kafka:spring-kafka:2.8.5")
|
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||||
|
implementation("org.flywaydb:flyway-core")
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")
|
||||||
|
|
||||||
@ -38,22 +45,33 @@ dependencies {
|
|||||||
implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")
|
implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")
|
||||||
implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
|
implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
|
||||||
implementation("org.jetbrains.exposed:exposed-java-time:$exposedVersion")
|
implementation("org.jetbrains.exposed:exposed-java-time:$exposedVersion")
|
||||||
implementation ("mysql:mysql-connector-java:8.0.29")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
implementation ("mysql:mysql-connector-java:8.0.33")
|
||||||
|
implementation("org.postgresql:postgresql:42.7.7")
|
||||||
|
implementation("org.xerial:sqlite-jdbc:3.43.2.0")
|
||||||
|
|
||||||
|
|
||||||
implementation("org.apache.commons:commons-lang3:3.12.0")
|
implementation("org.apache.commons:commons-lang3:3.12.0")
|
||||||
|
|
||||||
|
implementation("com.zaxxer:HikariCP:7.0.2")
|
||||||
|
|
||||||
implementation(project(mapOf("path" to ":shared:eventi")))
|
|
||||||
|
|
||||||
testImplementation(platform("org.junit:junit-bom:5.9.1"))
|
implementation("no.iktdev:eventi:1.0-rc13")
|
||||||
|
|
||||||
|
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
|
|
||||||
testImplementation("io.mockk:mockk:1.12.0")
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
testImplementation("com.h2database:h2:1.4.200")
|
|
||||||
testImplementation("org.assertj:assertj-core:3.4.1")
|
|
||||||
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.2")
|
testImplementation("io.mockk:mockk:1.12.0")
|
||||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.2")
|
implementation("com.h2database:h2:2.2.220")
|
||||||
testImplementation("io.kotlintest:kotlintest-assertions:3.3.2")
|
testImplementation("org.assertj:assertj-core:3.24.2")
|
||||||
|
|
||||||
|
testImplementation("io.kotest:kotest-assertions-core:5.7.2")
|
||||||
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0")
|
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0")
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -63,5 +81,5 @@ tasks.test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(17)
|
jvmToolchain(21)
|
||||||
}
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
import org.springframework.boot.runApplication
|
||||||
|
import org.springframework.context.annotation.ComponentScan
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@ComponentScan("no.iktdev.mediaprocessing") // sikrer at common beans blir plukket opp
|
||||||
|
abstract class DatabaseApplication {
|
||||||
|
companion object {
|
||||||
|
inline fun <reified T : DatabaseApplication> launch(args: Array<String>) {
|
||||||
|
runApplication<T>(*args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
package no.iktdev.mediaprocessing.shared.common
|
package no.iktdev.mediaprocessing.shared.common
|
||||||
|
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
|
import no.iktdev.eventi.ZDS
|
||||||
import org.springframework.messaging.simp.SimpMessagingTemplate
|
import org.springframework.messaging.simp.SimpMessagingTemplate
|
||||||
import org.springframework.web.client.RestTemplate
|
import org.springframework.web.client.RestTemplate
|
||||||
import org.springframework.web.client.postForEntity
|
import org.springframework.web.client.postForEntity
|
||||||
@ -10,6 +12,7 @@ import java.io.FileInputStream
|
|||||||
import java.io.RandomAccessFile
|
import java.io.RandomAccessFile
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
import java.time.LocalDateTime
|
||||||
import java.util.zip.CRC32
|
import java.util.zip.CRC32
|
||||||
|
|
||||||
private val logger = KotlinLogging.logger {}
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|||||||
@ -0,0 +1,51 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.database
|
||||||
|
|
||||||
|
import com.zaxxer.hikari.HikariConfig
|
||||||
|
import com.zaxxer.hikari.HikariDataSource
|
||||||
|
import org.jetbrains.exposed.sql.Database
|
||||||
|
import javax.sql.DataSource
|
||||||
|
|
||||||
|
object DatabaseConfig {
|
||||||
|
fun connect(
|
||||||
|
access: Access,
|
||||||
|
maxPoolSize: Int = 10
|
||||||
|
): Pair<Database, DataSource> {
|
||||||
|
val jdbcUrl = when (access.dbType) {
|
||||||
|
DbType.MySQL -> "jdbc:mysql://${access.address}:${access.port}/${access.databaseName}?useSSL=false&serverTimezone=UTC"
|
||||||
|
DbType.PostgreSQL -> "jdbc:postgresql://${access.address}:${access.port}/${access.databaseName}"
|
||||||
|
DbType.SQLite -> "jdbc:sqlite:${access.databaseName}.db"
|
||||||
|
DbType.H2 -> "jdbc:h2:mem:${access.databaseName};DB_CLOSE_DELAY=-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
val driver = when (access.dbType) {
|
||||||
|
DbType.MySQL -> "com.mysql.cj.jdbc.Driver"
|
||||||
|
DbType.PostgreSQL -> "org.postgresql.Driver"
|
||||||
|
DbType.SQLite -> "org.sqlite.JDBC"
|
||||||
|
DbType.H2 -> "org.h2.Driver"
|
||||||
|
}
|
||||||
|
|
||||||
|
val config = HikariConfig().apply {
|
||||||
|
this.jdbcUrl = jdbcUrl
|
||||||
|
this.driverClassName = driver
|
||||||
|
this.username = access.username
|
||||||
|
this.password = access.password
|
||||||
|
this.maximumPoolSize = maxPoolSize
|
||||||
|
this.isAutoCommit = false
|
||||||
|
this.transactionIsolation = "TRANSACTION_REPEATABLE_READ"
|
||||||
|
this.validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
val dataSource = HikariDataSource(config)
|
||||||
|
val db = Database.connect(dataSource)
|
||||||
|
return db to dataSource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Access(
|
||||||
|
val username: String,
|
||||||
|
val password: String,
|
||||||
|
val address: String,
|
||||||
|
val port: Int,
|
||||||
|
val databaseName: String,
|
||||||
|
val dbType: DbType
|
||||||
|
) {}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.database
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct
|
||||||
|
import org.flywaydb.core.Flyway
|
||||||
|
import org.jetbrains.exposed.sql.Database
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import javax.sql.DataSource
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
open class DatabaseConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun dataSource(): DataSource {
|
||||||
|
val access = Access(
|
||||||
|
username = "sa",
|
||||||
|
password = "",
|
||||||
|
address = "", // ikke brukt for H2
|
||||||
|
port = 0, // ikke brukt for H2
|
||||||
|
databaseName = "testdb",
|
||||||
|
dbType = DbType.H2
|
||||||
|
)
|
||||||
|
return DatabaseConfig.connect(access).second
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(FlywayProperties::class)
|
||||||
|
@ConditionalOnProperty(name = ["spring.flyway.enabled"], havingValue = "true", matchIfMissing = true)
|
||||||
|
class FlywayAutoConfig(
|
||||||
|
private val dataSource: DataSource,
|
||||||
|
private val props: FlywayProperties
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val log = LoggerFactory.getLogger(FlywayAutoConfig::class.java)
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
fun migrate() {
|
||||||
|
val locations = props.locations.ifEmpty { listOf("classpath:flyway") }
|
||||||
|
|
||||||
|
val flyway = Flyway.configure()
|
||||||
|
.dataSource(dataSource)
|
||||||
|
.locations(*locations.toTypedArray())
|
||||||
|
.baselineOnMigrate(true)
|
||||||
|
.load()
|
||||||
|
|
||||||
|
val pending = flyway.info().pending()
|
||||||
|
if (pending.isEmpty()) {
|
||||||
|
log.warn("⚠️ No pending Flyway migrations found in ${locations.joinToString()}")
|
||||||
|
} else {
|
||||||
|
log.info("📦 Pending migrations: ${pending.joinToString { it.script }}")
|
||||||
|
}
|
||||||
|
|
||||||
|
flyway.migrate()
|
||||||
|
log.info("✅ Flyway migration complete.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigurationProperties(prefix = "spring.flyway")
|
||||||
|
data class FlywayProperties(
|
||||||
|
var locations: List<String> = emptyList()
|
||||||
|
)
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.database
|
||||||
|
|
||||||
|
object DatabaseEnv {
|
||||||
|
val address: String? = System.getenv("DATABASE_ADDRESS")
|
||||||
|
val port: String? = System.getenv("DATABASE_PORT")
|
||||||
|
val username: String? = System.getenv("DATABASE_USERNAME")
|
||||||
|
val password: String? = System.getenv("DATABASE_PASSWORD")
|
||||||
|
val database: String? = System.getenv("DATABASE_NAME")
|
||||||
|
val databaseType: DbType = DbType.valueOf(System.getenv("DATABASE_TYPE") ?: "MySQL")
|
||||||
|
|
||||||
|
fun toAccess(): Access {
|
||||||
|
return Access(
|
||||||
|
username = username ?: "root",
|
||||||
|
password = password ?: "",
|
||||||
|
address = address ?: "localhost",
|
||||||
|
port = port?.toIntOrNull() ?: when (databaseType) {
|
||||||
|
DbType.MySQL -> 3306
|
||||||
|
DbType.PostgreSQL -> 5432
|
||||||
|
DbType.SQLite -> 0
|
||||||
|
DbType.H2 -> 0
|
||||||
|
},
|
||||||
|
databaseName = database ?: "mediaprocessing",
|
||||||
|
dbType = databaseType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class DbType {
|
||||||
|
MySQL, PostgreSQL, SQLite, H2
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.database
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
|
fun <T> withTransaction(
|
||||||
|
rollbackOnFailure: Boolean = false,
|
||||||
|
run: () -> T
|
||||||
|
): Result<T> {
|
||||||
|
return try {
|
||||||
|
val result = transaction {
|
||||||
|
try {
|
||||||
|
run()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (rollbackOnFailure) rollback()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Result.success(result)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
Result.failure(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.database.tables
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import org.jetbrains.exposed.sql.javatime.CurrentDateTime
|
||||||
|
import org.jetbrains.exposed.sql.javatime.datetime
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
object EventsTable: IntIdTable(name = "EVENTS") {
|
||||||
|
val referenceId: Column<UUID> = uuid("REFERENCE_ID")
|
||||||
|
val eventId: Column<UUID> = uuid("EVENT_ID")
|
||||||
|
val event: Column<String> = varchar("EVENT",100)
|
||||||
|
val data: Column<String> = text("DATA")
|
||||||
|
val persistedAt: Column<LocalDateTime> = datetime("PERSISTED_AT").defaultExpression(CurrentDateTime)
|
||||||
|
|
||||||
|
init {
|
||||||
|
uniqueIndex(referenceId, eventId, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.database.tables
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import org.jetbrains.exposed.sql.javatime.CurrentDateTime
|
||||||
|
import org.jetbrains.exposed.sql.javatime.datetime
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
object TasksTable: IntIdTable(name = "TASKS") {
|
||||||
|
val referenceId: Column<UUID> = uuid("REFERENCE_ID")
|
||||||
|
val taskId: Column<UUID> = uuid("TASK_ID")
|
||||||
|
val task: Column<String> = varchar("TASK",100)
|
||||||
|
val status: Column<TaskStatus> = enumerationByName("STATUS", 50, TaskStatus::class).default(TaskStatus.Pending)
|
||||||
|
val data: Column<String> = text("DATA")
|
||||||
|
val claimed: Column<Boolean> = bool("CLAIMED").default(false)
|
||||||
|
val claimedBy: Column<String?> = varchar("CLAIMED_BY",100).nullable()
|
||||||
|
val consumed: Column<Boolean> = bool("CONSUMED").default(false)
|
||||||
|
val lastCheckIn: Column<java.time.LocalDateTime?> = datetime("LAST_CHECK_IN").nullable()
|
||||||
|
val persistedAt: Column<java.time.LocalDateTime> = datetime("PERSISTED_AT").defaultExpression(CurrentDateTime)
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract
|
||||||
|
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.ConvertTaskCreatedEvents
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.ConvertTaskPerformedEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.FileAddedEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.FileReadyEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.FileRemovedEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.MediaParsedInfoEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.MetadataSearchTaskCreated
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.MetadataSearchTaskPerformed
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.ProcesserEncodePerformedEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.ProcesserEncodeTaskCreatedEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.ProcesserExtractTaskCreatedEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.ProcesserExtractedPerformedEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.ProcesserReadTaskCreatedEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.StartProcessingEvent
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
|
||||||
|
object EventRegistry {
|
||||||
|
fun getEvents(): List<Class<out Event>> {
|
||||||
|
return listOf(
|
||||||
|
ConvertTaskCreatedEvents::class.java,
|
||||||
|
ConvertTaskPerformedEvent::class.java,
|
||||||
|
|
||||||
|
FileAddedEvent::class.java,
|
||||||
|
FileReadyEvent::class.java,
|
||||||
|
FileRemovedEvent::class.java,
|
||||||
|
|
||||||
|
MediaParsedInfoEvent::class.java,
|
||||||
|
|
||||||
|
MetadataSearchTaskCreated::class.java,
|
||||||
|
MetadataSearchTaskPerformed::class.java,
|
||||||
|
|
||||||
|
ProcesserExtractTaskCreatedEvent::class.java,
|
||||||
|
ProcesserExtractedPerformedEvent::class.java,
|
||||||
|
|
||||||
|
ProcesserEncodeTaskCreatedEvent::class.java,
|
||||||
|
ProcesserEncodePerformedEvent::class.java,
|
||||||
|
|
||||||
|
ProcesserReadTaskCreatedEvent::class.java, // Do i need this?
|
||||||
|
|
||||||
|
StartProcessingEvent::class.java
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract
|
||||||
|
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks.ConvertTask
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks.EncodeTask
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks.ExtractTask
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks.MediaReadTask
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks.MetadataSearchTask
|
||||||
|
import no.iktdev.eventi.models.Task
|
||||||
|
|
||||||
|
object TaskRegistry {
|
||||||
|
fun getTasks(): List<Class<out Task>> {
|
||||||
|
return listOf(
|
||||||
|
ConvertTask::class.java,
|
||||||
|
EncodeTask::class.java,
|
||||||
|
ExtractTask::class.java,
|
||||||
|
MediaReadTask::class.java,
|
||||||
|
MetadataSearchTask::class.java
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
|
||||||
|
|
||||||
|
inline fun <reified T: Event> Event.az(): T? {
|
||||||
|
return if (this !is T) {
|
||||||
|
System.err.println("${this::class.java.name} is not a type of ${T::class.java.name}")
|
||||||
|
null
|
||||||
|
} else this
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
|
|
||||||
|
|
||||||
|
// Placeholder event, so that the listener does not continue to create tasks
|
||||||
|
class ConvertTaskCreatedEvents: Event() {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ConvertTaskPerformedEvent(
|
||||||
|
val data: ConvertedData?,
|
||||||
|
val status: TaskStatus,
|
||||||
|
): Event() {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ConvertedData(
|
||||||
|
val language: String,
|
||||||
|
val baseName: String,
|
||||||
|
val outputFiles: List<String>
|
||||||
|
)
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
|
||||||
|
data class CoverDownloadedEvent(
|
||||||
|
val data: CoverDownloadedData
|
||||||
|
): Event() {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class CoverDownloadedData(
|
||||||
|
val outputFile: String
|
||||||
|
)
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.DeleteEvent
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
|
||||||
|
data class FileAddedEvent(
|
||||||
|
val data: FileInfo
|
||||||
|
): Event() {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FileReadyEvent(
|
||||||
|
val data: FileInfo
|
||||||
|
): Event() {}
|
||||||
|
|
||||||
|
class FileRemovedEvent(
|
||||||
|
val data: FileInfo
|
||||||
|
): DeleteEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FileInfo(
|
||||||
|
val fileName: String,
|
||||||
|
val fileUri: String,
|
||||||
|
)
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
|
||||||
|
|
||||||
|
class MediaParsedInfoEvent(
|
||||||
|
val data: ParsedData
|
||||||
|
): Event() {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ParsedData(
|
||||||
|
val parsedTitle: String,
|
||||||
|
val parsedFileName: String,
|
||||||
|
val parsedSearchTitles: List<String>
|
||||||
|
) {
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
|
||||||
|
data class MediaReadyEvent(
|
||||||
|
val fileUri: String
|
||||||
|
): Event() {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.model.ParsedMediaStreams
|
||||||
|
|
||||||
|
data class MediaStreamParsedEvent(
|
||||||
|
val data: ParsedMediaStreams
|
||||||
|
): Event() {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
|
||||||
|
data class MediaStreamReadEvent(
|
||||||
|
val data: JsonObject
|
||||||
|
): Event() {
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
|
||||||
|
|
||||||
|
class MediaStreamReadTaskCreatedEvent(): Event() {
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
|
|
||||||
|
|
||||||
|
class MetadataSearchTaskCreated(): Event() {}
|
||||||
|
|
||||||
|
class MetadataSearchTaskPerformed(
|
||||||
|
val data: pyMetadata? = null,
|
||||||
|
val taskStatus: TaskStatus
|
||||||
|
): Event() {
|
||||||
|
init {
|
||||||
|
assert(taskStatus in listOf(TaskStatus.Completed, TaskStatus.Failed), { "Task status is not of acceptable state $taskStatus" })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class pyMetadata(
|
||||||
|
val title: String,
|
||||||
|
val altTitle: List<String> = emptyList(),
|
||||||
|
val cover: String? = null,
|
||||||
|
val type: String,
|
||||||
|
val summary: List<pySummary> = emptyList(),
|
||||||
|
val genres: List<String> = emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class pySummary(
|
||||||
|
val summary: String?,
|
||||||
|
val language: String = "eng"
|
||||||
|
)
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
|
|
||||||
|
// Placeholder event, so that the listener does not continue to create tasks
|
||||||
|
class ProcesserExtractTaskCreatedEvent: Event() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placeholder event, so that the listener does not continue to create tasks
|
||||||
|
class ProcesserEncodeTaskCreatedEvent: Event() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placeholder event, so that the listener does not continue to create tasks
|
||||||
|
class ProcesserReadTaskCreatedEvent: Event() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class ProcesserEncodePerformedEvent(
|
||||||
|
val data: EncodeResult
|
||||||
|
): Event() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class EncodeResult(
|
||||||
|
val status: TaskStatus,
|
||||||
|
val cachedOutputFile: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
data class ProcesserExtractedPerformedEvent(
|
||||||
|
val data: ExtractResult
|
||||||
|
): Event()
|
||||||
|
|
||||||
|
data class ExtractResult(
|
||||||
|
val status: TaskStatus,
|
||||||
|
val cachedOutputFile: String? = null
|
||||||
|
)
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.events
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
|
||||||
|
data class StartProcessingEvent(
|
||||||
|
val data: StartData
|
||||||
|
): Event() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class StartData(
|
||||||
|
val operation: Set<OperationType>,
|
||||||
|
val flow: ProcessFlow = ProcessFlow.Auto,
|
||||||
|
val fileUri: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class ProcessFlow {
|
||||||
|
Auto,
|
||||||
|
Manual
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class OperationType {
|
||||||
|
Extract,
|
||||||
|
Encode,
|
||||||
|
Convert
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Task
|
||||||
|
|
||||||
|
data class ConvertTask(
|
||||||
|
val data: Data
|
||||||
|
): Task() {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Data(
|
||||||
|
val inputFile: String,
|
||||||
|
val language: String,
|
||||||
|
val outputDirectory: String,
|
||||||
|
val outputFileName: String,
|
||||||
|
val storeFileName: String,
|
||||||
|
val formats: List<SubtitleFormats> = emptyList(),
|
||||||
|
val allowOverwrite: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
enum class SubtitleFormats {
|
||||||
|
ASS,
|
||||||
|
SRT,
|
||||||
|
VTT,
|
||||||
|
SMI
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Task
|
||||||
|
|
||||||
|
data class CoverDownloadTask(
|
||||||
|
val data: CoverDownloadData
|
||||||
|
): Task() {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class CoverDownloadData(val url: String, val outputFile: String)
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Task
|
||||||
|
|
||||||
|
data class EncodeTask(
|
||||||
|
val data: EncodeData
|
||||||
|
): Task() {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class EncodeData(
|
||||||
|
val arguments: List<String>,
|
||||||
|
val outputFile: String,
|
||||||
|
val inputFile: String
|
||||||
|
)
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Task
|
||||||
|
|
||||||
|
data class ExtractTask(
|
||||||
|
val data: ExtractData
|
||||||
|
): Task() {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ExtractData(
|
||||||
|
val arguments: List<String>,
|
||||||
|
val outputFileName: String,
|
||||||
|
val language: String,
|
||||||
|
val inputFile: String
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Task
|
||||||
|
|
||||||
|
// Task for reading and parsing media files
|
||||||
|
class MediaReadTask(val fileUri: String): Task() {
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.event_task_contract.tasks
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Task
|
||||||
|
|
||||||
|
data class MetadataSearchTask(
|
||||||
|
val data: MetadataSearchData
|
||||||
|
): Task() {}
|
||||||
|
|
||||||
|
data class MetadataSearchData(
|
||||||
|
val searchString: String,
|
||||||
|
val maxResults: Int = 10,
|
||||||
|
val offset: Int = 0,
|
||||||
|
)
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.model
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
|
||||||
|
data class EpisodeInfo(
|
||||||
|
override val type: String = "serie",
|
||||||
|
override val title: String,
|
||||||
|
val episode: Int,
|
||||||
|
val season: Int,
|
||||||
|
val episodeTitle: String?,
|
||||||
|
override val fullName: String
|
||||||
|
): MediaInfo(type, title, fullName)
|
||||||
|
|
||||||
|
data class MovieInfo(
|
||||||
|
override val type: String = "movie",
|
||||||
|
override val title: String,
|
||||||
|
override val fullName: String
|
||||||
|
) : MediaInfo(type, title, fullName)
|
||||||
|
|
||||||
|
data class SubtitleInfo(
|
||||||
|
val inputFile: String,
|
||||||
|
val collection: String,
|
||||||
|
val language: String
|
||||||
|
)
|
||||||
|
|
||||||
|
open class MediaInfo(
|
||||||
|
@Transient open val type: String,
|
||||||
|
@Transient open val title: String,
|
||||||
|
@Transient open val fullName: String
|
||||||
|
) {
|
||||||
|
fun toJsonObject(): JsonObject {
|
||||||
|
return Gson().toJsonTree(this).asJsonObject
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,191 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.model
|
||||||
|
|
||||||
|
data class ParsedMediaStreams(
|
||||||
|
val videoStream: List<VideoStream> = listOf(),
|
||||||
|
val audioStream: List<AudioStream> = listOf(),
|
||||||
|
val subtitleStream: List<SubtitleStream> = listOf()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MediaStreams(
|
||||||
|
val streams: List<Stream>
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed class Stream(
|
||||||
|
@Transient open val index: Int,
|
||||||
|
@Transient open val codec_name: String,
|
||||||
|
@Transient open val codec_long_name: String,
|
||||||
|
@Transient open val codec_type: String,
|
||||||
|
@Transient open val codec_tag_string: String,
|
||||||
|
@Transient open val codec_tag: String,
|
||||||
|
@Transient open val r_frame_rate: String,
|
||||||
|
@Transient open val avg_frame_rate: String,
|
||||||
|
@Transient open val time_base: String,
|
||||||
|
@Transient open val start_pts: Long,
|
||||||
|
@Transient open val start_time: String,
|
||||||
|
@Transient open val duration_ts: Long? = null,
|
||||||
|
@Transient open val duration: String? = null,
|
||||||
|
@Transient open val disposition: Disposition? = null,
|
||||||
|
@Transient open val tags: Tags
|
||||||
|
)
|
||||||
|
|
||||||
|
data class VideoStream(
|
||||||
|
override val index: Int,
|
||||||
|
override val codec_name: String,
|
||||||
|
override val codec_long_name: String,
|
||||||
|
override val codec_type: String,
|
||||||
|
override val codec_tag_string: String,
|
||||||
|
override val codec_tag: String,
|
||||||
|
override val r_frame_rate: String,
|
||||||
|
override val avg_frame_rate: String,
|
||||||
|
override val time_base: String,
|
||||||
|
override val start_pts: Long,
|
||||||
|
override val start_time: String,
|
||||||
|
override val disposition: Disposition,
|
||||||
|
override val tags: Tags,
|
||||||
|
override val duration: String?,
|
||||||
|
override val duration_ts: Long?,
|
||||||
|
val profile: String,
|
||||||
|
val width: Int,
|
||||||
|
val height: Int,
|
||||||
|
val coded_width: Int,
|
||||||
|
val coded_height: Int,
|
||||||
|
val closed_captions: Int,
|
||||||
|
val has_b_frames: Int,
|
||||||
|
val sample_aspect_ratio: String,
|
||||||
|
val display_aspect_ratio: String,
|
||||||
|
val pix_fmt: String,
|
||||||
|
val level: Int,
|
||||||
|
val color_range: String,
|
||||||
|
val color_space: String,
|
||||||
|
val color_transfer: String,
|
||||||
|
val color_primaries: String,
|
||||||
|
val chroma_location: String,
|
||||||
|
val refs: Int
|
||||||
|
) : Stream(
|
||||||
|
index,
|
||||||
|
codec_name,
|
||||||
|
codec_long_name,
|
||||||
|
codec_type,
|
||||||
|
codec_tag_string,
|
||||||
|
codec_tag,
|
||||||
|
r_frame_rate,
|
||||||
|
avg_frame_rate,
|
||||||
|
time_base,
|
||||||
|
start_pts,
|
||||||
|
start_time,
|
||||||
|
duration_ts,
|
||||||
|
duration,
|
||||||
|
disposition,
|
||||||
|
tags
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AudioStream(
|
||||||
|
override val index: Int,
|
||||||
|
override val codec_name: String,
|
||||||
|
override val codec_long_name: String,
|
||||||
|
override val codec_type: String,
|
||||||
|
override val codec_tag_string: String,
|
||||||
|
override val codec_tag: String,
|
||||||
|
override val r_frame_rate: String,
|
||||||
|
override val avg_frame_rate: String,
|
||||||
|
override val time_base: String,
|
||||||
|
override val start_pts: Long,
|
||||||
|
override val start_time: String,
|
||||||
|
override val duration: String?,
|
||||||
|
override val duration_ts: Long?,
|
||||||
|
override val disposition: Disposition,
|
||||||
|
override val tags: Tags,
|
||||||
|
val profile: String,
|
||||||
|
val sample_fmt: String,
|
||||||
|
val sample_rate: String,
|
||||||
|
val channels: Int,
|
||||||
|
val channel_layout: String,
|
||||||
|
val bits_per_sample: Int
|
||||||
|
) : Stream(
|
||||||
|
index,
|
||||||
|
codec_name,
|
||||||
|
codec_long_name,
|
||||||
|
codec_type,
|
||||||
|
codec_tag_string,
|
||||||
|
codec_tag,
|
||||||
|
r_frame_rate,
|
||||||
|
avg_frame_rate,
|
||||||
|
time_base,
|
||||||
|
start_pts,
|
||||||
|
start_time,
|
||||||
|
duration_ts,
|
||||||
|
duration,
|
||||||
|
disposition,
|
||||||
|
tags
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SubtitleStream(
|
||||||
|
override val index: Int,
|
||||||
|
override val codec_name: String,
|
||||||
|
override val codec_long_name: String,
|
||||||
|
override val codec_type: String,
|
||||||
|
override val codec_tag_string: String,
|
||||||
|
override val codec_tag: String,
|
||||||
|
override val r_frame_rate: String,
|
||||||
|
override val avg_frame_rate: String,
|
||||||
|
override val time_base: String,
|
||||||
|
override val start_pts: Long,
|
||||||
|
override val start_time: String,
|
||||||
|
override val duration: String?,
|
||||||
|
override val duration_ts: Long?,
|
||||||
|
override val disposition: Disposition? = null,
|
||||||
|
override val tags: Tags,
|
||||||
|
val subtitle_tags: SubtitleTags
|
||||||
|
) : Stream(
|
||||||
|
index,
|
||||||
|
codec_name,
|
||||||
|
codec_long_name,
|
||||||
|
codec_type,
|
||||||
|
codec_tag_string,
|
||||||
|
codec_tag,
|
||||||
|
r_frame_rate,
|
||||||
|
avg_frame_rate,
|
||||||
|
time_base,
|
||||||
|
start_pts,
|
||||||
|
start_time,
|
||||||
|
duration_ts,
|
||||||
|
duration,
|
||||||
|
disposition,
|
||||||
|
tags
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Disposition(
|
||||||
|
val default: Int,
|
||||||
|
val dub: Int,
|
||||||
|
val original: Int,
|
||||||
|
val comment: Int,
|
||||||
|
val lyrics: Int,
|
||||||
|
val karaoke: Int,
|
||||||
|
val forced: Int,
|
||||||
|
val hearing_impaired: Int,
|
||||||
|
val captions: Int,
|
||||||
|
val visual_impaired: Int,
|
||||||
|
val clean_effects: Int,
|
||||||
|
val attached_pic: Int,
|
||||||
|
val timed_thumbnails: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Tags(
|
||||||
|
val title: String?,
|
||||||
|
val BPS: String?,
|
||||||
|
val DURATION: String?,
|
||||||
|
val NUMBER_OF_FRAMES: Int? = 0,
|
||||||
|
val NUMBER_OF_BYTES: String?,
|
||||||
|
val _STATISTICS_WRITING_APP: String?,
|
||||||
|
val _STATISTICS_WRITING_DATE_UTC: String?,
|
||||||
|
val _STATISTICS_TAGS: String?,
|
||||||
|
val language: String?,
|
||||||
|
val filename: String?,
|
||||||
|
val mimetype: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SubtitleTags(
|
||||||
|
val language: String?,
|
||||||
|
val filename: String?,
|
||||||
|
val mimetype: String?
|
||||||
|
)
|
||||||
@ -0,0 +1,177 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.parsing
|
||||||
|
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.model.EpisodeInfo
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.model.MediaInfo
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.model.MovieInfo
|
||||||
|
|
||||||
|
|
||||||
|
class FileNameDeterminate(val title: String, val sanitizedName: String, val ctype: ContentType = ContentType.UNDEFINED) {
|
||||||
|
|
||||||
|
enum class ContentType {
|
||||||
|
MOVIE,
|
||||||
|
SERIE,
|
||||||
|
UNDEFINED
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDeterminedVideoInfo(): MediaInfo? {
|
||||||
|
return when (ctype) {
|
||||||
|
ContentType.MOVIE -> determineMovieFileName()
|
||||||
|
ContentType.SERIE -> determineSerieFileName()
|
||||||
|
ContentType.UNDEFINED -> determineUndefinedFileName()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun determineMovieFileName(): MovieInfo? {
|
||||||
|
val movieEx = MovieEx(title, sanitizedName)
|
||||||
|
val stripped = when {
|
||||||
|
movieEx.isDefinedWithYear() -> sanitizedName.replace(movieEx.yearRegex(), "").trim()
|
||||||
|
movieEx.doesContainMovieKeywords() -> sanitizedName.replace(Regex("(?i)\\s*\\(\\s*movie\\s*\\)\\s*"), "").trim()
|
||||||
|
else -> sanitizedName
|
||||||
|
}
|
||||||
|
val nonResolutioned = movieEx.removeResolutionAndBeyond(stripped) ?: stripped
|
||||||
|
return MovieInfo(title = cleanup(nonResolutioned), fullName = cleanup(nonResolutioned))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun determineSerieFileName(): EpisodeInfo? {
|
||||||
|
val serieEx = SerieEx(title, sanitizedName)
|
||||||
|
|
||||||
|
val (season, episode) = serieEx.findSeasonAndEpisode(sanitizedName)
|
||||||
|
val episodeNumberSingle = serieEx.findEpisodeNumber()
|
||||||
|
|
||||||
|
val seasonNumber = season ?: "1"
|
||||||
|
val episodeNumber = episode ?: (episodeNumberSingle ?: return null)
|
||||||
|
val seasonEpisodeCombined = serieEx.getSeasonEpisodeCombined(seasonNumber, episodeNumber)
|
||||||
|
val episodeTitle = serieEx.findEpisodeTitle()
|
||||||
|
|
||||||
|
val useTitle = if (title == sanitizedName) {
|
||||||
|
if (title.contains(" - ")) {
|
||||||
|
title.split(" - ").firstOrNull() ?: title
|
||||||
|
} else {
|
||||||
|
val seasonNumberIndex = if (title.indexOf(seasonNumber) < 0) title.length -1 else title.indexOf(seasonNumber)
|
||||||
|
val episodeNumberIndex = if (title.indexOf(episodeNumber) < 0) title.length -1 else title.indexOf(episodeNumber)
|
||||||
|
val closest = listOf<Int>(seasonNumberIndex, episodeNumberIndex).min()
|
||||||
|
val shrunkenTitle = title.substring(0, closest)
|
||||||
|
if (closest - shrunkenTitle.lastIndexOf(" ") < 3) {
|
||||||
|
title.substring(0, shrunkenTitle.lastIndexOf(" "))
|
||||||
|
} else title.substring(0, closest)
|
||||||
|
|
||||||
|
}
|
||||||
|
} else title
|
||||||
|
val fullName = "${useTitle.trim()} - $seasonEpisodeCombined ${if (episodeTitle.isNullOrEmpty()) "" else " - $episodeTitle"}".trim()
|
||||||
|
return EpisodeInfo(
|
||||||
|
title = title,
|
||||||
|
episode = episodeNumber.toInt(),
|
||||||
|
season = seasonNumber.toInt(),
|
||||||
|
episodeTitle = episodeTitle,
|
||||||
|
fullName = cleanup(fullName)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun determineUndefinedFileName(): MediaInfo? {
|
||||||
|
val serieEx = SerieEx(title, sanitizedName)
|
||||||
|
val (season, episode) = serieEx.findSeasonAndEpisode(sanitizedName)
|
||||||
|
val episodeNumber = serieEx.findEpisodeNumber()
|
||||||
|
return if ((sanitizedName.contains(" - ") && episodeNumber != null) || season != null || episode != null) {
|
||||||
|
determineSerieFileName()
|
||||||
|
} else {
|
||||||
|
determineMovieFileName()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanup(input: String): String {
|
||||||
|
var cleaned = Regex("(?<=\\w)[_.](?=\\w)").replace(input, " ")
|
||||||
|
cleaned = Regexes.illegalCharacters.replace(cleaned, " - ")
|
||||||
|
cleaned = Regexes.trimWhiteSpaces.replace(cleaned, " ")
|
||||||
|
return NameHelper.normalize(cleaned)
|
||||||
|
}
|
||||||
|
|
||||||
|
open internal class Base(val title: String, val sanitizedName: String) {
|
||||||
|
fun getMatch(regex: String): String? {
|
||||||
|
return Regex(regex, RegexOption.IGNORE_CASE).find(sanitizedName)?.value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeResolutionAndBeyond(input: String): String? {
|
||||||
|
val removalValue = Regex("(i?)([0-9].*[pk]|[ ._-]+[UHD]+[ ._-])").find(input)?.value ?: return null
|
||||||
|
return input.substring(0, input.indexOf(removalValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun yearRegex(): Regex {
|
||||||
|
return Regex("[ .(][0-9]{4}[ .)]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MovieEx(title: String, sanitizedName: String) : Base(title, sanitizedName) {
|
||||||
|
/**
|
||||||
|
* @return not null if matches " 2020 " or ".2020."
|
||||||
|
*/
|
||||||
|
fun isDefinedWithYear(): Boolean {
|
||||||
|
return getMatch(yearRegex().pattern)?.isNotBlank() ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the filename contains the keyword movie, if so, default to movie
|
||||||
|
*/
|
||||||
|
fun doesContainMovieKeywords(): Boolean {
|
||||||
|
return getMatch("[(](?<=\\()movie(?=\\))[)]")?.isNotBlank() ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class SerieEx(title: String, sanitizedName: String) : Base(title, sanitizedName) {
|
||||||
|
|
||||||
|
fun getSeasonEpisodeCombined(season: String, episode: String): String {
|
||||||
|
return StringBuilder()
|
||||||
|
.append("S")
|
||||||
|
.append(if (season.length < 2) season.padStart(2, '0') else season)
|
||||||
|
.append("E")
|
||||||
|
.append(if (episode.length < 2) episode.padStart(2, '0') else episode)
|
||||||
|
.toString().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sjekken matcher tekst som dette:
|
||||||
|
* Cool - Season 1 Episode 13
|
||||||
|
* Cool - s1e13
|
||||||
|
* Cool - S1E13
|
||||||
|
* Cool - S1 13
|
||||||
|
*/
|
||||||
|
fun findSeasonAndEpisode(inputText: String): Pair<String?, String?> {
|
||||||
|
val matchResult = Regexes.SeasonEpisodeBlock.find(inputText)
|
||||||
|
val season = matchResult?.groups?.get(1)?.value
|
||||||
|
val episode = matchResult?.groups?.get(2)?.value
|
||||||
|
return season to episode
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findEpisodeNumber(): String? {
|
||||||
|
val regex = Regex("\\b(\\d+)\\b")
|
||||||
|
val matchResult = regex.findAll(sanitizedName)
|
||||||
|
val usabeNumber = if (matchResult.toList().size > 1) {
|
||||||
|
Regex("[-_] \\b(\\d+)\\b").find(sanitizedName)?.groups?.lastOrNull()?.value
|
||||||
|
} else {
|
||||||
|
matchResult.lastOrNull()?.value
|
||||||
|
}
|
||||||
|
return usabeNumber?.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findEpisodeTitle(): String? {
|
||||||
|
var startPosition: Int = 0
|
||||||
|
startPosition = Regexes.SeasonEpisodeBlock.find(sanitizedName)?.value?.let { block ->
|
||||||
|
sanitizedName.indexOf(block) + block.length
|
||||||
|
} ?: 0
|
||||||
|
|
||||||
|
val seCombo = findSeasonAndEpisode(sanitizedName)
|
||||||
|
val episodeNumber = findEpisodeNumber()
|
||||||
|
|
||||||
|
startPosition = if (startPosition != 0) startPosition else if (seCombo.second != null) sanitizedName.indexOf(seCombo.second!!)+ seCombo.second!!.length
|
||||||
|
else if (episodeNumber != null) sanitizedName.indexOf(episodeNumber) + episodeNumber.length else 0
|
||||||
|
val availableText = sanitizedName.substring(startPosition)
|
||||||
|
|
||||||
|
val cleanedEpisodeTitle = availableText.replace(Regex("""(?i)\b(?:season|episode|ep)\b"""), "")
|
||||||
|
.replace(Regex("""^\s*-\s*"""), "")
|
||||||
|
.replace(Regex("""\s+"""), " ")
|
||||||
|
.trim()
|
||||||
|
|
||||||
|
return cleanedEpisodeTitle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,146 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.parsing
|
||||||
|
|
||||||
|
|
||||||
|
class FileNameParser(val fileName: String) {
|
||||||
|
var cleanedFileName: String
|
||||||
|
private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
cleanedFileName = removeBracketedText(fileName)
|
||||||
|
cleanedFileName = removeParenthesizedText(cleanedFileName)
|
||||||
|
cleanedFileName = removeResolutionAndTrailing(cleanedFileName)
|
||||||
|
cleanedFileName = removeResolutionAndTags(cleanedFileName)
|
||||||
|
cleanedFileName = removeParenthesizedText(cleanedFileName)
|
||||||
|
cleanedFileName = removeYear(cleanedFileName)
|
||||||
|
cleanedFileName = removeDot(cleanedFileName)
|
||||||
|
cleanedFileName = removeExtraWhiteSpace(cleanedFileName)
|
||||||
|
cleanedFileName = removeTrailingAndLeadingCharacters(cleanedFileName).trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun guessDesiredFileName(): String {
|
||||||
|
val parts = cleanedFileName.split(" - ")
|
||||||
|
return when {
|
||||||
|
parts.size == 2 && parts[1].matches(Regex("\\d{4}")) -> {
|
||||||
|
val title = parts[0]
|
||||||
|
val year = parts[1]
|
||||||
|
"$title ($year)"
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.size >= 3 && parts[1].matches(Regex("S\\d+")) && parts[2].matches(Regex("\\d+[vV]\\d+")) -> {
|
||||||
|
val title = parts[0]
|
||||||
|
val episodeWithRevision = parts[2]
|
||||||
|
val episodeParts = episodeWithRevision.split("v", "V")
|
||||||
|
val episodeNumber = episodeParts[0].toInt()
|
||||||
|
val revisionNumber = episodeParts[1].toInt()
|
||||||
|
val seasonEpisode =
|
||||||
|
"S${episodeNumber.toString().padStart(2, '0')}E${revisionNumber.toString().padStart(2, '0')}"
|
||||||
|
val episodeTitle = if (parts.size > 3) parts[3] else ""
|
||||||
|
"$title - $seasonEpisode - $episodeTitle"
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> cleanedFileName
|
||||||
|
}.trim()
|
||||||
|
.replace(Regex("[-\\s]+$"), "") // fjern trailing "-" og whitespace
|
||||||
|
}
|
||||||
|
|
||||||
|
fun guessDesiredTitle(): String {
|
||||||
|
val desiredFileName = guessDesiredFileName()
|
||||||
|
return if (Regexes.season.containsMatchIn(desiredFileName)) {
|
||||||
|
Regexes.season.split(desiredFileName).firstOrNull()?.trim() ?: desiredFileName
|
||||||
|
} else {
|
||||||
|
val result = if (desiredFileName.contains(" - ")) {
|
||||||
|
desiredFileName.split(" - ").firstOrNull() ?: desiredFileName
|
||||||
|
} else desiredFileName
|
||||||
|
result.trim()
|
||||||
|
}.trim('.', '-').trim()
|
||||||
|
.replace(Regex("[-\\s]+$"), "") // fjern trailing "-" og whitespace
|
||||||
|
}
|
||||||
|
|
||||||
|
fun guessSearchableTitle(): MutableList<String> {
|
||||||
|
var cleaned = removeBracketedText(fileName)
|
||||||
|
cleaned = keepParanthesesWithYear(cleaned)
|
||||||
|
cleaned = removeResolutionAndTrailing(cleaned)
|
||||||
|
cleaned = removeResolutionAndTags(cleaned)
|
||||||
|
cleaned = removeDot(cleaned)
|
||||||
|
|
||||||
|
val titles = mutableListOf<String>()
|
||||||
|
var ch = cleaned.split('-').firstOrNull() ?: cleaned
|
||||||
|
ch = removeExtraWhiteSpace(ch)
|
||||||
|
ch = ch.trim('.', ',', ' ')
|
||||||
|
titles.add(ch)
|
||||||
|
|
||||||
|
return titles
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the input value and removes "[Text]"
|
||||||
|
* @param text "[TEST] Dummy - 01 [AZ 1080p] "
|
||||||
|
*/
|
||||||
|
fun removeBracketedText(text: String): String {
|
||||||
|
return Regex("\\[.*?]").replace(text, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun removeParenthesizedText(text: String): String {
|
||||||
|
return Regex("\\(.*?\\)").replace(text, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeResolutionAndTrailing(text: String): String {
|
||||||
|
return Regex("[0-9]+[pP].*").replace(text, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeTrailingAndLeadingCharacters(text: String): String {
|
||||||
|
return Regex("^[^a-zA-Z0-9!,]+|[^a-zA-Z0-9!~,]+\$").replace(text, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun removeResolutionAndTags(input: String): String {
|
||||||
|
var text = Regex("(?i)(\\d+[pk]\\b|(hd|uhd))", RegexOption.IGNORE_CASE).replace(input, " ")
|
||||||
|
text = Regex("(?i)(bluray|laserdisc|dvd|web)", RegexOption.IGNORE_CASE).replace(text, " ")
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun keepParanthesesWithYear(text: String): String {
|
||||||
|
val regex = "\\((?!\\d{4}\\))(?>[^()]+|\\b)\\)"
|
||||||
|
return Regex(regex).replace(text, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeYear(text: String): String {
|
||||||
|
val match = Regex("\\b\\d{4}\\W").find(text, 0)?.value
|
||||||
|
if (match == null || text.indexOf(match) > 0) {
|
||||||
|
//return Regex("\\b\\d{4}\\b(.*)").replace(text, " ")
|
||||||
|
return Regex("\\b\\d{4}\\b").replace(text, "")
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeDot(input: String): String {
|
||||||
|
//var text = Regex("(?<=\\s)\\.|\\.(?=\\s)").replace(input, "")
|
||||||
|
//return Regex("\\.(?<!(Dr|Mr|Ms|Mrs|Lt|Capt|Prof|St|Ave)\\.)\\b").replace(text, " ")
|
||||||
|
return Regex("(?<!\\b(?:Dr|Mr|Ms|Mrs|Lt|Capt|Prof|St|Ave))\\.").replace(input, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeInBetweenCharacters(text: String): String {
|
||||||
|
return Regex("[.]").replace(text, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param text "example text with extra spaces"
|
||||||
|
* @return example text with extra spaces
|
||||||
|
*/
|
||||||
|
fun removeExtraWhiteSpace(text: String): String {
|
||||||
|
return Regex("\\s{2,}").replace(text, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getMatch(regex: String): String? {
|
||||||
|
return Regex(regex).find(fileName)?.value ?: return null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.parsing
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils
|
||||||
|
import java.text.Normalizer
|
||||||
|
|
||||||
|
object NameHelper {
|
||||||
|
fun normalize(text: String): String {
|
||||||
|
val normalized = Normalizer.normalize(text, Normalizer.Form.NFC)
|
||||||
|
val result = normalized.replace("\\p{M}".toRegex(), "")
|
||||||
|
val cleaned = "[^A-Za-z0-9 -]".toRegex().replace(result, "")
|
||||||
|
return StringUtils.stripAccents(cleaned).trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cleanup(input: String): String {
|
||||||
|
var cleaned = Regex("(?<=\\w)[_.](?=\\w)").replace(input, " ")
|
||||||
|
cleaned = Regexes.illegalCharacters.replace(cleaned, " - ")
|
||||||
|
cleaned = Regexes.trimWhiteSpaces.replace(cleaned, " ")
|
||||||
|
return NameHelper.normalize(cleaned).trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.isCharOnlyUpperCase(): Boolean {
|
||||||
|
return "[^A-Za-z]".toRegex().replace(this, "").all { it.isUpperCase() }
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.parsing
|
||||||
|
|
||||||
|
object Regexes {
|
||||||
|
val illegalCharacters = Regex("""[\\\\\\/\\:\\*\\?\\\"\\<\\>|!`()\[\]]""")
|
||||||
|
val trimWhiteSpaces = Regex("\\s{2,}")
|
||||||
|
val SeasonEpisodeBlock = Regex("""(?i)\b(?:S|Season)\s*(\d+).*?(?:E|Episode)?\s*(\d+)\b""", RegexOption.IGNORE_CASE)
|
||||||
|
val season = Regex("""(?i)\b(?:S|Season)\s*-?\s*(\d+)""", RegexOption.IGNORE_CASE)
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.stores
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import no.iktdev.eventi.ZDS.toPersisted
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
import no.iktdev.eventi.models.store.PersistedEvent
|
||||||
|
import no.iktdev.eventi.stores.EventStore
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.database.tables.EventsTable
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.database.withTransaction
|
||||||
|
import org.jetbrains.exposed.sql.insert
|
||||||
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
object EventStore: EventStore {
|
||||||
|
override fun getPersistedEventsAfter(timestamp: LocalDateTime): List<PersistedEvent> {
|
||||||
|
val result = withTransaction {
|
||||||
|
EventsTable.selectAll()
|
||||||
|
.where { EventsTable.persistedAt greater timestamp }
|
||||||
|
.map {
|
||||||
|
PersistedEvent(
|
||||||
|
id = it[EventsTable.id].value.toLong(),
|
||||||
|
referenceId = it[EventsTable.referenceId],
|
||||||
|
eventId = it[EventsTable.eventId],
|
||||||
|
event = "", // You might want to store the event type as well
|
||||||
|
data = it[EventsTable.data],
|
||||||
|
persistedAt = it[EventsTable.persistedAt]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.getOrDefault(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPersistedEventsFor(referenceId: UUID): List<PersistedEvent> {
|
||||||
|
val result = withTransaction {
|
||||||
|
EventsTable.selectAll()
|
||||||
|
.where { EventsTable.referenceId eq referenceId}
|
||||||
|
.map {
|
||||||
|
PersistedEvent(
|
||||||
|
id = it[EventsTable.id].value.toLong(),
|
||||||
|
referenceId = it[EventsTable.referenceId],
|
||||||
|
eventId = it[EventsTable.eventId],
|
||||||
|
event = "", // You might want to store the event type as well
|
||||||
|
data = it[EventsTable.data],
|
||||||
|
persistedAt = it[EventsTable.persistedAt]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.getOrDefault(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun persist(event: Event) {
|
||||||
|
val asData = Gson().toJson(event)
|
||||||
|
val eventName = event::class.simpleName ?: run {
|
||||||
|
throw RuntimeException("Missing class name for event: $event")
|
||||||
|
}
|
||||||
|
withTransaction {
|
||||||
|
EventsTable.insert {
|
||||||
|
it[EventsTable.referenceId] = event.referenceId
|
||||||
|
it[EventsTable.eventId] = event.eventId
|
||||||
|
it[EventsTable.event] = eventName
|
||||||
|
it[EventsTable.data] = asData
|
||||||
|
it[EventsTable.persistedAt] = LocalDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,170 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.stores
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import no.iktdev.eventi.ZDS
|
||||||
|
import no.iktdev.eventi.models.Task
|
||||||
|
import no.iktdev.eventi.models.store.PersistedTask
|
||||||
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
|
import no.iktdev.eventi.stores.TaskStore
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.database.tables.TasksTable
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.database.withTransaction
|
||||||
|
import org.jetbrains.exposed.sql.and
|
||||||
|
import org.jetbrains.exposed.sql.insert
|
||||||
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
|
import org.jetbrains.exposed.sql.update
|
||||||
|
import java.time.Duration
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
object TaskStore: TaskStore {
|
||||||
|
override fun persist(task: Task) {
|
||||||
|
val asData = ZDS.WGson.toJson(task)
|
||||||
|
val taskName = task::class.simpleName ?: run {
|
||||||
|
throw RuntimeException("Missing class name for task: $task")
|
||||||
|
}
|
||||||
|
withTransaction {
|
||||||
|
TasksTable.insert {
|
||||||
|
it[referenceId] = task.referenceId
|
||||||
|
it[taskId] = task.taskId
|
||||||
|
it[TasksTable.task] = taskName
|
||||||
|
it[status] = TaskStatus.Pending
|
||||||
|
it[data] = asData
|
||||||
|
it[persistedAt] = LocalDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findByTaskId(taskId: UUID): PersistedTask? {
|
||||||
|
return withTransaction {
|
||||||
|
TasksTable.selectAll()
|
||||||
|
.where { TasksTable.taskId eq taskId }
|
||||||
|
.singleOrNull()?.let {
|
||||||
|
PersistedTask(
|
||||||
|
id = it[TasksTable.id].value.toLong(),
|
||||||
|
referenceId = it[TasksTable.referenceId],
|
||||||
|
status = it[TasksTable.status],
|
||||||
|
taskId = it[TasksTable.taskId],
|
||||||
|
task = it[TasksTable.task],
|
||||||
|
data = it[TasksTable.data],
|
||||||
|
claimed = it[TasksTable.claimed],
|
||||||
|
claimedBy = it[TasksTable.claimedBy],
|
||||||
|
consumed = it[TasksTable.consumed],
|
||||||
|
lastCheckIn = it[TasksTable.lastCheckIn],
|
||||||
|
persistedAt = it[TasksTable.persistedAt]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findByReferenceId(referenceId: UUID): List<PersistedTask> {
|
||||||
|
return withTransaction {
|
||||||
|
TasksTable.selectAll()
|
||||||
|
.where { TasksTable.referenceId eq referenceId }
|
||||||
|
.map {
|
||||||
|
PersistedTask(
|
||||||
|
id = it[TasksTable.id].value.toLong(),
|
||||||
|
referenceId = it[TasksTable.referenceId],
|
||||||
|
status = it[TasksTable.status],
|
||||||
|
taskId = it[TasksTable.taskId],
|
||||||
|
task = it[TasksTable.task],
|
||||||
|
data = it[TasksTable.data],
|
||||||
|
claimed = it[TasksTable.claimed],
|
||||||
|
claimedBy = it[TasksTable.claimedBy],
|
||||||
|
consumed = it[TasksTable.consumed],
|
||||||
|
lastCheckIn = it[TasksTable.lastCheckIn],
|
||||||
|
persistedAt = it[TasksTable.persistedAt]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.getOrDefault(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findUnclaimed(referenceId: UUID): List<PersistedTask> {
|
||||||
|
return withTransaction {
|
||||||
|
TasksTable.selectAll()
|
||||||
|
.where { (TasksTable.referenceId eq referenceId) and (TasksTable.claimed eq false) and (TasksTable.consumed eq false) }
|
||||||
|
.map {
|
||||||
|
PersistedTask(
|
||||||
|
id = it[TasksTable.id].value.toLong(),
|
||||||
|
referenceId = it[TasksTable.referenceId],
|
||||||
|
status = it[TasksTable.status],
|
||||||
|
taskId = it[TasksTable.taskId],
|
||||||
|
task = it[TasksTable.task],
|
||||||
|
data = it[TasksTable.data],
|
||||||
|
claimed = it[TasksTable.claimed],
|
||||||
|
claimedBy = it[TasksTable.claimedBy],
|
||||||
|
consumed = it[TasksTable.consumed],
|
||||||
|
lastCheckIn = it[TasksTable.lastCheckIn],
|
||||||
|
persistedAt = it[TasksTable.persistedAt]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.getOrDefault(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun claim(taskId: UUID, workerId: String): Boolean {
|
||||||
|
return withTransaction {
|
||||||
|
TasksTable.update({
|
||||||
|
(TasksTable.taskId eq taskId) and
|
||||||
|
(TasksTable.claimed eq false)
|
||||||
|
}) {
|
||||||
|
it[claimed] = true
|
||||||
|
it[claimedBy] = workerId
|
||||||
|
it[lastCheckIn] = LocalDateTime.now()
|
||||||
|
}
|
||||||
|
}.isSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun heartbeat(taskId: UUID) {
|
||||||
|
withTransaction {
|
||||||
|
TasksTable.update({ TasksTable.taskId eq taskId }) {
|
||||||
|
it[lastCheckIn] = LocalDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun markConsumed(taskId: UUID) {
|
||||||
|
withTransaction {
|
||||||
|
TasksTable.update({ TasksTable.taskId eq taskId }) {
|
||||||
|
it[consumed] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun releaseExpiredTasks(timeout: Duration) {
|
||||||
|
val now = LocalDateTime.now()
|
||||||
|
val expirationTime = now.minus(timeout)
|
||||||
|
withTransaction {
|
||||||
|
TasksTable.update({
|
||||||
|
(TasksTable.claimed eq true) and
|
||||||
|
(TasksTable.consumed eq false) and
|
||||||
|
(TasksTable.lastCheckIn.isNotNull()) and
|
||||||
|
(TasksTable.lastCheckIn less expirationTime)
|
||||||
|
}) {
|
||||||
|
it[claimed] = false
|
||||||
|
it[claimedBy] = null
|
||||||
|
it[lastCheckIn] = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPendingTasks(): List<PersistedTask> {
|
||||||
|
return withTransaction {
|
||||||
|
TasksTable.selectAll()
|
||||||
|
.where { (TasksTable.consumed eq false) and (TasksTable.claimed eq false) }
|
||||||
|
.map {
|
||||||
|
PersistedTask(
|
||||||
|
id = it[TasksTable.id].value.toLong(),
|
||||||
|
referenceId = it[TasksTable.referenceId],
|
||||||
|
status = it[TasksTable.status],
|
||||||
|
taskId = it[TasksTable.taskId],
|
||||||
|
task = it[TasksTable.task],
|
||||||
|
data = it[TasksTable.data],
|
||||||
|
claimed = it[TasksTable.claimed],
|
||||||
|
claimedBy = it[TasksTable.claimedBy],
|
||||||
|
consumed = it[TasksTable.consumed],
|
||||||
|
lastCheckIn = it[TasksTable.lastCheckIn],
|
||||||
|
persistedAt = it[TasksTable.persistedAt]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.getOrDefault(emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
10
shared/common/src/main/resources/application.yml
Normal file
10
shared/common/src/main/resources/application.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
spring:
|
||||||
|
flyway:
|
||||||
|
enabled: true
|
||||||
|
locations: classpath:flyway
|
||||||
|
baseline-on-migrate: true
|
||||||
|
datasource:
|
||||||
|
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
|
||||||
|
driver-class-name: org.h2.Driver
|
||||||
|
username: sa
|
||||||
|
password:
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
CREATE TABLE Events (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
REFERENCE_ID UUID NOT NULL,
|
||||||
|
EVENT_ID UUID NOT NULL,
|
||||||
|
EVENT VARCHAR(100) NOT NULL,
|
||||||
|
DATA TEXT NOT NULL,
|
||||||
|
PERSISTED_AT TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT events_unique UNIQUE (REFERENCE_ID, EVENT_ID, EVENT)
|
||||||
|
);
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
CREATE TABLE Tasks (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
REFERENCE_ID UUID NOT NULL,
|
||||||
|
TASK_ID UUID NOT NULL,
|
||||||
|
TASK VARCHAR(100) NOT NULL,
|
||||||
|
STATUS VARCHAR(50) NOT NULL,
|
||||||
|
DATA TEXT NOT NULL,
|
||||||
|
CLAIMED BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
CLAIMED_BY VARCHAR(100),
|
||||||
|
CONSUMED BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
LAST_CHECK_IN TIMESTAMP,
|
||||||
|
PERSISTED_AT TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
@ -1,100 +0,0 @@
|
|||||||
package no.iktdev.mediaprocessing.shared.common
|
|
||||||
|
|
||||||
import no.iktdev.mediaprocessing.shared.common.contract.Events
|
|
||||||
import no.iktdev.mediaprocessing.shared.common.contract.data.EpisodeInfo
|
|
||||||
import no.iktdev.mediaprocessing.shared.common.contract.data.MediaMetadataReceivedEvent
|
|
||||||
import no.iktdev.mediaprocessing.shared.common.contract.data.MediaOutInformationConstructedEvent
|
|
||||||
import no.iktdev.mediaprocessing.shared.common.contract.data.StartEventData
|
|
||||||
import no.iktdev.mediaprocessing.shared.common.contract.jsonToEvent
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
class DatabaseDeserializerTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun validateParsingOfStartEvent() {
|
|
||||||
//language=json
|
|
||||||
val data = """{"metadata":{"eventId":"45e92856-37a6-4266-81cd-a8cf16dd7eb2","referenceId":"a42bf0a4-d31d-4715-8e4d-1432d52f9786","status":"Success","created":"2025-02-10T21:35:52.437791666","source":"Coordinator"},"data":{"type":"FLOW","operations":["ENCODE","EXTRACT","CONVERT"],"file":"/potato"},"eventType":"EventMediaProcessStarted"}"""
|
|
||||||
val result = data.jsonToEvent("event:media-process:started")
|
|
||||||
assertThat(result.data!!.javaClass).hasSameClassAs(StartEventData::class.java)
|
|
||||||
assertThat(result.eventType).isNotNull()
|
|
||||||
assertThat(result.eventType).isEqualTo(Events.ProcessStarted)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun validateMediaInfo() {
|
|
||||||
//language=json
|
|
||||||
val data = """
|
|
||||||
{
|
|
||||||
"metadata": {
|
|
||||||
"derivedFromEventId": "c2ec1424-3d8f-444c-ad85-ce04e6c583fd",
|
|
||||||
"eventId": "0b164f37-fa23-4b43-a31e-edfa56325eb2",
|
|
||||||
"referenceId": "920e67cc-1f07-43f0-b121-e5ae87195122",
|
|
||||||
"status": "Success",
|
|
||||||
"created": "2025-02-24T00:39:16.057643776",
|
|
||||||
"source": "MediaOutInformationTaskListener"
|
|
||||||
},
|
|
||||||
"eventType": "ReadOutNameAndType",
|
|
||||||
"data": {
|
|
||||||
"info": {
|
|
||||||
"type": "serie",
|
|
||||||
"title": "The Potato",
|
|
||||||
"episode": 1,
|
|
||||||
"season": 2,
|
|
||||||
"episodeTitle": "",
|
|
||||||
"fullName": "The Bit potato"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""".trimIndent()
|
|
||||||
val result = data.jsonToEvent("event:media-read-out-name-and-type:performed")
|
|
||||||
assertThat(result.data!!.javaClass).hasSameClassAs(MediaOutInformationConstructedEvent::class.java)
|
|
||||||
assertThat(result.eventType).isNotNull()
|
|
||||||
val serieInfo = (result as MediaOutInformationConstructedEvent).data?.toValueObject()
|
|
||||||
assertThat(serieInfo).isNotNull()
|
|
||||||
assertThat(serieInfo!!.javaClass).hasSameClassAs(EpisodeInfo::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun validateMetadataRead() {
|
|
||||||
//language=json
|
|
||||||
val data = """
|
|
||||||
{
|
|
||||||
"metadata": {
|
|
||||||
"derivedFromEventId": "855b6de0-38f1-4ac9-9397-1ca7fc83fa4d",
|
|
||||||
"eventId": "c2ec1424-3d8f-444c-ad85-ce04e6c583fd",
|
|
||||||
"referenceId": "920e67cc-1f07-43f0-b121-e5ae87195122",
|
|
||||||
"status": "Success",
|
|
||||||
"created": "2025-02-24T00:39:15.674278",
|
|
||||||
"source": "metadataApp"
|
|
||||||
},
|
|
||||||
"eventType": "EventMediaMetadataSearchPerformed",
|
|
||||||
"data": {
|
|
||||||
"title": "Cabbage",
|
|
||||||
"altTitle": [
|
|
||||||
"Cabbage man"
|
|
||||||
],
|
|
||||||
"cover": "https://cabbageman.co",
|
|
||||||
"banner": null,
|
|
||||||
"type": "serie",
|
|
||||||
"summary": [
|
|
||||||
{
|
|
||||||
"summary": "Forced to becoma a cabbage farmer after getting their passport confiscated",
|
|
||||||
"language": "eng"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"genres": [
|
|
||||||
"Drama",
|
|
||||||
"Mystery"
|
|
||||||
],
|
|
||||||
"source": "yt"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""".trimIndent()
|
|
||||||
val result = data.jsonToEvent("event:media-metadata-search:performed")
|
|
||||||
assertThat(result.data!!.javaClass).hasSameClassAs(MediaMetadataReceivedEvent::class.java)
|
|
||||||
assertThat(result.eventType).isNotNull()
|
|
||||||
assertThat(result.data).isNotNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common
|
||||||
|
|
||||||
|
import mu.KotlinLogging
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.database.Access
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.database.DatabaseConfig
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.database.DbType
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.database.withTransaction
|
||||||
|
import org.flywaydb.core.Flyway
|
||||||
|
import org.jetbrains.exposed.sql.statements.jdbc.JdbcConnectionImpl
|
||||||
|
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension
|
||||||
|
|
||||||
|
@ExtendWith(SpringExtension::class)
|
||||||
|
class FlywayMigrationTest {
|
||||||
|
|
||||||
|
private val log = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should run flyway migrations and create expected tables`() {
|
||||||
|
val access = Access(
|
||||||
|
username = "sa",
|
||||||
|
password = "",
|
||||||
|
address = "", // ikke brukt for H2
|
||||||
|
port = 0, // ikke brukt for H2
|
||||||
|
databaseName = "testdb",
|
||||||
|
dbType = DbType.H2
|
||||||
|
)
|
||||||
|
|
||||||
|
val connection = DatabaseConfig.connect(access)
|
||||||
|
|
||||||
|
val flyway = Flyway.configure()
|
||||||
|
.dataSource(connection.second)
|
||||||
|
.locations("classpath:flyway")
|
||||||
|
.baselineOnMigrate(true)
|
||||||
|
.load()
|
||||||
|
|
||||||
|
// Flyway migrering
|
||||||
|
flyway.migrate()
|
||||||
|
|
||||||
|
// Verifiser at tabellene finnes
|
||||||
|
|
||||||
|
withTransaction {
|
||||||
|
val jdbc = (TransactionManager.current().connection as JdbcConnectionImpl).connection
|
||||||
|
|
||||||
|
val meta = jdbc.metaData
|
||||||
|
val eventsExists = meta.getTables(null, null, "EVENTS", null).next()
|
||||||
|
val tasksExists = meta.getTables(null, null, "TASKS", null).next()
|
||||||
|
|
||||||
|
assertTrue(eventsExists, "Events table should exist")
|
||||||
|
assertTrue(tasksExists, "Tasks table should exist")
|
||||||
|
|
||||||
|
log.info { "Found migrations: ${flyway.info().all().map { it.script }}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,78 +0,0 @@
|
|||||||
package no.iktdev.mediaprocessing.shared.common
|
|
||||||
|
|
||||||
import no.iktdev.eventi.database.DatabaseConnectionConfig
|
|
||||||
import no.iktdev.eventi.database.MySqlDataSource
|
|
||||||
import org.h2.jdbcx.JdbcDataSource
|
|
||||||
import java.io.PrintWriter
|
|
||||||
import java.sql.Connection
|
|
||||||
import java.sql.SQLFeatureNotSupportedException
|
|
||||||
import java.util.logging.Logger
|
|
||||||
import javax.sql.DataSource
|
|
||||||
|
|
||||||
class H2DataSource(private val jdbcDataSource: JdbcDataSource, databaseName: String) : DataSource, MySqlDataSource(
|
|
||||||
DatabaseConnectionConfig(
|
|
||||||
databaseName = databaseName, address = jdbcDataSource.getUrl(), username = jdbcDataSource.user, password = "", port = null
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val connectionUrl = "jdbc:h2:test;MODE=MySQL" //"jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_DELAY=-1;"
|
|
||||||
fun getDatasource(): JdbcDataSource {
|
|
||||||
val ds = JdbcDataSource()
|
|
||||||
ds.setUrl(connectionUrl)
|
|
||||||
ds.user = "test"
|
|
||||||
ds.password = ""
|
|
||||||
return ds
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getConnection(): Connection {
|
|
||||||
return jdbcDataSource.connection
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getConnection(username: String?, password: String?): Connection {
|
|
||||||
return jdbcDataSource.getConnection(username, password)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setLoginTimeout(seconds: Int) {
|
|
||||||
jdbcDataSource.loginTimeout = seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLoginTimeout(): Int {
|
|
||||||
return jdbcDataSource.loginTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLogWriter(): PrintWriter? {
|
|
||||||
return jdbcDataSource.logWriter
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setLogWriter(out: PrintWriter?) {
|
|
||||||
jdbcDataSource.logWriter = out
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getParentLogger(): Logger? {
|
|
||||||
throw SQLFeatureNotSupportedException("getParentLogger is not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : Any?> unwrap(iface: Class<T>?): T {
|
|
||||||
if (iface != null && iface.isAssignableFrom(this.javaClass)) {
|
|
||||||
return this as T
|
|
||||||
}
|
|
||||||
return jdbcDataSource.unwrap(iface)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isWrapperFor(iface: Class<*>?): Boolean {
|
|
||||||
if (iface != null && iface.isAssignableFrom(this.javaClass)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return jdbcDataSource.isWrapperFor(iface)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createDatabaseStatement(): String {
|
|
||||||
return "CREATE SCHEMA ${config.databaseName}"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toConnectionUrl(): String {
|
|
||||||
return connectionUrl // "jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_DELAY=-1;"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
package no.iktdev.mediaprocessing.shared.common
|
|
||||||
|
|
||||||
import no.iktdev.eventi.database.DatabaseConnectionConfig
|
|
||||||
import no.iktdev.eventi.database.MySqlDataSource
|
|
||||||
import org.jetbrains.exposed.sql.Database
|
|
||||||
|
|
||||||
class H2DataSource2(conf: DatabaseConnectionConfig): MySqlDataSource(conf) {
|
|
||||||
|
|
||||||
override fun createDatabaseStatement(): String {
|
|
||||||
return "CREATE SCHEMA ${config.databaseName};"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toDatabaseConnectionUrl(database: String): String {
|
|
||||||
return toConnectionUrl()
|
|
||||||
}
|
|
||||||
override fun toDatabase(): Database {
|
|
||||||
return super.toDatabase()
|
|
||||||
}
|
|
||||||
override fun toConnectionUrl(): String {
|
|
||||||
return "jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=TRUE;"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
package no.iktdev.mediaprocessing.shared.common
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
class PersistentMessageFromJsonDump(events: String) {
|
|
||||||
private var data: JsonArray?
|
|
||||||
|
|
||||||
init {
|
|
||||||
val jsonArray = Json.parseToJsonElement(events) as JsonArray
|
|
||||||
data = jsonArray.firstOrNull { it.jsonObject["data"] != null }?.jsonObject?.get("data") as? JsonArray
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getPersistentMessages(): List<PersistentMessage> {
|
|
||||||
return data?.mapNotNull {
|
|
||||||
try {
|
|
||||||
mapToPersistentMessage(it)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
System.err.print(it.toString())
|
|
||||||
e.printStackTrace()
|
|
||||||
null
|
|
||||||
}
|
|
||||||
} ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val dzz = DeserializingRegistry()
|
|
||||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS")
|
|
||||||
private fun mapToPersistentMessage(e: JsonElement): PersistentMessage? {
|
|
||||||
val referenceId: String = e.jsonObject["referenceId"]?.jsonPrimitive?.content ?: throw RuntimeException("No ReferenceId found")
|
|
||||||
val eventId: String = e.jsonObject["eventId"]?.jsonPrimitive?.content ?: throw RuntimeException("No EventId")
|
|
||||||
val event: String = e.jsonObject["event"]?.jsonPrimitive?.content ?: throw RuntimeException("No Event")
|
|
||||||
val data: String = e.jsonObject["data"]?.jsonPrimitive?.content ?: throw RuntimeException("No data")
|
|
||||||
val created: String = e.jsonObject["created"]?.jsonPrimitive?.content ?: throw RuntimeException("No Created date time found")
|
|
||||||
|
|
||||||
val kev = KafkaEvents.toEvent(event) ?: throw RuntimeException("Not able to convert event to Enum")
|
|
||||||
val dzdata = dzz.deserializeData(kev, data)
|
|
||||||
|
|
||||||
return PersistentMessage(
|
|
||||||
referenceId = referenceId,
|
|
||||||
eventId = eventId,
|
|
||||||
event = kev,
|
|
||||||
data = dzdata,
|
|
||||||
created = LocalDateTime.parse(created, formatter)
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}*/
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package no.iktdev.mediaprocessing.shared.common.parsing
|
package no.iktdev.mediaprocessing.shared.common.parsing
|
||||||
|
|
||||||
import no.iktdev.mediaprocessing.shared.common.contract.data.EpisodeInfo
|
import no.iktdev.mediaprocessing.shared.common.model.EpisodeInfo
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.Assertions.*
|
import org.junit.jupiter.api.Assertions.*
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|||||||
@ -1,480 +0,0 @@
|
|||||||
package no.iktdev.mediaprocessing.shared.common.tests
|
|
||||||
|
|
||||||
/*
|
|
||||||
class PersistentEventMangerTestBase {
|
|
||||||
val defaultReferenceId = UUID.randomUUID().toString()
|
|
||||||
val dataSource = H2DataSource2(DatabaseConnectionConfig(
|
|
||||||
address = "",
|
|
||||||
username = "",
|
|
||||||
password = "",
|
|
||||||
databaseName = "test",
|
|
||||||
port = null
|
|
||||||
))
|
|
||||||
val eventManager: PersistentEventManager = PersistentEventManager(dataSource)
|
|
||||||
|
|
||||||
init {
|
|
||||||
val kafkaTables = listOf(
|
|
||||||
events, // For kafka
|
|
||||||
)
|
|
||||||
dataSource.createDatabase()
|
|
||||||
dataSource.createTables(*kafkaTables.toTypedArray())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testDatabaseIsCreated() {
|
|
||||||
val success = dataSource.createDatabase()
|
|
||||||
assertThat(success).isNotNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testDatabaseInit() {
|
|
||||||
val referenceId = UUID.randomUUID().toString()
|
|
||||||
val mStart = Message<MediaProcessStarted>(
|
|
||||||
referenceId = referenceId,
|
|
||||||
eventId = UUID.randomUUID().toString(),
|
|
||||||
data = MediaProcessStarted(
|
|
||||||
status = Status.COMPLETED,
|
|
||||||
file = "Nan"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
eventManager.setEvent(KafkaEvents.EventMediaProcessStarted, mStart)
|
|
||||||
val stored = eventManager.getEventsWith(referenceId);
|
|
||||||
assertThat(stored).isNotEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSuperseded1() {
|
|
||||||
val startEvent = EventToMessage(KafkaEvents.EventMediaProcessStarted, createMessage())
|
|
||||||
val oldStack = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadStreamPerformed,
|
|
||||||
createMessage(eventId = "48c72454-6c7b-406b-b598-fc0a961dabde", derivedFromEventId = startEvent.message.eventId)),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParseStreamPerformed,
|
|
||||||
createMessage(eventId = "1d8d995d-a7e4-4d6e-a501-fe82f521cf72", derivedFromEventId ="48c72454-6c7b-406b-b598-fc0a961dabde")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadBaseInfoPerformed,
|
|
||||||
createMessage(eventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e", derivedFromEventId ="1d8d995d-a7e4-4d6e-a501-fe82f521cf72")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaMetadataSearchPerformed,
|
|
||||||
createMessage(eventId = "cbb1e871-e9a5-496d-a655-db719ac4903c", derivedFromEventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutNameAndType,
|
|
||||||
createMessage(eventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutCover,
|
|
||||||
createMessage(eventId = "98a39721-41ff-4d79-905e-ced260478524", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterEncodeCreated,
|
|
||||||
createMessage(eventId = "9e8f2e04-4950-437f-a203-cfd566203078", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterExtractCreated,
|
|
||||||
createMessage(eventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
)
|
|
||||||
eventManager.setEvent(startEvent.event, startEvent.message)
|
|
||||||
for (entry in oldStack) {
|
|
||||||
eventManager.setEvent(entry.event, entry.message)
|
|
||||||
}
|
|
||||||
val currentTableWithOldStack = eventManager.getEventsWith(defaultReferenceId)
|
|
||||||
assertThat(currentTableWithOldStack).hasSize(oldStack.size +1)
|
|
||||||
|
|
||||||
val supersedingStack = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutNameAndType,
|
|
||||||
createMessage(eventId = "2c3a40bb-2225-4dd4-a8c3-32c6356f8764", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c"))
|
|
||||||
).forEach {entry -> eventManager.setEvent(entry.event, entry.message)}
|
|
||||||
|
|
||||||
|
|
||||||
// Final check
|
|
||||||
|
|
||||||
val result = eventManager.getEventsWith(defaultReferenceId)
|
|
||||||
val idsThatShouldBeRemoved = listOf(
|
|
||||||
"9e8f2e04-4950-437f-a203-cfd566203078",
|
|
||||||
"af7f2519-0f1d-4679-82bd-0314d1b97b68"
|
|
||||||
)
|
|
||||||
val search = result.filter { it.eventId in idsThatShouldBeRemoved }
|
|
||||||
assertThat(search).isEmpty()
|
|
||||||
|
|
||||||
|
|
||||||
val expectedInList = listOf(
|
|
||||||
startEvent.message.eventId,
|
|
||||||
"48c72454-6c7b-406b-b598-fc0a961dabde",
|
|
||||||
"1d8d995d-a7e4-4d6e-a501-fe82f521cf72",
|
|
||||||
"f6cae204-7c8e-4003-b598-f7b4e566d03e",
|
|
||||||
"cbb1e871-e9a5-496d-a655-db719ac4903c",
|
|
||||||
"98a39721-41ff-4d79-905e-ced260478524",
|
|
||||||
"2c3a40bb-2225-4dd4-a8c3-32c6356f8764"
|
|
||||||
)
|
|
||||||
val searchForExpected = result.map { it.eventId }
|
|
||||||
assertThat(expectedInList).isEqualTo(searchForExpected)
|
|
||||||
withTransaction(dataSource) {
|
|
||||||
events.deleteAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSuperseded2() {
|
|
||||||
val startEvent = EventToMessage(KafkaEvents.EventMediaProcessStarted, createMessage()).also {
|
|
||||||
eventManager.setEvent(it.event, it.message)
|
|
||||||
}
|
|
||||||
val keepStack = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadStreamPerformed,
|
|
||||||
createMessage(eventId = "48c72454-6c7b-406b-b598-fc0a961dabde", derivedFromEventId = startEvent.message.eventId)),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParseStreamPerformed,
|
|
||||||
createMessage(eventId = "1d8d995d-a7e4-4d6e-a501-fe82f521cf72", derivedFromEventId ="48c72454-6c7b-406b-b598-fc0a961dabde")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadBaseInfoPerformed,
|
|
||||||
createMessage(eventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e", derivedFromEventId ="1d8d995d-a7e4-4d6e-a501-fe82f521cf72")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaMetadataSearchPerformed,
|
|
||||||
createMessage(eventId = "cbb1e871-e9a5-496d-a655-db719ac4903c", derivedFromEventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutCover,
|
|
||||||
createMessage(eventId = "98a39721-41ff-4d79-905e-ced260478524", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
val toBeReplaced = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutNameAndType,
|
|
||||||
createMessage(eventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterEncodeCreated,
|
|
||||||
createMessage(eventId = "9e8f2e04-4950-437f-a203-cfd566203078", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterExtractCreated,
|
|
||||||
createMessage(eventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
|
|
||||||
val currentTableWithOldStack = eventManager.getEventsWith(defaultReferenceId)
|
|
||||||
assertThat(currentTableWithOldStack).hasSize(keepStack.size + toBeReplaced.size +1)
|
|
||||||
|
|
||||||
val supersedingStack = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutNameAndType,
|
|
||||||
createMessage(eventId = "2c3a40bb-2225-4dd4-a8c3-32c6356f8764", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c"))
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
|
|
||||||
// Final check
|
|
||||||
|
|
||||||
val result = eventManager.getEventsWith(defaultReferenceId)
|
|
||||||
|
|
||||||
val idsRemoved = toBeReplaced.map { it.message.eventId }
|
|
||||||
val search = result.filter { it.eventId in idsRemoved }
|
|
||||||
assertThat(search).isEmpty()
|
|
||||||
|
|
||||||
|
|
||||||
val expectedInList = listOf(startEvent.message.eventId) + keepStack.map { it.message.eventId } + supersedingStack.map { it.message.eventId }
|
|
||||||
val searchForExpected = result.map { it.eventId }
|
|
||||||
assertThat(expectedInList).isEqualTo(searchForExpected)
|
|
||||||
|
|
||||||
withTransaction(dataSource) {
|
|
||||||
events.deleteAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSuperseded3() {
|
|
||||||
val startEvent = EventToMessage(KafkaEvents.EventMediaProcessStarted, createMessage()).also {
|
|
||||||
eventManager.setEvent(it.event, it.message)
|
|
||||||
}
|
|
||||||
val keepStack = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadStreamPerformed,
|
|
||||||
createMessage(eventId = "48c72454-6c7b-406b-b598-fc0a961dabde", derivedFromEventId = startEvent.message.eventId)),
|
|
||||||
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
val toBeReplaced = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParseStreamPerformed,
|
|
||||||
createMessage(eventId = "1d8d995d-a7e4-4d6e-a501-fe82f521cf72", derivedFromEventId ="48c72454-6c7b-406b-b598-fc0a961dabde")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadBaseInfoPerformed,
|
|
||||||
createMessage(eventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e", derivedFromEventId ="1d8d995d-a7e4-4d6e-a501-fe82f521cf72")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaMetadataSearchPerformed,
|
|
||||||
createMessage(eventId = "cbb1e871-e9a5-496d-a655-db719ac4903c", derivedFromEventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutCover,
|
|
||||||
createMessage(eventId = "98a39721-41ff-4d79-905e-ced260478524", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutNameAndType,
|
|
||||||
createMessage(eventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterEncodeCreated,
|
|
||||||
createMessage(eventId = "9e8f2e04-4950-437f-a203-cfd566203078", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterExtractCreated,
|
|
||||||
createMessage(eventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
|
|
||||||
val currentTableWithOldStack = eventManager.getEventsWith(defaultReferenceId)
|
|
||||||
assertThat(currentTableWithOldStack).hasSize(keepStack.size + toBeReplaced.size +1)
|
|
||||||
|
|
||||||
val supersedingStack = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParseStreamPerformed,
|
|
||||||
createMessage(eventId = "2c3a40bb-2225-4dd4-a8c3-32c6356f8764", derivedFromEventId = "48c72454-6c7b-406b-b598-fc0a961dabde"))
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
|
|
||||||
// Final check
|
|
||||||
|
|
||||||
val result = eventManager.getEventsWith(defaultReferenceId)
|
|
||||||
|
|
||||||
val idsRemoved = toBeReplaced.map { it.message.eventId }
|
|
||||||
val search = result.filter { it.eventId in idsRemoved }
|
|
||||||
assertThat(search).isEmpty()
|
|
||||||
|
|
||||||
|
|
||||||
val expectedInList = listOf(startEvent.message.eventId) + keepStack.map { it.message.eventId } + supersedingStack.map { it.message.eventId }
|
|
||||||
val searchForExpected = result.map { it.eventId }
|
|
||||||
assertThat(expectedInList).isEqualTo(searchForExpected)
|
|
||||||
|
|
||||||
withTransaction(dataSource) {
|
|
||||||
events.deleteAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSupersededButKeepWork() {
|
|
||||||
val startEventPayload = createMessage()
|
|
||||||
val keepStack = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaProcessStarted, startEventPayload),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadStreamPerformed,
|
|
||||||
createMessage(eventId = "48c72454-6c7b-406b-b598-fc0a961dabde", derivedFromEventId = startEventPayload.eventId)),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParseStreamPerformed,
|
|
||||||
createMessage(eventId = "1d8d995d-a7e4-4d6e-a501-fe82f521cf72", derivedFromEventId ="48c72454-6c7b-406b-b598-fc0a961dabde")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadBaseInfoPerformed,
|
|
||||||
createMessage(eventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e", derivedFromEventId ="1d8d995d-a7e4-4d6e-a501-fe82f521cf72")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaMetadataSearchPerformed,
|
|
||||||
createMessage(eventId = "cbb1e871-e9a5-496d-a655-db719ac4903c", derivedFromEventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutCover,
|
|
||||||
createMessage(eventId = "98a39721-41ff-4d79-905e-ced260478524", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutNameAndType,
|
|
||||||
createMessage(eventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterEncodeCreated,
|
|
||||||
createMessage(eventId = "9e8f2e04-4950-437f-a203-cfd566203078", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterExtractCreated,
|
|
||||||
createMessage(eventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "ad93a41a-db08-436b-84e4-55adb4752f38", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
val newEvents = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "cfeee961-69c1-4eed-8ec5-82ebca01c9e1", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "64625872-bbfe-4604-85cd-02f58e904267", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "0ab96b32-45a5-4517-b0c0-c03d48145340", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "cabd9038-307f-48e4-ac99-88232b1a817c", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "10c0fd42-b5be-42b2-a27b-12ecccc51635", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "b69fb306-e390-4a9e-8d11-89d0688dff16", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
val result = eventManager.getEventsWith(defaultReferenceId)
|
|
||||||
|
|
||||||
val expected = (keepStack + newEvents).map { it.message.eventId }
|
|
||||||
val missing = expected - result.map { it.eventId }
|
|
||||||
assertThat(missing).isEmpty()
|
|
||||||
assertThat(expected.size).isEqualTo(result.size)
|
|
||||||
|
|
||||||
withTransaction(dataSource) {
|
|
||||||
events.deleteAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSupersededWork() {
|
|
||||||
val startEventPayload = createMessage()
|
|
||||||
val keepStack = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaProcessStarted, startEventPayload),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadStreamPerformed,
|
|
||||||
createMessage(eventId = "48c72454-6c7b-406b-b598-fc0a961dabde", derivedFromEventId = startEventPayload.eventId)),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParseStreamPerformed,
|
|
||||||
createMessage(eventId = "1d8d995d-a7e4-4d6e-a501-fe82f521cf72", derivedFromEventId ="48c72454-6c7b-406b-b598-fc0a961dabde")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadBaseInfoPerformed,
|
|
||||||
createMessage(eventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e", derivedFromEventId ="1d8d995d-a7e4-4d6e-a501-fe82f521cf72")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaMetadataSearchPerformed,
|
|
||||||
createMessage(eventId = "cbb1e871-e9a5-496d-a655-db719ac4903c", derivedFromEventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutCover,
|
|
||||||
createMessage(eventId = "98a39721-41ff-4d79-905e-ced260478524", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutNameAndType,
|
|
||||||
createMessage(eventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterEncodeCreated,
|
|
||||||
createMessage(eventId = "9e8f2e04-4950-437f-a203-cfd566203078", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
val newEvents = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterExtractCreated,
|
|
||||||
createMessage(eventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "ad93a41a-db08-436b-84e4-55adb4752f38", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "cfeee961-69c1-4eed-8ec5-82ebca01c9e1", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "64625872-bbfe-4604-85cd-02f58e904267", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "0ab96b32-45a5-4517-b0c0-c03d48145340", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "cabd9038-307f-48e4-ac99-88232b1a817c", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "10c0fd42-b5be-42b2-a27b-12ecccc51635", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
val replacedWith = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterExtractCreated,
|
|
||||||
createMessage(eventId = "e40b2096-2e6f-4672-9c5a-6c81fe8fc302", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "b69fb306-e390-4a9e-8d11-89d0688dff16", derivedFromEventId = "e40b2096-2e6f-4672-9c5a-6c81fe8fc302")),
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
val result = eventManager.getEventsWith(defaultReferenceId)
|
|
||||||
|
|
||||||
val expected = (keepStack + replacedWith).map { it.message.eventId }
|
|
||||||
val missing = expected - result.map { it.eventId }
|
|
||||||
assertThat(missing).isEmpty()
|
|
||||||
assertThat(expected.size).isEqualTo(result.size)
|
|
||||||
|
|
||||||
withTransaction(dataSource) {
|
|
||||||
events.deleteAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testConvertBatchFromExtract() {
|
|
||||||
val startEventPayload = createMessage()
|
|
||||||
val keepStack = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaProcessStarted, startEventPayload),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadStreamPerformed,
|
|
||||||
createMessage(eventId = "48c72454-6c7b-406b-b598-fc0a961dabde", derivedFromEventId = startEventPayload.eventId)),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParseStreamPerformed,
|
|
||||||
createMessage(eventId = "1d8d995d-a7e4-4d6e-a501-fe82f521cf72", derivedFromEventId ="48c72454-6c7b-406b-b598-fc0a961dabde")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadBaseInfoPerformed,
|
|
||||||
createMessage(eventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e", derivedFromEventId ="1d8d995d-a7e4-4d6e-a501-fe82f521cf72")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaMetadataSearchPerformed,
|
|
||||||
createMessage(eventId = "cbb1e871-e9a5-496d-a655-db719ac4903c", derivedFromEventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutCover,
|
|
||||||
createMessage(eventId = "98a39721-41ff-4d79-905e-ced260478524", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutNameAndType,
|
|
||||||
createMessage(eventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterEncodeCreated,
|
|
||||||
createMessage(eventId = "9e8f2e04-4950-437f-a203-cfd566203078", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterExtractCreated,
|
|
||||||
createMessage(eventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
val convertEvents = mutableListOf<PersistentEventMangerTestBase.EventToMessage>();
|
|
||||||
|
|
||||||
val extractEvents = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "ad93a41a-db08-436b-84e4-55adb4752f38", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "cfeee961-69c1-4eed-8ec5-82ebca01c9e1", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "64625872-bbfe-4604-85cd-02f58e904267", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "0ab96b32-45a5-4517-b0c0-c03d48145340", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "cabd9038-307f-48e4-ac99-88232b1a817c", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "10c0fd42-b5be-42b2-a27b-12ecccc51635", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "b69fb306-e390-4a9e-8d11-89d0688dff16", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
).onEach { entry ->
|
|
||||||
run {
|
|
||||||
eventManager.setEvent(entry.event, entry.message)
|
|
||||||
convertEvents.add(EventToMessage(KafkaEvents.EventWorkConvertCreated,
|
|
||||||
createMessage(derivedFromEventId = entry.message.eventId)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val simpleCascade = eventManager.getEventsWith(defaultReferenceId)
|
|
||||||
assertThat(simpleCascade.size).isEqualTo(keepStack.size+extractEvents.size)
|
|
||||||
|
|
||||||
assertThat(convertEvents.size).isEqualTo(extractEvents.size)
|
|
||||||
convertEvents.forEach {
|
|
||||||
eventManager.setEvent(it.event, it.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
val result = eventManager.getEventsWith(defaultReferenceId)
|
|
||||||
|
|
||||||
assertThat(result.size).isEqualTo(keepStack.size+extractEvents.size+convertEvents.size)
|
|
||||||
|
|
||||||
withTransaction(dataSource) {
|
|
||||||
events.deleteAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSomeAreSingleSomeAreNot() {
|
|
||||||
val startEventPayload = createMessage()
|
|
||||||
val keepStack = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventMediaProcessStarted, startEventPayload),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadStreamPerformed,
|
|
||||||
createMessage(eventId = "48c72454-6c7b-406b-b598-fc0a961dabde", derivedFromEventId = startEventPayload.eventId)),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParseStreamPerformed,
|
|
||||||
createMessage(eventId = "1d8d995d-a7e4-4d6e-a501-fe82f521cf72", derivedFromEventId ="48c72454-6c7b-406b-b598-fc0a961dabde")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadBaseInfoPerformed,
|
|
||||||
createMessage(eventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e", derivedFromEventId ="1d8d995d-a7e4-4d6e-a501-fe82f521cf72")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaMetadataSearchPerformed,
|
|
||||||
createMessage(eventId = "cbb1e871-e9a5-496d-a655-db719ac4903c", derivedFromEventId = "f6cae204-7c8e-4003-b598-f7b4e566d03e")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutCover,
|
|
||||||
createMessage(eventId = "98a39721-41ff-4d79-905e-ced260478524", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaReadOutNameAndType,
|
|
||||||
createMessage(eventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9", derivedFromEventId = "cbb1e871-e9a5-496d-a655-db719ac4903c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterEncodeCreated,
|
|
||||||
createMessage(eventId = "9e8f2e04-4950-437f-a203-cfd566203078", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaParameterExtractCreated,
|
|
||||||
createMessage(eventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68", derivedFromEventId = "3f376b72-f55a-4dd7-af87-fb1755ba4ad9")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "ad93a41a-db08-436b-84e4-55adb4752f38", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
val newEvents = listOf(
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "cfeee961-69c1-4eed-8ec5-82ebca01c9e1", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "64625872-bbfe-4604-85cd-02f58e904267", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "0ab96b32-45a5-4517-b0c0-c03d48145340", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventWorkExtractCreated,
|
|
||||||
createMessage(eventId = "cabd9038-307f-48e4-ac99-88232b1a817c", derivedFromEventId = "af7f2519-0f1d-4679-82bd-0314d1b97b68")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaProcessCompleted,
|
|
||||||
createMessage(eventId = "10c0fd42-b5be-42b2-a27b-12ecccc51635", derivedFromEventId = "cabd9038-307f-48e4-ac99-88232b1a817c")),
|
|
||||||
EventToMessage(KafkaEvents.EventMediaProcessCompleted,
|
|
||||||
createMessage(eventId = "3519af2e-0767-4dbb-b0c5-f19cb926900d", derivedFromEventId = "cabd9038-307f-48e4-ac99-88232b1a817c")),
|
|
||||||
|
|
||||||
EventToMessage(KafkaEvents.EventCollectAndStore,
|
|
||||||
createMessage(eventId = "b69fb306-e390-4a9e-8d11-89d0688dff16", derivedFromEventId = "3519af2e-0767-4dbb-b0c5-f19cb926900d")),
|
|
||||||
EventToMessage(KafkaEvents.EventCollectAndStore,
|
|
||||||
createMessage(eventId = "4e6d3a6a-ab89-4627-9158-3c3f92ff7b4c", derivedFromEventId = "3519af2e-0767-4dbb-b0c5-f19cb926900d")),
|
|
||||||
EventToMessage(KafkaEvents.EventCollectAndStore,
|
|
||||||
createMessage(eventId = "4e6d3a6a-ab89-4627-9158-3c3f92ff7b4c", derivedFromEventId = "3519af2e-0767-4dbb-b0c5-f19cb926900d")),
|
|
||||||
).onEach { entry -> eventManager.setEvent(entry.event, entry.message) }
|
|
||||||
|
|
||||||
val result = eventManager.getEventsWith(defaultReferenceId)
|
|
||||||
val singles = result.filter { it.event != KafkaEvents.EventWorkExtractCreated }
|
|
||||||
singles.forEach {
|
|
||||||
val instancesOfMe = singles.filter { sit -> it.event == sit.event }
|
|
||||||
assertThat(instancesOfMe).hasSize(1)
|
|
||||||
}
|
|
||||||
assertThat(result.filter { it.event == KafkaEvents.EventCollectAndStore }).hasSize(1)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
withTransaction(dataSource) {
|
|
||||||
events.deleteAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testDerivedOrphanNotInserted() {
|
|
||||||
val startEvent = EventToMessage(KafkaEvents.EventMediaProcessStarted, createMessage()).also {
|
|
||||||
eventManager.setEvent(it.event, it.message)
|
|
||||||
}
|
|
||||||
val result = eventManager.setEvent(KafkaEvents.EventMediaReadStreamPerformed,
|
|
||||||
createMessage(derivedFromEventId = UUID.randomUUID().toString()))
|
|
||||||
assertThat(result).isFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
data class EventToMessage(val event: KafkaEvents, val message: Message<*>)
|
|
||||||
|
|
||||||
private fun createMessage(referenceId: String = defaultReferenceId, eventId: String = UUID.randomUUID().toString(), derivedFromEventId: String? = null): Message<SimpleMessageData>{
|
|
||||||
return Message<SimpleMessageData>(
|
|
||||||
referenceId = referenceId,
|
|
||||||
eventId = eventId,
|
|
||||||
data = SimpleMessageData(
|
|
||||||
status = Status.COMPLETED,
|
|
||||||
message = "Potato",
|
|
||||||
derivedFromEventId = derivedFromEventId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}*/
|
|
||||||
36
shared/ffmpeg/build.gradle.kts
Normal file
36
shared/ffmpeg/build.gradle.kts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "no.iktdev.mediaprocessing"
|
||||||
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven("https://jitpack.io")
|
||||||
|
maven {
|
||||||
|
url = uri("https://reposilite.iktdev.no/releases")
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
url = uri("https://reposilite.iktdev.no/snapshots")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
|
||||||
|
implementation("com.github.pgreze:kotlin-process:1.5.1")
|
||||||
|
implementation("com.google.code.gson:gson:2.8.9")
|
||||||
|
|
||||||
|
implementation("no.iktdev:exfl:1.0-rc1")
|
||||||
|
|
||||||
|
|
||||||
|
testImplementation(kotlin("test"))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(21)
|
||||||
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
package no.iktdev.mediaprocessing.ffmpeg
|
||||||
|
|
||||||
|
import com.github.pgreze.process.ProcessResult
|
||||||
|
import com.github.pgreze.process.Redirect
|
||||||
|
import com.github.pgreze.process.process
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import no.iktdev.mediaprocessing.ffmpeg.data.FFinfoOutput
|
||||||
|
|
||||||
|
abstract class FFinfo {
|
||||||
|
open val defaultArguments: List<String> = listOf("-v", "quiet")
|
||||||
|
abstract val executable: String
|
||||||
|
|
||||||
|
open suspend fun readJsonStreams(inputFile: String): FFinfoOutput {
|
||||||
|
var error: String? = null
|
||||||
|
val output = mutableListOf<String>()
|
||||||
|
val args = defaultArguments + listOf("-print_format", "json", "-show_format", "-show_streams", inputFile)
|
||||||
|
val processResult = execute(args) { output.add(it) }
|
||||||
|
|
||||||
|
val success = processResult.resultCode == 0
|
||||||
|
val longString = output.joinToString(" ")
|
||||||
|
val json = try {
|
||||||
|
Gson().fromJson(longString, JsonObject::class.java)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
error = "Could not parse ffinfo output to JSON: ${e.message}"
|
||||||
|
null
|
||||||
|
}
|
||||||
|
return FFinfoOutput(
|
||||||
|
success = success,
|
||||||
|
data = json,
|
||||||
|
error = error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun execute(arguments: List<String>, output: (String) -> Unit): ProcessResult {
|
||||||
|
return process(executable, *arguments.toTypedArray(),
|
||||||
|
stderr = Redirect.CAPTURE,
|
||||||
|
stdout = Redirect.CAPTURE,
|
||||||
|
consumer = {
|
||||||
|
output(it)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
package no.iktdev.mediaprocessing.ffmpeg
|
||||||
|
|
||||||
|
import com.github.pgreze.process.ProcessResult
|
||||||
|
import com.github.pgreze.process.Redirect
|
||||||
|
import com.github.pgreze.process.process
|
||||||
|
import no.iktdev.exfl.using
|
||||||
|
import no.iktdev.mediaprocessing.ffmpeg.arguments.MpegArgument
|
||||||
|
import no.iktdev.mediaprocessing.ffmpeg.decoder.FfmpegDecodedProgress
|
||||||
|
import no.iktdev.mediaprocessing.ffmpeg.decoder.FfmpegProgressDecoder
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
abstract class FFmpeg {
|
||||||
|
abstract val executable: String
|
||||||
|
abstract val logDir: File
|
||||||
|
open val listener: Listener? = null
|
||||||
|
|
||||||
|
private var progress: FfmpegDecodedProgress? = null
|
||||||
|
val decoder = FfmpegProgressDecoder()
|
||||||
|
private val outputCache = mutableListOf<String>()
|
||||||
|
|
||||||
|
//region Log File formatting
|
||||||
|
val currentDateTime = LocalDateTime.now()
|
||||||
|
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd.HH.mm")
|
||||||
|
val formattedDateTime = currentDateTime.format(formatter)
|
||||||
|
//endregion
|
||||||
|
lateinit var logFile: File
|
||||||
|
|
||||||
|
lateinit var result: ProcessResult
|
||||||
|
protected set
|
||||||
|
|
||||||
|
private lateinit var inputFile: String
|
||||||
|
open suspend fun run(argument: MpegArgument) {
|
||||||
|
inputFile = if (argument.inputFile == null) throw RuntimeException("Input file is required") else argument.inputFile!!
|
||||||
|
logFile = logDir.using("$formattedDateTime-${File(inputFile).nameWithoutExtension}.log")
|
||||||
|
listener?.onStarted(argument.inputFile!!)
|
||||||
|
result = execute(argument.build()) {
|
||||||
|
onNewOutput(it)
|
||||||
|
}
|
||||||
|
onNewOutput("Received exit code: ${result.resultCode}")
|
||||||
|
if (result.resultCode != 0) {
|
||||||
|
listener?.onError(inputFile, result.output.joinToString("\n"))
|
||||||
|
} else {
|
||||||
|
val success = moveAndVerify(argument)
|
||||||
|
if (!success) {
|
||||||
|
listener?.onError(inputFile, "Could not find output file at ${argument.outputFile}")
|
||||||
|
} else {
|
||||||
|
listener?.onCompleted(inputFile, argument.outputFile!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun moveAndVerify(argument: MpegArgument): Boolean {
|
||||||
|
return if (argument.outputCacheFile) {
|
||||||
|
File(argument.getOutputFileUsed()).renameTo(File(argument.outputFile!!))
|
||||||
|
} else File(argument.outputFile!!).exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun execute(arguments: List<String>, output: (String) -> Unit): ProcessResult {
|
||||||
|
return process(executable, *arguments.toTypedArray(),
|
||||||
|
stdout = Redirect.CAPTURE,
|
||||||
|
stderr = Redirect.CAPTURE,
|
||||||
|
consumer = {
|
||||||
|
output(it)
|
||||||
|
},
|
||||||
|
destroyForcibly = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun onNewOutput(line: String) {
|
||||||
|
outputCache.add(line)
|
||||||
|
writeToLog(line)
|
||||||
|
decoder.defineDuration(line)
|
||||||
|
decoder.parseVideoProgress(outputCache.toList())?.let { decoded ->
|
||||||
|
try {
|
||||||
|
val _progress = decoder.getProgress(decoded)
|
||||||
|
if (progress == null || _progress.progress > (progress?.progress ?: -1)) {
|
||||||
|
progress = _progress
|
||||||
|
listener?.onProgressChanged(inputFile, _progress)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun writeToLog(line: String) {
|
||||||
|
FileOutputStream(logFile, true).bufferedWriter(Charsets.UTF_8).use {
|
||||||
|
it.appendLine(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Listener {
|
||||||
|
fun onStarted(inputFile: String)
|
||||||
|
fun onCompleted(inputFile: String, outputFile: String)
|
||||||
|
fun onProgressChanged(inputFile: String, progress: FfmpegDecodedProgress)
|
||||||
|
fun onError(inputFile: String, message: String) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
package no.iktdev.mediaprocessing.ffmpeg.arguments
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class MpegArgument {
|
||||||
|
private val defaultArguments = listOf(
|
||||||
|
"-nostdin",
|
||||||
|
"-nostats",
|
||||||
|
"-hide_banner"
|
||||||
|
)
|
||||||
|
var inputFile: String? = null
|
||||||
|
private set
|
||||||
|
var outputFile: String? = null
|
||||||
|
private set
|
||||||
|
private var overwrite: Boolean = false
|
||||||
|
private var progress: Boolean = false
|
||||||
|
private var suppliedArgs: List<String> = emptyList()
|
||||||
|
var outputCacheFile: Boolean = true
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun inputFile(inputFile: String) = apply {
|
||||||
|
this.inputFile = inputFile
|
||||||
|
}
|
||||||
|
|
||||||
|
fun outputFile(outputFile: String) = apply {
|
||||||
|
this.outputFile = outputFile
|
||||||
|
}
|
||||||
|
|
||||||
|
fun allowOverwrite(allowOverwrite: Boolean) = apply {
|
||||||
|
this.overwrite = allowOverwrite
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withProgress(withProgress: Boolean) = apply {
|
||||||
|
this.progress = withProgress
|
||||||
|
}
|
||||||
|
|
||||||
|
fun args(args: List<String>) = apply {
|
||||||
|
this.suppliedArgs = args
|
||||||
|
}
|
||||||
|
|
||||||
|
fun useCacheFile(useCacheFile: Boolean) = apply {
|
||||||
|
this.outputCacheFile = useCacheFile
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCacheOutputFile(): String {
|
||||||
|
return if (outputCacheFile) {
|
||||||
|
File(outputFile!!).let {
|
||||||
|
File(it.parentFile.absoluteFile, "${it.nameWithoutExtension}.work.${it.extension}")
|
||||||
|
}.absolutePath
|
||||||
|
} else {
|
||||||
|
this.outputFile!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOutputFileUsed(): String {
|
||||||
|
if (outputFile == null || outputFile?.isBlank() == true) {
|
||||||
|
throw RuntimeException("Outputfile is required")
|
||||||
|
}
|
||||||
|
return if (outputCacheFile) {
|
||||||
|
File(outputFile!!).let {
|
||||||
|
File(it.parentFile.absoluteFile, "${it.nameWithoutExtension}.work.${it.extension}")
|
||||||
|
}.absolutePath
|
||||||
|
} else {
|
||||||
|
this.outputFile!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): List<String> {
|
||||||
|
val args = mutableListOf<String>()
|
||||||
|
val inFile = if (inputFile == null || inputFile?.isBlank() == true) {
|
||||||
|
throw RuntimeException("Inputfile is required")
|
||||||
|
} else this.inputFile!!
|
||||||
|
val outFile: String = getOutputFileUsed()
|
||||||
|
if (overwrite) {
|
||||||
|
args.add("-y")
|
||||||
|
}
|
||||||
|
args.addAll(defaultArguments)
|
||||||
|
args.addAll(listOf("-i", inFile))
|
||||||
|
args.addAll(suppliedArgs)
|
||||||
|
args.add(outFile)
|
||||||
|
if (progress) {
|
||||||
|
args.addAll(listOf("-progress", "pipe:1"))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
package no.iktdev.mediaprocessing.ffmpeg.data
|
||||||
|
|
||||||
|
abstract class FFOutput {
|
||||||
|
abstract val success: Boolean
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package no.iktdev.mediaprocessing.ffmpeg.data
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
|
||||||
|
data class FFinfoOutput(
|
||||||
|
override val success: Boolean,
|
||||||
|
val error: String? = null,
|
||||||
|
val data: JsonObject? = null
|
||||||
|
) : FFOutput() {
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package no.iktdev.mediaprocessing.ffmpeg.data
|
||||||
|
|
||||||
|
data class FFmpegOutput(
|
||||||
|
override val success: Boolean
|
||||||
|
) : FFOutput() {
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package no.iktdev.mediaprocessing.ffmpeg.decoder
|
||||||
|
|
||||||
|
|
||||||
|
data class FfmpegDecodedProgress(
|
||||||
|
val progress: Int = -1,
|
||||||
|
val time: String,
|
||||||
|
val duration: String,
|
||||||
|
val speed: String,
|
||||||
|
val estimatedCompletionSeconds: Long = -1,
|
||||||
|
val estimatedCompletion: String = "Unknown",
|
||||||
|
) {
|
||||||
|
fun toProcessProgress(): ProcesserProgress {
|
||||||
|
return ProcesserProgress(
|
||||||
|
progress = this.progress,
|
||||||
|
speed = this.speed,
|
||||||
|
timeWorkedOn = this.time,
|
||||||
|
timeLeft = this.estimatedCompletion
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ProcesserProgress(
|
||||||
|
val progress: Int = -1,
|
||||||
|
val speed: String? = null,
|
||||||
|
val timeWorkedOn: String? = null,
|
||||||
|
val timeLeft: String? = "Unknown", // HH mm
|
||||||
|
)
|
||||||
|
|
||||||
|
data class ECT(val day: Int = 0, val hour: Int = 0, val minute: Int = 0, val second: Int = 0)
|
||||||
@ -0,0 +1,173 @@
|
|||||||
|
package no.iktdev.mediaprocessing.ffmpeg.decoder
|
||||||
|
|
||||||
|
import java.lang.StringBuilder
|
||||||
|
import java.time.LocalTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
class FfmpegProgressDecoder {
|
||||||
|
|
||||||
|
data class DecodedProgressData(
|
||||||
|
val frame: Int?,
|
||||||
|
val fps: Double?,
|
||||||
|
val stream_0_0_q: Double?,
|
||||||
|
val bitrate: String?,
|
||||||
|
val total_size: Int?,
|
||||||
|
val out_time_us: Long?,
|
||||||
|
val out_time_ms: Long?,
|
||||||
|
val out_time: String?,
|
||||||
|
val dup_frames: Int?,
|
||||||
|
val drop_frames: Int?,
|
||||||
|
val speed: Double?,
|
||||||
|
val progress: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
val expectedKeys = listOf<String>(
|
||||||
|
"frame=",
|
||||||
|
"fps=",
|
||||||
|
"stream_0_0_q=",
|
||||||
|
"bitrate=",
|
||||||
|
"total_size=",
|
||||||
|
"out_time_us=",
|
||||||
|
"out_time_ms=",
|
||||||
|
"out_time=",
|
||||||
|
"dup_frames=",
|
||||||
|
"drop_frames=",
|
||||||
|
"speed=",
|
||||||
|
"progress="
|
||||||
|
)
|
||||||
|
var duration: Int? = null
|
||||||
|
set(value) {
|
||||||
|
if (field == null || field == 0)
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
var durationTime: String = "NA"
|
||||||
|
fun parseVideoProgress(lines: List<String>): DecodedProgressData? {
|
||||||
|
var frame: Int? = null
|
||||||
|
var progress: String? = null
|
||||||
|
val metadataMap = mutableMapOf<String, String>()
|
||||||
|
|
||||||
|
try {
|
||||||
|
val eqValue = Regex("=")
|
||||||
|
for (line in lines) {
|
||||||
|
val keyValuePairs = Regex("=\\s*").replace(line, "=").split(" ").filter { it.isNotBlank() }.filter { eqValue.containsMatchIn(it) }
|
||||||
|
for (keyValuePair in keyValuePairs) {
|
||||||
|
val (key, value) = keyValuePair.split("=")
|
||||||
|
metadataMap[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame == null) {
|
||||||
|
frame = metadataMap["frame"]?.toIntOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
progress = metadataMap["progress"]
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (progress != null) {
|
||||||
|
// When "progress" is found, build and return the VideoMetadata object
|
||||||
|
DecodedProgressData(
|
||||||
|
frame, metadataMap["fps"]?.toDoubleOrNull(), metadataMap["stream_0_0_q"]?.toDoubleOrNull(),
|
||||||
|
metadataMap["bitrate"], metadataMap["total_size"]?.toIntOrNull(), metadataMap["out_time_us"]?.toLongOrNull(),
|
||||||
|
metadataMap["out_time_ms"]?.toLongOrNull(), metadataMap["out_time"], metadataMap["dup_frames"]?.toIntOrNull(),
|
||||||
|
metadataMap["drop_frames"]?.toIntOrNull(), metadataMap["speed"]?.replace("x", "", ignoreCase = true)?.toDoubleOrNull(), progress
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null // If "progress" is not found, return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val parsedDurations: MutableList<String> = mutableListOf()
|
||||||
|
var hasReadContinue: Boolean = false
|
||||||
|
fun defineDuration(value: String) {
|
||||||
|
if (hasReadContinue) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (value.contains(Regex("progress=continue"))) {
|
||||||
|
hasReadContinue = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val results = Regex("Duration:\\s*([^,]+),").find(value)?.groupValues?.firstOrNull() ?: return
|
||||||
|
|
||||||
|
val parsedDuration = Regex("[0-9]+:[0-9]+:[0-9]+.[0-9]+").find(results.toString())?.value ?: return
|
||||||
|
|
||||||
|
if (!parsedDurations.contains(parsedDuration)) {
|
||||||
|
parsedDurations.add(parsedDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
val longestDuration = parsedDurations.mapNotNull {
|
||||||
|
timeSpanToSeconds(it)
|
||||||
|
}.maxOrNull() ?: return
|
||||||
|
|
||||||
|
duration = longestDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun timeSpanToSeconds(time: String?): Int?
|
||||||
|
{
|
||||||
|
time ?: return null
|
||||||
|
val timeString = Regex("[0-9]+:[0-9]+:[0-9]+.[0-9]+").find(time) ?: return null
|
||||||
|
val strippedMS = Regex("[0-9]+:[0-9]+:[0-9]+").find(timeString.value) ?: return null
|
||||||
|
val outTime = LocalTime.parse(strippedMS.value, DateTimeFormatter.ofPattern("HH:mm:ss"))
|
||||||
|
return outTime.toSecondOfDay()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getProgress(decoded: DecodedProgressData): FfmpegDecodedProgress {
|
||||||
|
if (duration == null)
|
||||||
|
return FfmpegDecodedProgress(duration = durationTime, time = "NA", speed = "NA")
|
||||||
|
val progressTime = timeSpanToSeconds(decoded.out_time) ?: 0
|
||||||
|
val progress = floor((progressTime.toDouble() / duration!!.toDouble()) *100).toInt()
|
||||||
|
|
||||||
|
val ect = getEstimatedTimeRemaining(decoded)
|
||||||
|
|
||||||
|
return FfmpegDecodedProgress(
|
||||||
|
progress = progress,
|
||||||
|
estimatedCompletionSeconds = ect,
|
||||||
|
estimatedCompletion = getETA(ect),
|
||||||
|
duration = durationTime,
|
||||||
|
time = decoded.out_time ?: "NA",
|
||||||
|
speed = decoded.speed?.toString() ?: "NA"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getEstimatedTimeRemaining(decoded: DecodedProgressData): Long {
|
||||||
|
val position = timeSpanToSeconds(decoded.out_time) ?: 0
|
||||||
|
return if(duration == null || decoded.speed == null) -1 else
|
||||||
|
Math.round(Math.round(duration!!.toDouble() - position.toDouble()) / decoded.speed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getECT(time: Long): ECT {
|
||||||
|
var seconds = time
|
||||||
|
val day = TimeUnit.SECONDS.toDays(seconds)
|
||||||
|
seconds -= TimeUnit.DAYS.toSeconds(day)
|
||||||
|
|
||||||
|
val hour = TimeUnit.SECONDS.toHours(seconds)
|
||||||
|
seconds -= TimeUnit.HOURS.toSeconds(hour)
|
||||||
|
|
||||||
|
val minute = TimeUnit.SECONDS.toMinutes(seconds)
|
||||||
|
seconds -= TimeUnit.MINUTES.toSeconds(minute)
|
||||||
|
|
||||||
|
return ECT(day.toInt(), hour.toInt(), minute.toInt(), seconds.toInt())
|
||||||
|
}
|
||||||
|
private fun getETA(time: Long): String {
|
||||||
|
val etc = getECT(time) ?: return "Unknown"
|
||||||
|
val str = StringBuilder()
|
||||||
|
if (etc.day > 0) {
|
||||||
|
str.append("${etc.day}d").append(" ")
|
||||||
|
}
|
||||||
|
if (etc.hour > 0) {
|
||||||
|
str.append("${etc.hour}h").append(" ")
|
||||||
|
}
|
||||||
|
if (etc.day == 0 && etc.minute > 0) {
|
||||||
|
str.append("${etc.minute}m").append(" ")
|
||||||
|
}
|
||||||
|
if (etc.hour == 0 && etc.second > 0) {
|
||||||
|
str.append("${etc.second}s").append(" ")
|
||||||
|
}
|
||||||
|
return str.toString().trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user