mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-25 18:24:31 +01:00
Instance Keeper Freeze fix .IOS List Screen and Shared Code changes.
This commit is contained in:
parent
f80675cd13
commit
02d137588f
@ -265,8 +265,6 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
}
|
||||
|
||||
override val isInternetAvailable get() = internetAvailability.value ?: true
|
||||
override val dispatcherIO = Dispatchers.IO
|
||||
override val currentPlatform = AllPlatforms.Jvm
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -36,6 +36,7 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import com.shabinder.common.database.R
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.di.dispatcherIO
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@ -50,7 +51,7 @@ actual fun ImageLoad(
|
||||
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
||||
|
||||
LaunchedEffect(link) {
|
||||
withContext(methods.value.dispatcherIO) {
|
||||
withContext(dispatcherIO) {
|
||||
pic = loader(link).image
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.platform.Font
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.di.dispatcherIO
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@ -48,7 +49,7 @@ actual fun ImageLoad(
|
||||
) {
|
||||
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
||||
LaunchedEffect(link) {
|
||||
withContext(methods.value.dispatcherIO) {
|
||||
withContext(dispatcherIO) {
|
||||
pic = loader(link).image
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,19 @@ actual interface PlatformActions {
|
||||
|
||||
val imageCacheDir: String
|
||||
|
||||
val sharedPreferences: SharedPreferences
|
||||
val sharedPreferences: SharedPreferences?
|
||||
|
||||
fun addToLibrary(path: String)
|
||||
|
||||
fun sendTracksToService(array: ArrayList<TrackDetails>)
|
||||
}
|
||||
|
||||
actual val StubPlatformActions = object: PlatformActions {
|
||||
override val imageCacheDir: String = ""
|
||||
|
||||
override val sharedPreferences: SharedPreferences? = null
|
||||
|
||||
override fun addToLibrary(path: String) {}
|
||||
|
||||
override fun sendTracksToService(array: ArrayList<TrackDetails>) {}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
import co.touchlab.stately.freeze
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
/*
|
||||
* Holder to call platform actions from anywhere
|
||||
@ -17,6 +15,9 @@ interface Actions {
|
||||
// Platform Specific Actions
|
||||
val platformActions: PlatformActions
|
||||
|
||||
// Platform Specific Implementation Preferred
|
||||
val isInternetAvailable: Boolean
|
||||
|
||||
// Show Toast
|
||||
fun showPopUpMessage(string: String, long: Boolean = false)
|
||||
|
||||
@ -37,15 +38,6 @@ interface Actions {
|
||||
|
||||
// Open / Redirect to another Platform
|
||||
fun openPlatform(packageID: String, platformLink: String)
|
||||
|
||||
// IO-Dispatcher
|
||||
val dispatcherIO: CoroutineDispatcher
|
||||
|
||||
// Internet Connectivity Check
|
||||
val isInternetAvailable: Boolean
|
||||
|
||||
// Current Platform Info
|
||||
val currentPlatform: AllPlatforms
|
||||
}
|
||||
|
||||
|
||||
@ -57,7 +49,5 @@ private fun stubActions() = object :Actions{
|
||||
override fun giveDonation() {}
|
||||
override fun shareApp() {}
|
||||
override fun openPlatform(packageID: String, platformLink: String) {}
|
||||
override val dispatcherIO: CoroutineDispatcher = Dispatchers.Default
|
||||
override val isInternetAvailable: Boolean = true
|
||||
override val currentPlatform: AllPlatforms = AllPlatforms.Jvm
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
expect interface PlatformActions
|
||||
expect interface PlatformActions
|
||||
|
||||
expect val StubPlatformActions : PlatformActions
|
@ -1,4 +1,5 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
actual interface PlatformActions {
|
||||
}
|
||||
actual interface PlatformActions {}
|
||||
|
||||
actual val StubPlatformActions = object: PlatformActions {}
|
||||
|
@ -2,6 +2,7 @@ package com.shabinder.common.models
|
||||
|
||||
import kotlin.native.concurrent.AtomicReference
|
||||
|
||||
actual interface PlatformActions
|
||||
actual interface PlatformActions {}
|
||||
actual val StubPlatformActions = object: PlatformActions {}
|
||||
|
||||
actual typealias NativeAtomicReference<T> = AtomicReference<T>
|
@ -1,4 +1,4 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
actual interface PlatformActions {
|
||||
}
|
||||
actual interface PlatformActions {}
|
||||
actual val StubPlatformActions = object: PlatformActions {}
|
@ -16,8 +16,17 @@
|
||||
|
||||
package com.shabinder.common.di
|
||||
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
// IO-Dispatcher
|
||||
actual val dispatcherIO: CoroutineDispatcher = Dispatchers.IO
|
||||
|
||||
// Current Platform Info
|
||||
actual val currentPlatform: AllPlatforms = AllPlatforms.Jvm
|
||||
|
||||
actual suspend fun downloadTracks(
|
||||
list: List<TrackDetails>,
|
||||
|
@ -46,7 +46,8 @@ actual class Dir actual constructor(
|
||||
const val DirKey = "downloadDir"
|
||||
}
|
||||
|
||||
private val sharedPreferences:SharedPreferences by lazy { methods.value.platformActions.sharedPreferences }
|
||||
// This Wont throw `NPE` as We will never pass null
|
||||
private val sharedPreferences:SharedPreferences by lazy { methods.value.platformActions.sharedPreferences!! }
|
||||
|
||||
fun setDownloadDirectory(newBasePath:String){
|
||||
sharedPreferences.edit().putString(DirKey,newBasePath).apply()
|
||||
@ -77,7 +78,7 @@ actual class Dir actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
actual suspend fun clearCache() {
|
||||
actual suspend fun clearCache(): Unit = withContext(dispatcherIO) {
|
||||
File(imageCacheDir()).deleteRecursively()
|
||||
}
|
||||
|
||||
@ -86,76 +87,74 @@ actual class Dir actual constructor(
|
||||
mp3ByteArray: ByteArray,
|
||||
trackDetails: TrackDetails,
|
||||
postProcess:(track: TrackDetails)->Unit
|
||||
) {
|
||||
withContext(Dispatchers.IO){
|
||||
val songFile = File(trackDetails.outputFilePath)
|
||||
try {
|
||||
/*
|
||||
* Check , if Fetch was Used, File is saved Already, else write byteArray we Received
|
||||
* */
|
||||
if(!songFile.exists()) {
|
||||
/*Make intermediate Dirs if they don't exist yet*/
|
||||
songFile.parentFile.mkdirs()
|
||||
) = withContext(dispatcherIO) {
|
||||
val songFile = File(trackDetails.outputFilePath)
|
||||
try {
|
||||
/*
|
||||
* Check , if Fetch was Used, File is saved Already, else write byteArray we Received
|
||||
* */
|
||||
if(!songFile.exists()) {
|
||||
/*Make intermediate Dirs if they don't exist yet*/
|
||||
songFile.parentFile.mkdirs()
|
||||
}
|
||||
|
||||
if(mp3ByteArray.isNotEmpty()) songFile.writeBytes(mp3ByteArray)
|
||||
|
||||
when (trackDetails.outputFilePath.substringAfterLast('.')) {
|
||||
".mp3" -> {
|
||||
Mp3File(File(songFile.absolutePath))
|
||||
.removeAllTags()
|
||||
.setId3v1Tags(trackDetails)
|
||||
.setId3v2TagsAndSaveFile(trackDetails)
|
||||
addToLibrary(songFile.absolutePath)
|
||||
}
|
||||
|
||||
if(mp3ByteArray.isNotEmpty()) songFile.writeBytes(mp3ByteArray)
|
||||
|
||||
when (trackDetails.outputFilePath.substringAfterLast('.')) {
|
||||
".mp3" -> {
|
||||
".m4a" -> {
|
||||
/*FFmpeg.executeAsync(
|
||||
"-i ${m4aFile.absolutePath} -y -b:a 160k -acodec libmp3lame -vn ${m4aFile.absolutePath.substringBeforeLast('.') + ".mp3"}"
|
||||
){ _, returnCode ->
|
||||
when (returnCode) {
|
||||
Config.RETURN_CODE_SUCCESS -> {
|
||||
//FFMPEG task Completed
|
||||
logger.d{ "Async command execution completed successfully." }
|
||||
scope.launch {
|
||||
Mp3File(File(m4aFile.absolutePath.substringBeforeLast('.') + ".mp3"))
|
||||
.removeAllTags()
|
||||
.setId3v1Tags(trackDetails)
|
||||
.setId3v2TagsAndSaveFile(trackDetails)
|
||||
addToLibrary(m4aFile.absolutePath.substringBeforeLast('.') + ".mp3")
|
||||
}
|
||||
}
|
||||
Config.RETURN_CODE_CANCEL -> {
|
||||
logger.d{"Async command execution cancelled by user."}
|
||||
}
|
||||
else -> {
|
||||
logger.d { "Async command execution failed with rc=$returnCode" }
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
else -> {
|
||||
try {
|
||||
Mp3File(File(songFile.absolutePath))
|
||||
.removeAllTags()
|
||||
.setId3v1Tags(trackDetails)
|
||||
.setId3v2TagsAndSaveFile(trackDetails)
|
||||
addToLibrary(songFile.absolutePath)
|
||||
}
|
||||
".m4a" -> {
|
||||
/*FFmpeg.executeAsync(
|
||||
"-i ${m4aFile.absolutePath} -y -b:a 160k -acodec libmp3lame -vn ${m4aFile.absolutePath.substringBeforeLast('.') + ".mp3"}"
|
||||
){ _, returnCode ->
|
||||
when (returnCode) {
|
||||
Config.RETURN_CODE_SUCCESS -> {
|
||||
//FFMPEG task Completed
|
||||
logger.d{ "Async command execution completed successfully." }
|
||||
scope.launch {
|
||||
Mp3File(File(m4aFile.absolutePath.substringBeforeLast('.') + ".mp3"))
|
||||
.removeAllTags()
|
||||
.setId3v1Tags(trackDetails)
|
||||
.setId3v2TagsAndSaveFile(trackDetails)
|
||||
addToLibrary(m4aFile.absolutePath.substringBeforeLast('.') + ".mp3")
|
||||
}
|
||||
}
|
||||
Config.RETURN_CODE_CANCEL -> {
|
||||
logger.d{"Async command execution cancelled by user."}
|
||||
}
|
||||
else -> {
|
||||
logger.d { "Async command execution failed with rc=$returnCode" }
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
else -> {
|
||||
try {
|
||||
Mp3File(File(songFile.absolutePath))
|
||||
.removeAllTags()
|
||||
.setId3v1Tags(trackDetails)
|
||||
.setId3v2TagsAndSaveFile(trackDetails)
|
||||
addToLibrary(songFile.absolutePath)
|
||||
} catch (e: Exception) { e.printStackTrace() }
|
||||
}
|
||||
} catch (e: Exception) { e.printStackTrace() }
|
||||
}
|
||||
}catch (e:Exception){
|
||||
withContext(Dispatchers.Main){
|
||||
//Toast.makeText(appContext,"Could Not Create File:\n${songFile.absolutePath}",Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
if(songFile.exists()) songFile.delete()
|
||||
logger.e { "${songFile.absolutePath} could not be created" }
|
||||
}
|
||||
}catch (e:Exception){
|
||||
withContext(Dispatchers.Main){
|
||||
//Toast.makeText(appContext,"Could Not Create File:\n${songFile.absolutePath}",Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
if(songFile.exists()) songFile.delete()
|
||||
logger.e { "${songFile.absolutePath} could not be created" }
|
||||
}
|
||||
}
|
||||
|
||||
actual fun addToLibrary(path: String) = methods.value.platformActions.addToLibrary(path)
|
||||
|
||||
actual suspend fun loadImage(url: String): Picture = withContext(Dispatchers.IO){
|
||||
actual suspend fun loadImage(url: String): Picture = withContext(dispatcherIO){
|
||||
val cachePath = imageCacheDir() + getNameURL(url)
|
||||
Picture(image = (loadCachedImage(cachePath) ?: freshImage(url))?.asImageBitmap())
|
||||
}
|
||||
@ -169,19 +168,17 @@ actual class Dir actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
actual suspend fun cacheImage(image: Any, path: String) {
|
||||
withContext(Dispatchers.IO){
|
||||
try {
|
||||
FileOutputStream(path).use { out ->
|
||||
(image as? Bitmap)?.compress(Bitmap.CompressFormat.JPEG, 100, out)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
actual suspend fun cacheImage(image: Any, path: String):Unit = withContext(dispatcherIO) {
|
||||
try {
|
||||
FileOutputStream(path).use { out ->
|
||||
(image as? Bitmap)?.compress(Bitmap.CompressFormat.JPEG, 100, out)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun freshImage(url: String): Bitmap? = withContext(Dispatchers.IO) {
|
||||
private suspend fun freshImage(url: String): Bitmap? = withContext(dispatcherIO) {
|
||||
try {
|
||||
val source = URL(url)
|
||||
val connection: HttpURLConnection = source.openConnection() as HttpURLConnection
|
||||
|
@ -16,11 +16,14 @@
|
||||
|
||||
package com.shabinder.common.di
|
||||
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import io.ktor.client.request.*
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.native.concurrent.SharedImmutable
|
||||
|
||||
expect suspend fun downloadTracks(
|
||||
list: List<TrackDetails>,
|
||||
@ -28,8 +31,17 @@ expect suspend fun downloadTracks(
|
||||
dir: Dir
|
||||
)
|
||||
|
||||
|
||||
// IO-Dispatcher
|
||||
@SharedImmutable
|
||||
expect val dispatcherIO: CoroutineDispatcher
|
||||
|
||||
// Current Platform Info
|
||||
@SharedImmutable
|
||||
expect val currentPlatform: AllPlatforms
|
||||
|
||||
suspend fun isInternetAccessible(): Boolean {
|
||||
return withContext(methods.value.dispatcherIO) {
|
||||
return withContext(dispatcherIO) {
|
||||
try {
|
||||
ktorHttpClient.head<String>("http://google.com")
|
||||
true
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.shabinder.common.di.gaana
|
||||
|
||||
import com.shabinder.common.di.currentPlatform
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.corsProxy
|
||||
import com.shabinder.common.models.gaana.GaanaAlbum
|
||||
@ -27,7 +28,7 @@ import com.shabinder.common.models.methods
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.request.get
|
||||
|
||||
val corsApi get() = if (methods.value.currentPlatform is AllPlatforms.Js) {
|
||||
val corsApi get() = if (currentPlatform is AllPlatforms.Js) {
|
||||
corsProxy.url
|
||||
} // "https://spotiflyer-cors.azurewebsites.net/" //"https://spotiflyer-cors.herokuapp.com/"//"https://cors.bridged.cc/"
|
||||
else ""
|
||||
|
@ -46,7 +46,7 @@ class SpotifyProvider(
|
||||
/* init {
|
||||
logger.d { "Creating Spotify Provider" }
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
if (methods.value.currentPlatform is AllPlatforms.Js) {
|
||||
if (currentPlatform is AllPlatforms.Js) {
|
||||
authenticateSpotifyClient(override = true)
|
||||
} else authenticateSpotifyClient()
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package com.shabinder.common.di.providers
|
||||
|
||||
import co.touchlab.kermit.Kermit
|
||||
import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.di.currentPlatform
|
||||
import com.shabinder.common.di.youtubeMp3.Yt1sMp3
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.methods
|
||||
@ -31,7 +32,7 @@ class YoutubeMp3(
|
||||
suspend fun getMp3DownloadLink(videoID: String): String? = try {
|
||||
getLinkFromYt1sMp3(videoID)?.let {
|
||||
logger.i { "Download Link: $it" }
|
||||
if (methods.value.currentPlatform is AllPlatforms.Js/* && corsProxy !is CorsProxy.PublicProxyWithExtension*/)
|
||||
if (currentPlatform is AllPlatforms.Js/* && corsProxy !is CorsProxy.PublicProxyWithExtension*/)
|
||||
"https://kind-grasshopper-73.telebit.io/cors/$it"
|
||||
// "https://spotiflyer.azurewebsites.net/$it" // Data OUT Limit issue
|
||||
else it
|
||||
|
@ -21,6 +21,7 @@ package com.shabinder.common.di.utils
|
||||
// implementation("org.jetbrains.kotlinx:atomicfu:0.14.4")
|
||||
// Gist: https://gist.github.com/fluidsonic/ba32de21c156bbe8424c8d5fc20dcd8e
|
||||
|
||||
import com.shabinder.common.di.dispatcherIO
|
||||
import com.shabinder.common.models.methods
|
||||
import io.ktor.utils.io.core.Closeable
|
||||
import kotlinx.atomicfu.atomic
|
||||
@ -37,7 +38,7 @@ import kotlinx.coroutines.withContext
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class ParallelExecutor(
|
||||
parentContext: CoroutineContext = methods.value.dispatcherIO,
|
||||
parentContext: CoroutineContext = dispatcherIO,
|
||||
) : Closeable {
|
||||
|
||||
private val concurrentOperationLimit = atomic(4)
|
||||
|
@ -17,10 +17,12 @@
|
||||
package com.shabinder.common.di
|
||||
|
||||
import com.shabinder.common.di.utils.ParallelExecutor
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.DownloadResult
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.downloader.YoutubeDownloader
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
@ -30,6 +32,12 @@ val DownloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>> = M
|
||||
// Scope Allowing 4 Parallel Downloads
|
||||
val DownloadScope = ParallelExecutor(Dispatchers.IO)
|
||||
|
||||
// IO-Dispatcher
|
||||
actual val dispatcherIO: CoroutineDispatcher = Dispatchers.IO
|
||||
|
||||
// Current Platform Info
|
||||
actual val currentPlatform: AllPlatforms = AllPlatforms.Jvm
|
||||
|
||||
actual suspend fun downloadTracks(
|
||||
list: List<TrackDetails>,
|
||||
fetcher: FetchPlatformQueryResult,
|
||||
|
@ -1,6 +1,14 @@
|
||||
package com.shabinder.common.di
|
||||
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
@SharedImmutable
|
||||
actual val dispatcherIO = Dispatchers.Default
|
||||
|
||||
@SharedImmutable
|
||||
actual val currentPlatform: AllPlatforms = AllPlatforms.Native
|
||||
|
||||
actual suspend fun downloadTracks(
|
||||
list: List<TrackDetails>,
|
||||
|
@ -6,6 +6,7 @@ import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.database.Database
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import platform.Foundation.NSCachesDirectory
|
||||
import platform.Foundation.NSDirectoryEnumerationSkipsHiddenFiles
|
||||
import platform.Foundation.NSFileManager
|
||||
@ -25,10 +26,6 @@ actual class Dir actual constructor(
|
||||
private val spotiFlyerDatabase: SpotiFlyerDatabase,
|
||||
) {
|
||||
|
||||
init {
|
||||
//createDirectories()
|
||||
}
|
||||
|
||||
actual fun isPresent(path: String): Boolean = NSFileManager.defaultManager.fileExistsAtPath(path)
|
||||
|
||||
actual fun fileSeparator(): String = "/"
|
||||
@ -37,6 +34,7 @@ actual class Dir actual constructor(
|
||||
actual fun defaultDir(): String = defaultDirURL.path!!
|
||||
|
||||
val defaultDirURL: NSURL by lazy {
|
||||
createDirectories()
|
||||
val musicDir = NSFileManager.defaultManager.URLForDirectory(NSMusicDirectory, NSUserDomainMask,null,true,null)!!
|
||||
musicDir.URLByAppendingPathComponent("SpotiFlyer",true)!!
|
||||
}
|
||||
@ -44,6 +42,7 @@ actual class Dir actual constructor(
|
||||
actual fun imageCacheDir(): String = imageCacheURL.path!!
|
||||
|
||||
val imageCacheURL: NSURL by lazy {
|
||||
createDirectories()
|
||||
val cacheDir = NSFileManager.defaultManager.URLForDirectory(NSCachesDirectory, NSUserDomainMask,null,true,null)
|
||||
cacheDir?.URLByAppendingPathComponent("SpotiFlyer",true)!!
|
||||
}
|
||||
@ -64,7 +63,7 @@ actual class Dir actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
actual suspend fun cacheImage(image: Any, path: String) {
|
||||
actual suspend fun cacheImage(image: Any, path: String): Unit = withContext(dispatcherIO){
|
||||
try {
|
||||
(image as? UIImage)?.let {
|
||||
// We Will Be Using JPEG as default format everywhere
|
||||
@ -76,8 +75,8 @@ actual class Dir actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
actual suspend fun loadImage(url: String): Picture {
|
||||
return try {
|
||||
actual suspend fun loadImage(url: String): Picture = withContext(dispatcherIO){
|
||||
try {
|
||||
val cachePath = imageCacheURL.URLByAppendingPathComponent(getNameURL(url))
|
||||
Picture(image = cachePath?.path?.let { loadCachedImage(it) } ?: loadFreshImage(url))
|
||||
} catch (e: Exception) {
|
||||
@ -95,8 +94,8 @@ actual class Dir actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadFreshImage(url: String):UIImage? {
|
||||
return try {
|
||||
private suspend fun loadFreshImage(url: String):UIImage? = withContext(dispatcherIO){
|
||||
try {
|
||||
val nsURL = NSURL(string = url)
|
||||
val data = NSURLConnection.sendSynchronousRequest(NSURLRequest.requestWithURL(nsURL),null,null)
|
||||
if (data != null) {
|
||||
@ -112,7 +111,7 @@ actual class Dir actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
actual suspend fun clearCache() {
|
||||
actual suspend fun clearCache(): Unit = withContext(dispatcherIO) {
|
||||
try {
|
||||
val fileManager = NSFileManager.defaultManager
|
||||
val paths = fileManager.contentsOfDirectoryAtURL(imageCacheURL,
|
||||
@ -136,7 +135,7 @@ actual class Dir actual constructor(
|
||||
mp3ByteArray: ByteArray,
|
||||
trackDetails: TrackDetails,
|
||||
postProcess:(track: TrackDetails)->Unit
|
||||
) {
|
||||
) : Unit = withContext(dispatcherIO) {
|
||||
when (trackDetails.outputFilePath.substringAfterLast('.')) {
|
||||
".mp3" -> {
|
||||
postProcess(trackDetails)
|
||||
|
@ -16,10 +16,13 @@
|
||||
|
||||
package com.shabinder.common.di
|
||||
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.DownloadResult
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.withContext
|
||||
@ -29,13 +32,19 @@ val DownloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>> = M
|
||||
// val DownloadScope = ParallelExecutor(Dispatchers.Default) //Download Pool of 4 parallel
|
||||
val allTracksStatus: HashMap<String, DownloadStatus> = hashMapOf()
|
||||
|
||||
// IO-Dispatcher
|
||||
actual val dispatcherIO: CoroutineDispatcher = Dispatchers.Default
|
||||
|
||||
// Current Platform Info
|
||||
actual val currentPlatform: AllPlatforms = AllPlatforms.Js
|
||||
|
||||
actual suspend fun downloadTracks(
|
||||
list: List<TrackDetails>,
|
||||
fetcher: FetchPlatformQueryResult,
|
||||
dir: Dir
|
||||
) {
|
||||
list.forEach {
|
||||
withContext(methods.value.dispatcherIO) {
|
||||
withContext(dispatcherIO) {
|
||||
allTracksStatus[it.title] = DownloadStatus.Queued
|
||||
if (!it.videoID.isNullOrBlank()) { // Video ID already known!
|
||||
downloadTrack(it.videoID!!, it, fetcher, dir)
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.shabinder.common.list.integration
|
||||
|
||||
import co.touchlab.stately.ensureNeverFrozen
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.shabinder.common.di.Picture
|
||||
@ -33,6 +34,10 @@ internal class SpotiFlyerListImpl(
|
||||
dependencies: Dependencies
|
||||
) : SpotiFlyerList, ComponentContext by componentContext, Dependencies by dependencies {
|
||||
|
||||
init {
|
||||
instanceKeeper.ensureNeverFrozen()
|
||||
}
|
||||
|
||||
private val store =
|
||||
instanceKeeper.getStore {
|
||||
SpotiFlyerListStoreProvider(
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.shabinder.common.main.integration
|
||||
|
||||
import co.touchlab.stately.ensureNeverFrozen
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.shabinder.common.di.Picture
|
||||
@ -35,6 +36,10 @@ internal class SpotiFlyerMainImpl(
|
||||
dependencies: Dependencies
|
||||
) : SpotiFlyerMain, ComponentContext by componentContext, Dependencies by dependencies {
|
||||
|
||||
init {
|
||||
instanceKeeper.ensureNeverFrozen()
|
||||
}
|
||||
|
||||
private val store =
|
||||
instanceKeeper.getStore {
|
||||
SpotiFlyerMainStoreProvider(
|
||||
|
@ -25,7 +25,6 @@ fun <T : Store<*, *, *>> InstanceKeeper.getStore(key: Any, factory: () -> T): T
|
||||
.store
|
||||
|
||||
inline fun <reified T :
|
||||
|
||||
Store<*, *, *>> InstanceKeeper.getStore(noinline factory: () -> T): T =
|
||||
getStore(T::class, factory)
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.shabinder.common.root.integration
|
||||
|
||||
import co.touchlab.stately.ensureNeverFrozen
|
||||
import co.touchlab.stately.freeze
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.RouterState
|
||||
@ -26,9 +27,9 @@ import com.arkivanov.decompose.router
|
||||
import com.arkivanov.decompose.statekeeper.Parcelable
|
||||
import com.arkivanov.decompose.statekeeper.Parcelize
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.shabinder.common.database.getLogger
|
||||
import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.di.createDirectories
|
||||
import com.shabinder.common.di.currentPlatform
|
||||
import com.shabinder.common.di.providers.SpotifyProvider
|
||||
import com.shabinder.common.list.SpotiFlyerList
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.models.Actions
|
||||
@ -45,23 +46,40 @@ import kotlinx.coroutines.launch
|
||||
|
||||
internal class SpotiFlyerRootImpl(
|
||||
componentContext: ComponentContext,
|
||||
dependencies: Dependencies,
|
||||
) : SpotiFlyerRoot, ComponentContext by componentContext, Dependencies by dependencies, Actions by dependencies.actions {
|
||||
private val main: (ComponentContext, output:Consumer<SpotiFlyerMain.Output>)->SpotiFlyerMain,
|
||||
private val list: (ComponentContext, link:String, output:Consumer<SpotiFlyerList.Output>)->SpotiFlyerList,
|
||||
private val actions: Actions
|
||||
) : SpotiFlyerRoot, ComponentContext by componentContext {
|
||||
|
||||
init {
|
||||
methods.value = actions.freeze()
|
||||
GlobalScope.launch {
|
||||
/*TESTING*/
|
||||
getLogger().apply {
|
||||
d("Hey...","Background Thread")
|
||||
//d(directories.defaultDir(),"Background Thread")
|
||||
d("Hey...","Background Thread")
|
||||
}
|
||||
//*Authenticate Spotify Client*//*
|
||||
/*fetchPlatformQueryResult.spotifyProvider.authenticateSpotifyClient(
|
||||
override = true //methods.value.currentPlatform is AllPlatforms.Js
|
||||
)*/
|
||||
}
|
||||
constructor(
|
||||
componentContext: ComponentContext,
|
||||
dependencies: Dependencies,
|
||||
):this(
|
||||
componentContext = componentContext,
|
||||
main = { childContext,output ->
|
||||
spotiFlyerMain(
|
||||
childContext,
|
||||
output,
|
||||
dependencies
|
||||
)
|
||||
},
|
||||
list = { childContext, link, output ->
|
||||
spotiFlyerList(
|
||||
childContext,
|
||||
link,
|
||||
output,
|
||||
dependencies
|
||||
)
|
||||
},
|
||||
actions = dependencies.actions.freeze()
|
||||
) {
|
||||
instanceKeeper.ensureNeverFrozen()
|
||||
methods.value = dependencies.actions.freeze()
|
||||
/*Authenticate Spotify Client*/
|
||||
authenticateSpotify(
|
||||
dependencies.fetchPlatformQueryResult.spotifyProvider,
|
||||
currentPlatform is AllPlatforms.Js
|
||||
)
|
||||
}
|
||||
|
||||
private val router =
|
||||
@ -80,36 +98,15 @@ internal class SpotiFlyerRootImpl(
|
||||
it !is Configuration.Main
|
||||
}
|
||||
}
|
||||
override fun setDownloadDirectory() { setDownloadDirectoryAction() }
|
||||
override fun setDownloadDirectory() { actions.setDownloadDirectoryAction() }
|
||||
}
|
||||
|
||||
private fun createChild(configuration: Configuration, componentContext: ComponentContext): Child =
|
||||
when (configuration) {
|
||||
is Configuration.Main -> Child.Main(spotiFlyerMain(componentContext))
|
||||
is Configuration.List -> Child.List(spotiFlyerList(componentContext, link = configuration.link))
|
||||
is Configuration.Main -> Child.Main(main(componentContext, Consumer(::onMainOutput)))
|
||||
is Configuration.List -> Child.List(list(componentContext, configuration.link, Consumer(::onListOutput)))
|
||||
}
|
||||
|
||||
private fun spotiFlyerMain(componentContext: ComponentContext): SpotiFlyerMain =
|
||||
SpotiFlyerMain(
|
||||
componentContext = componentContext,
|
||||
dependencies = object : SpotiFlyerMain.Dependencies, Dependencies by this {
|
||||
override val mainOutput: Consumer<SpotiFlyerMain.Output> = Consumer(::onMainOutput)
|
||||
override val dir: Dir = directories
|
||||
}
|
||||
)
|
||||
|
||||
private fun spotiFlyerList(componentContext: ComponentContext, link: String): SpotiFlyerList =
|
||||
SpotiFlyerList(
|
||||
componentContext = componentContext,
|
||||
dependencies = object : SpotiFlyerList.Dependencies, Dependencies by this {
|
||||
override val fetchQuery = fetchPlatformQueryResult
|
||||
override val dir: Dir = directories
|
||||
override val link: String = link
|
||||
override val listOutput: Consumer<SpotiFlyerList.Output> = Consumer(::onListOutput)
|
||||
override val downloadProgressFlow = downloadProgressReport
|
||||
}
|
||||
)
|
||||
|
||||
private fun onMainOutput(output: SpotiFlyerMain.Output) =
|
||||
when (output) {
|
||||
is SpotiFlyerMain.Output.Search -> router.push(Configuration.List(link = output.link))
|
||||
@ -120,6 +117,13 @@ internal class SpotiFlyerRootImpl(
|
||||
is SpotiFlyerList.Output.Finished -> router.pop()
|
||||
}
|
||||
|
||||
private fun authenticateSpotify(spotifyProvider: SpotifyProvider, override:Boolean){
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
/*Authenticate Spotify Client*/
|
||||
spotifyProvider.authenticateSpotifyClient(override)
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Configuration : Parcelable {
|
||||
@Parcelize
|
||||
object Main : Configuration()
|
||||
@ -128,3 +132,24 @@ internal class SpotiFlyerRootImpl(
|
||||
data class List(val link: String) : Configuration()
|
||||
}
|
||||
}
|
||||
|
||||
private fun spotiFlyerMain(componentContext: ComponentContext, output: Consumer<SpotiFlyerMain.Output> ,dependencies: Dependencies): SpotiFlyerMain =
|
||||
SpotiFlyerMain(
|
||||
componentContext = componentContext,
|
||||
dependencies = object : SpotiFlyerMain.Dependencies, Dependencies by dependencies {
|
||||
override val mainOutput: Consumer<SpotiFlyerMain.Output> = output
|
||||
override val dir: Dir = directories
|
||||
}
|
||||
)
|
||||
|
||||
private fun spotiFlyerList(componentContext: ComponentContext, link: String, output: Consumer<SpotiFlyerList.Output>, dependencies: Dependencies): SpotiFlyerList =
|
||||
SpotiFlyerList(
|
||||
componentContext = componentContext,
|
||||
dependencies = object : SpotiFlyerList.Dependencies, Dependencies by dependencies {
|
||||
override val fetchQuery = fetchPlatformQueryResult
|
||||
override val dir: Dir = directories
|
||||
override val link: String = link
|
||||
override val listOutput: Consumer<SpotiFlyerList.Output> = output
|
||||
override val downloadProgressFlow = downloadProgressReport
|
||||
}
|
||||
)
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 31517f90ef04efa4aea88c61ca627647c146f471
|
||||
Subproject commit f218336b5b31b365bbf34503b79f2c4f2b703d7d
|
Loading…
Reference in New Issue
Block a user