mirror of https://github.com/usbharu/Hideout.git
Merge pull request #177 from usbharu/bugfix/duplicate-media
Bugfix/duplicate media
This commit is contained in:
commit
6af7cd67c8
|
@ -55,7 +55,7 @@ class WebFingerTest {
|
||||||
@Test
|
@Test
|
||||||
fun `webfinger 存在しないユーザーに404`() {
|
fun `webfinger 存在しないユーザーに404`() {
|
||||||
mockMvc
|
mockMvc
|
||||||
.get("/.well-known/webfinger?resource=acct:test-user@example.com")
|
.get("/.well-known/webfinger?resource=acct:invalid-user-notfound-afdjashfal@example.com")
|
||||||
.andExpect { status { isNotFound() } }
|
.andExpect { status { isNotFound() } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ class MediaTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun メディアをアップロードできる() = runTest {
|
fun メディアをアップロードできる() = runTest {
|
||||||
whenever(mediaDataStore.save(any<MediaSaveRequest>())).doReturn(SuccessSavedMedia("", "", ""))
|
whenever(mediaDataStore.save(any<MediaSaveRequest>())).doReturn(SuccessSavedMedia("", "a", "a"))
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.multipart("/api/v1/media") {
|
.multipart("/api/v1/media") {
|
||||||
|
@ -73,7 +73,7 @@ class MediaTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun write_mediaスコープでメディアをアップロードできる() = runTest {
|
fun write_mediaスコープでメディアをアップロードできる() = runTest {
|
||||||
whenever(mediaDataStore.save(any<MediaSaveRequest>())).doReturn(SuccessSavedMedia("", "", ""))
|
whenever(mediaDataStore.save(any<MediaSaveRequest>())).doReturn(SuccessSavedMedia("", "b", "b"))
|
||||||
|
|
||||||
mockMvc
|
mockMvc
|
||||||
.multipart("/api/v1/media") {
|
.multipart("/api/v1/media") {
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$Random
|
||||||
|
junit.jupiter.testmethod.order.default=org.junit.jupiter.api.MethodOrderer$Random
|
|
@ -1,17 +1,20 @@
|
||||||
package dev.usbharu.hideout.core.infrastructure.exposedquery
|
package dev.usbharu.hideout.core.infrastructure.exposedquery
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.media.Media
|
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
|
||||||
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Media
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.PostsMedia
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toMedia
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toMedia
|
||||||
import dev.usbharu.hideout.core.query.MediaQueryService
|
import dev.usbharu.hideout.core.query.MediaQueryService
|
||||||
|
import dev.usbharu.hideout.util.singleOr
|
||||||
import org.jetbrains.exposed.sql.innerJoin
|
import org.jetbrains.exposed.sql.innerJoin
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
|
import dev.usbharu.hideout.core.domain.model.media.Media as MediaEntity
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
class MediaQueryServiceImpl : MediaQueryService {
|
class MediaQueryServiceImpl : MediaQueryService {
|
||||||
override suspend fun findByPostId(postId: Long): List<Media> {
|
override suspend fun findByPostId(postId: Long): List<MediaEntity> {
|
||||||
return dev.usbharu.hideout.core.infrastructure.exposedrepository.Media.innerJoin(
|
return Media.innerJoin(
|
||||||
PostsMedia,
|
PostsMedia,
|
||||||
onColumn = { id },
|
onColumn = { id },
|
||||||
otherColumn = { mediaId }
|
otherColumn = { mediaId }
|
||||||
|
@ -19,4 +22,10 @@ class MediaQueryServiceImpl : MediaQueryService {
|
||||||
.select { PostsMedia.postId eq postId }
|
.select { PostsMedia.postId eq postId }
|
||||||
.map { it.toMedia() }
|
.map { it.toMedia() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun findByRemoteUrl(remoteUrl: String): MediaEntity {
|
||||||
|
return Media.select { Media.remoteUrl eq remoteUrl }
|
||||||
|
.singleOr { FailedToGetResourcesException("remoteUrl: $remoteUrl is duplicate or not exist.", it) }
|
||||||
|
.toMedia()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,9 +99,9 @@ fun ResultRow.toMediaOrNull(): EntityMedia? {
|
||||||
object Media : Table("media") {
|
object Media : Table("media") {
|
||||||
val id = long("id")
|
val id = long("id")
|
||||||
val name = varchar("name", 255)
|
val name = varchar("name", 255)
|
||||||
val url = varchar("url", 255)
|
val url = varchar("url", 255).uniqueIndex()
|
||||||
val remoteUrl = varchar("remote_url", 255).nullable()
|
val remoteUrl = varchar("remote_url", 255).uniqueIndex().nullable()
|
||||||
val thumbnailUrl = varchar("thumbnail_url", 255).nullable()
|
val thumbnailUrl = varchar("thumbnail_url", 255).uniqueIndex().nullable()
|
||||||
val type = integer("type")
|
val type = integer("type")
|
||||||
val blurhash = varchar("blurhash", 255).nullable()
|
val blurhash = varchar("blurhash", 255).nullable()
|
||||||
val mimeType = varchar("mime_type", 255)
|
val mimeType = varchar("mime_type", 255)
|
||||||
|
|
|
@ -4,4 +4,5 @@ import dev.usbharu.hideout.core.domain.model.media.Media
|
||||||
|
|
||||||
interface MediaQueryService {
|
interface MediaQueryService {
|
||||||
suspend fun findByPostId(postId: Long): List<Media>
|
suspend fun findByPostId(postId: Long): List<Media>
|
||||||
|
suspend fun findByRemoteUrl(remoteUrl: String): Media
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package dev.usbharu.hideout.core.service.media
|
package dev.usbharu.hideout.core.service.media
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
|
||||||
import dev.usbharu.hideout.core.domain.exception.media.MediaSaveException
|
import dev.usbharu.hideout.core.domain.exception.media.MediaSaveException
|
||||||
import dev.usbharu.hideout.core.domain.exception.media.UnsupportedMediaException
|
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.Media
|
||||||
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
||||||
|
import dev.usbharu.hideout.core.query.MediaQueryService
|
||||||
import dev.usbharu.hideout.core.service.media.converter.MediaProcessService
|
import dev.usbharu.hideout.core.service.media.converter.MediaProcessService
|
||||||
import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest
|
import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest
|
||||||
import dev.usbharu.hideout.util.withDelete
|
import dev.usbharu.hideout.util.withDelete
|
||||||
|
@ -22,7 +24,8 @@ class MediaServiceImpl(
|
||||||
private val mediaRepository: MediaRepository,
|
private val mediaRepository: MediaRepository,
|
||||||
private val mediaProcessServices: List<MediaProcessService>,
|
private val mediaProcessServices: List<MediaProcessService>,
|
||||||
private val remoteMediaDownloadService: RemoteMediaDownloadService,
|
private val remoteMediaDownloadService: RemoteMediaDownloadService,
|
||||||
private val renameService: MediaFileRenameService
|
private val renameService: MediaFileRenameService,
|
||||||
|
private val mediaQueryService: MediaQueryService
|
||||||
) : MediaService {
|
) : MediaService {
|
||||||
@Suppress("LongMethod", "NestedBlockDepth")
|
@Suppress("LongMethod", "NestedBlockDepth")
|
||||||
override suspend fun uploadLocalMedia(mediaRequest: MediaRequest): EntityMedia {
|
override suspend fun uploadLocalMedia(mediaRequest: MediaRequest): EntityMedia {
|
||||||
|
@ -99,6 +102,13 @@ class MediaServiceImpl(
|
||||||
override suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media {
|
override suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media {
|
||||||
logger.info("MEDIA Remote media. filename:${remoteMedia.name} url:${remoteMedia.url}")
|
logger.info("MEDIA Remote media. filename:${remoteMedia.name} url:${remoteMedia.url}")
|
||||||
|
|
||||||
|
try {
|
||||||
|
val findByRemoteUrl = mediaQueryService.findByRemoteUrl(remoteMedia.url)
|
||||||
|
logger.warn("DUPLICATED Remote media is duplicated. url: {}", remoteMedia.url)
|
||||||
|
return findByRemoteUrl
|
||||||
|
} catch (_: FailedToGetResourcesException) {
|
||||||
|
}
|
||||||
|
|
||||||
remoteMediaDownloadService.download(remoteMedia.url).withDelete().use {
|
remoteMediaDownloadService.download(remoteMedia.url).withDelete().use {
|
||||||
val mimeType = fileTypeDeterminationService.fileType(it.path, remoteMedia.name)
|
val mimeType = fileTypeDeterminationService.fileType(it.path, remoteMedia.name)
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
package dev.usbharu.hideout.core.service.media
|
package dev.usbharu.hideout.core.service.media
|
||||||
|
|
||||||
import io.ktor.client.*
|
import dev.usbharu.hideout.core.service.resource.KtorResourceResolveService
|
||||||
import io.ktor.client.request.*
|
|
||||||
import io.ktor.client.statement.*
|
|
||||||
import io.ktor.http.*
|
|
||||||
import io.ktor.utils.io.jvm.javaio.*
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
@ -12,16 +8,16 @@ import java.nio.file.Path
|
||||||
import kotlin.io.path.outputStream
|
import kotlin.io.path.outputStream
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class RemoteMediaDownloadServiceImpl(private val httpClient: HttpClient) : RemoteMediaDownloadService {
|
class RemoteMediaDownloadServiceImpl(private val resourceResolveService: KtorResourceResolveService) :
|
||||||
|
RemoteMediaDownloadService {
|
||||||
override suspend fun download(url: String): Path {
|
override suspend fun download(url: String): Path {
|
||||||
logger.info("START Download remote file. url: {}", url)
|
logger.info("START Download remote file. url: {}", url)
|
||||||
val httpResponse = httpClient.get(url)
|
val httpResponse = resourceResolveService.resolve(url).body()
|
||||||
httpResponse.contentLength()
|
|
||||||
val createTempFile = Files.createTempFile("hideout-remote-download", ".tmp")
|
val createTempFile = Files.createTempFile("hideout-remote-download", ".tmp")
|
||||||
|
|
||||||
logger.debug("Save to {} url: {} ", createTempFile, url)
|
logger.debug("Save to {} url: {} ", createTempFile, url)
|
||||||
|
|
||||||
httpResponse.bodyAsChannel().toInputStream().use { inputStream ->
|
httpResponse.use { inputStream ->
|
||||||
createTempFile.outputStream().use {
|
createTempFile.outputStream().use {
|
||||||
inputStream.transferTo(it)
|
inputStream.transferTo(it)
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,9 +46,9 @@ create table if not exists media
|
||||||
(
|
(
|
||||||
id bigint primary key,
|
id bigint primary key,
|
||||||
"name" varchar(255) not null,
|
"name" varchar(255) not null,
|
||||||
url varchar(255) not null,
|
url varchar(255) not null unique,
|
||||||
remote_url varchar(255) null,
|
remote_url varchar(255) null unique,
|
||||||
thumbnail_url varchar(255) null,
|
thumbnail_url varchar(255) null unique,
|
||||||
"type" int not null,
|
"type" int not null,
|
||||||
blurhash varchar(255) null,
|
blurhash varchar(255) null,
|
||||||
mime_type varchar(255) not null,
|
mime_type varchar(255) not null,
|
||||||
|
|
Loading…
Reference in New Issue