148 lines
5.7 KiB
Kotlin
148 lines
5.7 KiB
Kotlin
package dev.inmo.postssystem.features.content.binary.server
|
|
|
|
import com.benasher44.uuid.uuid4
|
|
import dev.inmo.micro_utils.coroutines.plus
|
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
|
import dev.inmo.micro_utils.mime_types.KnownMimeTypes
|
|
import dev.inmo.micro_utils.pagination.*
|
|
import dev.inmo.micro_utils.repos.*
|
|
import dev.inmo.postssystem.features.common.common.FileBasedInputProvider
|
|
import dev.inmo.postssystem.features.content.common.*
|
|
import dev.inmo.postssystem.features.content.server.storage.ServerContentStorage
|
|
import dev.inmo.postssystem.features.files.common.*
|
|
import dev.inmo.postssystem.features.files.common.storage.FilesStorage
|
|
import io.ktor.util.asStream
|
|
import io.ktor.utils.io.core.copyTo
|
|
import io.ktor.utils.io.streams.asOutput
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.flow.Flow
|
|
import kotlinx.coroutines.flow.map
|
|
import java.io.File
|
|
|
|
class BinaryServerContentStorage(
|
|
private val filesStorage: FilesStorage,
|
|
private val previewFilesStorage: FilesStorage,
|
|
private val cropper: ImagesCropper,
|
|
private val scope: CoroutineScope
|
|
) : ServerContentStorage<BinaryContent> {
|
|
private val FileId.asContentId
|
|
get() = ContentId(string)
|
|
private val ContentId.asFileId
|
|
get() = FileId(string)
|
|
private val FullFileInfoStorageWrapper.asRegisteredContent
|
|
get() = RegisteredContent(
|
|
id.asContentId,
|
|
BinaryContent(
|
|
fileInfo.name,
|
|
fileInfo.mimeType,
|
|
fileInfo.inputProvider
|
|
)
|
|
)
|
|
private val BinaryContent.asFullFileInfo
|
|
get() = FullFileInfo(
|
|
filename,
|
|
mimeType,
|
|
inputProvider
|
|
)
|
|
override val deletedObjectsIdsFlow: Flow<ContentId> = filesStorage.deletedObjectsIdsFlow.map { it.asContentId }
|
|
override val newObjectsFlow: Flow<RegisteredContent> = filesStorage.newObjectsFlow.map { it.asRegisteredContent }
|
|
override val updatedObjectsFlow: Flow<RegisteredContent> = filesStorage.updatedObjectsFlow.map { it.asRegisteredContent }
|
|
|
|
private val fullsRemovedJob = deletedObjectsIdsFlow.subscribeSafelyWithoutExceptions(scope) {
|
|
previewFilesStorage.deleteById(it.asFileId)
|
|
}
|
|
|
|
private val previewCroppingJob = (newObjectsFlow + updatedObjectsFlow).subscribeSafelyWithoutExceptions(scope) {
|
|
val content = it.content
|
|
val fileId = it.id.asFileId
|
|
if (content !is BinaryContent) {
|
|
return@subscribeSafelyWithoutExceptions
|
|
}
|
|
val fullFileInfo = filesStorage.getFullFileInfo(fileId) ?: return@subscribeSafelyWithoutExceptions
|
|
val fileInfo = fullFileInfo.fileInfo
|
|
|
|
if (fileInfo.mimeType is KnownMimeTypes.Image) {
|
|
cropper.crop(fileInfo.inputProvider).subscribeSafelyWithoutExceptions(scope) {
|
|
val tempFile = File.createTempFile(uuid4().toString(), ".${fileInfo.name.extension}").apply {
|
|
deleteOnExit()
|
|
createNewFile()
|
|
}
|
|
runCatching {
|
|
tempFile.outputStream().use { output ->
|
|
it.asStream().use { input ->
|
|
input.copyTo(output)
|
|
}
|
|
}
|
|
val newFullFileInfo = FullFileInfo(
|
|
fileInfo.name,
|
|
fileInfo.mimeType,
|
|
FileBasedInputProvider(tempFile)
|
|
)
|
|
if (previewFilesStorage.contains(fileId)) {
|
|
previewFilesStorage.update(
|
|
fullFileInfo.id,
|
|
newFullFileInfo
|
|
)
|
|
} else {
|
|
previewFilesStorage.create(newFullFileInfo).firstOrNull()
|
|
}
|
|
}
|
|
tempFile.delete()
|
|
}
|
|
}
|
|
}
|
|
|
|
override suspend fun create(values: List<BinaryContent>): List<RegisteredContent> {
|
|
return filesStorage.create(
|
|
values.map { it.asFullFileInfo }
|
|
).map { it.asRegisteredContent }
|
|
}
|
|
|
|
override suspend fun deleteById(ids: List<ContentId>) {
|
|
filesStorage.deleteById(ids.map { it.asFileId })
|
|
}
|
|
|
|
override suspend fun update(id: ContentId, value: BinaryContent): RegisteredContent? {
|
|
return filesStorage.update(
|
|
id.asFileId,
|
|
value.asFullFileInfo
|
|
) ?.asRegisteredContent
|
|
}
|
|
|
|
override suspend fun update(values: List<UpdatedValuePair<ContentId, BinaryContent>>): List<RegisteredContent> {
|
|
return filesStorage.update(
|
|
values.map { (id, content) ->
|
|
id.asFileId to content.asFullFileInfo
|
|
}
|
|
).map {
|
|
it.asRegisteredContent
|
|
}
|
|
}
|
|
|
|
override suspend fun contains(id: ContentId): Boolean {
|
|
return filesStorage.contains(id.asFileId)
|
|
}
|
|
|
|
override suspend fun count(): Long = filesStorage.count()
|
|
|
|
override suspend fun getById(id: ContentId): RegisteredContent? {
|
|
return filesStorage.getFullFileInfo(id.asFileId) ?.asRegisteredContent
|
|
}
|
|
|
|
override suspend fun getByPagination(pagination: Pagination): PaginationResult<RegisteredContent> {
|
|
return filesStorage.getByPagination(pagination).let {
|
|
it.changeResultsUnchecked(
|
|
it.results.mapNotNull {
|
|
filesStorage.getFullFileInfo(it.id) ?.asRegisteredContent
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
override suspend fun getContentPreview(id: ContentId): RegisteredContent? {
|
|
val fileId = id.asFileId
|
|
val fileInfo = previewFilesStorage.getFullFileInfo(fileId) ?: return null
|
|
return fileInfo.asRegisteredContent
|
|
}
|
|
}
|