mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-29 03:44:33 +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 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 androidx.compose.ui.text.font.FontWeight
|
||||||
import com.shabinder.common.database.R
|
import com.shabinder.common.database.R
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
|
import com.shabinder.common.di.dispatcherIO
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.methods
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ actual fun ImageLoad(
|
|||||||
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
||||||
|
|
||||||
LaunchedEffect(link) {
|
LaunchedEffect(link) {
|
||||||
withContext(methods.value.dispatcherIO) {
|
withContext(dispatcherIO) {
|
||||||
pic = loader(link).image
|
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.font.FontWeight
|
||||||
import androidx.compose.ui.text.platform.Font
|
import androidx.compose.ui.text.platform.Font
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
|
import com.shabinder.common.di.dispatcherIO
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.methods
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ actual fun ImageLoad(
|
|||||||
) {
|
) {
|
||||||
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
||||||
LaunchedEffect(link) {
|
LaunchedEffect(link) {
|
||||||
withContext(methods.value.dispatcherIO) {
|
withContext(dispatcherIO) {
|
||||||
pic = loader(link).image
|
pic = loader(link).image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,19 @@ actual interface PlatformActions {
|
|||||||
|
|
||||||
val imageCacheDir: String
|
val imageCacheDir: String
|
||||||
|
|
||||||
val sharedPreferences: SharedPreferences
|
val sharedPreferences: SharedPreferences?
|
||||||
|
|
||||||
fun addToLibrary(path: String)
|
fun addToLibrary(path: String)
|
||||||
|
|
||||||
fun sendTracksToService(array: ArrayList<TrackDetails>)
|
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
|
package com.shabinder.common.models
|
||||||
|
|
||||||
import co.touchlab.stately.freeze
|
import co.touchlab.stately.freeze
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Holder to call platform actions from anywhere
|
* Holder to call platform actions from anywhere
|
||||||
@ -17,6 +15,9 @@ interface Actions {
|
|||||||
// Platform Specific Actions
|
// Platform Specific Actions
|
||||||
val platformActions: PlatformActions
|
val platformActions: PlatformActions
|
||||||
|
|
||||||
|
// Platform Specific Implementation Preferred
|
||||||
|
val isInternetAvailable: Boolean
|
||||||
|
|
||||||
// Show Toast
|
// Show Toast
|
||||||
fun showPopUpMessage(string: String, long: Boolean = false)
|
fun showPopUpMessage(string: String, long: Boolean = false)
|
||||||
|
|
||||||
@ -37,15 +38,6 @@ interface Actions {
|
|||||||
|
|
||||||
// Open / Redirect to another Platform
|
// Open / Redirect to another Platform
|
||||||
fun openPlatform(packageID: String, platformLink: String)
|
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 giveDonation() {}
|
||||||
override fun shareApp() {}
|
override fun shareApp() {}
|
||||||
override fun openPlatform(packageID: String, platformLink: String) {}
|
override fun openPlatform(packageID: String, platformLink: String) {}
|
||||||
override val dispatcherIO: CoroutineDispatcher = Dispatchers.Default
|
|
||||||
override val isInternetAvailable: Boolean = true
|
override val isInternetAvailable: Boolean = true
|
||||||
override val currentPlatform: AllPlatforms = AllPlatforms.Jvm
|
|
||||||
}
|
}
|
@ -1,3 +1,5 @@
|
|||||||
package com.shabinder.common.models
|
package com.shabinder.common.models
|
||||||
|
|
||||||
expect interface PlatformActions
|
expect interface PlatformActions
|
||||||
|
|
||||||
|
expect val StubPlatformActions : PlatformActions
|
@ -1,4 +1,5 @@
|
|||||||
package com.shabinder.common.models
|
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
|
import kotlin.native.concurrent.AtomicReference
|
||||||
|
|
||||||
actual interface PlatformActions
|
actual interface PlatformActions {}
|
||||||
|
actual val StubPlatformActions = object: PlatformActions {}
|
||||||
|
|
||||||
actual typealias NativeAtomicReference<T> = AtomicReference<T>
|
actual typealias NativeAtomicReference<T> = AtomicReference<T>
|
@ -1,4 +1,4 @@
|
|||||||
package com.shabinder.common.models
|
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
|
package com.shabinder.common.di
|
||||||
|
|
||||||
|
import com.shabinder.common.models.AllPlatforms
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.models.methods
|
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(
|
actual suspend fun downloadTracks(
|
||||||
list: List<TrackDetails>,
|
list: List<TrackDetails>,
|
||||||
|
@ -46,7 +46,8 @@ actual class Dir actual constructor(
|
|||||||
const val DirKey = "downloadDir"
|
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){
|
fun setDownloadDirectory(newBasePath:String){
|
||||||
sharedPreferences.edit().putString(DirKey,newBasePath).apply()
|
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()
|
File(imageCacheDir()).deleteRecursively()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,8 +87,7 @@ actual class Dir actual constructor(
|
|||||||
mp3ByteArray: ByteArray,
|
mp3ByteArray: ByteArray,
|
||||||
trackDetails: TrackDetails,
|
trackDetails: TrackDetails,
|
||||||
postProcess:(track: TrackDetails)->Unit
|
postProcess:(track: TrackDetails)->Unit
|
||||||
) {
|
) = withContext(dispatcherIO) {
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
val songFile = File(trackDetails.outputFilePath)
|
val songFile = File(trackDetails.outputFilePath)
|
||||||
try {
|
try {
|
||||||
/*
|
/*
|
||||||
@ -151,11 +151,10 @@ actual class Dir actual constructor(
|
|||||||
logger.e { "${songFile.absolutePath} could not be created" }
|
logger.e { "${songFile.absolutePath} could not be created" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
actual fun addToLibrary(path: String) = methods.value.platformActions.addToLibrary(path)
|
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)
|
val cachePath = imageCacheDir() + getNameURL(url)
|
||||||
Picture(image = (loadCachedImage(cachePath) ?: freshImage(url))?.asImageBitmap())
|
Picture(image = (loadCachedImage(cachePath) ?: freshImage(url))?.asImageBitmap())
|
||||||
}
|
}
|
||||||
@ -169,8 +168,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) {
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
try {
|
try {
|
||||||
FileOutputStream(path).use { out ->
|
FileOutputStream(path).use { out ->
|
||||||
(image as? Bitmap)?.compress(Bitmap.CompressFormat.JPEG, 100, out)
|
(image as? Bitmap)?.compress(Bitmap.CompressFormat.JPEG, 100, out)
|
||||||
@ -179,9 +177,8 @@ actual class Dir actual constructor(
|
|||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun freshImage(url: String): Bitmap? = withContext(Dispatchers.IO) {
|
private suspend fun freshImage(url: String): Bitmap? = withContext(dispatcherIO) {
|
||||||
try {
|
try {
|
||||||
val source = URL(url)
|
val source = URL(url)
|
||||||
val connection: HttpURLConnection = source.openConnection() as HttpURLConnection
|
val connection: HttpURLConnection = source.openConnection() as HttpURLConnection
|
||||||
|
@ -16,11 +16,14 @@
|
|||||||
|
|
||||||
package com.shabinder.common.di
|
package com.shabinder.common.di
|
||||||
|
|
||||||
|
import com.shabinder.common.models.AllPlatforms
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.methods
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlin.native.concurrent.SharedImmutable
|
||||||
|
|
||||||
expect suspend fun downloadTracks(
|
expect suspend fun downloadTracks(
|
||||||
list: List<TrackDetails>,
|
list: List<TrackDetails>,
|
||||||
@ -28,8 +31,17 @@ expect suspend fun downloadTracks(
|
|||||||
dir: Dir
|
dir: Dir
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// IO-Dispatcher
|
||||||
|
@SharedImmutable
|
||||||
|
expect val dispatcherIO: CoroutineDispatcher
|
||||||
|
|
||||||
|
// Current Platform Info
|
||||||
|
@SharedImmutable
|
||||||
|
expect val currentPlatform: AllPlatforms
|
||||||
|
|
||||||
suspend fun isInternetAccessible(): Boolean {
|
suspend fun isInternetAccessible(): Boolean {
|
||||||
return withContext(methods.value.dispatcherIO) {
|
return withContext(dispatcherIO) {
|
||||||
try {
|
try {
|
||||||
ktorHttpClient.head<String>("http://google.com")
|
ktorHttpClient.head<String>("http://google.com")
|
||||||
true
|
true
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.shabinder.common.di.gaana
|
package com.shabinder.common.di.gaana
|
||||||
|
|
||||||
|
import com.shabinder.common.di.currentPlatform
|
||||||
import com.shabinder.common.models.AllPlatforms
|
import com.shabinder.common.models.AllPlatforms
|
||||||
import com.shabinder.common.models.corsProxy
|
import com.shabinder.common.models.corsProxy
|
||||||
import com.shabinder.common.models.gaana.GaanaAlbum
|
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.HttpClient
|
||||||
import io.ktor.client.request.get
|
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
|
corsProxy.url
|
||||||
} // "https://spotiflyer-cors.azurewebsites.net/" //"https://spotiflyer-cors.herokuapp.com/"//"https://cors.bridged.cc/"
|
} // "https://spotiflyer-cors.azurewebsites.net/" //"https://spotiflyer-cors.herokuapp.com/"//"https://cors.bridged.cc/"
|
||||||
else ""
|
else ""
|
||||||
|
@ -46,7 +46,7 @@ class SpotifyProvider(
|
|||||||
/* init {
|
/* init {
|
||||||
logger.d { "Creating Spotify Provider" }
|
logger.d { "Creating Spotify Provider" }
|
||||||
GlobalScope.launch(Dispatchers.Default) {
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
if (methods.value.currentPlatform is AllPlatforms.Js) {
|
if (currentPlatform is AllPlatforms.Js) {
|
||||||
authenticateSpotifyClient(override = true)
|
authenticateSpotifyClient(override = true)
|
||||||
} else authenticateSpotifyClient()
|
} else authenticateSpotifyClient()
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package com.shabinder.common.di.providers
|
|||||||
|
|
||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.Dir
|
||||||
|
import com.shabinder.common.di.currentPlatform
|
||||||
import com.shabinder.common.di.youtubeMp3.Yt1sMp3
|
import com.shabinder.common.di.youtubeMp3.Yt1sMp3
|
||||||
import com.shabinder.common.models.AllPlatforms
|
import com.shabinder.common.models.AllPlatforms
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.methods
|
||||||
@ -31,7 +32,7 @@ class YoutubeMp3(
|
|||||||
suspend fun getMp3DownloadLink(videoID: String): String? = try {
|
suspend fun getMp3DownloadLink(videoID: String): String? = try {
|
||||||
getLinkFromYt1sMp3(videoID)?.let {
|
getLinkFromYt1sMp3(videoID)?.let {
|
||||||
logger.i { "Download Link: $it" }
|
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://kind-grasshopper-73.telebit.io/cors/$it"
|
||||||
// "https://spotiflyer.azurewebsites.net/$it" // Data OUT Limit issue
|
// "https://spotiflyer.azurewebsites.net/$it" // Data OUT Limit issue
|
||||||
else it
|
else it
|
||||||
|
@ -21,6 +21,7 @@ package com.shabinder.common.di.utils
|
|||||||
// implementation("org.jetbrains.kotlinx:atomicfu:0.14.4")
|
// implementation("org.jetbrains.kotlinx:atomicfu:0.14.4")
|
||||||
// Gist: https://gist.github.com/fluidsonic/ba32de21c156bbe8424c8d5fc20dcd8e
|
// Gist: https://gist.github.com/fluidsonic/ba32de21c156bbe8424c8d5fc20dcd8e
|
||||||
|
|
||||||
|
import com.shabinder.common.di.dispatcherIO
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.methods
|
||||||
import io.ktor.utils.io.core.Closeable
|
import io.ktor.utils.io.core.Closeable
|
||||||
import kotlinx.atomicfu.atomic
|
import kotlinx.atomicfu.atomic
|
||||||
@ -37,7 +38,7 @@ import kotlinx.coroutines.withContext
|
|||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class ParallelExecutor(
|
class ParallelExecutor(
|
||||||
parentContext: CoroutineContext = methods.value.dispatcherIO,
|
parentContext: CoroutineContext = dispatcherIO,
|
||||||
) : Closeable {
|
) : Closeable {
|
||||||
|
|
||||||
private val concurrentOperationLimit = atomic(4)
|
private val concurrentOperationLimit = atomic(4)
|
||||||
|
@ -17,10 +17,12 @@
|
|||||||
package com.shabinder.common.di
|
package com.shabinder.common.di
|
||||||
|
|
||||||
import com.shabinder.common.di.utils.ParallelExecutor
|
import com.shabinder.common.di.utils.ParallelExecutor
|
||||||
|
import com.shabinder.common.models.AllPlatforms
|
||||||
import com.shabinder.common.models.DownloadResult
|
import com.shabinder.common.models.DownloadResult
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.downloader.YoutubeDownloader
|
import com.shabinder.downloader.YoutubeDownloader
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
@ -30,6 +32,12 @@ val DownloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>> = M
|
|||||||
// Scope Allowing 4 Parallel Downloads
|
// Scope Allowing 4 Parallel Downloads
|
||||||
val DownloadScope = ParallelExecutor(Dispatchers.IO)
|
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(
|
actual suspend fun downloadTracks(
|
||||||
list: List<TrackDetails>,
|
list: List<TrackDetails>,
|
||||||
fetcher: FetchPlatformQueryResult,
|
fetcher: FetchPlatformQueryResult,
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
package com.shabinder.common.di
|
package com.shabinder.common.di
|
||||||
|
|
||||||
|
import com.shabinder.common.models.AllPlatforms
|
||||||
import com.shabinder.common.models.TrackDetails
|
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(
|
actual suspend fun downloadTracks(
|
||||||
list: List<TrackDetails>,
|
list: List<TrackDetails>,
|
||||||
|
@ -6,6 +6,7 @@ import com.shabinder.common.models.TrackDetails
|
|||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import platform.Foundation.NSCachesDirectory
|
import platform.Foundation.NSCachesDirectory
|
||||||
import platform.Foundation.NSDirectoryEnumerationSkipsHiddenFiles
|
import platform.Foundation.NSDirectoryEnumerationSkipsHiddenFiles
|
||||||
import platform.Foundation.NSFileManager
|
import platform.Foundation.NSFileManager
|
||||||
@ -25,10 +26,6 @@ actual class Dir actual constructor(
|
|||||||
private val spotiFlyerDatabase: SpotiFlyerDatabase,
|
private val spotiFlyerDatabase: SpotiFlyerDatabase,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
init {
|
|
||||||
//createDirectories()
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun isPresent(path: String): Boolean = NSFileManager.defaultManager.fileExistsAtPath(path)
|
actual fun isPresent(path: String): Boolean = NSFileManager.defaultManager.fileExistsAtPath(path)
|
||||||
|
|
||||||
actual fun fileSeparator(): String = "/"
|
actual fun fileSeparator(): String = "/"
|
||||||
@ -37,6 +34,7 @@ actual class Dir actual constructor(
|
|||||||
actual fun defaultDir(): String = defaultDirURL.path!!
|
actual fun defaultDir(): String = defaultDirURL.path!!
|
||||||
|
|
||||||
val defaultDirURL: NSURL by lazy {
|
val defaultDirURL: NSURL by lazy {
|
||||||
|
createDirectories()
|
||||||
val musicDir = NSFileManager.defaultManager.URLForDirectory(NSMusicDirectory, NSUserDomainMask,null,true,null)!!
|
val musicDir = NSFileManager.defaultManager.URLForDirectory(NSMusicDirectory, NSUserDomainMask,null,true,null)!!
|
||||||
musicDir.URLByAppendingPathComponent("SpotiFlyer",true)!!
|
musicDir.URLByAppendingPathComponent("SpotiFlyer",true)!!
|
||||||
}
|
}
|
||||||
@ -44,6 +42,7 @@ actual class Dir actual constructor(
|
|||||||
actual fun imageCacheDir(): String = imageCacheURL.path!!
|
actual fun imageCacheDir(): String = imageCacheURL.path!!
|
||||||
|
|
||||||
val imageCacheURL: NSURL by lazy {
|
val imageCacheURL: NSURL by lazy {
|
||||||
|
createDirectories()
|
||||||
val cacheDir = NSFileManager.defaultManager.URLForDirectory(NSCachesDirectory, NSUserDomainMask,null,true,null)
|
val cacheDir = NSFileManager.defaultManager.URLForDirectory(NSCachesDirectory, NSUserDomainMask,null,true,null)
|
||||||
cacheDir?.URLByAppendingPathComponent("SpotiFlyer",true)!!
|
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 {
|
try {
|
||||||
(image as? UIImage)?.let {
|
(image as? UIImage)?.let {
|
||||||
// We Will Be Using JPEG as default format everywhere
|
// 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 {
|
actual suspend fun loadImage(url: String): Picture = withContext(dispatcherIO){
|
||||||
return try {
|
try {
|
||||||
val cachePath = imageCacheURL.URLByAppendingPathComponent(getNameURL(url))
|
val cachePath = imageCacheURL.URLByAppendingPathComponent(getNameURL(url))
|
||||||
Picture(image = cachePath?.path?.let { loadCachedImage(it) } ?: loadFreshImage(url))
|
Picture(image = cachePath?.path?.let { loadCachedImage(it) } ?: loadFreshImage(url))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -95,8 +94,8 @@ actual class Dir actual constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadFreshImage(url: String):UIImage? {
|
private suspend fun loadFreshImage(url: String):UIImage? = withContext(dispatcherIO){
|
||||||
return try {
|
try {
|
||||||
val nsURL = NSURL(string = url)
|
val nsURL = NSURL(string = url)
|
||||||
val data = NSURLConnection.sendSynchronousRequest(NSURLRequest.requestWithURL(nsURL),null,null)
|
val data = NSURLConnection.sendSynchronousRequest(NSURLRequest.requestWithURL(nsURL),null,null)
|
||||||
if (data != 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 {
|
try {
|
||||||
val fileManager = NSFileManager.defaultManager
|
val fileManager = NSFileManager.defaultManager
|
||||||
val paths = fileManager.contentsOfDirectoryAtURL(imageCacheURL,
|
val paths = fileManager.contentsOfDirectoryAtURL(imageCacheURL,
|
||||||
@ -136,7 +135,7 @@ actual class Dir actual constructor(
|
|||||||
mp3ByteArray: ByteArray,
|
mp3ByteArray: ByteArray,
|
||||||
trackDetails: TrackDetails,
|
trackDetails: TrackDetails,
|
||||||
postProcess:(track: TrackDetails)->Unit
|
postProcess:(track: TrackDetails)->Unit
|
||||||
) {
|
) : Unit = withContext(dispatcherIO) {
|
||||||
when (trackDetails.outputFilePath.substringAfterLast('.')) {
|
when (trackDetails.outputFilePath.substringAfterLast('.')) {
|
||||||
".mp3" -> {
|
".mp3" -> {
|
||||||
postProcess(trackDetails)
|
postProcess(trackDetails)
|
||||||
|
@ -16,10 +16,13 @@
|
|||||||
|
|
||||||
package com.shabinder.common.di
|
package com.shabinder.common.di
|
||||||
|
|
||||||
|
import com.shabinder.common.models.AllPlatforms
|
||||||
import com.shabinder.common.models.DownloadResult
|
import com.shabinder.common.models.DownloadResult
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.models.methods
|
import com.shabinder.common.models.methods
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.withContext
|
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 DownloadScope = ParallelExecutor(Dispatchers.Default) //Download Pool of 4 parallel
|
||||||
val allTracksStatus: HashMap<String, DownloadStatus> = hashMapOf()
|
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(
|
actual suspend fun downloadTracks(
|
||||||
list: List<TrackDetails>,
|
list: List<TrackDetails>,
|
||||||
fetcher: FetchPlatformQueryResult,
|
fetcher: FetchPlatformQueryResult,
|
||||||
dir: Dir
|
dir: Dir
|
||||||
) {
|
) {
|
||||||
list.forEach {
|
list.forEach {
|
||||||
withContext(methods.value.dispatcherIO) {
|
withContext(dispatcherIO) {
|
||||||
allTracksStatus[it.title] = DownloadStatus.Queued
|
allTracksStatus[it.title] = DownloadStatus.Queued
|
||||||
if (!it.videoID.isNullOrBlank()) { // Video ID already known!
|
if (!it.videoID.isNullOrBlank()) { // Video ID already known!
|
||||||
downloadTrack(it.videoID!!, it, fetcher, dir)
|
downloadTrack(it.videoID!!, it, fetcher, dir)
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.shabinder.common.list.integration
|
package com.shabinder.common.list.integration
|
||||||
|
|
||||||
|
import co.touchlab.stately.ensureNeverFrozen
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.decompose.value.Value
|
import com.arkivanov.decompose.value.Value
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
@ -33,6 +34,10 @@ internal class SpotiFlyerListImpl(
|
|||||||
dependencies: Dependencies
|
dependencies: Dependencies
|
||||||
) : SpotiFlyerList, ComponentContext by componentContext, Dependencies by dependencies {
|
) : SpotiFlyerList, ComponentContext by componentContext, Dependencies by dependencies {
|
||||||
|
|
||||||
|
init {
|
||||||
|
instanceKeeper.ensureNeverFrozen()
|
||||||
|
}
|
||||||
|
|
||||||
private val store =
|
private val store =
|
||||||
instanceKeeper.getStore {
|
instanceKeeper.getStore {
|
||||||
SpotiFlyerListStoreProvider(
|
SpotiFlyerListStoreProvider(
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.shabinder.common.main.integration
|
package com.shabinder.common.main.integration
|
||||||
|
|
||||||
|
import co.touchlab.stately.ensureNeverFrozen
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.decompose.value.Value
|
import com.arkivanov.decompose.value.Value
|
||||||
import com.shabinder.common.di.Picture
|
import com.shabinder.common.di.Picture
|
||||||
@ -35,6 +36,10 @@ internal class SpotiFlyerMainImpl(
|
|||||||
dependencies: Dependencies
|
dependencies: Dependencies
|
||||||
) : SpotiFlyerMain, ComponentContext by componentContext, Dependencies by dependencies {
|
) : SpotiFlyerMain, ComponentContext by componentContext, Dependencies by dependencies {
|
||||||
|
|
||||||
|
init {
|
||||||
|
instanceKeeper.ensureNeverFrozen()
|
||||||
|
}
|
||||||
|
|
||||||
private val store =
|
private val store =
|
||||||
instanceKeeper.getStore {
|
instanceKeeper.getStore {
|
||||||
SpotiFlyerMainStoreProvider(
|
SpotiFlyerMainStoreProvider(
|
||||||
|
@ -25,7 +25,6 @@ fun <T : Store<*, *, *>> InstanceKeeper.getStore(key: Any, factory: () -> T): T
|
|||||||
.store
|
.store
|
||||||
|
|
||||||
inline fun <reified T :
|
inline fun <reified T :
|
||||||
|
|
||||||
Store<*, *, *>> InstanceKeeper.getStore(noinline factory: () -> T): T =
|
Store<*, *, *>> InstanceKeeper.getStore(noinline factory: () -> T): T =
|
||||||
getStore(T::class, factory)
|
getStore(T::class, factory)
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.shabinder.common.root.integration
|
package com.shabinder.common.root.integration
|
||||||
|
|
||||||
|
import co.touchlab.stately.ensureNeverFrozen
|
||||||
import co.touchlab.stately.freeze
|
import co.touchlab.stately.freeze
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.decompose.RouterState
|
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.Parcelable
|
||||||
import com.arkivanov.decompose.statekeeper.Parcelize
|
import com.arkivanov.decompose.statekeeper.Parcelize
|
||||||
import com.arkivanov.decompose.value.Value
|
import com.arkivanov.decompose.value.Value
|
||||||
import com.shabinder.common.database.getLogger
|
|
||||||
import com.shabinder.common.di.Dir
|
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.list.SpotiFlyerList
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.models.Actions
|
import com.shabinder.common.models.Actions
|
||||||
@ -45,23 +46,40 @@ import kotlinx.coroutines.launch
|
|||||||
|
|
||||||
internal class SpotiFlyerRootImpl(
|
internal class SpotiFlyerRootImpl(
|
||||||
componentContext: ComponentContext,
|
componentContext: ComponentContext,
|
||||||
dependencies: Dependencies,
|
private val main: (ComponentContext, output:Consumer<SpotiFlyerMain.Output>)->SpotiFlyerMain,
|
||||||
) : SpotiFlyerRoot, ComponentContext by componentContext, Dependencies by dependencies, Actions by dependencies.actions {
|
private val list: (ComponentContext, link:String, output:Consumer<SpotiFlyerList.Output>)->SpotiFlyerList,
|
||||||
|
private val actions: Actions
|
||||||
|
) : SpotiFlyerRoot, ComponentContext by componentContext {
|
||||||
|
|
||||||
init {
|
constructor(
|
||||||
methods.value = actions.freeze()
|
componentContext: ComponentContext,
|
||||||
GlobalScope.launch {
|
dependencies: Dependencies,
|
||||||
/*TESTING*/
|
):this(
|
||||||
getLogger().apply {
|
componentContext = componentContext,
|
||||||
d("Hey...","Background Thread")
|
main = { childContext,output ->
|
||||||
//d(directories.defaultDir(),"Background Thread")
|
spotiFlyerMain(
|
||||||
d("Hey...","Background Thread")
|
childContext,
|
||||||
}
|
output,
|
||||||
//*Authenticate Spotify Client*//*
|
dependencies
|
||||||
/*fetchPlatformQueryResult.spotifyProvider.authenticateSpotifyClient(
|
)
|
||||||
override = true //methods.value.currentPlatform is AllPlatforms.Js
|
},
|
||||||
)*/
|
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 =
|
private val router =
|
||||||
@ -80,36 +98,15 @@ internal class SpotiFlyerRootImpl(
|
|||||||
it !is Configuration.Main
|
it !is Configuration.Main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun setDownloadDirectory() { setDownloadDirectoryAction() }
|
override fun setDownloadDirectory() { actions.setDownloadDirectoryAction() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createChild(configuration: Configuration, componentContext: ComponentContext): Child =
|
private fun createChild(configuration: Configuration, componentContext: ComponentContext): Child =
|
||||||
when (configuration) {
|
when (configuration) {
|
||||||
is Configuration.Main -> Child.Main(spotiFlyerMain(componentContext))
|
is Configuration.Main -> Child.Main(main(componentContext, Consumer(::onMainOutput)))
|
||||||
is Configuration.List -> Child.List(spotiFlyerList(componentContext, link = configuration.link))
|
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) =
|
private fun onMainOutput(output: SpotiFlyerMain.Output) =
|
||||||
when (output) {
|
when (output) {
|
||||||
is SpotiFlyerMain.Output.Search -> router.push(Configuration.List(link = output.link))
|
is SpotiFlyerMain.Output.Search -> router.push(Configuration.List(link = output.link))
|
||||||
@ -120,6 +117,13 @@ internal class SpotiFlyerRootImpl(
|
|||||||
is SpotiFlyerList.Output.Finished -> router.pop()
|
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 {
|
private sealed class Configuration : Parcelable {
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object Main : Configuration()
|
object Main : Configuration()
|
||||||
@ -128,3 +132,24 @@ internal class SpotiFlyerRootImpl(
|
|||||||
data class List(val link: String) : Configuration()
|
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