Skip to content

Commit

Permalink
Merge pull request #48 from KevinSchildhorn/JsonPlaylist
Browse files Browse the repository at this point in the history
Json playlist
  • Loading branch information
KevinSchildhorn authored Feb 8, 2024
2 parents 0dcf6b0 + 6594f0d commit 328e62e
Show file tree
Hide file tree
Showing 26 changed files with 553 additions and 163 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ plugins {
id("org.jlleitschuh.gradle.ktlint").version("12.0.2").apply(false)
id("dev.icerock.mobile.multiplatform-resources").version("0.23.0").apply(false)
id("app.cash.sqldelight").version("2.0.1").apply(false)
kotlin("plugin.serialization").version("1.9.21").apply(false)
}
3 changes: 3 additions & 0 deletions shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ plugins {
id("org.jlleitschuh.gradle.ktlint")
id("dev.icerock.mobile.multiplatform-resources")
id("app.cash.sqldelight")
kotlin("plugin.serialization")
}

kotlin {
Expand Down Expand Up @@ -36,6 +37,7 @@ kotlin {

implementation("io.github.reactivecircus.cache4k:cache4k:0.12.0")

implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
implementation("io.insert-koin:koin-core:3.4.0")
implementation("androidx.security:security-crypto:1.1.0-alpha06")
implementation("co.touchlab:kermit:1.2.2")
Expand All @@ -53,6 +55,7 @@ kotlin {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
implementation("io.insert-koin:koin-test:3.4.0")
implementation("app.cash.turbine:turbine:1.0.0")
implementation("app.cash.sqldelight:sqlite-driver:2.0.1")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import com.kevinschildhorn.fotopresenter.data.datasources.CredentialsDataSource
import com.kevinschildhorn.fotopresenter.data.datasources.DirectoryDataSource
import com.kevinschildhorn.fotopresenter.data.datasources.ImageCacheDataSource
import com.kevinschildhorn.fotopresenter.data.datasources.ImageRemoteDataSource
import com.kevinschildhorn.fotopresenter.data.datasources.PlaylistDataSource
import com.kevinschildhorn.fotopresenter.data.datasources.PlaylistFileDataSource
import com.kevinschildhorn.fotopresenter.data.datasources.PlaylistSQLDataSource
import com.kevinschildhorn.fotopresenter.data.repositories.CredentialsRepository
import com.kevinschildhorn.fotopresenter.data.repositories.DirectoryRepository
import com.kevinschildhorn.fotopresenter.data.repositories.ImageRepository
Expand Down Expand Up @@ -43,8 +44,9 @@ val commonModule =
single { ImageRemoteDataSource(get()) }
single { ImageRepository(get()) }
single { ImageCacheDataSource(get(), get(), baseLogger.withTag("ImageCacheDataSource")) }
single { PlaylistDataSource(get(), baseLogger.withTag("PlaylistDataSource")) }
single { PlaylistRepository(get()) }
single { PlaylistFileDataSource(baseLogger.withTag("PlaylistDataSource"), get()) }
single { PlaylistSQLDataSource(get(), baseLogger.withTag("PlaylistDataSource")) }
single { PlaylistRepository(get(), get()) }

// Domain
factory { ConnectToServerUseCase(get(), baseLogger.withTag("ConnectToServerUseCase")) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,16 @@ data class LoginCredentials(
username.isNotBlank() &&
password.isNotBlank() &&
sharedFolder.isNotBlank()

override fun toString(): String {
return """
LoginCredentials(
hostname: $hostname
username: $username
password: $password
sharedFolder: $sharedFolder
shouldAutoConnect: $shouldAutoConnect
)
""".trimIndent()
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.kevinschildhorn.fotopresenter.data

import com.kevinschildhorn.fotopresenter.PlaylistItems
import com.kevinschildhorn.fotopresenter.data.network.DefaultNetworkDirectoryDetails
import com.kevinschildhorn.fotopresenter.extension.isImagePath
import kotlinx.serialization.Serializable

@Serializable
data class PlaylistDetails(
val id: Long,
val name: String,
val items: List<PlaylistItems> = emptyList(),
){
val items: List<PlaylistItem> = emptyList(),
) {
override fun toString(): String {
return """
Playlist Details:
Expand All @@ -17,4 +17,19 @@ data class PlaylistDetails(
images: ${items.count()}
"""
}
}

@Serializable
data class PlaylistItem(
val id: Long,
val playlistId: Long,
val directoryPath: String,
val directoryId: Long,
) {
constructor(item: PlaylistItems) : this(
id = item.id,
playlistId = item.playlist_id,
directoryPath = item.directory_path,
directoryId = item.directory_id
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ Fetches Directory info from a NAS
**/
class DirectoryDataSource(
private val networkHandler: NetworkHandler,
private val logger: Logger,
private val logger: Logger?,
) {
@Throws(NetworkHandlerException::class, CancellationException::class)
suspend fun changeDirectory(directoryName: String): String {
logger.i { "Changing directory to $directoryName" }
logger.i { "Is network Connected? ${networkHandler.isConnected}" }
logger?.i { "Changing directory to $directoryName" }
logger?.i { "Is network Connected? ${networkHandler.isConnected}" }
if (!networkHandler.isConnected) throw NetworkHandlerException(NetworkHandlerError.NOT_CONNECTED)

logger.i { "Does the directory exist?" }
logger?.i { "Does the directory exist?" }
//val exists = networkHandler.folderExists(directoryName)
//logger.i { "Does the directory exist? $exists" }
//logger?.i { "Does the directory exist? $exists" }

logger.i { "Opening the directory..." }
logger?.i { "Opening the directory..." }
networkHandler.openDirectory(directoryName)?.let { return it }
throw NetworkHandlerException(NetworkHandlerError.DIRECTORY_NOT_FOUND)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.kevinschildhorn.fotopresenter.data.datasources

import co.touchlab.kermit.Logger
import com.kevinschildhorn.fotopresenter.data.PlaylistDetails
import com.kevinschildhorn.fotopresenter.data.network.NetworkHandler
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.koin.core.component.KoinComponent

class PlaylistFileDataSource(
private val logger: Logger?,
private val networkHandler: NetworkHandler,
) : KoinComponent {

suspend fun importPlaylists(): List<PlaylistDetails> =
networkHandler.getPlaylists().mapNotNull {
try {
Json.decodeFromString<PlaylistDetails>(it)
} catch (e: Exception) {
logger?.e(e) { "Error importing Playlist" }
null
}
}

suspend fun deletePlaylist(playlist: PlaylistDetails): Boolean =
try {
networkHandler.deletePlaylist(playlist.name)
true
} catch (e: Exception) {
logger?.e(e) { "Error Deleting Playlist" }
false
}

suspend fun exportPlaylist(playlist: PlaylistDetails): Boolean {
try {
val jsonString = Json.encodeToString(playlist)
networkHandler.savePlaylist(playlist.name, jsonString)
} catch (e: Exception) {
logger?.e(e) { "Error Exporting Playlists" }
return false
}
return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import com.kevinschildhorn.fotopresenter.PlaylistItems
import com.kevinschildhorn.fotopresenter.data.Directory
import com.kevinschildhorn.fotopresenter.data.ImageDirectory
import com.kevinschildhorn.fotopresenter.data.PlaylistDetails
import com.kevinschildhorn.fotopresenter.data.PlaylistItem
import org.koin.core.component.KoinComponent

class PlaylistDataSource(
class PlaylistSQLDataSource(
driver: SqlDriver,
private val logger: Logger? = null,
) : KoinComponent {
Expand All @@ -35,10 +36,10 @@ class PlaylistDataSource(

fun getAllPlaylists(): List<PlaylistDetails> {
return try {
database.playlistQueries.selectAllPlaylists().executeAsList().map {
database.playlistQueries.selectAllPlaylists().executeAsList().map { playlist ->
val images =
database.playlistItemsQueries.selectPlaylistImages(it.id).executeAsList()
PlaylistDetails(it.id,it.name, images)
database.playlistItemsQueries.selectPlaylistImages(playlist.id).executeAsList()
PlaylistDetails(playlist.id, playlist.name, images.map { PlaylistItem(it) })
}
} catch (e: Exception) {
emptyList()
Expand All @@ -53,13 +54,27 @@ class PlaylistDataSource(
val images =
database.playlistItemsQueries.selectPlaylistImages(playList.id).executeAsList()
logger?.i { "Retrieved Playlist!" }
PlaylistDetails(playList.id, playList.name, images)
PlaylistDetails(playList.id, playList.name, images.map { PlaylistItem(it) })
} catch (e: Exception) {
null
}
}

fun insertPlaylistImage(playlistId: Long, directory: Directory): PlaylistItems? {
fun getPlaylistById(id: Long): PlaylistDetails? {
return try {
logger?.i { "Selecting playlist by id $id" }
val playList: Playlist =
database.playlistQueries.selectPlaylistById(id).executeAsOne()
val images =
database.playlistItemsQueries.selectPlaylistImages(playList.id).executeAsList()
logger?.i { "Retrieved Playlist!" }
PlaylistDetails(playList.id, playList.name, images.map { PlaylistItem(it) })
} catch (e: Exception) {
null
}
}

fun insertPlaylistImage(playlistId: Long, directory: Directory): PlaylistItem? {
logger?.i { "Inserting Playlist Image ${directory.name}" }
database.playlistItemsQueries.insertPlaylistImage(
playlist_id = playlistId,
Expand All @@ -69,14 +84,14 @@ class PlaylistDataSource(
return getPlaylistImage(playlistId, directory.details.fullPath)
}

fun getPlaylistImage(playlistId: Long, directoryPath: String): PlaylistItems? {
fun getPlaylistImage(playlistId: Long, directoryPath: String): PlaylistItem? {
return try {
logger?.i { "Selecting Playlist Image $playlistId" }
val image: PlaylistItems =
database.playlistItemsQueries.selectPlaylistImage(playlistId, directoryPath)
.executeAsOne()
logger?.i { "Selecting Playlist Image" }
image
PlaylistItem(image)
} catch (e: Exception) {
null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ interface NetworkHandler {
suspend fun openImage(path: String): SharedImage?

suspend fun folderExists(path: String): Boolean?

suspend fun savePlaylist(playlistName:String, json:String): Boolean
suspend fun getPlaylists(): List<String>

suspend fun setMetadata(json:String): Boolean
suspend fun getMetadata(): String?

suspend fun deletePlaylist(playlistName: String)
}

class NetworkHandlerException : Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,44 @@
package com.kevinschildhorn.fotopresenter.data.repositories

import com.kevinschildhorn.fotopresenter.Playlist
import com.kevinschildhorn.fotopresenter.PlaylistItems
import com.kevinschildhorn.fotopresenter.data.Directory
import com.kevinschildhorn.fotopresenter.data.ImageDirectory
import com.kevinschildhorn.fotopresenter.data.PlaylistDetails
import com.kevinschildhorn.fotopresenter.data.datasources.PlaylistDataSource
import com.kevinschildhorn.fotopresenter.data.PlaylistItem
import com.kevinschildhorn.fotopresenter.data.datasources.PlaylistFileDataSource
import com.kevinschildhorn.fotopresenter.data.datasources.PlaylistSQLDataSource

class PlaylistRepository(
private val playlistDataSource: PlaylistDataSource,
private val playlistSQLDataSource: PlaylistSQLDataSource,
private val playlistFileDataSource: PlaylistFileDataSource,
) {

fun createPlaylist(name: String, directories: List<ImageDirectory> = emptyList()): Playlist? =
playlistDataSource.createPlaylist(name, directories)
suspend fun createPlaylist(name: String, directories: List<ImageDirectory> = emptyList()): Playlist? {
val playlist = playlistSQLDataSource.createPlaylist(name, directories)
playlistSQLDataSource.getPlaylistByName(name)?.let {
playlistFileDataSource.exportPlaylist(it)
}
return playlist
}

suspend fun getAllPlaylists(): List<PlaylistDetails> {
playlistFileDataSource.importPlaylists()
return playlistSQLDataSource.getAllPlaylists()
}

suspend fun insertPlaylistImage(playlistId: Long, directory: Directory): PlaylistItem? {
val item = playlistSQLDataSource.insertPlaylistImage(playlistId, directory)
playlistSQLDataSource.getPlaylistById(playlistId)?.let {
playlistFileDataSource.exportPlaylist(it)
}
return item
}

suspend fun deletePlaylist(id: Long): Boolean {
playlistSQLDataSource.getPlaylistById(id)?.let {
playlistFileDataSource.deletePlaylist(it)
}
return playlistSQLDataSource.deletePlaylist(id)
}

fun getAllPlaylists(): List<PlaylistDetails> =
playlistDataSource.getAllPlaylists()

fun getPlaylistByName(name: String): PlaylistDetails? =
playlistDataSource.getPlaylistByName(name)

fun insertPlaylistImage(playlistId: Long, directory: Directory): PlaylistItems? =
playlistDataSource.insertPlaylistImage(playlistId, directory)

fun getPlaylistImage(playlistId: Long, directoryPath: String): PlaylistItems? =
playlistDataSource.getPlaylistImage(playlistId, directoryPath)

fun deletePlaylist(id: Long): Boolean =
playlistDataSource.deletePlaylist(id)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ConnectToServerUseCase(
) {
suspend operator fun invoke(credentials: LoginCredentials): Boolean =
try {
logger.i { "Connecting to Client" }
logger.i { "Connecting to Client ${credentials.hostname}" }
client.connect(credentials)
} catch (e: Exception) {
logger.e(e) { "Something went wrong" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ class RetrieveSlideshowFromPlaylistUseCase(
logger.i { "Starting to get details from playlist ${playlistDetails.name}" }
val directories: List<ImageDirectory> = playlistDetails.items.map { item ->
val directoryDetails = DefaultNetworkDirectoryDetails(
id = item.directory_id.toInt(),
fullPath = item.directory_path
id = item.directoryId.toInt(),
fullPath = item.directoryPath
)
if (item.directory_path.isImagePath) {
if (item.directoryPath.isImagePath) {
listOf(ImageDirectory(directoryDetails))
} else {
retrieveDirectoryUseCase(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class DefaultImageViewModel(private val logger: Logger? = null) : ImageViewModel
}

override fun cancelImageJobs() {
logger?.d { "Cancelling Image Jobs" }
jobs.forEach {
it.cancel()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,15 @@ class DirectoryViewModel(
fun startSlideshow() {
logger.i { "Starting Slideshow" }
cancelJobs()
logger.d { "Checking for Selected Directory" }
uiState.value.selectedDirectory?.id?.let { id ->
logger.d { "Finding Folder" }
_directoryContentsState.value.folders.find { it.id == id }?.let {
logger.d { "Folder found, starting to retrieve images" }
val job = viewModelScope.launch(Dispatchers.Default) {
val retrieveImagesUseCase = UseCaseFactory.retrieveImageDirectoriesUseCase
val images = retrieveImagesUseCase(it.details)
logger.v { "Retrieved images, copying them to state" }
_uiState.update { it.copy(slideshowDetails = ImageSlideshowDetails(images)) }
}
jobs.add(job)
Expand Down Expand Up @@ -273,11 +277,13 @@ class DirectoryViewModel(
}

private fun cancelJobs() {
logger.i { "Cancelling Jobs!" }
logger.d { "Cancelling Jobs!" }
cancelImageJobs()
jobs.forEach {
it.cancel()
}
jobs.clear()
logger.v { "Finished Cancelling Jobs!" }

}
}
Loading

0 comments on commit 328e62e

Please sign in to comment.