-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added logic for db access and storage
- Loading branch information
Showing
12 changed files
with
320 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
server/src/main/kotlin/io/github/jsixface/codexvert/db/VideoFilesRepo.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package io.github.jsixface.codexvert.db | ||
|
||
import io.github.jsixface.codexvert.ffprobe.ProbeInfo | ||
import io.github.jsixface.codexvert.ffprobe.ProbeStream | ||
import io.github.jsixface.codexvert.logger | ||
import io.github.jsixface.codexvert.utils.updateInfo | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.datetime.Clock | ||
import org.jetbrains.exposed.sql.Database | ||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction | ||
import java.nio.file.Path | ||
import kotlin.io.path.absolutePathString | ||
import kotlin.io.path.fileSize | ||
import kotlin.io.path.getLastModifiedTime | ||
|
||
|
||
interface IVideoFilesRepo { | ||
suspend fun getAll(): List<VideoFileEntity> | ||
suspend fun get(id: Int): VideoFileEntity? | ||
suspend fun getFile(path: Path): VideoFileEntity? | ||
suspend fun delete(id: Int) | ||
suspend fun update(videoInfo: ProbeInfo, entity: VideoFileEntity): Boolean | ||
suspend fun create(videoInfo: ProbeInfo, file: Path): VideoFileEntity | ||
} | ||
|
||
class VideoFilesRepo(private val db: Database) : IVideoFilesRepo { | ||
private val logger = logger() | ||
|
||
private suspend fun <T> dbQuery(block: suspend () -> T): T = | ||
newSuspendedTransaction(Dispatchers.IO) { block() } | ||
|
||
override suspend fun getAll() = dbQuery { VideoFileEntity.all().toList() } | ||
|
||
override suspend fun get(id: Int) = dbQuery { VideoFileEntity.findById(id) } | ||
|
||
override suspend fun getFile(path: Path) = dbQuery { | ||
VideoFileEntity.find { VideoFilesTable.path eq path.toAbsolutePath().toString() }.firstOrNull() | ||
} | ||
|
||
override suspend fun create(videoInfo: ProbeInfo, file: Path): VideoFileEntity { | ||
return dbQuery { | ||
val v = VideoFileEntity.new { | ||
path = file.absolutePathString() | ||
name = file.fileName.toString() | ||
sizeMb = file.fileSize().toInt() / 1024 / 1024 | ||
modified = file.getLastModifiedTime().toMillis() | ||
added = Clock.System.now().toEpochMilliseconds() | ||
} | ||
logger.debug("Added video file: ${v.path}") | ||
videoInfo.streams.forEach { createStream(it, v) } | ||
v | ||
} | ||
} | ||
|
||
private fun createStream(stream: ProbeStream, v: VideoFileEntity) { | ||
when (stream.codecType) { | ||
"audio" -> { | ||
AudioEntity.new { | ||
videoFile = v | ||
updateInfo(stream) | ||
} | ||
} | ||
|
||
"video" -> { | ||
VideoEntity.new { | ||
videoFile = v | ||
updateInfo(stream) | ||
} | ||
} | ||
|
||
"subtitle" -> { | ||
SubtitleEntity.new { | ||
videoFile = v | ||
index = stream.index | ||
codec = stream.codecName | ||
language = stream.tags["language"] ?: "" | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
override suspend fun delete(id: Int) { | ||
dbQuery { | ||
val v = get(id) | ||
logger.debug("deleting video file: ${v?.path}") | ||
v?.delete() | ||
} | ||
} | ||
|
||
override suspend fun update(videoInfo: ProbeInfo, entity: VideoFileEntity) = dbQuery { | ||
try { | ||
entity.videoStream.delete() | ||
entity.audioStreams.forEach { it.delete() } | ||
entity.subtitles.forEach { it.delete() } | ||
videoInfo.streams.forEach { createStream(it, entity) } | ||
true | ||
} catch (e: Exception) { | ||
false | ||
} | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
server/src/main/kotlin/io/github/jsixface/codexvert/ffprobe/Parser.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package io.github.jsixface.codexvert.ffprobe | ||
|
||
import io.github.jsixface.codexvert.db.IVideoFilesRepo | ||
import io.github.jsixface.codexvert.logger | ||
import java.nio.file.Path | ||
import kotlin.io.path.Path | ||
import kotlin.io.path.PathWalkOption | ||
import kotlin.io.path.extension | ||
import kotlin.io.path.getLastModifiedTime | ||
import kotlin.io.path.isDirectory | ||
import kotlin.io.path.walk | ||
|
||
|
||
interface IParser { | ||
suspend fun parseVideoFile(file: Path) | ||
suspend fun parseAll(locations: List<String>, extensions: List<String>) | ||
} | ||
|
||
class Parser(private val repo: IVideoFilesRepo) : IParser { | ||
private val logger = logger() | ||
|
||
override suspend fun parseVideoFile(file: Path) { | ||
val p = ProbeUtils.parseMediaInfo(file) ?: return | ||
val entity = repo.getFile(file) | ||
if (entity == null) { | ||
repo.create(p, file) | ||
} else { | ||
repo.update(p, entity) | ||
} | ||
} | ||
|
||
override suspend fun parseAll(locations: List<String>, extensions: List<String>) { | ||
val videos = locations.map { Path(it) }.flatMap { loc -> | ||
loc.walk(PathWalkOption.FOLLOW_LINKS) | ||
.filter { it.isDirectory().not() } | ||
.filter { extensions.contains(it.extension.lowercase()) } | ||
}.associateWith { it.getLastModifiedTime().toMillis() } | ||
.mapKeys { it.key.toAbsolutePath().toString() } | ||
val entries = repo.getAll().associateBy { it.path } | ||
|
||
val added = videos - entries.keys | ||
val deleted = entries - videos.keys | ||
val modified = videos.filter { it.value != entries[it.key]?.modified } - added.keys | ||
logger.info("Total files: ${videos.size}, Added: ${added.size}, Deleted: ${deleted.size}, Modified: ${modified.size}") | ||
added.keys.forEach { parseVideoFile(Path(it)) } | ||
modified.keys.forEach { parseVideoFile(Path(it)) } | ||
deleted.values.forEach { repo.delete(it.id.value) } | ||
} | ||
|
||
} |
39 changes: 39 additions & 0 deletions
39
server/src/main/kotlin/io/github/jsixface/codexvert/ffprobe/ProbeInfo.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package io.github.jsixface.codexvert.ffprobe | ||
|
||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable | ||
data class ProbeStream( | ||
val index: Int, | ||
@SerialName("codec_name") val codecName: String = "", | ||
@SerialName("codec_type") val codecType: String, | ||
@SerialName("codec_tag_string") val codecTagName: String = "", | ||
val profile: String = "", | ||
val width: Int = 0, | ||
val height: Int = 0, | ||
@SerialName("display_aspect_ratio") val aspectRatio: String = "", | ||
@SerialName("avg_frame_rate") val frameRate: String = "", | ||
val bitRate: String = "", | ||
val bitDepth: String = "", | ||
@SerialName("pix_fmt") val pixelFormat: String = "", | ||
|
||
val channels: Int = 1, | ||
@SerialName("channel_layout") val channelLayout: String = "", | ||
@SerialName("sample_rate") val sampleRate: String = "", | ||
@SerialName("tags") val tags: Map<String, String> = emptyMap(), | ||
) | ||
|
||
@Serializable | ||
data class ProbeFormat( | ||
@SerialName("filename") val fileName: String, | ||
@SerialName("nb_streams") val numStreams: Int, | ||
@SerialName("format_long_name") val formatName: String, | ||
@SerialName("duration") val duration: String = "", | ||
) | ||
|
||
@Serializable | ||
data class ProbeInfo( | ||
val streams: List<ProbeStream>, | ||
val format: ProbeFormat, | ||
) |
36 changes: 36 additions & 0 deletions
36
server/src/main/kotlin/io/github/jsixface/codexvert/ffprobe/ProbeUtils.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package io.github.jsixface.codexvert.ffprobe | ||
|
||
import io.github.jsixface.codexvert.logger | ||
import kotlinx.serialization.json.Json | ||
import java.nio.file.Path | ||
|
||
object ProbeUtils { | ||
private val logger = logger() | ||
private val json = Json { | ||
ignoreUnknownKeys = true | ||
} | ||
|
||
fun parseMediaInfo(path: Path): ProbeInfo? { | ||
logger.info("Parsing file $path") | ||
val builder = | ||
ProcessBuilder( | ||
"ffprobe", | ||
"-v", "error", | ||
"-of", "json", | ||
"-pretty", | ||
"-show_entries", "stream:program:format:chapter", | ||
path.toAbsolutePath().toString(), | ||
) | ||
return try { | ||
val process = builder.start() | ||
val output = process.inputStream.use { it.bufferedReader().readText() } | ||
process.waitFor() | ||
val probeInfo = json.decodeFromString<ProbeInfo>(output) | ||
logger.debug("Probe info: $probeInfo") | ||
probeInfo | ||
} catch (e: Exception) { | ||
logger.error("Cant get file information for $path", e) | ||
null | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 6 additions & 5 deletions
11
server/src/main/kotlin/io/github/jsixface/codexvert/plugins/Routing.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
server/src/main/kotlin/io/github/jsixface/codexvert/utils/AspectRatio.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package io.github.jsixface.codexvert.utils | ||
|
||
class AspectRatio(private val w: Float, private val h: Float) { | ||
|
||
override fun toString(): String { | ||
val (wi, hi) = when { | ||
w < 10 || h < 10 -> w to h | ||
w > h -> w / h to 1.0f | ||
else -> 1.0f to h / w | ||
} | ||
return "${wi.shortString()}:${hi.shortString()}" | ||
} | ||
|
||
companion object { | ||
operator fun invoke(ratio: String): AspectRatio { | ||
val measures = ratio.split(":").mapNotNull { it.toFloatOrNull() } | ||
assert(measures.size == 2) { "Should be in the format 'w:h" } | ||
return AspectRatio(measures[0], measures[1]) | ||
} | ||
} | ||
} |
Oops, something went wrong.