mirror of https://github.com/usbharu/Hideout.git
feat: メディアアップロード処理を追加
This commit is contained in:
parent
a93131b5dc
commit
43ef619eee
|
@ -24,12 +24,13 @@ import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
|||
import dev.usbharu.hideout.core.external.media.MediaProcessor
|
||||
import dev.usbharu.hideout.core.external.mediastore.MediaStore
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Service
|
||||
import dev.usbharu.hideout.core.domain.model.media.Media as MediaModel
|
||||
|
||||
@Service
|
||||
class UploadMediaApplicationService(
|
||||
private val mediaProcessor: MediaProcessor,
|
||||
@Qualifier("delegate") private val mediaProcessor: MediaProcessor,
|
||||
private val mediaStore: MediaStore,
|
||||
private val mediaRepository: MediaRepository,
|
||||
private val idGenerateService: IdGenerateService,
|
||||
|
@ -42,7 +43,7 @@ class UploadMediaApplicationService(
|
|||
}
|
||||
|
||||
override suspend fun internalExecute(command: UploadMedia, executor: CommandExecutor): Media {
|
||||
val process = mediaProcessor.process(command.path)
|
||||
val process = mediaProcessor.process(command.path, command.name, null)
|
||||
val id = idGenerateService.generateId()
|
||||
val thumbnailUri = if (process.thumbnailPath != null) {
|
||||
mediaStore.upload(process.thumbnailPath, "thumbnail-$id")
|
||||
|
@ -59,7 +60,7 @@ class UploadMediaApplicationService(
|
|||
thumbnailUri,
|
||||
process.fileType,
|
||||
process.mimeType,
|
||||
MediaBlurHash(process.blurHash),
|
||||
process.blurHash?.let { MediaBlurHash(it) },
|
||||
command.description?.let { MediaDescription(it) }
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package dev.usbharu.hideout.core.config
|
||||
|
||||
import org.bytedeco.ffmpeg.global.avcodec
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
|
||||
@ConfigurationProperties("hideout.media.video.ffmpeg")
|
||||
data class FFmpegVideoConfig(
|
||||
val frameRate: Int = 60,
|
||||
val maxWidth: Int = 1920,
|
||||
val maxHeight: Int = 1080,
|
||||
val format: String = "mp4",
|
||||
val videoCodec: Int = avcodec.AV_CODEC_ID_H264,
|
||||
val audioCodec: Int = avcodec.AV_CODEC_ID_AAC,
|
||||
val videoQuality: Double = 1.0,
|
||||
val videoOption: List<String> = listOf("preset", "ultrafast"),
|
||||
val maxBitrate: Int = 1300000,
|
||||
)
|
|
@ -0,0 +1,10 @@
|
|||
package dev.usbharu.hideout.core.config
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
|
||||
@ConfigurationProperties("hideout.media.image.imageio")
|
||||
data class ImageIOImageConfig(
|
||||
val thumbnailsWidth: Int = 1000,
|
||||
val thumbnailsHeight: Int = 1000,
|
||||
val format: String = "jpeg"
|
||||
)
|
|
@ -0,0 +1,17 @@
|
|||
package dev.usbharu.hideout.core.config
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
|
||||
/**
|
||||
* メディアの保存にローカルファイルシステムを使用する際のコンフィグ
|
||||
*
|
||||
* @property path フォゾンする場所へのパス。 /から始めると絶対パスとなります。
|
||||
* @property publicUrl 公開用URL 省略可能 指定するとHideoutがファイルを配信しなくなります。
|
||||
*/
|
||||
@ConfigurationProperties("hideout.storage.local")
|
||||
@ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true)
|
||||
data class LocalStorageConfig(
|
||||
val path: String = "files",
|
||||
val publicUrl: String?
|
||||
)
|
|
@ -0,0 +1,34 @@
|
|||
package dev.usbharu.hideout.core.config
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
|
||||
import software.amazon.awssdk.regions.Region
|
||||
import software.amazon.awssdk.services.s3.S3Client
|
||||
import java.net.URI
|
||||
|
||||
@ConfigurationProperties("hideout.storage.s3")
|
||||
@ConditionalOnProperty("hideout.storage.type", havingValue = "s3")
|
||||
data class S3StorageConfig(
|
||||
val endpoint: String,
|
||||
val publicUrl: String,
|
||||
val bucket: String,
|
||||
val region: String,
|
||||
val accessKey: String,
|
||||
val secretKey: String
|
||||
)
|
||||
|
||||
@Configuration
|
||||
class AwsConfig {
|
||||
@Bean
|
||||
@ConditionalOnProperty("hideout.storage.type", havingValue = "s3")
|
||||
fun s3Client(awsConfig: S3StorageConfig): S3Client {
|
||||
return S3Client.builder()
|
||||
.endpointOverride(URI.create(awsConfig.endpoint))
|
||||
.region(Region.of(awsConfig.region))
|
||||
.credentialsProvider { AwsBasicCredentials.create(awsConfig.accessKey, awsConfig.secretKey) }
|
||||
.build()
|
||||
}
|
||||
}
|
22
hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/DelegateMediaProcessor.kt
vendored
Normal file
22
hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/DelegateMediaProcessor.kt
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
package dev.usbharu.hideout.core.external.media
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.media.MimeType
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Component
|
||||
import java.nio.file.Path
|
||||
|
||||
@Component
|
||||
@Qualifier("delegate")
|
||||
class DelegateMediaProcessor(
|
||||
private val fileTypeDeterminer: FileTypeDeterminer,
|
||||
private val mediaProcessors: List<MediaProcessor>
|
||||
) : MediaProcessor {
|
||||
override fun isSupported(mimeType: MimeType): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia {
|
||||
val fileType = fileTypeDeterminer.fileType(path, filename)
|
||||
return mediaProcessors.first { it.isSupported(fileType) }.process(path, filename, fileType)
|
||||
}
|
||||
}
|
8
hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminator.kt
vendored
Normal file
8
hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/FileTypeDeterminator.kt
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
package dev.usbharu.hideout.core.external.media
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.media.MimeType
|
||||
import java.nio.file.Path
|
||||
|
||||
interface FileTypeDeterminer {
|
||||
fun fileType(path: Path, filename: String): MimeType
|
||||
}
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
package dev.usbharu.hideout.core.external.media
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.media.MimeType
|
||||
import java.nio.file.Path
|
||||
|
||||
interface MediaProcessor {
|
||||
suspend fun process(path: Path): ProcessedMedia
|
||||
fun isSupported(mimeType: MimeType): Boolean
|
||||
suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia
|
||||
}
|
|
@ -25,5 +25,5 @@ data class ProcessedMedia(
|
|||
val thumbnailPath: Path?,
|
||||
val fileType: FileType,
|
||||
val mimeType: MimeType,
|
||||
val blurHash: String,
|
||||
val blurHash: String?,
|
||||
)
|
||||
|
|
51
hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/TikaFileTypeDeterminer.kt
vendored
Normal file
51
hideout-core/src/main/kotlin/dev/usbharu/hideout/core/external/media/TikaFileTypeDeterminer.kt
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
package dev.usbharu.hideout.core.external.media
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.media.FileType
|
||||
import dev.usbharu.hideout.core.domain.model.media.MimeType
|
||||
import org.apache.tika.Tika
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Component
|
||||
import java.nio.file.Path
|
||||
|
||||
@Component
|
||||
class TikaFileTypeDeterminer : FileTypeDeterminer {
|
||||
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(TikaFileTypeDeterminer::class.java)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.awss3
|
||||
|
||||
import dev.usbharu.hideout.core.config.S3StorageConfig
|
||||
import dev.usbharu.hideout.core.external.mediastore.MediaStore
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.stereotype.Component
|
||||
import software.amazon.awssdk.core.sync.RequestBody
|
||||
import software.amazon.awssdk.services.s3.S3Client
|
||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest
|
||||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
|
||||
@Component
|
||||
@ConditionalOnProperty("hideout.storage.type", havingValue = "s3")
|
||||
class AWSS3MediaStore(
|
||||
private val s3StorageConfig: S3StorageConfig,
|
||||
private val s3Client: S3Client
|
||||
) : MediaStore {
|
||||
override suspend fun upload(path: Path, id: String): URI {
|
||||
logger.info("MEDIA upload. {}", id)
|
||||
|
||||
val fileUploadRequest = PutObjectRequest.builder()
|
||||
.bucket(s3StorageConfig.bucket)
|
||||
.key(id)
|
||||
.build()
|
||||
|
||||
logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, id)
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
s3Client.putObject(fileUploadRequest, RequestBody.fromFile(path))
|
||||
}
|
||||
val successSavedMedia = URI.create("${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${id}")
|
||||
|
||||
|
||||
logger.info("SUCCESS Media upload. {}", id)
|
||||
logger.debug(
|
||||
"name: {} url: {}",
|
||||
id,
|
||||
successSavedMedia,
|
||||
)
|
||||
|
||||
return successSavedMedia
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(AWSS3MediaStore::class.java)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.localfilesystem
|
||||
|
||||
import dev.usbharu.hideout.core.config.ApplicationConfig
|
||||
import dev.usbharu.hideout.core.config.LocalStorageConfig
|
||||
import dev.usbharu.hideout.core.external.mediastore.MediaStore
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.stereotype.Component
|
||||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.copyTo
|
||||
|
||||
@Component
|
||||
@ConditionalOnProperty("hideout.storage.type", havingValue = "local", matchIfMissing = true)
|
||||
class LocalFileSystemMediaStore(
|
||||
localStorageConfig: LocalStorageConfig,
|
||||
applicationConfig: ApplicationConfig
|
||||
) :
|
||||
MediaStore {
|
||||
|
||||
private val publicUrl = localStorageConfig.publicUrl ?: "${applicationConfig.url}/files/"
|
||||
override suspend fun upload(path: Path, id: String): URI {
|
||||
logger.info("START Media upload. {}", id)
|
||||
val fileSavePath = buildSavePath(path, id)
|
||||
|
||||
val fileSavePathString = fileSavePath.toAbsolutePath().toString()
|
||||
logger.info("MEDIA save. path: {}", fileSavePathString)
|
||||
|
||||
@Suppress("TooGenericExceptionCaught") try {
|
||||
path.copyTo(fileSavePath)
|
||||
} catch (e: Exception) {
|
||||
logger.warn("FAILED to Save the media.", e)
|
||||
throw e
|
||||
}
|
||||
|
||||
logger.info("SUCCESS Media upload. {}", id)
|
||||
return URI.create(publicUrl).resolve(id)
|
||||
}
|
||||
|
||||
private fun buildSavePath(savePath: Path, name: String): Path = savePath.resolve(name)
|
||||
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(LocalFileSystemMediaStore::class.java)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.media.common
|
||||
|
||||
import java.awt.image.BufferedImage
|
||||
|
||||
interface GenerateBlurhash {
|
||||
fun generateBlurhash(bufferedImage: BufferedImage): String
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.media.common
|
||||
|
||||
import io.trbl.blurhash.BlurHash
|
||||
import org.springframework.stereotype.Component
|
||||
import java.awt.image.BufferedImage
|
||||
|
||||
@Component
|
||||
class GenerateBlurhashImpl : GenerateBlurhash {
|
||||
override fun generateBlurhash(bufferedImage: BufferedImage): String {
|
||||
return BlurHash.encode(bufferedImage)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.media.image
|
||||
|
||||
import dev.usbharu.hideout.core.config.ImageIOImageConfig
|
||||
import dev.usbharu.hideout.core.domain.model.media.FileType
|
||||
import dev.usbharu.hideout.core.domain.model.media.MimeType
|
||||
import dev.usbharu.hideout.core.external.media.MediaProcessor
|
||||
import dev.usbharu.hideout.core.external.media.ProcessedMedia
|
||||
import dev.usbharu.hideout.core.infrastructure.media.common.GenerateBlurhash
|
||||
import net.coobird.thumbnailator.Thumbnails
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.stereotype.Component
|
||||
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
|
||||
|
||||
@Component
|
||||
@Qualifier("image")
|
||||
class ImageIOImageProcessor(
|
||||
private val imageIOImageConfig: ImageIOImageConfig,
|
||||
private val blurhash: GenerateBlurhash
|
||||
) : MediaProcessor {
|
||||
override fun isSupported(mimeType: MimeType): Boolean {
|
||||
return mimeType.fileType == FileType.Image || mimeType.type == "image"
|
||||
}
|
||||
|
||||
override suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia {
|
||||
val read = ImageIO.read(path.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 = run {
|
||||
val tempThumbnailFile = Files.createTempFile("thumbnail-$tempFileName", ".tmp")
|
||||
|
||||
tempThumbnailFile.outputStream().use {
|
||||
val write = ImageIO.write(
|
||||
Thumbnails.of(bufferedImage)
|
||||
.size(imageIOImageConfig.thumbnailsWidth, imageIOImageConfig.thumbnailsHeight)
|
||||
.imageType(BufferedImage.TYPE_INT_RGB)
|
||||
.asBufferedImage(),
|
||||
imageIOImageConfig.format,
|
||||
it
|
||||
)
|
||||
tempThumbnailFile.takeIf { write }
|
||||
}
|
||||
}
|
||||
|
||||
tempFile.outputStream().use {
|
||||
if (ImageIO.write(bufferedImage, imageIOImageConfig.format, it).not()) {
|
||||
logger.warn("Failed to save a temporary file. type: {} ,path: {}", imageIOImageConfig.format, tempFile)
|
||||
throw Exception("Failed to save a temporary file.")
|
||||
}
|
||||
}
|
||||
|
||||
return ProcessedMedia(
|
||||
tempFile,
|
||||
thumbnailPath,
|
||||
FileType.Image,
|
||||
MimeType("image", imageIOImageConfig.format, FileType.Image),
|
||||
blurhash.generateBlurhash(bufferedImage)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(ImageIOImageProcessor::class.java)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package dev.usbharu.hideout.core.infrastructure.media.video
|
||||
|
||||
import dev.usbharu.hideout.core.config.FFmpegVideoConfig
|
||||
import dev.usbharu.hideout.core.domain.model.media.FileType
|
||||
import dev.usbharu.hideout.core.domain.model.media.MimeType
|
||||
import dev.usbharu.hideout.core.external.media.MediaProcessor
|
||||
import dev.usbharu.hideout.core.external.media.ProcessedMedia
|
||||
import dev.usbharu.hideout.core.infrastructure.media.common.GenerateBlurhash
|
||||
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.Component
|
||||
import java.awt.image.BufferedImage
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import javax.imageio.ImageIO
|
||||
import kotlin.math.min
|
||||
|
||||
@Component
|
||||
@Qualifier("video")
|
||||
class FFmpegVideoProcessor(
|
||||
private val fFmpegVideoConfig: FFmpegVideoConfig,
|
||||
private val generateBlurhash: GenerateBlurhash
|
||||
) : MediaProcessor {
|
||||
override fun isSupported(mimeType: MimeType): Boolean {
|
||||
return mimeType.fileType == FileType.Video || mimeType.type == "video"
|
||||
}
|
||||
|
||||
override suspend fun process(path: Path, filename: String, mimeType: MimeType?): ProcessedMedia {
|
||||
val tempFile = Files.createTempFile("hideout-movie-processor-", ".tmp")
|
||||
val thumbnailFile = Files.createTempFile("hideout-movie-thumbnail-generate-", ".tmp")
|
||||
logger.info("START Convert Movie Media {}", filename)
|
||||
var bufferedImage: BufferedImage? = null
|
||||
FFmpegFrameGrabber(path.toFile()).use { grabber ->
|
||||
grabber.start()
|
||||
val width = min(fFmpegVideoConfig.maxWidth, grabber.imageWidth)
|
||||
val height = min(fFmpegVideoConfig.maxHeight, grabber.imageHeight)
|
||||
val frameRate = fFmpegVideoConfig.frameRate
|
||||
|
||||
logger.debug("Movie Media Width {}, Height {}", width, height)
|
||||
|
||||
FFmpegFrameFilter(
|
||||
"fps=fps=$frameRate",
|
||||
"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(fFmpegVideoConfig.maxBitrate, (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 = fFmpegVideoConfig.format
|
||||
it.videoCodec = fFmpegVideoConfig.videoCodec
|
||||
it.audioCodec = fFmpegVideoConfig.audioCodec
|
||||
it.audioChannels = grabber.audioChannels
|
||||
it.videoQuality = fFmpegVideoConfig.videoQuality
|
||||
it.frameRate = frameRate.toDouble()
|
||||
it.setVideoOption("preset", "ultrafast")
|
||||
it.timestamp = 0
|
||||
it.gopSize = frameRate
|
||||
it.videoBitrate = videoBitRate
|
||||
it.start()
|
||||
|
||||
|
||||
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 ProcessedMedia(
|
||||
tempFile,
|
||||
thumbnailFile,
|
||||
FileType.Video,
|
||||
MimeType("video", fFmpegVideoConfig.format, FileType.Video),
|
||||
bufferedImage?.let { generateBlurhash.generateBlurhash(it) }
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(FFmpegVideoProcessor::class.java)
|
||||
}
|
||||
}
|
|
@ -215,7 +215,7 @@ private fun toStatus(it: ResultRow) = Status(
|
|||
followingCount = it[Actors.followingCount],
|
||||
noindex = false,
|
||||
moved = false,
|
||||
suspendex = false,
|
||||
suspended = false,
|
||||
limited = false
|
||||
),
|
||||
content = it[Posts.text],
|
||||
|
|
Loading…
Reference in New Issue