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$/shared" />
|
||||
<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>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<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>
|
||||
543
.idea/workspace.xml
generated
543
.idea/workspace.xml
generated
@ -4,194 +4,86 @@
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<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$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/ConverterApplication.kt" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/ConverterEnv.kt" beforeDir="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/kotlin/no/iktdev/mediaprocessing/converter/TaskCoordinator.kt" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/convert/ConvertListener.kt" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/convert/Converter2.kt" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter/tasks/ConvertService.kt" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/CoordinatorApplication.kt" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/CoordinatorEventCoordinator.kt" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/CoordinatorEventListener.kt" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/CoordinatorUtils.kt" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/EventsDatabase.kt" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/Implementations.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$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/controller/InfoController.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$/apps/coordinator/src/main/kotlin/no/iktdev/mediaprocessing/coordinator/controller/RequestEventController.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" />
|
||||
<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/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/converter/build.gradle.kts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/converter/src/main/resources/application.properties" 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/processer/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/processer/build.gradle.kts" afterDir="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$/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/build.gradle.kts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/settings.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/settings.gradle.kts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/shared/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/shared/build.gradle.kts" afterDir="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$/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$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/DatabaseDeserializerTest.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$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/H2DataSource2.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$/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$/shared/common/src/test/kotlin/no/iktdev/mediaprocessing/shared/common/tests/PersistentEventMangerTestBase.kt" beforeDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@ -263,9 +155,10 @@
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="JUnit5 Test Class" />
|
||||
<option value="Kotlin Interface" />
|
||||
<option value="Kotlin Object" />
|
||||
<option value="Kotlin File" />
|
||||
<option value="JUnit5 Test Class" />
|
||||
<option value="Kotlin Class" />
|
||||
</list>
|
||||
</option>
|
||||
@ -282,6 +175,9 @@
|
||||
<component name="MarkdownSettingsMigration">
|
||||
<option name="stateVersion" value="1" />
|
||||
</component>
|
||||
<component name="ProblemsViewState">
|
||||
<option name="selectedTabId" value="DEPENDENCY_CHECKER_PROBLEMS_TAB" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"customColor": "",
|
||||
"associatedIndex": 8
|
||||
@ -303,6 +199,9 @@
|
||||
"Gradle.ConvertWorkTaskListenerTest.validate_shouldIProcessAndHandleEvent1.executor": "Run",
|
||||
"Gradle.ConvertWorkTaskListenerTest.validate_shouldIProcessAndHandleEvent2.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.validateMediaInfo.executor": "Run",
|
||||
"Gradle.DatabaseDeserializerTest.validateMetadataRead.executor": "Debug",
|
||||
@ -314,6 +213,8 @@
|
||||
"Gradle.FileNameParserTest.assertDotRemoval.executor": "Run",
|
||||
"Gradle.FileNameParserTest.assertTitleFails.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:clean].executor": "Run",
|
||||
"Gradle.MediaProcessing2 [build].executor": "Run",
|
||||
@ -354,10 +255,10 @@
|
||||
"git-widget-placeholder": "v5",
|
||||
"ignore.virus.scanning.warn.message": "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.proportion": "0.0",
|
||||
"project.structure.side.proportion": "0.0",
|
||||
"project.structure.proportion": "0.15",
|
||||
"project.structure.side.proportion": "0.2",
|
||||
"settings.editor.selected.configurable": "reference.settingsdialog.project.gradle"
|
||||
},
|
||||
"keyToStringList": {
|
||||
@ -368,10 +269,11 @@
|
||||
}]]></component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="D:\Workspace\MediaProcessing2\apps\processer\src\test\kotlin\no\iktdev\mediaprocessing\processer" />
|
||||
<recent name="D:\Workspace\MediaProcessing2\shared\eventi\src\main\kotlin\no\iktdev\eventi" />
|
||||
<recent name="D:\Workspace\MediaProcessing2\shared\common\src\main\kotlin\no\iktdev\mediaprocessing\shared\common\contract" />
|
||||
<recent name="D:\Workspace\MediaProcessing2\shared\contract\src\main\kotlin\no\iktdev\mediaprocessing\shared\contract\tables" />
|
||||
<recent name="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/model" />
|
||||
<recent name="$PROJECT_DIR$/shared/common/src/main/kotlin/no/iktdev/mediaprocessing/shared/common/event_task_contract" />
|
||||
<recent name="$PROJECT_DIR$/shared/event-task-contract/src/main/kotlin/no/iktdev/mediaprocessing/event_task_contract" />
|
||||
<recent name="$PROJECT_DIR$/apps/converter/src/main/kotlin/no/iktdev/mediaprocessing/converter" />
|
||||
<recent name="$PROJECT_DIR$/apps/converter/src/main/resources" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<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" />
|
||||
</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.shared.common.database" />
|
||||
<recent name="no.iktdev.mediaprocessing.ui.service" />
|
||||
<recent name="no.iktdev.mediaprocessing.shared.common.database.cal" />
|
||||
<recent name="no.iktdev.mediaprocessing.shared.common.contract" />
|
||||
</key>
|
||||
<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>
|
||||
</component>
|
||||
<component name="RunAnythingCache">
|
||||
@ -412,30 +319,8 @@
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
<component name="RunManager" selected="Gradle.Tests in 'MediaProcessing.apps.coordinator.test'">
|
||||
<configuration name="MediaProcessing2 [clean]" 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">
|
||||
<component name="RunManager" selected="Gradle.ConverterApplicationTest">
|
||||
<configuration name="ConverterApplicationTest" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
@ -446,31 +331,9 @@
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":apps:coordinator: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=":apps:converter:test" />
|
||||
<option value="--tests" />
|
||||
<option value=""no.iktdev.mediaprocessing.coordinator.tasksV2.mapping.streams.VideoArgumentsTest.hevcStream2"" />
|
||||
<option value=""no.iktdev.mediaprocessing.converter.ConverterApplicationTest"" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
@ -481,7 +344,7 @@
|
||||
<RunAsTest>true</RunAsTest>
|
||||
<method v="2" />
|
||||
</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>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
@ -492,9 +355,9 @@
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":apps:coordinator:test" />
|
||||
<option value=":apps:converter:test" />
|
||||
<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>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
@ -505,7 +368,7 @@
|
||||
<RunAsTest>true</RunAsTest>
|
||||
<method v="2" />
|
||||
</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>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
@ -516,9 +379,57 @@
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":apps:coordinator:test" />
|
||||
<option value=":apps:converter:test" />
|
||||
<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>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
@ -530,20 +441,20 @@
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<list>
|
||||
<item itemvalue="Gradle.MediaProcessing2 [clean]" />
|
||||
<item itemvalue="Gradle.Tests in 'MediaProcessing.apps.coordinator.test'" />
|
||||
<item itemvalue="Gradle.VideoArgumentsTest.hevcStream2" />
|
||||
<item itemvalue="Gradle.VideoArgumentsTest.vc1Stream" />
|
||||
<item itemvalue="Gradle.VideoArgumentsTest.vc1Stream2" />
|
||||
<item itemvalue="Gradle.ConverterApplicationTest.Verify that we can access TaskStore" />
|
||||
<item itemvalue="Gradle.ConverterApplicationTest.context loads and common configuration is available" />
|
||||
<item itemvalue="Gradle.ConverterApplicationTest" />
|
||||
<item itemvalue="Gradle.FlywayMigrationTest" />
|
||||
<item itemvalue="Gradle.FlywayMigrationTest.should run flyway migrations and create expected tables" />
|
||||
<item itemvalue="Kotlin.UIApplicationKt" />
|
||||
</list>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Gradle.Tests in 'MediaProcessing.apps.coordinator.test'" />
|
||||
<item itemvalue="Gradle.MediaProcessing2 [clean]" />
|
||||
<item itemvalue="Gradle.VideoArgumentsTest.vc1Stream2" />
|
||||
<item itemvalue="Gradle.VideoArgumentsTest.vc1Stream" />
|
||||
<item itemvalue="Gradle.VideoArgumentsTest.hevcStream2" />
|
||||
<item itemvalue="Gradle.ConverterApplicationTest" />
|
||||
<item itemvalue="Gradle.FlywayMigrationTest.should run flyway migrations and create expected tables" />
|
||||
<item itemvalue="Gradle.ConverterApplicationTest.Verify that we can access TaskStore" />
|
||||
<item itemvalue="Gradle.ConverterApplicationTest.context loads and common configuration is available" />
|
||||
<item itemvalue="Gradle.FlywayMigrationTest" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
@ -556,13 +467,6 @@
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1722029797420</updated>
|
||||
</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">
|
||||
<option name="closed" value="true" />
|
||||
<created>1742231127291</created>
|
||||
@ -906,7 +810,15 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1755640931383</updated>
|
||||
</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 />
|
||||
</component>
|
||||
<component name="Vcs.Log.Tabs.Properties">
|
||||
@ -933,7 +845,6 @@
|
||||
</option>
|
||||
</component>
|
||||
<component name="VcsManagerConfiguration">
|
||||
<MESSAGE value="Fixed created" />
|
||||
<MESSAGE value="Changes to behaviour" />
|
||||
<MESSAGE value="Trimming data in meta" />
|
||||
<MESSAGE value="Fixes" />
|
||||
@ -958,7 +869,8 @@
|
||||
<MESSAGE value="Updated ignore" />
|
||||
<MESSAGE value="Info controller" />
|
||||
<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 name="XDebuggerManager">
|
||||
<breakpoint-manager>
|
||||
@ -1028,16 +940,6 @@
|
||||
<line>31</line>
|
||||
<option name="timeStamp" value="43" />
|
||||
</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">
|
||||
<url>file://$PROJECT_DIR$/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/explorer/ExplorerCore.kt</url>
|
||||
<line>40</line>
|
||||
@ -1279,6 +1181,85 @@
|
||||
<line>32</line>
|
||||
<option name="timeStamp" value="189" />
|
||||
</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>
|
||||
</breakpoint-manager>
|
||||
</component>
|
||||
|
||||
@ -20,5 +20,5 @@ tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
jvmToolchain(21)
|
||||
}
|
||||
@ -1,9 +1,8 @@
|
||||
plugins {
|
||||
id("java")
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.spring") version "1.5.31"
|
||||
id("org.springframework.boot") version "2.5.5"
|
||||
id("io.spring.dependency-management") version "1.0.11.RELEASE"
|
||||
id("org.springframework.boot")
|
||||
id("io.spring.dependency-management")
|
||||
}
|
||||
|
||||
group = "no.iktdev.mediaprocessing.apps"
|
||||
@ -27,40 +26,32 @@ repositories {
|
||||
}
|
||||
}
|
||||
|
||||
val exposedVersion = "0.44.0"
|
||||
dependencies {
|
||||
|
||||
/*Spring boot*/
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
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.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("com.google.code.gson:gson:2.8.9")
|
||||
implementation("org.json:json:20210307")
|
||||
|
||||
implementation("no.iktdev:exfl:0.0.16-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("com.github.vishna:watchservice-ktx:master-SNAPSHOT")
|
||||
implementation("com.github.pgreze:kotlin-process:1.4.1")
|
||||
|
||||
implementation(project(mapOf("path" to ":shared:eventi")))
|
||||
implementation(project(mapOf("path" to ":shared:common")))
|
||||
|
||||
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
|
||||
testImplementation("io.mockk:mockk:1.12.0")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
@ -78,5 +69,5 @@ tasks.jar {
|
||||
}
|
||||
|
||||
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 {
|
||||
id("java")
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.spring") version "1.5.31"
|
||||
id("org.springframework.boot") version "3.2.0"
|
||||
id("io.spring.dependency-management") version "1.1.4"
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "1.5.0" // Legg til Kotlin Serialization-plugin
|
||||
kotlin("plugin.spring")
|
||||
id("org.springframework.boot")
|
||||
id("io.spring.dependency-management")
|
||||
id("org.jetbrains.kotlin.plugin.serialization")
|
||||
}
|
||||
|
||||
group = "no.iktdev.mediaprocessing"
|
||||
@ -44,9 +44,8 @@ dependencies {
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
||||
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("org.jetbrains.exposed:exposed-core:$exposedVersion")
|
||||
@ -85,7 +84,7 @@ tasks.withType<Test> {
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
jvmToolchain(21)
|
||||
}
|
||||
|
||||
tasks.bootJar {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
plugins {
|
||||
id("java")
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.spring") version "1.5.31"
|
||||
id("org.springframework.boot") version "2.5.5"
|
||||
id("io.spring.dependency-management") version "1.0.11.RELEASE"
|
||||
kotlin("plugin.spring")
|
||||
id("org.springframework.boot")
|
||||
id("io.spring.dependency-management")
|
||||
}
|
||||
|
||||
group = "no.iktdev.mediaprocessing.apps"
|
||||
@ -49,7 +49,7 @@ dependencies {
|
||||
implementation("com.github.pgreze:kotlin-process:1.4.1")
|
||||
|
||||
//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")))
|
||||
|
||||
|
||||
@ -83,5 +83,5 @@ tasks.jar {
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
jvmToolchain(21)
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
plugins {
|
||||
id("java")
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.spring") version "1.5.31"
|
||||
id("org.springframework.boot") version "2.5.5"
|
||||
id("io.spring.dependency-management") version "1.0.11.RELEASE"
|
||||
kotlin("plugin.spring")
|
||||
id("org.springframework.boot")
|
||||
id("io.spring.dependency-management")
|
||||
}
|
||||
|
||||
group = "no.iktdev.mediaprocessing"
|
||||
@ -44,7 +44,6 @@ dependencies {
|
||||
implementation ("mysql:mysql-connector-java:8.0.29")
|
||||
|
||||
implementation("no.iktdev:exfl:0.0.16-SNAPSHOT")
|
||||
implementation(project(mapOf("path" to ":shared:eventi")))
|
||||
implementation(project(mapOf("path" to ":shared:common")))
|
||||
|
||||
|
||||
@ -56,7 +55,7 @@ tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
jvmToolchain(21)
|
||||
}
|
||||
|
||||
tasks.bootJar {
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
plugins {
|
||||
id("java")
|
||||
kotlin("plugin.spring") version "1.5.31"
|
||||
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"
|
||||
@ -21,5 +24,5 @@ tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
jvmToolchain(21)
|
||||
}
|
||||
@ -10,7 +10,7 @@ findProject(":apps:processer")?.name = "processer"
|
||||
|
||||
|
||||
findProject(":shared")?.name = "shared"
|
||||
findProject(":shared:eventi")?.name = "eventi"
|
||||
findProject(":shared:ffmpeg")?.name = "ffmpeg"
|
||||
findProject(":shared:common")?.name = "common"
|
||||
|
||||
include("apps")
|
||||
@ -20,5 +20,7 @@ include("apps:converter")
|
||||
include("apps:processer")
|
||||
|
||||
include("shared")
|
||||
include("shared:eventi")
|
||||
include("shared:common")
|
||||
|
||||
include("shared:ffmpeg")
|
||||
include("shared:event-task-contract")
|
||||
@ -45,5 +45,5 @@ tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
jvmToolchain(21)
|
||||
}
|
||||
@ -1,7 +1,10 @@
|
||||
plugins {
|
||||
id("java")
|
||||
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"
|
||||
@ -11,14 +14,16 @@ repositories {
|
||||
mavenCentral()
|
||||
maven("https://jitpack.io")
|
||||
maven {
|
||||
name = "ReposiliteReleases"
|
||||
url = uri("https://reposilite.iktdev.no/releases")
|
||||
}
|
||||
maven {
|
||||
name = "ReposiliteSnapshot"
|
||||
url = uri("https://reposilite.iktdev.no/snapshots")
|
||||
}
|
||||
}
|
||||
|
||||
val exposedVersion = "0.44.0"
|
||||
val exposedVersion = "0.61.0"
|
||||
|
||||
dependencies {
|
||||
|
||||
@ -28,9 +33,11 @@ dependencies {
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
|
||||
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.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")
|
||||
|
||||
@ -38,22 +45,33 @@ dependencies {
|
||||
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 ("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("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("io.mockk:mockk:1.12.0")
|
||||
testImplementation("com.h2database:h2:1.4.200")
|
||||
testImplementation("org.assertj:assertj-core:3.4.1")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.2")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.2")
|
||||
testImplementation("io.kotlintest:kotlintest-assertions:3.3.2")
|
||||
testImplementation("io.mockk:mockk:1.12.0")
|
||||
implementation("com.h2database:h2:2.2.220")
|
||||
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")
|
||||
|
||||
}
|
||||
@ -63,5 +81,5 @@ tasks.test {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import kotlinx.coroutines.delay
|
||||
import mu.KotlinLogging
|
||||
import no.iktdev.eventi.ZDS
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate
|
||||
import org.springframework.web.client.RestTemplate
|
||||
import org.springframework.web.client.postForEntity
|
||||
@ -10,6 +12,7 @@ import java.io.FileInputStream
|
||||
import java.io.RandomAccessFile
|
||||
import java.net.InetAddress
|
||||
import java.security.MessageDigest
|
||||
import java.time.LocalDateTime
|
||||
import java.util.zip.CRC32
|
||||
|
||||
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
|
||||
|
||||
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.junit.jupiter.api.Assertions.*
|
||||
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