Fixed projection
This commit is contained in:
parent
ed6bddb95f
commit
438044faaf
@ -6,11 +6,18 @@ import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.*
|
|||||||
import no.iktdev.mediaprocessing.shared.common.resolveConflict
|
import no.iktdev.mediaprocessing.shared.common.resolveConflict
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
open class MigrateContentProject(val events: List<Event>, val storageArea: File) {
|
open class MigrateContentProject(
|
||||||
|
val events: List<Event>,
|
||||||
|
val storageArea: File
|
||||||
|
) {
|
||||||
|
|
||||||
val useStore: File? = getDesiredStoreFolder()
|
val useStore: File? = getDesiredStoreFolder()
|
||||||
|
|
||||||
open fun getFoldersInStore(): List<String> {
|
open fun getFoldersInStore(): List<String> {
|
||||||
return storageArea.listFiles { file -> file.isDirectory }?.map { it.name }?.toList() ?: emptyList()
|
return storageArea
|
||||||
|
.listFiles { file -> file.isDirectory }
|
||||||
|
?.map { it.name }
|
||||||
|
?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getFileName(): String? {
|
internal fun getFileName(): String? {
|
||||||
@ -19,75 +26,77 @@ open class MigrateContentProject(val events: List<Event>, val storageArea: File)
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun getDesiredStoreFolder(): File? {
|
internal fun getDesiredStoreFolder(): File? {
|
||||||
val assuredCollection = getDesiredCollection() ?: return null
|
val desiredCollection = getDesiredCollection() ?: return null
|
||||||
val assuredStore = storageArea.using(assuredCollection)
|
val assuredStore = storageArea.using(desiredCollection)
|
||||||
val existingCollectionNames = getFoldersInStore().ifEmpty {
|
|
||||||
|
val existingCollectionNames = getFoldersInStore()
|
||||||
|
if (existingCollectionNames.isEmpty()) {
|
||||||
return assuredStore
|
return assuredStore
|
||||||
}
|
}
|
||||||
|
|
||||||
val titles = getMetadataTitles()
|
val titles = getMetadataTitles()
|
||||||
val existingCollection = titles.filter { it in existingCollectionNames }.firstOrNull()?.let {
|
|
||||||
File(it)
|
val matchedExisting = titles
|
||||||
} ?: assuredStore
|
.firstOrNull { it in existingCollectionNames }
|
||||||
return existingCollection
|
?.let { storageArea.using(it) }
|
||||||
|
|
||||||
|
return matchedExisting ?: assuredStore
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getMetadataTitles(): List<String> {
|
internal fun getMetadataTitles(): List<String> {
|
||||||
val metadataEvent = events.filterIsInstance<MetadataSearchResultEvent>().lastOrNull()?.recommended?.metadata
|
val metadataEvent = events
|
||||||
|
.filterIsInstance<MetadataSearchResultEvent>()
|
||||||
|
.lastOrNull()
|
||||||
|
?.recommended
|
||||||
|
?.metadata
|
||||||
?: return emptyList()
|
?: return emptyList()
|
||||||
return (metadataEvent.alternateTitles + listOf(metadataEvent.title))
|
|
||||||
|
return metadataEvent.alternateTitles + metadataEvent.title
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getDesiredCollection(): String? {
|
internal fun getDesiredCollection(): String? {
|
||||||
val metadataEvent = events.filterIsInstance<MediaParsedInfoEvent>().lastOrNull()?.data
|
return events
|
||||||
return metadataEvent?.parsedCollection
|
.filterIsInstance<MediaParsedInfoEvent>()
|
||||||
|
.lastOrNull()
|
||||||
|
?.data
|
||||||
|
?.parsedCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getVideoStoreFile(): CachedToStore? {
|
fun getVideoStoreFile(): CachedToStore? {
|
||||||
val encoded = events.filterIsInstance<ProcesserEncodeResultEvent>().lastOrNull()
|
val encoded = events.filterIsInstance<ProcesserEncodeResultEvent>().lastOrNull()
|
||||||
val cached = encoded?.data?.cachedOutputFile?.let { File(it) } ?: return null
|
val cached = encoded?.data?.cachedOutputFile?.let(::File) ?: return null
|
||||||
val useFilename = getFileName()?.let { "$it.${cached.extension}" } ?: return null
|
|
||||||
val store = useStore?.using(useFilename) ?: return null
|
val filename = getFileName()?.let { "$it.${cached.extension}" } ?: return null
|
||||||
return CachedToStore(
|
val store = useStore?.using(filename) ?: return null
|
||||||
cachedFile = cached,
|
|
||||||
storeFile = store
|
return CachedToStore(cachedFile = cached, storeFile = store)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSubtitleStoreFiles(): List<CachedToStoreLanguage>? {
|
fun getSubtitleStoreFiles(): List<CachedToStoreLanguage>? {
|
||||||
val extractedFiles: Map<String, List<File>> = events
|
val extractedFiles = events
|
||||||
.filterIsInstance<ProcesserExtractResultEvent>()
|
.filterIsInstance<ProcesserExtractResultEvent>()
|
||||||
.mapNotNull { it.data }
|
.mapNotNull { it.data }
|
||||||
.map { data -> data.language to File(data.cachedOutputFile) }
|
.map { it.language to File(it.cachedOutputFile) }
|
||||||
.groupBy({ it.first }, { it.second })
|
.groupBy({ it.first }, { it.second })
|
||||||
|
|
||||||
val convertedFilesGrouped: Map<String, List<File>> = events
|
val convertedFiles = events
|
||||||
.filterIsInstance<ConvertTaskResultEvent>()
|
.filterIsInstance<ConvertTaskResultEvent>()
|
||||||
.mapNotNull { it.data }
|
.mapNotNull { it.data }
|
||||||
.map { x -> x.outputFiles.map { x.language to File(it) } }
|
.flatMap { d -> d.outputFiles.map { d.language to File(it) } }
|
||||||
.flatten()
|
|
||||||
.groupBy({ it.first }, { it.second })
|
.groupBy({ it.first }, { it.second })
|
||||||
|
|
||||||
|
|
||||||
val byLanguage = mutableMapOf<String, MutableList<File>>()
|
val byLanguage = mutableMapOf<String, MutableList<File>>()
|
||||||
extractedFiles.forEach { (lang, files) ->
|
extractedFiles.forEach { (lang, files) -> byLanguage.getOrPut(lang) { mutableListOf() }.addAll(files) }
|
||||||
byLanguage.getOrPut(lang) { mutableListOf() }.addAll(files)
|
convertedFiles.forEach { (lang, files) -> byLanguage.getOrPut(lang) { mutableListOf() }.addAll(files) }
|
||||||
}
|
|
||||||
convertedFilesGrouped.forEach { (lang, files) ->
|
|
||||||
byLanguage.getOrPut(lang) { mutableListOf() }.addAll(files)
|
|
||||||
}
|
|
||||||
|
|
||||||
val useFilename = getFileName() ?: return null
|
val baseName = getFileName() ?: return null
|
||||||
|
|
||||||
return byLanguage.flatMap { (language, files) ->
|
return byLanguage.flatMap { (language, files) ->
|
||||||
files.mapNotNull { cached ->
|
files.mapNotNull { cached ->
|
||||||
val useFilename = "$useFilename.${cached.extension}"
|
val filename = "$baseName.${cached.extension}"
|
||||||
val store = useStore?.using(language, useFilename) ?: return@mapNotNull null
|
val store = useStore?.using(language, filename) ?: return@mapNotNull null
|
||||||
CachedToStoreLanguage(
|
CachedToStoreLanguage(
|
||||||
CachedToStore(
|
cts = CachedToStore(cachedFile = cached, storeFile = store),
|
||||||
cachedFile = cached,
|
|
||||||
storeFile = store
|
|
||||||
),
|
|
||||||
language = language
|
language = language
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -95,10 +104,11 @@ open class MigrateContentProject(val events: List<Event>, val storageArea: File)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getCoverStoreFiles(): List<CachedToStore>? {
|
fun getCoverStoreFiles(): List<CachedToStore>? {
|
||||||
val downloaded = events.filterIsInstance<CoverDownloadResultEvent>()
|
val downloaded = events
|
||||||
.mapNotNull { event ->
|
.filterIsInstance<CoverDownloadResultEvent>()
|
||||||
val file = event.data?.outputFile?.let(::File) ?: return@mapNotNull null
|
.mapNotNull { e ->
|
||||||
event to file
|
val file = e.data?.outputFile?.let(::File) ?: return@mapNotNull null
|
||||||
|
e to file
|
||||||
}
|
}
|
||||||
|
|
||||||
val baseName = getDesiredCollection() ?: return null
|
val baseName = getDesiredCollection() ?: return null
|
||||||
@ -110,10 +120,7 @@ open class MigrateContentProject(val events: List<Event>, val storageArea: File)
|
|||||||
val ext = cached.extension
|
val ext = cached.extension
|
||||||
val source = event.data?.source ?: "unknown"
|
val source = event.data?.source ?: "unknown"
|
||||||
|
|
||||||
// Bestem om vi skal bruke source i navnet
|
val filename = if (multiple || store.using("$baseName.$ext").exists()) {
|
||||||
val useSource = multiple || store.using("$baseName.$ext").exists()
|
|
||||||
|
|
||||||
val filename = if (useSource) {
|
|
||||||
"$baseName-$source.$ext"
|
"$baseName-$source.$ext"
|
||||||
} else {
|
} else {
|
||||||
"$baseName.$ext"
|
"$baseName.$ext"
|
||||||
@ -121,21 +128,10 @@ open class MigrateContentProject(val events: List<Event>, val storageArea: File)
|
|||||||
|
|
||||||
val storeFile = store.using(filename).resolveConflict()
|
val storeFile = store.using(filename).resolveConflict()
|
||||||
|
|
||||||
CachedToStore(
|
CachedToStore(cachedFile = cached, storeFile = storeFile)
|
||||||
cachedFile = cached,
|
|
||||||
storeFile = storeFile
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class CachedToStore(val cachedFile: File, val storeFile: File)
|
||||||
data class CachedToStore(
|
data class CachedToStoreLanguage(val cts: CachedToStore, val language: String)
|
||||||
val cachedFile: File,
|
|
||||||
val storeFile: File
|
|
||||||
)
|
|
||||||
|
|
||||||
data class CachedToStoreLanguage(
|
|
||||||
val cts: CachedToStore,
|
|
||||||
val language: String
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,119 @@
|
|||||||
|
package no.iktdev.mediaprocessing.shared.common.projection
|
||||||
|
|
||||||
|
import no.iktdev.eventi.models.Event
|
||||||
|
import no.iktdev.eventi.models.store.TaskStatus
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.MediaParsedInfoEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.MediaParsedInfoEvent.ParsedData
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.MetadataSearchResultEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.event_task_contract.events.ProcesserEncodeResultEvent
|
||||||
|
import no.iktdev.mediaprocessing.shared.common.model.MediaType
|
||||||
|
import org.junit.jupiter.api.Assertions.*
|
||||||
|
import org.junit.jupiter.api.DisplayName
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class MigrateContentProjectPathTest {
|
||||||
|
|
||||||
|
private fun tempDir(): File {
|
||||||
|
val dir = createTempDir(prefix = "store-test-")
|
||||||
|
dir.deleteOnExit()
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"""
|
||||||
|
Når projeksjonen beregner video-path
|
||||||
|
Så:
|
||||||
|
Skal pathen alltid ligge under storageArea
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun videoPathIsUnderStorageArea() {
|
||||||
|
val storage = tempDir()
|
||||||
|
|
||||||
|
val events = listOf<Event>(
|
||||||
|
MediaParsedInfoEvent(
|
||||||
|
ParsedData(
|
||||||
|
parsedCollection = "Breaking Bad",
|
||||||
|
parsedFileName = "bb.s01e01",
|
||||||
|
parsedSearchTitles = emptyList(),
|
||||||
|
mediaType = MediaType.Serie
|
||||||
|
)
|
||||||
|
),
|
||||||
|
ProcesserEncodeResultEvent(
|
||||||
|
status = TaskStatus.Completed,
|
||||||
|
data = ProcesserEncodeResultEvent.EncodeResult(
|
||||||
|
cachedOutputFile = "/tmp/encoded.mp4"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val project = MigrateContentProject(events, storage)
|
||||||
|
|
||||||
|
val video = project.getVideoStoreFile()
|
||||||
|
assertNotNull(video)
|
||||||
|
|
||||||
|
val storeFile = video!!.storeFile
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
storeFile.absolutePath.startsWith(storage.absolutePath),
|
||||||
|
"Store path must be inside storageArea"
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
File(storage, "Breaking Bad/bb.s01e01.mp4").absolutePath,
|
||||||
|
storeFile.absolutePath
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"""
|
||||||
|
Når metadata-titler matcher eksisterende mapper
|
||||||
|
Så:
|
||||||
|
Skal projeksjonen bruke eksisterende mappe
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun usesExistingCollectionFolder() {
|
||||||
|
val storage = tempDir()
|
||||||
|
val existing = File(storage, "Breaking Bad")
|
||||||
|
existing.mkdirs()
|
||||||
|
|
||||||
|
val events = listOf<Event>(
|
||||||
|
MediaParsedInfoEvent(
|
||||||
|
ParsedData(
|
||||||
|
parsedCollection = "bb",
|
||||||
|
parsedFileName = "bb.s01e01",
|
||||||
|
parsedSearchTitles = emptyList(),
|
||||||
|
mediaType = MediaType.Serie
|
||||||
|
)
|
||||||
|
),
|
||||||
|
MetadataSearchResultEvent(
|
||||||
|
results = emptyList(),
|
||||||
|
recommended = MetadataSearchResultEvent.SearchResult(
|
||||||
|
simpleScore = 10,
|
||||||
|
prefixScore = 10,
|
||||||
|
advancedScore = 10,
|
||||||
|
sourceWeight = 1f,
|
||||||
|
metadata = MetadataSearchResultEvent.SearchResult.MetadataResult(
|
||||||
|
source = "tmdb",
|
||||||
|
title = "Breaking Bad",
|
||||||
|
alternateTitles = listOf("BB"),
|
||||||
|
cover = "x.jpg",
|
||||||
|
type = MediaType.Serie,
|
||||||
|
summary = emptyList(),
|
||||||
|
genres = emptyList()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
status = TaskStatus.Completed
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val project = MigrateContentProject(events, storage)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
existing.absolutePath,
|
||||||
|
project.useStore!!.absolutePath
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user