mirror of https://github.com/usbharu/Hideout.git
wip
This commit is contained in:
parent
e13858e068
commit
922bdb4991
|
@ -17,7 +17,7 @@
|
|||
package activitypub.webfinger
|
||||
|
||||
import dev.usbharu.hideout.SpringApplication
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.owl.producer.api.OwlProducer
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.flywaydb.core.Flyway
|
||||
|
|
|
@ -20,8 +20,6 @@ import dev.usbharu.hideout.SpringApplication
|
|||
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.CustomEmojis
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toReaction
|
||||
import dev.usbharu.owl.producer.api.OwlProducer
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package util
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
|
||||
object TestTransaction : Transaction {
|
||||
override suspend fun <T> transaction(block: suspend () -> T): T = block()
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package util
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUser
|
||||
import dev.usbharu.httpsignature.common.HttpHeaders
|
||||
|
|
|
@ -25,7 +25,7 @@ import com.nimbusds.jose.jwk.source.JWKSource
|
|||
import com.nimbusds.jose.proc.SecurityContext
|
||||
import dev.usbharu.hideout.activitypub.domain.model.StringORObjectSerializer
|
||||
import dev.usbharu.hideout.activitypub.domain.model.StringOrObject
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureFilter
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureHeaderChecker
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.httpsignature.HttpSignatureUserDetailsService
|
||||
|
|
|
@ -35,7 +35,6 @@ class OwlProducerRunner(private val owlProducer: OwlProducer, private val taskDe
|
|||
}
|
||||
|
||||
override fun destroy() {
|
||||
System.err.println("destroy aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
runBlocking {
|
||||
owlProducer.stop()
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package dev.usbharu.hideout.application.infrastructure.exposed
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.slf4j.MDCContext
|
||||
import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package dev.usbharu.hideout.application.service.init
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.meta.Jwt
|
||||
import dev.usbharu.hideout.core.domain.model.meta.Meta
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
|
|
|
@ -16,11 +16,8 @@
|
|||
|
||||
package dev.usbharu.hideout.application.service.init
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.exception.NotInitException
|
||||
import dev.usbharu.hideout.core.domain.model.meta.Jwt
|
||||
import dev.usbharu.hideout.core.domain.model.meta.Meta
|
||||
import dev.usbharu.hideout.core.domain.model.meta.MetaRepository
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.application.service.init
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.meta.Jwt
|
||||
import dev.usbharu.hideout.core.domain.model.meta.Meta
|
||||
import dev.usbharu.hideout.core.domain.model.meta.MetaRepository
|
||||
import dev.usbharu.hideout.util.ServerUtil
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
import java.security.KeyPairGenerator
|
||||
import java.util.*
|
||||
|
||||
@Service
|
||||
class ServerInitialiseServiceImpl(
|
||||
private val metaRepository: MetaRepository,
|
||||
private val transaction: Transaction
|
||||
) :
|
||||
ServerInitialiseService {
|
||||
|
||||
val logger: Logger = LoggerFactory.getLogger(ServerInitialiseServiceImpl::class.java)
|
||||
|
||||
override suspend fun init() {
|
||||
transaction.transaction {
|
||||
val savedMeta = metaRepository.get()
|
||||
val implementationVersion = ServerUtil.getImplementationVersion()
|
||||
if (wasInitialised(savedMeta).not()) {
|
||||
logger.info("Start Initialise")
|
||||
initialise(implementationVersion)
|
||||
logger.info("Finish Initialise")
|
||||
return@transaction
|
||||
}
|
||||
|
||||
if (isVersionChanged(requireNotNull(savedMeta))) {
|
||||
logger.info("Version changed!! (${savedMeta.version} -> $implementationVersion)")
|
||||
updateVersion(savedMeta, implementationVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun wasInitialised(meta: Meta?): Boolean {
|
||||
logger.debug("Initialise checking...")
|
||||
return meta != null
|
||||
}
|
||||
|
||||
private fun isVersionChanged(meta: Meta): Boolean = meta.version != ServerUtil.getImplementationVersion()
|
||||
|
||||
private suspend fun initialise(implementationVersion: String) {
|
||||
val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
|
||||
keyPairGenerator.initialize(2048)
|
||||
val generateKeyPair = keyPairGenerator.generateKeyPair()
|
||||
val jwt = Jwt(
|
||||
UUID.randomUUID(),
|
||||
Base64.getEncoder().encodeToString(generateKeyPair.private.encoded),
|
||||
Base64.getEncoder().encodeToString(generateKeyPair.public.encoded)
|
||||
)
|
||||
val meta = Meta(implementationVersion, jwt)
|
||||
metaRepository.save(meta)
|
||||
}
|
||||
|
||||
private suspend fun updateVersion(meta: Meta, version: String) {
|
||||
metaRepository.save(meta.copy(version = version))
|
||||
}
|
||||
}
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.usecase.actor
|
||||
package dev.usbharu.hideout.core.application.actor
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import org.springframework.stereotype.Service
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.usecase.actor
|
||||
package dev.usbharu.hideout.core.application.actor
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.*
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.usecase.actor
|
||||
package dev.usbharu.hideout.core.application.actor
|
||||
|
||||
data class RegisterLocalActor(
|
||||
val name: String,
|
|
@ -14,11 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.usecase.actor
|
||||
package dev.usbharu.hideout.core.application.actor
|
||||
|
||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
|
@ -1,4 +1,4 @@
|
|||
package dev.usbharu.hideout.core.usecase.actor
|
||||
package dev.usbharu.hideout.core.application.actor
|
||||
|
||||
import org.springframework.stereotype.Service
|
||||
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.usecase.actor
|
||||
package dev.usbharu.hideout.core.application.actor
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import org.springframework.stereotype.Service
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.usecase.actor
|
||||
package dev.usbharu.hideout.core.application.actor
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||
import org.springframework.stereotype.Service
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.usecase.post
|
||||
package dev.usbharu.hideout.core.application.post
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.post.Post2Repository
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.usecase.post
|
||||
package dev.usbharu.hideout.core.application.post
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.usecase.post
|
||||
package dev.usbharu.hideout.core.application.post
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Actor2Repository
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.usecase.post
|
||||
package dev.usbharu.hideout.core.application.post
|
||||
|
||||
data class UpdateLocalNote(
|
||||
val postId: Long,
|
|
@ -14,9 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.usecase.post
|
||||
package dev.usbharu.hideout.core.application.post
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.media.MediaId
|
||||
import dev.usbharu.hideout.core.domain.model.post.Post2Repository
|
||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.application.external
|
||||
package dev.usbharu.hideout.core.application.shared
|
||||
|
||||
import org.springframework.stereotype.Service
|
||||
|
|
@ -16,10 +16,13 @@
|
|||
|
||||
package dev.usbharu.hideout.core.domain.model.emoji
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
|
||||
import dev.usbharu.hideout.core.domain.model.shared.Domain
|
||||
import java.net.URI
|
||||
import java.time.Instant
|
||||
|
||||
sealed class Emoji {
|
||||
abstract val domain: String
|
||||
abstract val domain: Domain
|
||||
abstract val name: String
|
||||
|
||||
@Suppress("FunctionMinLength")
|
||||
|
@ -33,13 +36,13 @@ sealed class Emoji {
|
|||
}
|
||||
|
||||
data class CustomEmoji(
|
||||
val id: Long,
|
||||
val id: EmojiId,
|
||||
override val name: String,
|
||||
override val domain: String,
|
||||
val instanceId: Long?,
|
||||
val url: String,
|
||||
override val domain: Domain,
|
||||
val instanceId: InstanceId,
|
||||
val url: URI,
|
||||
val category: String?,
|
||||
val createdAt: Instant
|
||||
val createdAt: Instant,
|
||||
) : Emoji() {
|
||||
override fun id(): String = id.toString()
|
||||
}
|
||||
|
@ -47,6 +50,6 @@ data class CustomEmoji(
|
|||
data class UnicodeEmoji(
|
||||
override val name: String
|
||||
) : Emoji() {
|
||||
override val domain: String = "unicode.org"
|
||||
override val domain: Domain = Domain("unicode.org")
|
||||
override fun id(): String = name
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.filter
|
||||
|
||||
data class Filter(
|
||||
val id: Long,
|
||||
val userId: Long,
|
||||
val name: String,
|
||||
val context: List<FilterType>,
|
||||
val filterAction: FilterAction,
|
||||
)
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.filter
|
||||
|
||||
@Suppress("EnumEntryNameCase", "EnumNaming")
|
||||
enum class FilterAction {
|
||||
warn,
|
||||
hide
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.filter
|
||||
|
||||
interface FilterRepository {
|
||||
|
||||
suspend fun generateId(): Long
|
||||
suspend fun save(filter: Filter): Filter
|
||||
suspend fun findById(id: Long): Filter?
|
||||
|
||||
suspend fun findByUserIdAndId(userId: Long, id: Long): Filter?
|
||||
suspend fun findByUserIdAndType(userId: Long, types: List<FilterType>): List<Filter>
|
||||
suspend fun deleteById(id: Long)
|
||||
|
||||
suspend fun deleteByUserIdAndId(userId: Long, id: Long)
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.filter
|
||||
|
||||
@Suppress("EnumEntryNameCase", "EnumNaming")
|
||||
enum class FilterType {
|
||||
home,
|
||||
notifications,
|
||||
public,
|
||||
thread,
|
||||
account
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.filterkeyword
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterMode
|
||||
|
||||
data class FilterKeyword(
|
||||
val id: Long,
|
||||
val filterId: Long,
|
||||
val keyword: String,
|
||||
val mode: FilterMode
|
||||
)
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.filterkeyword
|
||||
|
||||
interface FilterKeywordRepository {
|
||||
suspend fun generateId(): Long
|
||||
suspend fun save(filterKeyword: FilterKeyword): FilterKeyword
|
||||
suspend fun saveAll(filterKeywordList: List<FilterKeyword>)
|
||||
suspend fun findById(id: Long): FilterKeyword?
|
||||
suspend fun deleteById(id: Long)
|
||||
suspend fun deleteByFilterId(filterId: Long)
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.instance
|
||||
|
||||
class Nodeinfo private constructor() {
|
||||
|
||||
var links: List<Links> = emptyList()
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Nodeinfo
|
||||
|
||||
return links == other.links
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = links.hashCode()
|
||||
}
|
||||
|
||||
class Links private constructor() {
|
||||
var rel: String? = null
|
||||
var href: String? = null
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Links
|
||||
|
||||
if (rel != other.rel) return false
|
||||
if (href != other.href) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = rel?.hashCode() ?: 0
|
||||
result = 31 * result + (href?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:Suppress("Filename")
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.instance
|
||||
|
||||
@Suppress("ClassNaming")
|
||||
class Nodeinfo2_0 {
|
||||
var metadata: Metadata? = null
|
||||
var software: Software? = null
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Nodeinfo2_0
|
||||
|
||||
if (metadata != other.metadata) return false
|
||||
if (software != other.software) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = metadata?.hashCode() ?: 0
|
||||
result = 31 * result + (software?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class Metadata {
|
||||
var nodeName: String? = null
|
||||
var nodeDescription: String? = null
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Metadata
|
||||
|
||||
if (nodeName != other.nodeName) return false
|
||||
if (nodeDescription != other.nodeDescription) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = nodeName?.hashCode() ?: 0
|
||||
result = 31 * result + (nodeDescription?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class Software {
|
||||
var name: String? = null
|
||||
var version: String? = null
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Software
|
||||
|
||||
if (name != other.name) return false
|
||||
if (version != other.version) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = name?.hashCode() ?: 0
|
||||
result = 31 * result + (version?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
package dev.usbharu.hideout.core.domain.model.media
|
||||
|
||||
enum class FileType {
|
||||
Image,
|
|
@ -16,34 +16,17 @@
|
|||
|
||||
package dev.usbharu.hideout.core.domain.model.media
|
||||
|
||||
import dev.usbharu.hideout.core.service.media.FileType
|
||||
import dev.usbharu.hideout.core.service.media.MimeType
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.MediaAttachment
|
||||
import java.net.URI
|
||||
|
||||
data class Media(
|
||||
val id: Long,
|
||||
val name: String,
|
||||
val url: String,
|
||||
val remoteUrl: String?,
|
||||
val thumbnailUrl: String?,
|
||||
val id: MediaId,
|
||||
val name: MediaName,
|
||||
val url: URI,
|
||||
val remoteUrl: URI?,
|
||||
val thumbnailUrl: URI?,
|
||||
val type: FileType,
|
||||
val mimeType: MimeType,
|
||||
val blurHash: String?,
|
||||
val description: String? = null
|
||||
val blurHash: MediaBlurHash?,
|
||||
val description: MediaDescription? = null,
|
||||
)
|
||||
|
||||
fun Media.toMediaAttachments(): MediaAttachment = MediaAttachment(
|
||||
id = id.toString(),
|
||||
type = when (type) {
|
||||
FileType.Image -> MediaAttachment.Type.image
|
||||
FileType.Video -> MediaAttachment.Type.video
|
||||
FileType.Audio -> MediaAttachment.Type.audio
|
||||
FileType.Unknown -> MediaAttachment.Type.unknown
|
||||
},
|
||||
url = url,
|
||||
previewUrl = thumbnailUrl,
|
||||
remoteUrl = remoteUrl,
|
||||
description = description,
|
||||
blurhash = blurHash,
|
||||
textUrl = url
|
||||
)
|
||||
|
|
|
@ -14,10 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.filter
|
||||
package dev.usbharu.hideout.core.domain.model.media
|
||||
|
||||
enum class FilterMode {
|
||||
WHOLE_WORD,
|
||||
REGEX,
|
||||
NONE
|
||||
}
|
||||
@JvmInline
|
||||
value class MediaBlurHash(val hash: String)
|
|
@ -14,9 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.post
|
||||
package dev.usbharu.hideout.core.domain.model.media
|
||||
|
||||
data class FormattedPostContent(
|
||||
val html: String,
|
||||
val content: String
|
||||
)
|
||||
@JvmInline
|
||||
value class MediaDescription(val description: String)
|
|
@ -14,6 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.meta
|
||||
package dev.usbharu.hideout.core.domain.model.media
|
||||
|
||||
data class Meta(val version: String, val jwt: Jwt)
|
||||
@JvmInline
|
||||
value class MediaName(val name: String)
|
|
@ -14,6 +14,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
package dev.usbharu.hideout.core.domain.model.media
|
||||
|
||||
data class MimeType(val type: String, val subtype: String, val fileType: FileType)
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.meta
|
||||
|
||||
import java.util.*
|
||||
|
||||
data class Jwt(val kid: UUID, val privateKey: String, val publicKey: String)
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.meta
|
||||
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
interface MetaRepository {
|
||||
|
||||
suspend fun save(meta: Meta)
|
||||
|
||||
suspend fun get(): Meta?
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.notification
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
data class Notification(
|
||||
val id: Long,
|
||||
val type: String,
|
||||
val userId: Long,
|
||||
val sourceActorId: Long?,
|
||||
val postId: Long?,
|
||||
val text: String?,
|
||||
val reactionId: Long?,
|
||||
val createdAt: Instant
|
||||
)
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.notification
|
||||
|
||||
interface NotificationRepository {
|
||||
suspend fun generateId(): Long
|
||||
suspend fun save(notification: Notification): Notification
|
||||
suspend fun findById(id: Long): Notification?
|
||||
suspend fun deleteById(id: Long)
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.reaction
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.Emoji
|
||||
|
||||
data class Reaction(val id: Long, val emoji: Emoji, val postId: Long, val actorId: Long)
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.reaction
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.Emoji
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
@Suppress("FunctionMaxLength", "TooManyFunctions")
|
||||
interface ReactionRepository {
|
||||
suspend fun generateId(): Long
|
||||
suspend fun save(reaction: Reaction): Reaction
|
||||
suspend fun delete(reaction: Reaction): Reaction
|
||||
suspend fun deleteByPostId(postId: Long): Int
|
||||
suspend fun deleteByActorId(actorId: Long): Int
|
||||
suspend fun deleteByPostIdAndActorId(postId: Long, actorId: Long)
|
||||
suspend fun deleteByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji)
|
||||
suspend fun findById(id: Long): Reaction?
|
||||
suspend fun findByPostId(postId: Long): List<Reaction>
|
||||
suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction?
|
||||
suspend fun existByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Boolean
|
||||
suspend fun existByPostIdAndActorIdAndUnicodeEmoji(postId: Long, actorId: Long, unicodeEmoji: String): Boolean
|
||||
suspend fun existByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji): Boolean
|
||||
suspend fun existByPostIdAndActor(postId: Long, actorId: Long): Boolean
|
||||
suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List<Reaction>
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.relationship
|
||||
|
||||
/**
|
||||
* ユーザーとの関係を表します
|
||||
*
|
||||
* @property actorId ユーザー
|
||||
* @property targetActorId 相手ユーザー
|
||||
* @property following フォローしているか
|
||||
* @property blocking ブロックしているか
|
||||
* @property muting ミュートしているか
|
||||
* @property followRequest フォローリクエストを送っているか
|
||||
* @property ignoreFollowRequestToTarget フォローリクエストを無視しているか
|
||||
*/
|
||||
data class Relationship(
|
||||
val actorId: Long,
|
||||
val targetActorId: Long,
|
||||
val following: Boolean,
|
||||
val blocking: Boolean,
|
||||
val muting: Boolean,
|
||||
val followRequest: Boolean,
|
||||
val ignoreFollowRequestToTarget: Boolean
|
||||
)
|
|
@ -1,178 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.relationship
|
||||
|
||||
import dev.usbharu.hideout.application.infrastructure.exposed.Page
|
||||
import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList
|
||||
import dev.usbharu.hideout.application.infrastructure.exposed.withPagination
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository
|
||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() {
|
||||
override val logger: Logger
|
||||
get() = Companion.logger
|
||||
|
||||
override suspend fun save(relationship: Relationship): Relationship = query {
|
||||
val singleOrNull = Relationships.selectAll().where {
|
||||
(Relationships.actorId eq relationship.actorId).and(
|
||||
Relationships.targetActorId eq relationship.targetActorId
|
||||
)
|
||||
}.forUpdate().singleOrNull()
|
||||
|
||||
if (singleOrNull == null) {
|
||||
Relationships.insert {
|
||||
it[actorId] = relationship.actorId
|
||||
it[targetActorId] = relationship.targetActorId
|
||||
it[following] = relationship.following
|
||||
it[blocking] = relationship.blocking
|
||||
it[muting] = relationship.muting
|
||||
it[followRequest] = relationship.followRequest
|
||||
it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestToTarget
|
||||
}
|
||||
} else {
|
||||
Relationships.update({
|
||||
(Relationships.actorId eq relationship.actorId).and(
|
||||
Relationships.targetActorId eq relationship.targetActorId
|
||||
)
|
||||
}) {
|
||||
it[following] = relationship.following
|
||||
it[blocking] = relationship.blocking
|
||||
it[muting] = relationship.muting
|
||||
it[followRequest] = relationship.followRequest
|
||||
it[ignoreFollowRequestFromTarget] = relationship.ignoreFollowRequestToTarget
|
||||
}
|
||||
}
|
||||
return@query relationship
|
||||
}
|
||||
|
||||
override suspend fun delete(relationship: Relationship): Unit = query {
|
||||
Relationships.deleteWhere {
|
||||
(actorId eq relationship.actorId).and(
|
||||
targetActorId eq relationship.targetActorId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship? = query {
|
||||
return@query Relationships.selectAll()
|
||||
.where { (Relationships.actorId eq actorId).and(Relationships.targetActorId eq targetActorId) }
|
||||
.singleOrNull()?.toRelationships()
|
||||
}
|
||||
|
||||
override suspend fun deleteByActorIdOrTargetActorId(actorId: Long, targetActorId: Long): Unit = query {
|
||||
Relationships.deleteWhere {
|
||||
Relationships.actorId.eq(actorId).or(Relationships.targetActorId.eq(targetActorId))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List<Relationship> = query {
|
||||
return@query Relationships
|
||||
.selectAll().where { Relationships.targetActorId eq targetId and (Relationships.following eq following) }
|
||||
.map { it.toRelationships() }
|
||||
}
|
||||
|
||||
override suspend fun countByTargetIdAndFollowing(targetId: Long, following: Boolean): Int = query {
|
||||
return@query Relationships
|
||||
.selectAll()
|
||||
.where {
|
||||
Relationships.targetActorId eq targetId and (Relationships.following eq following)
|
||||
}
|
||||
.count()
|
||||
.toInt()
|
||||
}
|
||||
|
||||
override suspend fun countByUserIdAndFollowing(userId: Long, following: Boolean): Int = query {
|
||||
return@query Relationships
|
||||
.selectAll()
|
||||
.where {
|
||||
Relationships.actorId eq userId and (Relationships.following eq following)
|
||||
}
|
||||
.count()
|
||||
.toInt()
|
||||
}
|
||||
|
||||
override suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest(
|
||||
targetId: Long,
|
||||
followRequest: Boolean,
|
||||
ignoreFollowRequest: Boolean,
|
||||
page: Page.PageByMaxId,
|
||||
): PaginationList<Relationship, Long> = query {
|
||||
val query = Relationships.selectAll().where {
|
||||
Relationships.targetActorId.eq(targetId).and(Relationships.followRequest.eq(followRequest))
|
||||
.and(Relationships.ignoreFollowRequestFromTarget.eq(ignoreFollowRequest))
|
||||
}
|
||||
|
||||
val resultRowList = query.withPagination(page, Relationships.id)
|
||||
|
||||
return@query PaginationList(
|
||||
resultRowList.map { it.toRelationships() },
|
||||
resultRowList.next?.value,
|
||||
resultRowList.prev?.value
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun findByActorIdAndMuting(
|
||||
actorId: Long,
|
||||
muting: Boolean,
|
||||
page: Page.PageByMaxId,
|
||||
): PaginationList<Relationship, Long> = query {
|
||||
val query =
|
||||
Relationships.selectAll().where { Relationships.actorId.eq(actorId).and(Relationships.muting.eq(muting)) }
|
||||
|
||||
val resultRowList = query.withPagination(page, Relationships.id)
|
||||
|
||||
return@query PaginationList(
|
||||
resultRowList.map { it.toRelationships() },
|
||||
resultRowList.next?.value,
|
||||
resultRowList.prev?.value
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(RelationshipRepositoryImpl::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
fun ResultRow.toRelationships(): Relationship = Relationship(
|
||||
actorId = this[Relationships.actorId],
|
||||
targetActorId = this[Relationships.targetActorId],
|
||||
following = this[Relationships.following],
|
||||
blocking = this[Relationships.blocking],
|
||||
muting = this[Relationships.muting],
|
||||
followRequest = this[Relationships.followRequest],
|
||||
ignoreFollowRequestToTarget = this[Relationships.ignoreFollowRequestFromTarget]
|
||||
)
|
||||
|
||||
object Relationships : LongIdTable("relationships") {
|
||||
val actorId = long("actor_id").references(Actors.id)
|
||||
val targetActorId = long("target_actor_id").references(Actors.id)
|
||||
val following = bool("following")
|
||||
val blocking = bool("blocking")
|
||||
val muting = bool("muting")
|
||||
val followRequest = bool("follow_request")
|
||||
val ignoreFollowRequestFromTarget = bool("ignore_follow_request")
|
||||
|
||||
init {
|
||||
uniqueIndex(actorId, targetActorId)
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.shared.domainevent
|
||||
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
|
||||
data class DomainEvent(
|
||||
private val id: String,
|
||||
private val name: String,
|
||||
private val occurredOn: Instant,
|
||||
private val body: DomainEventBody,
|
||||
) {
|
||||
companion object {
|
||||
fun create(name: String, body: DomainEventBody): DomainEvent {
|
||||
return DomainEvent(UUID.randomUUID().toString(), name, Instant.now(), body)
|
||||
}
|
||||
|
||||
fun reconstruct(id: String, name: String, occurredOn: Instant, body: DomainEventBody): DomainEvent {
|
||||
return DomainEvent(id, name, occurredOn, body)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.shared.domainevent
|
||||
|
||||
abstract class DomainEventBody(val map: Map<String, Any>) {
|
||||
fun toMap(): Map<String, Any> {
|
||||
return map
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.timeline
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
import org.springframework.data.annotation.Id
|
||||
import org.springframework.data.mongodb.core.index.CompoundIndex
|
||||
import org.springframework.data.mongodb.core.mapping.Document
|
||||
|
||||
@Document
|
||||
@CompoundIndex(def = "{'userId':1,'timelineId':1,'postId':1}", unique = true)
|
||||
data class Timeline(
|
||||
@Id
|
||||
val id: Long,
|
||||
val userId: Long,
|
||||
val timelineId: Long,
|
||||
val postId: Long,
|
||||
val postActorId: Long,
|
||||
val createdAt: Long,
|
||||
val replyId: Long?,
|
||||
val repostId: Long?,
|
||||
val visibility: Visibility,
|
||||
val sensitive: Boolean,
|
||||
val isLocal: Boolean,
|
||||
val isPureRepost: Boolean = false,
|
||||
val mediaIds: List<Long> = emptyList(),
|
||||
val emojiIds: List<Long> = emptyList()
|
||||
)
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.domain.model.timeline
|
||||
|
||||
interface TimelineRepository {
|
||||
suspend fun generateId(): Long
|
||||
suspend fun save(timeline: Timeline): Timeline
|
||||
suspend fun saveAll(timelines: List<Timeline>): List<Timeline>
|
||||
suspend fun findByUserId(id: Long): List<Timeline>
|
||||
suspend fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
|
||||
}
|
|
@ -18,8 +18,6 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
|||
|
||||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
|
|
@ -17,10 +17,9 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||
|
||||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
||||
import dev.usbharu.hideout.core.domain.model.media.FileType
|
||||
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Media.mimeType
|
||||
import dev.usbharu.hideout.core.service.media.FileType
|
||||
import dev.usbharu.hideout.core.service.media.MimeType
|
||||
import dev.usbharu.hideout.core.domain.model.media.MimeType
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.slf4j.Logger
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.meta.Jwt
|
||||
import dev.usbharu.hideout.core.domain.model.meta.MetaRepository
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.util.*
|
||||
|
|
|
@ -1,232 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.infrastructure.exposedrepository
|
||||
|
||||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.Emoji
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
|
||||
import dev.usbharu.hideout.core.domain.model.reaction.Reaction
|
||||
import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository
|
||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
class ReactionRepositoryImpl(
|
||||
private val idGenerateService: IdGenerateService
|
||||
) : ReactionRepository, AbstractRepository() {
|
||||
override val logger: Logger
|
||||
get() = Companion.logger
|
||||
|
||||
override suspend fun generateId(): Long = idGenerateService.generateId()
|
||||
|
||||
override suspend fun save(reaction: Reaction): Reaction = query {
|
||||
if (Reactions.selectAll().where { Reactions.id eq reaction.id }.forUpdate().empty()) {
|
||||
Reactions.insert {
|
||||
it[id] = reaction.id
|
||||
if (reaction.emoji is CustomEmoji) {
|
||||
it[customEmojiId] = reaction.emoji.id
|
||||
it[unicodeEmoji] = null
|
||||
} else {
|
||||
it[customEmojiId] = null
|
||||
it[unicodeEmoji] = reaction.emoji.name
|
||||
}
|
||||
it[postId] = reaction.postId
|
||||
it[actorId] = reaction.actorId
|
||||
}
|
||||
} else {
|
||||
Reactions.update({ Reactions.id eq reaction.id }) {
|
||||
if (reaction.emoji is CustomEmoji) {
|
||||
it[customEmojiId] = reaction.emoji.id
|
||||
it[unicodeEmoji] = null
|
||||
} else {
|
||||
it[customEmojiId] = null
|
||||
it[unicodeEmoji] = reaction.emoji.name
|
||||
}
|
||||
it[postId] = reaction.postId
|
||||
it[actorId] = reaction.actorId
|
||||
}
|
||||
}
|
||||
return@query reaction
|
||||
}
|
||||
|
||||
override suspend fun delete(reaction: Reaction): Reaction = query {
|
||||
if (reaction.emoji is CustomEmoji) {
|
||||
Reactions.deleteWhere {
|
||||
id.eq(reaction.id).and(postId.eq(reaction.postId)).and(actorId.eq(reaction.actorId))
|
||||
.and(customEmojiId.eq(reaction.emoji.id))
|
||||
}
|
||||
} else {
|
||||
Reactions.deleteWhere {
|
||||
id.eq(reaction.id).and(postId.eq(reaction.postId)).and(actorId.eq(reaction.actorId))
|
||||
.and(unicodeEmoji.eq(reaction.emoji.name))
|
||||
}
|
||||
}
|
||||
return@query reaction
|
||||
}
|
||||
|
||||
override suspend fun deleteByPostId(postId: Long): Int = query {
|
||||
return@query Reactions.deleteWhere {
|
||||
Reactions.postId eq postId
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteByActorId(actorId: Long): Int = query {
|
||||
return@query Reactions.deleteWhere {
|
||||
Reactions.actorId eq actorId
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteByPostIdAndActorId(postId: Long, actorId: Long): Unit = query {
|
||||
Reactions.deleteWhere {
|
||||
Reactions.postId eq postId and (Reactions.actorId eq actorId)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji): Unit = query {
|
||||
if (emoji is CustomEmoji) {
|
||||
Reactions.deleteWhere {
|
||||
Reactions.postId.eq(postId)
|
||||
.and(Reactions.actorId.eq(actorId))
|
||||
.and(customEmojiId.eq(emoji.id))
|
||||
}
|
||||
} else {
|
||||
Reactions.deleteWhere {
|
||||
Reactions.postId.eq(postId)
|
||||
.and(Reactions.actorId.eq(actorId))
|
||||
.and(unicodeEmoji.eq(emoji.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findById(id: Long): Reaction? = query {
|
||||
return@query Reactions.leftJoin(CustomEmojis).selectAll().where { Reactions.id eq id }.singleOrNull()
|
||||
?.toReaction()
|
||||
}
|
||||
|
||||
override suspend fun findByPostId(postId: Long): List<Reaction> = query {
|
||||
return@query Reactions.leftJoin(CustomEmojis).selectAll().where { Reactions.postId eq postId }
|
||||
.map { it.toReaction() }
|
||||
}
|
||||
|
||||
override suspend fun findByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Reaction? =
|
||||
query {
|
||||
return@query Reactions.leftJoin(CustomEmojis).selectAll().where {
|
||||
Reactions.postId eq postId and (Reactions.actorId eq actorId).and(
|
||||
Reactions.customEmojiId.eq<Long?>(
|
||||
emojiId
|
||||
)
|
||||
)
|
||||
}.singleOrNull()?.toReaction()
|
||||
}
|
||||
|
||||
override suspend fun existByPostIdAndActorIdAndEmojiId(postId: Long, actorId: Long, emojiId: Long): Boolean =
|
||||
query {
|
||||
return@query Reactions.selectAll().where {
|
||||
Reactions.postId
|
||||
.eq<Long>(postId)
|
||||
.and(Reactions.actorId.eq<Long>(actorId))
|
||||
.and(Reactions.customEmojiId.eq<Long?>(emojiId))
|
||||
}.empty().not()
|
||||
}
|
||||
|
||||
override suspend fun existByPostIdAndActorIdAndUnicodeEmoji(
|
||||
postId: Long,
|
||||
actorId: Long,
|
||||
unicodeEmoji: String
|
||||
): Boolean = query {
|
||||
return@query Reactions.selectAll().where {
|
||||
Reactions.postId
|
||||
.eq<Long>(postId)
|
||||
.and(Reactions.actorId.eq<Long>(actorId))
|
||||
.and(Reactions.unicodeEmoji.eq<String?>(unicodeEmoji))
|
||||
}.empty().not()
|
||||
}
|
||||
|
||||
override suspend fun existByPostIdAndActorIdAndEmoji(postId: Long, actorId: Long, emoji: Emoji): Boolean = query {
|
||||
val query = Reactions.selectAll().where {
|
||||
Reactions.postId
|
||||
.eq(postId)
|
||||
.and(Reactions.actorId.eq(actorId))
|
||||
}
|
||||
|
||||
if (emoji is UnicodeEmoji) {
|
||||
query.andWhere { Reactions.unicodeEmoji eq emoji.name }
|
||||
} else {
|
||||
emoji as CustomEmoji
|
||||
query.andWhere { Reactions.customEmojiId eq emoji.id }
|
||||
}
|
||||
|
||||
return@query query.empty().not()
|
||||
}
|
||||
|
||||
override suspend fun existByPostIdAndActor(postId: Long, actorId: Long): Boolean = query {
|
||||
Reactions.selectAll().where { Reactions.postId.eq(postId).and(Reactions.actorId.eq(actorId)) }.empty().not()
|
||||
}
|
||||
|
||||
override suspend fun findByPostIdAndActorId(postId: Long, actorId: Long): List<Reaction> = query {
|
||||
return@query Reactions.leftJoin(CustomEmojis)
|
||||
.selectAll().where { Reactions.postId eq postId and (Reactions.actorId eq actorId) }
|
||||
.map { it.toReaction() }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(ReactionRepositoryImpl::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
fun ResultRow.toReaction(): Reaction {
|
||||
val emoji = if (this[Reactions.customEmojiId] != null) {
|
||||
CustomEmoji(
|
||||
id = this[Reactions.customEmojiId]!!,
|
||||
name = this[CustomEmojis.name],
|
||||
domain = this[CustomEmojis.domain],
|
||||
instanceId = this[CustomEmojis.instanceId],
|
||||
url = this[CustomEmojis.url],
|
||||
category = this[CustomEmojis.category],
|
||||
createdAt = this[CustomEmojis.createdAt]
|
||||
)
|
||||
} else if (this[Reactions.unicodeEmoji] != null) {
|
||||
UnicodeEmoji(this[Reactions.unicodeEmoji]!!)
|
||||
} else {
|
||||
throw IllegalStateException("customEmojiId and unicodeEmoji is null.")
|
||||
}
|
||||
|
||||
return Reaction(
|
||||
this[Reactions.id].value,
|
||||
emoji,
|
||||
this[Reactions.postId],
|
||||
this[Reactions.actorId]
|
||||
)
|
||||
}
|
||||
|
||||
object Reactions : LongIdTable("reactions") {
|
||||
val customEmojiId = long("custom_emoji_id").references(CustomEmojis.id).nullable()
|
||||
val unicodeEmoji = varchar("unicode_emoji", 255).nullable()
|
||||
val postId: Column<Long> =
|
||||
long("post_id").references(Posts.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
|
||||
val actorId: Column<Long> =
|
||||
long("actor_id").references(Actors.id, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE)
|
||||
|
||||
init {
|
||||
uniqueIndex(customEmojiId, postId, actorId)
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.infrastructure.mongorepository
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.data.mongodb.repository.MongoRepository
|
||||
|
||||
@Suppress("LongParameterList", "FunctionMaxLength")
|
||||
interface MongoTimelineRepository : MongoRepository<Timeline, Long> {
|
||||
fun findByUserId(id: Long): List<Timeline>
|
||||
fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline>
|
||||
fun findByUserIdAndTimelineIdAndPostIdBetweenAndIsLocal(
|
||||
userId: Long?,
|
||||
timelineId: Long?,
|
||||
postIdMin: Long?,
|
||||
postIdMax: Long?,
|
||||
isLocal: Boolean?,
|
||||
pageable: Pageable
|
||||
): List<Timeline>
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.infrastructure.mongorepository
|
||||
|
||||
import dev.usbharu.hideout.application.service.id.IdGenerateService
|
||||
import dev.usbharu.hideout.core.domain.exception.resource.DuplicateException
|
||||
import dev.usbharu.hideout.core.domain.exception.resource.ResourceAccessException
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.dao.DataAccessException
|
||||
import org.springframework.dao.DuplicateKeyException
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
@ConditionalOnProperty("hideout.use-mongodb", havingValue = "true", matchIfMissing = false)
|
||||
class MongoTimelineRepositoryWrapper(
|
||||
private val mongoTimelineRepository: MongoTimelineRepository,
|
||||
private val idGenerateService: IdGenerateService
|
||||
) :
|
||||
TimelineRepository {
|
||||
override suspend fun generateId(): Long = idGenerateService.generateId()
|
||||
|
||||
override suspend fun save(timeline: Timeline): Timeline {
|
||||
return withContext(Dispatchers.IO) {
|
||||
mongoTimelineRepository.save(timeline)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun saveAll(timelines: List<Timeline>): List<Timeline> {
|
||||
try {
|
||||
return mongoTimelineRepository.saveAll(timelines)
|
||||
} catch (e: DuplicateKeyException) {
|
||||
throw DuplicateException("Timeline duplicate.", e)
|
||||
} catch (e: DataAccessException) {
|
||||
throw ResourceAccessException(e)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByUserId(id: Long): List<Timeline> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
mongoTimelineRepository.findByUserId(id)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByUserIdAndTimelineId(userId: Long, timelineId: Long): List<Timeline> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
mongoTimelineRepository.findByUserIdAndTimelineId(userId, timelineId)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.exception.HttpSignatureVerifyException
|
||||
import dev.usbharu.hideout.util.RsaUtil
|
||||
import dev.usbharu.httpsignature.common.HttpMethod
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
|
||||
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
|
|
|
@ -19,7 +19,7 @@ package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
|
|||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
|
||||
|
||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.application.external.Transaction
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.exception.resource.UserNotFoundException
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
|
|
@ -40,13 +40,7 @@ class AuthController(
|
|||
|
||||
@PostMapping("/auth/sign_up")
|
||||
suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm): String {
|
||||
val registerAccount = authApiService.registerAccount(
|
||||
RegisterAccountDto(
|
||||
signUpForm.username,
|
||||
signUpForm.password,
|
||||
signUpForm.recaptchaResponse
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
return "redirect:" + registerAccount.url
|
||||
}
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.query.model
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterType
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.FilterKeywords
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Filters
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toFilter
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toFilterKeyword
|
||||
import org.jetbrains.exposed.sql.Query
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
class ExposedFilterQueryService : FilterQueryService {
|
||||
override suspend fun findByUserIdAndType(userId: Long, types: List<FilterType>): List<FilterQueryModel> {
|
||||
return Filters
|
||||
.rightJoin(FilterKeywords)
|
||||
.selectAll()
|
||||
.where { Filters.userId eq userId }
|
||||
.toFilterQueryModel()
|
||||
}
|
||||
|
||||
override suspend fun findByUserId(userId: Long): List<FilterQueryModel> {
|
||||
return Filters
|
||||
.rightJoin(FilterKeywords)
|
||||
.selectAll()
|
||||
.where { Filters.userId eq userId }
|
||||
.toFilterQueryModel()
|
||||
}
|
||||
|
||||
override suspend fun findByUserIdAndId(userId: Long, id: Long): FilterQueryModel? {
|
||||
return Filters
|
||||
.leftJoin(FilterKeywords)
|
||||
.selectAll()
|
||||
.where { Filters.userId eq userId and (Filters.id eq id) }
|
||||
.toFilterQueryModel()
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByUserIdAndKeywordId(userId: Long, keywordId: Long): FilterQueryModel? {
|
||||
return Filters
|
||||
.leftJoin(FilterKeywords)
|
||||
.selectAll()
|
||||
.where { Filters.userId eq userId and (FilterKeywords.id eq keywordId) }
|
||||
.toFilterQueryModel()
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
private fun Query.toFilterQueryModel(): List<FilterQueryModel> {
|
||||
return this
|
||||
.groupBy { it[Filters.id] }
|
||||
.map { it.value }
|
||||
.map {
|
||||
FilterQueryModel.of(
|
||||
it.first().toFilter(),
|
||||
it.map { resultRow -> resultRow.toFilterKeyword() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.query.model
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.filter.Filter
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterAction
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterType
|
||||
import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword
|
||||
|
||||
data class FilterQueryModel(
|
||||
val id: Long,
|
||||
val userId: Long,
|
||||
val name: String,
|
||||
val context: List<FilterType>,
|
||||
val filterAction: FilterAction,
|
||||
val keywords: List<FilterKeyword>
|
||||
) {
|
||||
companion object {
|
||||
@Suppress("FunctionMinLength")
|
||||
fun of(filter: Filter, keywords: List<FilterKeyword>): FilterQueryModel = FilterQueryModel(
|
||||
id = filter.id,
|
||||
userId = filter.userId,
|
||||
name = filter.name,
|
||||
context = filter.context,
|
||||
filterAction = filter.filterAction,
|
||||
keywords = keywords
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.query.model
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterType
|
||||
|
||||
interface FilterQueryService {
|
||||
suspend fun findByUserIdAndType(userId: Long, types: List<FilterType>): List<FilterQueryModel>
|
||||
suspend fun findByUserId(userId: Long): List<FilterQueryModel>
|
||||
suspend fun findByUserIdAndId(userId: Long, id: Long): FilterQueryModel?
|
||||
suspend fun findByUserIdAndKeywordId(userId: Long, keywordId: Long): FilterQueryModel?
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.filter
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterMode
|
||||
|
||||
data class FilterKeyword(
|
||||
val keyword: String,
|
||||
val mode: FilterMode
|
||||
)
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.filter
|
||||
|
||||
import dev.usbharu.hideout.core.query.model.FilterQueryModel
|
||||
|
||||
data class FilterResult(
|
||||
val filter: FilterQueryModel,
|
||||
val keyword: String,
|
||||
)
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.filter
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterAction
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterType
|
||||
import dev.usbharu.hideout.core.query.model.FilterQueryModel
|
||||
|
||||
interface MuteService {
|
||||
suspend fun createFilter(
|
||||
title: String,
|
||||
context: List<FilterType>,
|
||||
action: FilterAction,
|
||||
keywords: List<FilterKeyword>,
|
||||
loginUser: Long
|
||||
): FilterQueryModel
|
||||
|
||||
suspend fun getFilters(userId: Long, types: List<FilterType> = emptyList()): List<FilterQueryModel>
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.filter
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.filter.Filter
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterAction
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterRepository
|
||||
import dev.usbharu.hideout.core.domain.model.filter.FilterType
|
||||
import dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeywordRepository
|
||||
import dev.usbharu.hideout.core.query.model.FilterQueryModel
|
||||
import dev.usbharu.hideout.core.query.model.FilterQueryService
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class MuteServiceImpl(
|
||||
private val filterRepository: FilterRepository,
|
||||
private val filterKeywordRepository: FilterKeywordRepository,
|
||||
private val filterQueryService: FilterQueryService
|
||||
) : MuteService {
|
||||
override suspend fun createFilter(
|
||||
title: String,
|
||||
context: List<FilterType>,
|
||||
action: FilterAction,
|
||||
keywords: List<FilterKeyword>,
|
||||
loginUser: Long
|
||||
): FilterQueryModel {
|
||||
val filter = Filter(
|
||||
filterRepository.generateId(),
|
||||
loginUser,
|
||||
title,
|
||||
context,
|
||||
action
|
||||
)
|
||||
|
||||
val filterKeywordList = keywords.map {
|
||||
dev.usbharu.hideout.core.domain.model.filterkeyword.FilterKeyword(
|
||||
filterKeywordRepository.generateId(),
|
||||
filter.id,
|
||||
it.keyword,
|
||||
it.mode
|
||||
)
|
||||
}
|
||||
|
||||
val savedFilter = filterRepository.save(filter)
|
||||
|
||||
filterKeywordRepository.saveAll(filterKeywordList)
|
||||
return FilterQueryModel.of(savedFilter, filterKeywordList)
|
||||
}
|
||||
|
||||
override suspend fun getFilters(userId: Long, types: List<FilterType>): List<FilterQueryModel> =
|
||||
filterQueryService.findByUserIdAndType(userId, types)
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.instance
|
||||
|
||||
data class InstanceCreateDto(
|
||||
val name: String?,
|
||||
val description: String?,
|
||||
val url: String,
|
||||
val iconUrl: String,
|
||||
val sharedInbox: String?,
|
||||
val software: String?,
|
||||
val version: String?,
|
||||
)
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.instance
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import dev.usbharu.hideout.core.domain.model.instance.Instance
|
||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository
|
||||
import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo
|
||||
import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo2_0
|
||||
import dev.usbharu.hideout.core.service.resource.ResourceResolveService
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
import java.net.URL
|
||||
import java.time.Instant
|
||||
|
||||
interface InstanceService {
|
||||
suspend fun fetchInstance(url: String, sharedInbox: String? = null): Instance
|
||||
suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance
|
||||
}
|
||||
|
||||
@Service
|
||||
class InstanceServiceImpl(
|
||||
private val instanceRepository: InstanceRepository,
|
||||
private val resourceResolveService: ResourceResolveService,
|
||||
@Qualifier("activitypub") private val objectMapper: ObjectMapper,
|
||||
) : InstanceService {
|
||||
override suspend fun fetchInstance(url: String, sharedInbox: String?): Instance {
|
||||
val u = URL(url)
|
||||
val resolveInstanceUrl = u.protocol + "://" + u.host
|
||||
|
||||
val instance = instanceRepository.findByUrl(resolveInstanceUrl)
|
||||
|
||||
if (instance != null) {
|
||||
return instance
|
||||
}
|
||||
|
||||
logger.info("Instance not found. try fetch instance info. url: {}", resolveInstanceUrl)
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
try {
|
||||
val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText()
|
||||
val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java)
|
||||
val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href }
|
||||
|
||||
for ((key, value) in nodeinfoPathMap) {
|
||||
when (key) {
|
||||
"http://nodeinfo.diaspora.software/ns/schema/2.0",
|
||||
"http://nodeinfo.diaspora.software/ns/schema/2.1",
|
||||
-> {
|
||||
val nodeinfo20 = objectMapper.readValue(
|
||||
resourceResolveService.resolve(value!!).bodyAsText(),
|
||||
Nodeinfo2_0::class.java
|
||||
)
|
||||
|
||||
val instanceCreateDto = InstanceCreateDto(
|
||||
name = nodeinfo20.metadata?.nodeName,
|
||||
description = nodeinfo20.metadata?.nodeDescription,
|
||||
url = resolveInstanceUrl,
|
||||
iconUrl = "$resolveInstanceUrl/favicon.ico",
|
||||
sharedInbox = sharedInbox,
|
||||
software = nodeinfo20.software?.name,
|
||||
version = nodeinfo20.software?.version
|
||||
)
|
||||
return createNewInstance(instanceCreateDto)
|
||||
}
|
||||
|
||||
else -> {
|
||||
throw IllegalStateException("Unknown nodeinfo versions: $key url: $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.warn("FAILED Fetch Instance", e)
|
||||
}
|
||||
return createNewInstance(
|
||||
InstanceCreateDto(
|
||||
name = null,
|
||||
description = null,
|
||||
url = resolveInstanceUrl,
|
||||
iconUrl = "$resolveInstanceUrl/favicon.ico",
|
||||
sharedInbox = null,
|
||||
software = null,
|
||||
version = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance {
|
||||
val instance = Instance(
|
||||
id = instanceRepository.generateId(),
|
||||
name = instanceCreateDto.name ?: instanceCreateDto.url,
|
||||
description = instanceCreateDto.description.orEmpty(),
|
||||
url = instanceCreateDto.url,
|
||||
iconUrl = instanceCreateDto.iconUrl,
|
||||
sharedInbox = instanceCreateDto.sharedInbox,
|
||||
software = instanceCreateDto.software ?: "unknown",
|
||||
version = instanceCreateDto.version ?: "unknown",
|
||||
isBlocked = false,
|
||||
isMuted = false,
|
||||
moderationNote = "",
|
||||
createdAt = Instant.now()
|
||||
)
|
||||
instanceRepository.save(instance)
|
||||
return instance
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(InstanceServiceImpl::class.java)
|
||||
}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import org.apache.tika.Tika
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Component
|
||||
import java.nio.file.Path
|
||||
|
||||
@Component
|
||||
class ApatcheTikaFileTypeDeterminationService : FileTypeDeterminationService {
|
||||
override fun fileType(
|
||||
byteArray: ByteArray,
|
||||
filename: String,
|
||||
contentType: String?
|
||||
): MimeType {
|
||||
logger.info("START Detect file type name: {}", filename)
|
||||
|
||||
val tika = Tika()
|
||||
|
||||
val detect = try {
|
||||
tika.detect(byteArray, filename)
|
||||
} catch (e: IllegalStateException) {
|
||||
logger.warn("FAILED Detect file type", e)
|
||||
"application/octet-stream"
|
||||
}
|
||||
|
||||
val type = detect.substringBefore("/")
|
||||
val fileType = when (type) {
|
||||
"image" -> {
|
||||
FileType.Image
|
||||
}
|
||||
|
||||
"video" -> {
|
||||
FileType.Video
|
||||
}
|
||||
|
||||
"audio" -> {
|
||||
FileType.Audio
|
||||
}
|
||||
|
||||
else -> {
|
||||
FileType.Unknown
|
||||
}
|
||||
}
|
||||
val mimeType = MimeType(type, detect.substringAfter("/"), fileType)
|
||||
|
||||
logger.info("SUCCESS Detect file type name: {},MimeType: {}", filename, mimeType)
|
||||
return mimeType
|
||||
}
|
||||
|
||||
override fun fileType(path: Path, filename: String): MimeType {
|
||||
logger.info("START Detect file type name: {}", filename)
|
||||
|
||||
val tika = Tika()
|
||||
|
||||
val detect = try {
|
||||
tika.detect(path)
|
||||
} catch (e: IllegalStateException) {
|
||||
logger.warn("FAILED Detect file type", e)
|
||||
"application/octet-stream"
|
||||
}
|
||||
|
||||
val type = detect.substringBefore("/")
|
||||
val fileType = when (type) {
|
||||
"image" -> {
|
||||
FileType.Image
|
||||
}
|
||||
|
||||
"video" -> {
|
||||
FileType.Video
|
||||
}
|
||||
|
||||
"audio" -> {
|
||||
FileType.Audio
|
||||
}
|
||||
|
||||
else -> {
|
||||
FileType.Unknown
|
||||
}
|
||||
}
|
||||
val mimeType = MimeType(type, detect.substringAfter("/"), fileType)
|
||||
|
||||
logger.info("SUCCESS Detect file type name: {},MimeType: {}", filename, mimeType)
|
||||
return mimeType
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(ApatcheTikaFileTypeDeterminationService::class.java)
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
interface FileTypeDeterminationService {
|
||||
fun fileType(byteArray: ByteArray, filename: String, contentType: String?): MimeType
|
||||
fun fileType(path: Path, filename: String): MimeType
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.application.config.LocalStorageConfig
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.stereotype.Service
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardOpenOption
|
||||
import kotlin.io.path.copyTo
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.outputStream
|
||||
|
||||
/**
|
||||
* ローカルファイルシステムにメディアを保存します
|
||||
*
|
||||
* @constructor
|
||||
* ApplicationConfigとLocalStorageConfigをもとに作成
|
||||
*
|
||||
* @param applicationConfig ApplicationConfig
|
||||
* @param localStorageConfig LocalStorageConfig
|
||||
*/
|
||||
@Service
|
||||
@ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true)
|
||||
class LocalFileSystemMediaDataStore(
|
||||
applicationConfig: ApplicationConfig,
|
||||
localStorageConfig: LocalStorageConfig
|
||||
) : MediaDataStore {
|
||||
|
||||
private val savePath: Path = Path.of(localStorageConfig.path).toAbsolutePath()
|
||||
|
||||
private val publicUrl = localStorageConfig.publicUrl ?: "${applicationConfig.url}/files/"
|
||||
|
||||
init {
|
||||
savePath.createDirectories()
|
||||
}
|
||||
|
||||
@Suppress("NestedBlockDepth")
|
||||
override suspend fun save(dataMediaSave: MediaSave): SavedMedia {
|
||||
val fileSavePath = buildSavePath(savePath, dataMediaSave.name)
|
||||
val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataMediaSave.name)
|
||||
|
||||
dataMediaSave.thumbnailInputStream?.inputStream()?.use {
|
||||
it.buffered().use { bufferedInputStream ->
|
||||
thumbnailSavePath.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)
|
||||
.use { outputStream ->
|
||||
outputStream.buffered().use {
|
||||
bufferedInputStream.transferTo(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dataMediaSave.fileInputStream.inputStream().use {
|
||||
it.buffered().use { bufferedInputStream ->
|
||||
fileSavePath.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)
|
||||
.use { outputStream -> outputStream.buffered().use { bufferedInputStream.transferTo(it) } }
|
||||
}
|
||||
}
|
||||
|
||||
return SuccessSavedMedia(
|
||||
dataMediaSave.name,
|
||||
publicUrl + dataMediaSave.name,
|
||||
publicUrl + "thumbnail-" + dataMediaSave.name
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia {
|
||||
logger.info("START Media upload. {}", dataSaveRequest.name)
|
||||
val fileSavePath = buildSavePath(savePath, dataSaveRequest.name)
|
||||
val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataSaveRequest.name)
|
||||
|
||||
val fileSavePathString = fileSavePath.toAbsolutePath().toString()
|
||||
logger.info("MEDIA save. path: {}", fileSavePathString)
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
try {
|
||||
dataSaveRequest.filePath.copyTo(fileSavePath)
|
||||
dataSaveRequest.thumbnailPath?.copyTo(thumbnailSavePath)
|
||||
} catch (e: Exception) {
|
||||
logger.warn("FAILED to Save the media.", e)
|
||||
return FaildSavedMedia("FAILED to Save the media.", "Failed copy to path: $fileSavePathString", e)
|
||||
}
|
||||
|
||||
logger.info("SUCCESS Media upload. {}", dataSaveRequest.name)
|
||||
return SuccessSavedMedia(
|
||||
dataSaveRequest.name,
|
||||
publicUrl + dataSaveRequest.name,
|
||||
publicUrl + "thumbnail-" + dataSaveRequest.name
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* メディアを削除します。サムネイルも削除されます。
|
||||
*
|
||||
* @param id 削除するメディアのid [SuccessSavedMedia.name]を指定します。
|
||||
*/
|
||||
override suspend fun delete(id: String) {
|
||||
logger.info("START Media delete. id: {}", id)
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
try {
|
||||
buildSavePath(savePath, id).deleteIfExists()
|
||||
buildSavePath(savePath, "thumbnail-$id").deleteIfExists()
|
||||
} catch (e: Exception) {
|
||||
logger.warn("FAILED Media delete. id: {}", id, e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildSavePath(savePath: Path, name: String): Path = savePath.resolve(name)
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(LocalFileSystemMediaDataStore::class.java)
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import java.awt.image.BufferedImage
|
||||
|
||||
interface MediaBlurhashService {
|
||||
fun generateBlurhash(bufferedImage: BufferedImage): String
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import io.trbl.blurhash.BlurHash
|
||||
import org.springframework.stereotype.Service
|
||||
import java.awt.image.BufferedImage
|
||||
|
||||
@Service
|
||||
class MediaBlurhashServiceImpl : MediaBlurhashService {
|
||||
override fun generateBlurhash(bufferedImage: BufferedImage): String = BlurHash.encode(bufferedImage)
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
/**
|
||||
* メディアを保存するインタフェース
|
||||
*
|
||||
*/
|
||||
interface MediaDataStore {
|
||||
/**
|
||||
* InputStreamを使用してメディアを保存します
|
||||
*
|
||||
* @param dataMediaSave FileとThumbnailのinputStream
|
||||
* @return 保存されたメディア
|
||||
*/
|
||||
suspend fun save(dataMediaSave: MediaSave): SavedMedia
|
||||
|
||||
/**
|
||||
* 一時ファイルのパスを使用してメディアを保存します
|
||||
*
|
||||
* @param dataSaveRequest FileとThumbnailのパス
|
||||
* @return 保存されたメディア
|
||||
*/
|
||||
suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia
|
||||
|
||||
/**
|
||||
* メディアを削除します
|
||||
* 実装はサムネイル、メタデータなども削除するべきです。
|
||||
*
|
||||
* @param id 削除するメディアのid 通常は[SuccessSavedMedia.name]を指定します。
|
||||
*/
|
||||
suspend fun delete(id: String)
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
interface MediaFileRenameService {
|
||||
/**
|
||||
* メディアをリネームします
|
||||
*
|
||||
* @param uploadName アップロードされた時点でのファイル名
|
||||
* @param uploadMimeType アップロードされた時点でのMimeType
|
||||
* @param processedName 処理後のファイル名
|
||||
* @param processedMimeType 処理後のMimeType
|
||||
* @return リネーム後のファイル名
|
||||
*/
|
||||
fun rename(uploadName: String, uploadMimeType: MimeType, processedName: String, processedMimeType: MimeType): String
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
data class MediaSave(
|
||||
val name: String,
|
||||
val prefix: String,
|
||||
val fileInputStream: ByteArray,
|
||||
val thumbnailInputStream: ByteArray?
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as MediaSave
|
||||
|
||||
if (name != other.name) return false
|
||||
if (prefix != other.prefix) return false
|
||||
if (!fileInputStream.contentEquals(other.fileInputStream)) return false
|
||||
if (thumbnailInputStream != null) {
|
||||
if (other.thumbnailInputStream == null) return false
|
||||
if (!thumbnailInputStream.contentEquals(other.thumbnailInputStream)) return false
|
||||
} else if (other.thumbnailInputStream != null) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = name.hashCode()
|
||||
result = 31 * result + prefix.hashCode()
|
||||
result = 31 * result + fileInputStream.contentHashCode()
|
||||
result = 31 * result + (thumbnailInputStream?.contentHashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
data class MediaSaveRequest(
|
||||
val name: String,
|
||||
val preffix: String,
|
||||
val filePath: Path,
|
||||
val thumbnailPath: Path?
|
||||
)
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.media.Media
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest
|
||||
|
||||
interface MediaService {
|
||||
suspend fun uploadLocalMedia(mediaRequest: MediaRequest): Media
|
||||
suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import dev.usbharu.hideout.core.domain.exception.media.MediaSaveException
|
||||
import dev.usbharu.hideout.core.domain.exception.media.UnsupportedMediaException
|
||||
import dev.usbharu.hideout.core.domain.model.media.Media
|
||||
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
||||
import dev.usbharu.hideout.core.service.media.converter.MediaProcessService
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest
|
||||
import dev.usbharu.hideout.util.withDelete
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
import java.nio.file.Files
|
||||
import javax.imageio.ImageIO
|
||||
import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia
|
||||
|
||||
@Service
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
class MediaServiceImpl(
|
||||
private val mediaDataStore: MediaDataStore,
|
||||
private val fileTypeDeterminationService: FileTypeDeterminationService,
|
||||
private val mediaBlurhashService: MediaBlurhashService,
|
||||
private val mediaRepository: MediaRepository,
|
||||
private val mediaProcessServices: List<MediaProcessService>,
|
||||
private val remoteMediaDownloadService: RemoteMediaDownloadService,
|
||||
private val renameService: MediaFileRenameService
|
||||
) : MediaService {
|
||||
@Suppress("LongMethod", "NestedBlockDepth")
|
||||
override suspend fun uploadLocalMedia(mediaRequest: MediaRequest): EntityMedia {
|
||||
val fileName = mediaRequest.file.name
|
||||
logger.info(
|
||||
"Media upload. filename:$fileName " +
|
||||
"contentType:${mediaRequest.file.contentType}"
|
||||
)
|
||||
|
||||
val tempFile = Files.createTempFile("hideout-tmp-file", ".tmp")
|
||||
|
||||
tempFile.withDelete().use {
|
||||
Files.newOutputStream(tempFile).use { outputStream ->
|
||||
mediaRequest.file.inputStream.use {
|
||||
it.transferTo(outputStream)
|
||||
}
|
||||
}
|
||||
val mimeType = fileTypeDeterminationService.fileType(tempFile, fileName)
|
||||
|
||||
val process = findMediaProcessor(mimeType).process(
|
||||
mimeType,
|
||||
fileName,
|
||||
tempFile,
|
||||
null
|
||||
)
|
||||
|
||||
val dataMediaSave = MediaSaveRequest(
|
||||
renameService.rename(
|
||||
mediaRequest.file.name,
|
||||
mimeType,
|
||||
process.filePath.fileName.toString(),
|
||||
process.fileMimeType
|
||||
),
|
||||
"",
|
||||
process.filePath,
|
||||
process.thumbnailPath
|
||||
)
|
||||
dataMediaSave.filePath.withDelete().use {
|
||||
dataMediaSave.thumbnailPath.withDelete().use {
|
||||
val save = try {
|
||||
mediaDataStore.save(dataMediaSave)
|
||||
} catch (e: Exception) {
|
||||
logger.warn("Failed to save the media", e)
|
||||
throw MediaSaveException("Failed to save the media.", e)
|
||||
}
|
||||
if (save.success.not()) {
|
||||
save as FaildSavedMedia
|
||||
logger.warn("Failed to save the media. reason: ${save.reason}")
|
||||
logger.warn(save.description, save.trace)
|
||||
throw MediaSaveException("Failed to save the media.")
|
||||
}
|
||||
save as SuccessSavedMedia
|
||||
val blurHash = generateBlurhash(process)
|
||||
return mediaRepository.save(
|
||||
EntityMedia(
|
||||
id = mediaRepository.generateId(),
|
||||
name = fileName,
|
||||
url = save.url,
|
||||
remoteUrl = null,
|
||||
thumbnailUrl = save.thumbnailUrl,
|
||||
type = process.fileMimeType.fileType,
|
||||
mimeType = process.fileMimeType,
|
||||
blurHash = blurHash,
|
||||
description = mediaRequest.description
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 仮の処理として保存したように動かす
|
||||
@Suppress("LongMethod", "NestedBlockDepth")
|
||||
override suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media {
|
||||
logger.info("MEDIA Remote media. filename:${remoteMedia.name} url:${remoteMedia.url}")
|
||||
|
||||
val findByRemoteUrl = mediaRepository.findByRemoteUrl(remoteMedia.url)
|
||||
if (findByRemoteUrl != null) {
|
||||
logger.warn("DUPLICATED Remote media is duplicated. url: {}", remoteMedia.url)
|
||||
return findByRemoteUrl
|
||||
}
|
||||
|
||||
remoteMediaDownloadService.download(remoteMedia.url).withDelete().use {
|
||||
val mimeType = fileTypeDeterminationService.fileType(it.path, remoteMedia.name)
|
||||
|
||||
val process = findMediaProcessor(mimeType).process(mimeType, remoteMedia.name, it.path, null)
|
||||
|
||||
val mediaSaveRequest = MediaSaveRequest(
|
||||
renameService.rename(
|
||||
remoteMedia.name,
|
||||
mimeType,
|
||||
process.filePath.fileName.toString(),
|
||||
process.fileMimeType
|
||||
),
|
||||
"",
|
||||
process.filePath,
|
||||
process.thumbnailPath
|
||||
)
|
||||
|
||||
mediaSaveRequest.filePath.withDelete().use {
|
||||
mediaSaveRequest.filePath.withDelete().use {
|
||||
val save = try {
|
||||
mediaDataStore.save(mediaSaveRequest)
|
||||
} catch (e: Exception) {
|
||||
logger.warn("Failed to save the media", e)
|
||||
throw MediaSaveException("Failed to save the media.", e)
|
||||
}
|
||||
|
||||
if (save is FaildSavedMedia) {
|
||||
logger.warn("Failed to save the media. reason: ${save.reason}")
|
||||
logger.warn(save.description, save.trace)
|
||||
throw MediaSaveException("Failed to save the media.")
|
||||
}
|
||||
save as SuccessSavedMedia
|
||||
val blurhash = generateBlurhash(process)
|
||||
return mediaRepository.save(
|
||||
EntityMedia(
|
||||
id = mediaRepository.generateId(),
|
||||
name = remoteMedia.name,
|
||||
url = save.url,
|
||||
remoteUrl = remoteMedia.url,
|
||||
thumbnailUrl = save.thumbnailUrl,
|
||||
type = process.fileMimeType.fileType,
|
||||
mimeType = process.fileMimeType,
|
||||
blurHash = blurhash
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findMediaProcessor(mimeType: MimeType): MediaProcessService {
|
||||
try {
|
||||
return mediaProcessServices.first {
|
||||
try {
|
||||
it.isSupport(mimeType)
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
} catch (_: NoSuchElementException) {
|
||||
throw UnsupportedMediaException("MediaType: $mimeType isn't supported.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateBlurhash(process: ProcessedMediaPath): String {
|
||||
val path = if (process.thumbnailPath != null && process.thumbnailMimeType != null) {
|
||||
process.thumbnailPath
|
||||
} else {
|
||||
process.filePath
|
||||
}
|
||||
val mimeType = if (process.thumbnailPath != null && process.thumbnailMimeType != null) {
|
||||
process.thumbnailMimeType
|
||||
} else {
|
||||
process.fileMimeType
|
||||
}
|
||||
|
||||
val imageReadersByMIMEType = ImageIO.getImageReadersByMIMEType(mimeType.type + "/" + mimeType.subtype)
|
||||
for (imageReader in imageReadersByMIMEType) {
|
||||
try {
|
||||
val bufferedImage = ImageIO.createImageInputStream(path.toFile()).use {
|
||||
imageReader.input = it
|
||||
imageReader.read(0)
|
||||
}
|
||||
return mediaBlurhashService.generateBlurhash(bufferedImage)
|
||||
} catch (e: Exception) {
|
||||
logger.warn("Failed to read thumbnail", e)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(MediaServiceImpl::class.java)
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
data class ProcessedFile(
|
||||
val byteArray: ByteArray,
|
||||
val extension: String
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ProcessedFile
|
||||
|
||||
if (!byteArray.contentEquals(other.byteArray)) return false
|
||||
if (extension != other.extension) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = byteArray.contentHashCode()
|
||||
result = 31 * result + extension.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
data class ProcessedMedia(
|
||||
val file: ProcessedFile,
|
||||
val thumbnail: ProcessedFile?
|
||||
)
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
data class ProcessedMediaPath(
|
||||
val filePath: Path,
|
||||
val thumbnailPath: Path?,
|
||||
val fileMimeType: MimeType,
|
||||
val thumbnailMimeType: MimeType?
|
||||
)
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
data class RemoteMedia(
|
||||
val name: String,
|
||||
val url: String,
|
||||
val mediaType: String,
|
||||
val description: String? = null
|
||||
)
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
interface RemoteMediaDownloadService {
|
||||
suspend fun download(url: String): Path
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import dev.usbharu.hideout.application.config.MediaConfig
|
||||
import dev.usbharu.hideout.core.domain.exception.media.RemoteMediaFileSizeException
|
||||
import dev.usbharu.hideout.core.service.resource.KtorResourceResolveService
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.outputStream
|
||||
|
||||
@Service
|
||||
class RemoteMediaDownloadServiceImpl(
|
||||
private val resourceResolveService: KtorResourceResolveService,
|
||||
private val mediaConfig: MediaConfig
|
||||
) :
|
||||
RemoteMediaDownloadService {
|
||||
override suspend fun download(url: String): Path {
|
||||
logger.info("START Download remote file. url: {}", url)
|
||||
val httpResponse = resourceResolveService.resolve(url).body()
|
||||
val createTempFile = Files.createTempFile("hideout-remote-download", ".tmp")
|
||||
|
||||
logger.debug("Save to {} url: {} ", createTempFile, url)
|
||||
|
||||
httpResponse.use { inputStream ->
|
||||
createTempFile.outputStream().use {
|
||||
inputStream.transferTo(it)
|
||||
}
|
||||
}
|
||||
|
||||
val contentLength = createTempFile.toFile().length()
|
||||
if (contentLength >= mediaConfig.remoteMediaFileSizeLimit) {
|
||||
throw RemoteMediaFileSizeException(
|
||||
"File size is too large. $contentLength >= ${mediaConfig.remoteMediaFileSizeLimit}"
|
||||
)
|
||||
}
|
||||
|
||||
logger.info("SUCCESS Download remote file. url: {}", url)
|
||||
return createTempFile
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(RemoteMediaDownloadServiceImpl::class.java)
|
||||
}
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import dev.usbharu.hideout.application.config.S3StorageConfig
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.stereotype.Service
|
||||
import software.amazon.awssdk.core.sync.RequestBody
|
||||
import software.amazon.awssdk.services.s3.S3Client
|
||||
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest
|
||||
import software.amazon.awssdk.services.s3.model.GetUrlRequest
|
||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest
|
||||
|
||||
@Service
|
||||
@ConditionalOnProperty("hideout.storage.type", havingValue = "s3")
|
||||
class S3MediaDataStore(private val s3Client: S3Client, private val s3StorageConfig: S3StorageConfig) : MediaDataStore {
|
||||
override suspend fun save(dataMediaSave: MediaSave): SavedMedia {
|
||||
val fileUploadRequest = PutObjectRequest.builder()
|
||||
.bucket(s3StorageConfig.bucket)
|
||||
.key(dataMediaSave.name)
|
||||
.build()
|
||||
|
||||
val thumbnailKey = "thumbnail-${dataMediaSave.name}"
|
||||
val thumbnailUploadRequest = PutObjectRequest.builder()
|
||||
.bucket(s3StorageConfig.bucket)
|
||||
.key(thumbnailKey)
|
||||
.build()
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
awaitAll(
|
||||
async {
|
||||
if (dataMediaSave.thumbnailInputStream != null) {
|
||||
s3Client.putObject(
|
||||
thumbnailUploadRequest,
|
||||
RequestBody.fromBytes(dataMediaSave.thumbnailInputStream)
|
||||
)
|
||||
s3Client.utilities()
|
||||
.getUrl(GetUrlRequest.builder().bucket(s3StorageConfig.bucket).key(thumbnailKey).build())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
async {
|
||||
s3Client.putObject(fileUploadRequest, RequestBody.fromBytes(dataMediaSave.fileInputStream))
|
||||
s3Client.utilities()
|
||||
.getUrl(GetUrlRequest.builder().bucket(s3StorageConfig.bucket).key(dataMediaSave.name).build())
|
||||
}
|
||||
)
|
||||
}
|
||||
return SuccessSavedMedia(
|
||||
name = dataMediaSave.name,
|
||||
url = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${dataMediaSave.name}",
|
||||
thumbnailUrl = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/$thumbnailKey"
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia {
|
||||
logger.info("MEDIA upload. {}", dataSaveRequest.name)
|
||||
|
||||
val fileUploadRequest = PutObjectRequest.builder()
|
||||
.bucket(s3StorageConfig.bucket)
|
||||
.key(dataSaveRequest.name)
|
||||
.build()
|
||||
|
||||
logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, dataSaveRequest.name)
|
||||
|
||||
val thumbnailKey = "thumbnail-${dataSaveRequest.name}"
|
||||
val thumbnailUploadRequest = PutObjectRequest.builder()
|
||||
.bucket(s3StorageConfig.bucket)
|
||||
.key(thumbnailKey)
|
||||
.build()
|
||||
|
||||
logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, thumbnailKey)
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
awaitAll(
|
||||
async {
|
||||
if (dataSaveRequest.thumbnailPath != null) {
|
||||
s3Client.putObject(
|
||||
thumbnailUploadRequest,
|
||||
RequestBody.fromFile(dataSaveRequest.thumbnailPath)
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
async {
|
||||
s3Client.putObject(fileUploadRequest, RequestBody.fromFile(dataSaveRequest.filePath))
|
||||
}
|
||||
)
|
||||
}
|
||||
val successSavedMedia = SuccessSavedMedia(
|
||||
name = dataSaveRequest.name,
|
||||
url = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${dataSaveRequest.name}",
|
||||
thumbnailUrl = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/$thumbnailKey"
|
||||
)
|
||||
|
||||
logger.info("SUCCESS Media upload. {}", dataSaveRequest.name)
|
||||
logger.debug(
|
||||
"name: {} url: {} thumbnail url: {}",
|
||||
successSavedMedia.name,
|
||||
successSavedMedia.url,
|
||||
successSavedMedia.thumbnailUrl
|
||||
)
|
||||
|
||||
return successSavedMedia
|
||||
}
|
||||
|
||||
override suspend fun delete(id: String) {
|
||||
val fileDeleteRequest = DeleteObjectRequest.builder().bucket(s3StorageConfig.bucket).key(id).build()
|
||||
val thumbnailDeleteRequest =
|
||||
DeleteObjectRequest.builder().bucket(s3StorageConfig.bucket).key("thumbnail-$id").build()
|
||||
s3Client.deleteObject(fileDeleteRequest)
|
||||
s3Client.deleteObject(thumbnailDeleteRequest)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(S3MediaDataStore::class.java)
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
sealed class SavedMedia(val success: Boolean) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as SavedMedia
|
||||
|
||||
return success == other.success
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = success.hashCode()
|
||||
}
|
||||
|
||||
class SuccessSavedMedia(
|
||||
val name: String,
|
||||
val url: String,
|
||||
val thumbnailUrl: String,
|
||||
) :
|
||||
SavedMedia(true) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
other as SuccessSavedMedia
|
||||
|
||||
if (name != other.name) return false
|
||||
if (url != other.url) return false
|
||||
if (thumbnailUrl != other.thumbnailUrl) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + name.hashCode()
|
||||
result = 31 * result + url.hashCode()
|
||||
result = 31 * result + thumbnailUrl.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class FaildSavedMedia(
|
||||
val reason: String,
|
||||
val description: String,
|
||||
val trace: Throwable? = null
|
||||
) : SavedMedia(false) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
other as FaildSavedMedia
|
||||
|
||||
if (reason != other.reason) return false
|
||||
if (description != other.description) return false
|
||||
if (trace != other.trace) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + reason.hashCode()
|
||||
result = 31 * result + description.hashCode()
|
||||
result = 31 * result + (trace?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import java.io.InputStream
|
||||
|
||||
interface ThumbnailGenerateService {
|
||||
fun generate(bufferedImage: InputStream, width: Int, height: Int): ProcessedFile?
|
||||
fun generate(outputStream: ByteArray, width: Int, height: Int): ProcessedFile?
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import org.springframework.stereotype.Service
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import javax.imageio.ImageIO
|
||||
|
||||
@Service
|
||||
class ThumbnailGenerateServiceImpl : ThumbnailGenerateService {
|
||||
override fun generate(bufferedImage: InputStream, width: Int, height: Int): ProcessedFile? {
|
||||
val image = ImageIO.read(bufferedImage)
|
||||
return internalGenerate(image)
|
||||
}
|
||||
|
||||
override fun generate(outputStream: ByteArray, width: Int, height: Int): ProcessedFile? {
|
||||
val image = ImageIO.read(outputStream.inputStream())
|
||||
return internalGenerate(image)
|
||||
}
|
||||
|
||||
private fun internalGenerate(image: BufferedImage): ProcessedFile {
|
||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||
ImageIO.write(image, "jpeg", byteArrayOutputStream)
|
||||
return ProcessedFile(byteArrayOutputStream.toByteArray(), "jpg")
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
import java.util.*
|
||||
|
||||
@Qualifier("uuid")
|
||||
@Service
|
||||
class UUIDMediaFileRenameService : MediaFileRenameService {
|
||||
override fun rename(
|
||||
uploadName: String,
|
||||
uploadMimeType: MimeType,
|
||||
processedName: String,
|
||||
processedMimeType: MimeType
|
||||
): String = "${UUID.randomUUID()}.${uploadMimeType.subtype}.${processedMimeType.subtype}"
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media.converter
|
||||
|
||||
import dev.usbharu.hideout.core.service.media.FileType
|
||||
import dev.usbharu.hideout.core.service.media.ProcessedFile
|
||||
import java.io.InputStream
|
||||
|
||||
interface MediaConverter {
|
||||
fun isSupport(fileType: FileType): Boolean
|
||||
fun convert(inputStream: InputStream): ProcessedFile
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media.converter
|
||||
|
||||
import dev.usbharu.hideout.core.service.media.FileType
|
||||
import dev.usbharu.hideout.core.service.media.ProcessedFile
|
||||
import java.io.InputStream
|
||||
|
||||
interface MediaConverterRoot {
|
||||
suspend fun convert(
|
||||
fileType: FileType,
|
||||
contentType: String,
|
||||
filename: String,
|
||||
inputStream: InputStream
|
||||
): ProcessedFile
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media.converter
|
||||
|
||||
import dev.usbharu.hideout.core.service.media.FileType
|
||||
import dev.usbharu.hideout.core.service.media.ProcessedFile
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.springframework.stereotype.Service
|
||||
import java.io.InputStream
|
||||
|
||||
@Service
|
||||
class MediaConverterRootImpl(private val converters: List<MediaConverter>) : MediaConverterRoot {
|
||||
override suspend fun convert(
|
||||
fileType: FileType,
|
||||
contentType: String,
|
||||
filename: String,
|
||||
inputStream: InputStream
|
||||
): ProcessedFile {
|
||||
val convert = converters.find {
|
||||
it.isSupport(fileType)
|
||||
}?.convert(inputStream)
|
||||
if (convert != null) {
|
||||
return convert
|
||||
}
|
||||
return withContext(Dispatchers.IO) {
|
||||
if (filename.contains('.')) {
|
||||
ProcessedFile(inputStream.readAllBytes(), filename.substringAfterLast("."))
|
||||
} else {
|
||||
ProcessedFile(inputStream.readAllBytes(), contentType.substringAfterLast("/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media.converter
|
||||
|
||||
import dev.usbharu.hideout.core.service.media.FileType
|
||||
import dev.usbharu.hideout.core.service.media.MimeType
|
||||
import dev.usbharu.hideout.core.service.media.ProcessedMedia
|
||||
import dev.usbharu.hideout.core.service.media.ProcessedMediaPath
|
||||
import java.nio.file.Path
|
||||
|
||||
interface MediaProcessService {
|
||||
fun isSupport(mimeType: MimeType): Boolean
|
||||
|
||||
suspend fun process(
|
||||
fileType: FileType,
|
||||
contentType: String,
|
||||
fileName: String,
|
||||
file: ByteArray,
|
||||
thumbnail: ByteArray?
|
||||
): ProcessedMedia
|
||||
|
||||
suspend fun process(
|
||||
mimeType: MimeType,
|
||||
fileName: String,
|
||||
filePath: Path,
|
||||
thumbnails: Path?
|
||||
): ProcessedMediaPath
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media.converter
|
||||
|
||||
import dev.usbharu.hideout.core.domain.exception.media.MediaConvertException
|
||||
import dev.usbharu.hideout.core.service.media.*
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
import java.nio.file.Path
|
||||
|
||||
@Service
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
class MediaProcessServiceImpl(
|
||||
private val mediaConverterRoot: MediaConverterRoot,
|
||||
private val thumbnailGenerateService: ThumbnailGenerateService
|
||||
) : MediaProcessService {
|
||||
override fun isSupport(mimeType: MimeType): Boolean = false
|
||||
|
||||
override suspend fun process(
|
||||
fileType: FileType,
|
||||
contentType: String,
|
||||
filename: String,
|
||||
file: ByteArray,
|
||||
thumbnail: ByteArray?
|
||||
): ProcessedMedia {
|
||||
val fileInputStream = try {
|
||||
mediaConverterRoot.convert(fileType, contentType, filename, file.inputStream().buffered())
|
||||
} catch (e: Exception) {
|
||||
logger.warn("Failed convert media.", e)
|
||||
throw MediaConvertException("Failed convert media.", e)
|
||||
}
|
||||
val thumbnailInputStream = try {
|
||||
thumbnail?.let { mediaConverterRoot.convert(fileType, contentType, filename, it.inputStream().buffered()) }
|
||||
} catch (e: Exception) {
|
||||
logger.warn("Failed convert thumbnail media.", e)
|
||||
null
|
||||
}
|
||||
return ProcessedMedia(
|
||||
fileInputStream,
|
||||
thumbnailGenerateService.generate(
|
||||
thumbnailInputStream?.byteArray ?: file,
|
||||
2048,
|
||||
2048
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun process(
|
||||
mimeType: MimeType,
|
||||
fileName: String,
|
||||
filePath: Path,
|
||||
thumbnails: Path?
|
||||
): ProcessedMediaPath {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(MediaProcessServiceImpl::class.java)
|
||||
}
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media.converter.image
|
||||
|
||||
import dev.usbharu.hideout.core.domain.exception.media.MediaProcessException
|
||||
import dev.usbharu.hideout.core.service.media.FileType
|
||||
import dev.usbharu.hideout.core.service.media.MimeType
|
||||
import dev.usbharu.hideout.core.service.media.ProcessedMedia
|
||||
import dev.usbharu.hideout.core.service.media.ProcessedMediaPath
|
||||
import dev.usbharu.hideout.core.service.media.converter.MediaProcessService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.slf4j.MDCContext
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.coobird.thumbnailator.Thumbnails
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
import java.awt.Color
|
||||
import java.awt.image.BufferedImage
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import javax.imageio.ImageIO
|
||||
import kotlin.io.path.inputStream
|
||||
import kotlin.io.path.outputStream
|
||||
|
||||
@Service
|
||||
@Qualifier("image")
|
||||
class ImageMediaProcessService(private val imageMediaProcessorConfiguration: ImageMediaProcessorConfiguration?) :
|
||||
MediaProcessService {
|
||||
|
||||
private val convertType = imageMediaProcessorConfiguration?.convert ?: "jpeg"
|
||||
|
||||
private val supportedTypes = imageMediaProcessorConfiguration?.supportedType ?: listOf("webp", "jpeg", "png")
|
||||
|
||||
private val genThumbnail = imageMediaProcessorConfiguration?.thubnail?.generate ?: true
|
||||
|
||||
private val width = imageMediaProcessorConfiguration?.thubnail?.width ?: 1000
|
||||
private val height = imageMediaProcessorConfiguration?.thubnail?.height ?: 1000
|
||||
|
||||
override fun isSupport(mimeType: MimeType): Boolean {
|
||||
if (mimeType.type != "image") {
|
||||
return false
|
||||
}
|
||||
return supportedTypes.contains(mimeType.subtype)
|
||||
}
|
||||
|
||||
override suspend fun process(
|
||||
fileType: FileType,
|
||||
contentType: String,
|
||||
fileName: String,
|
||||
file: ByteArray,
|
||||
thumbnail: ByteArray?
|
||||
): ProcessedMedia {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun process(
|
||||
mimeType: MimeType,
|
||||
fileName: String,
|
||||
filePath: Path,
|
||||
thumbnails: Path?
|
||||
): ProcessedMediaPath = withContext(Dispatchers.IO + MDCContext()) {
|
||||
val read = ImageIO.read(filePath.inputStream())
|
||||
|
||||
val bufferedImage = BufferedImage(read.width, read.height, BufferedImage.TYPE_INT_RGB)
|
||||
|
||||
val graphics = bufferedImage.createGraphics()
|
||||
|
||||
graphics.drawImage(read, 0, 0, Color.BLACK, null)
|
||||
|
||||
val tempFileName = UUID.randomUUID().toString()
|
||||
val tempFile = Files.createTempFile(tempFileName, "tmp")
|
||||
|
||||
val thumbnailPath = if (genThumbnail) {
|
||||
val tempThumbnailFile = Files.createTempFile("thumbnail-$tempFileName", ".tmp")
|
||||
|
||||
tempThumbnailFile.outputStream().use {
|
||||
val write = ImageIO.write(
|
||||
if (thumbnails != null) {
|
||||
Thumbnails.of(thumbnails.toFile())
|
||||
.size(width, height)
|
||||
.imageType(BufferedImage.TYPE_INT_RGB)
|
||||
.asBufferedImage()
|
||||
} else {
|
||||
Thumbnails.of(bufferedImage)
|
||||
.size(width, height)
|
||||
.imageType(BufferedImage.TYPE_INT_RGB)
|
||||
.asBufferedImage()
|
||||
},
|
||||
convertType,
|
||||
it
|
||||
)
|
||||
tempThumbnailFile.takeIf { write }
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
tempFile.outputStream().use {
|
||||
if (ImageIO.write(bufferedImage, convertType, it).not()) {
|
||||
logger.warn("Failed to save a temporary file. type: {} ,path: {}", convertType, tempFile)
|
||||
throw MediaProcessException("Failed to save a temporary file.")
|
||||
}
|
||||
}
|
||||
ProcessedMediaPath(
|
||||
tempFile,
|
||||
thumbnailPath,
|
||||
MimeType("image", convertType, FileType.Image),
|
||||
MimeType("image", convertType, FileType.Image).takeIf { genThumbnail }
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(ImageMediaProcessService::class.java)
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media.converter.image
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
|
||||
@ConfigurationProperties("hideout.media.image")
|
||||
data class ImageMediaProcessorConfiguration(
|
||||
val convert: String?,
|
||||
val thubnail: ImageMediaProcessorThumbnailConfiguration?,
|
||||
val supportedType: List<String>?,
|
||||
|
||||
)
|
||||
|
||||
data class ImageMediaProcessorThumbnailConfiguration(
|
||||
val generate: Boolean,
|
||||
val width: Int,
|
||||
val height: Int
|
||||
)
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.service.media.converter.movie
|
||||
|
||||
import dev.usbharu.hideout.core.service.media.FileType
|
||||
import dev.usbharu.hideout.core.service.media.MimeType
|
||||
import dev.usbharu.hideout.core.service.media.ProcessedMedia
|
||||
import dev.usbharu.hideout.core.service.media.ProcessedMediaPath
|
||||
import dev.usbharu.hideout.core.service.media.converter.MediaProcessService
|
||||
import org.bytedeco.ffmpeg.global.avcodec
|
||||
import org.bytedeco.javacv.FFmpegFrameFilter
|
||||
import org.bytedeco.javacv.FFmpegFrameGrabber
|
||||
import org.bytedeco.javacv.FFmpegFrameRecorder
|
||||
import org.bytedeco.javacv.Java2DFrameConverter
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
import java.awt.image.BufferedImage
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import javax.imageio.ImageIO
|
||||
import kotlin.math.min
|
||||
|
||||
@Service
|
||||
@Qualifier("video")
|
||||
class MovieMediaProcessService : MediaProcessService {
|
||||
override fun isSupport(mimeType: MimeType): Boolean = mimeType.type == "video"
|
||||
|
||||
override suspend fun process(
|
||||
fileType: FileType,
|
||||
contentType: String,
|
||||
fileName: String,
|
||||
file: ByteArray,
|
||||
thumbnail: ByteArray?
|
||||
): ProcessedMedia {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "NestedBlockDepth", "CognitiveComplexMethod")
|
||||
override suspend fun process(
|
||||
mimeType: MimeType,
|
||||
fileName: String,
|
||||
filePath: Path,
|
||||
thumbnails: Path?
|
||||
): ProcessedMediaPath {
|
||||
val tempFile = Files.createTempFile("hideout-movie-processor-", ".tmp")
|
||||
val thumbnailFile = Files.createTempFile("hideout-movie-thumbnail-generate-", ".tmp")
|
||||
logger.info("START Convert Movie Media {}", fileName)
|
||||
FFmpegFrameGrabber(filePath.toFile()).use { grabber ->
|
||||
grabber.start()
|
||||
val width = grabber.imageWidth
|
||||
val height = grabber.imageHeight
|
||||
val frameRate = 60.0
|
||||
|
||||
logger.debug("Movie Media Width {}, Height {}", width, height)
|
||||
|
||||
FFmpegFrameFilter(
|
||||
"fps=fps=${frameRate.toInt()}",
|
||||
"anull",
|
||||
width,
|
||||
height,
|
||||
grabber.audioChannels
|
||||
).use { filter ->
|
||||
|
||||
filter.sampleFormat = grabber.sampleFormat
|
||||
filter.sampleRate = grabber.sampleRate
|
||||
filter.pixelFormat = grabber.pixelFormat
|
||||
filter.frameRate = grabber.frameRate
|
||||
filter.start()
|
||||
|
||||
val videoBitRate = min(1300000, (width * height * frameRate * 1 * 0.07).toInt())
|
||||
|
||||
logger.debug("Movie Media BitRate {}", videoBitRate)
|
||||
|
||||
FFmpegFrameRecorder(tempFile.toFile(), width, height, grabber.audioChannels).use {
|
||||
it.sampleRate = grabber.sampleRate
|
||||
it.format = "mp4"
|
||||
it.videoCodec = avcodec.AV_CODEC_ID_H264
|
||||
it.audioCodec = avcodec.AV_CODEC_ID_AAC
|
||||
it.audioChannels = grabber.audioChannels
|
||||
it.videoQuality = 1.0
|
||||
it.frameRate = frameRate
|
||||
it.setVideoOption("preset", "ultrafast")
|
||||
it.timestamp = 0
|
||||
it.gopSize = frameRate.toInt()
|
||||
it.videoBitrate = videoBitRate
|
||||
it.start()
|
||||
|
||||
var bufferedImage: BufferedImage? = null
|
||||
|
||||
val frameConverter = Java2DFrameConverter()
|
||||
|
||||
while (true) {
|
||||
val grab = grabber.grab() ?: break
|
||||
|
||||
if (bufferedImage == null) {
|
||||
bufferedImage = frameConverter.convert(grab)
|
||||
}
|
||||
|
||||
if (grab.image != null || grab.samples != null) {
|
||||
filter.push(grab)
|
||||
}
|
||||
while (true) {
|
||||
val frame = filter.pull() ?: break
|
||||
it.record(frame)
|
||||
}
|
||||
}
|
||||
|
||||
if (bufferedImage != null) {
|
||||
ImageIO.write(bufferedImage, "jpeg", thumbnailFile.toFile())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("SUCCESS Convert Movie Media {}", fileName)
|
||||
|
||||
return ProcessedMediaPath(
|
||||
tempFile,
|
||||
thumbnailFile,
|
||||
MimeType("video", "mp4", FileType.Video),
|
||||
MimeType("image", "jpeg", FileType.Image)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(MovieMediaProcessService::class.java)
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue