Freezing Fixes, Database Adding in Background.

This commit is contained in:
shabinder 2021-05-04 13:29:53 +05:30
parent d8415b52ca
commit 7a3a299aa2
13 changed files with 124 additions and 44 deletions

View File

@ -17,6 +17,7 @@
package com.shabinder.common.di
import co.touchlab.kermit.Kermit
import co.touchlab.stately.ensureNeverFrozen
import com.shabinder.common.database.databaseModule
import com.shabinder.common.database.getLogger
import com.shabinder.common.di.providers.GaanaProvider
@ -37,6 +38,7 @@ import org.koin.core.context.startKoin
import org.koin.dsl.KoinAppDeclaration
import org.koin.dsl.module
import kotlin.native.concurrent.SharedImmutable
import kotlin.native.concurrent.ThreadLocal
fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclaration = {}) =
startKoin {
@ -60,7 +62,7 @@ fun commonModule(enableNetworkLogs: Boolean) = module {
single { FetchPlatformQueryResult(get(), get(), get(), get(), get(), get()) }
}
@SharedImmutable
@ThreadLocal
val kotlinxSerializer = KotlinxSerializer(
Json {
isLenient = true
@ -68,10 +70,16 @@ val kotlinxSerializer = KotlinxSerializer(
}
)
fun createHttpClient(enableNetworkLogs: Boolean = false, serializer: KotlinxSerializer = kotlinxSerializer) = HttpClient {
install(JsonFeature) {
this.serializer = serializer
fun createHttpClient(enableNetworkLogs: Boolean = false) = HttpClient {
// https://github.com/Kotlin/kotlinx.serialization/issues/1450
/*install(JsonFeature) {
serializer = KotlinxSerializer(
Json {
isLenient = true
ignoreUnknownKeys = true
}
)
}*/
// Timeout
install(HttpTimeout) {
requestTimeoutMillis = 15000L

View File

@ -43,7 +43,7 @@ expect val currentPlatform: AllPlatforms
suspend fun isInternetAccessible(): Boolean {
return withContext(dispatcherIO) {
try {
ktorHttpClient.head<String>("http://google.com")
ktorHttpClient.head<String>("https://google.com")
true
} catch (e: Exception) {
e.printStackTrace()

View File

@ -23,6 +23,10 @@ import com.shabinder.common.di.providers.YoutubeMp3
import com.shabinder.common.di.providers.YoutubeMusic
import com.shabinder.common.models.PlatformQueryResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class FetchPlatformQueryResult(
@ -54,13 +58,19 @@ class FetchPlatformQueryResult(
null
}
}
result?.run {
withContext(Dispatchers.Default) {
db?.add(
folderType, title, link, coverUrl, trackList.size.toLong()
if (result != null) {
addToDatabaseAsync(
link,
result.copy() // Send a copy in order to not to freeze Result itself
)
}
}
return result
}
private fun addToDatabaseAsync(link: String, result: PlatformQueryResult) {
GlobalScope.launch(dispatcherIO) {
db?.add(
result.folderType, result.title, link, result.coverUrl, result.trackList.size.toLong()
)
}
}
}

View File

@ -17,6 +17,7 @@
package com.shabinder.common.di.gaana
import com.shabinder.common.di.currentPlatform
import com.shabinder.common.di.utils.getData
import com.shabinder.common.models.AllPlatforms
import com.shabinder.common.models.corsProxy
import com.shabinder.common.models.gaana.GaanaAlbum
@ -52,7 +53,7 @@ interface GaanaRequests {
format: String = "JSON",
limit: Int = 2000
): GaanaPlaylist {
return httpClient.get(
return httpClient.getData(
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
)
}
@ -69,7 +70,7 @@ interface GaanaRequests {
format: String = "JSON",
limit: Int = 2000
): GaanaAlbum {
return httpClient.get(
return httpClient.getData(
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
)
}
@ -85,7 +86,7 @@ interface GaanaRequests {
seokey: String,
format: String = "JSON",
): GaanaSong {
return httpClient.get(
return httpClient.getData(
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format"
)
}
@ -101,7 +102,7 @@ interface GaanaRequests {
seokey: String,
format: String = "JSON",
): GaanaArtistDetails {
return httpClient.get(
return httpClient.getData(
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format"
)
}
@ -118,7 +119,7 @@ interface GaanaRequests {
format: String = "JSON",
limit: Int = 50
): GaanaArtistTracks {
return httpClient.get(
return httpClient.getData(
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
)
}

View File

@ -79,7 +79,7 @@ class GaanaProvider(
it.updateStatusIfPresent(folderType, subFolder)
trackList = listOf(it).toTrackDetailsList(folderType, subFolder)
title = it.track_title
coverUrl = it.artworkLink
coverUrl = it.artworkLink.replace("http:","https:")
}
}
"album" -> {
@ -91,7 +91,7 @@ class GaanaProvider(
}
trackList = it.tracks?.toTrackDetailsList(folderType, subFolder) ?: emptyList()
title = link
coverUrl = it.custom_artworks.size_480p
coverUrl = it.custom_artworks.size_480p.replace("http:","https:")
}
}
"playlist" -> {
@ -114,7 +114,7 @@ class GaanaProvider(
getGaanaArtistDetails(seokey = link).artist.firstOrNull()
?.also {
title = it.name
coverUrl = it.artworkLink ?: gaanaPlaceholderImageUrl
coverUrl = it.artworkLink?.replace("http:","https:") ?: gaanaPlaceholderImageUrl
}
getGaanaArtistTracks(seokey = link).also {
it.tracks?.forEach { track ->
@ -143,7 +143,7 @@ class GaanaProvider(
trackUrl = it.lyrics_url,
downloaded = it.downloaded ?: DownloadStatus.NotDownloaded,
source = Source.Gaana,
albumArtURL = it.artworkLink,
albumArtURL = it.artworkLink.replace("http:","https:"),
outputFilePath = dir.finalOutputDir(it.track_title, type, subFolder, dir.defaultDir()/*,".m4a"*/)
)
}

View File

@ -17,9 +17,11 @@
package com.shabinder.common.di.providers
import co.touchlab.kermit.Kermit
import co.touchlab.stately.ensureNeverFrozen
import co.touchlab.stately.freeze
import com.shabinder.common.di.Dir
import com.shabinder.common.di.TokenStore
import com.shabinder.common.di.createHttpClient
import com.shabinder.common.di.finalOutputDir
import com.shabinder.common.di.kotlinxSerializer
import com.shabinder.common.di.ktorHttpClient
@ -33,9 +35,12 @@ import com.shabinder.common.models.spotify.Image
import com.shabinder.common.models.spotify.Source
import com.shabinder.common.models.spotify.Track
import io.ktor.client.HttpClient
import io.ktor.client.features.auth.*
import io.ktor.client.features.auth.providers.*
import io.ktor.client.features.defaultRequest
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.request.header
import kotlin.native.concurrent.SharedImmutable
class SpotifyProvider(
private val tokenStore: TokenStore,
@ -62,14 +67,14 @@ class SpotifyProvider(
defaultRequest {
header("Authorization", "Bearer ${token.access_token}")
}
install(JsonFeature) {
/*install(JsonFeature) {
serializer = kotlinxSerializer
}
}.also { httpClientRef.value = it.freeze() }
}*/
}.also { httpClientRef.value = it }
}
}
override val httpClientRef = NativeAtomicReference(ktorHttpClient)
override val httpClientRef = NativeAtomicReference(createHttpClient(true))
suspend fun query(fullLink: String): PlatformQueryResult? {

View File

@ -18,6 +18,7 @@ package com.shabinder.common.di.providers
import co.touchlab.kermit.Kermit
import com.shabinder.common.di.gaana.corsApi
import com.shabinder.common.di.utils.postData
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.YoutubeTrack
import com.shabinder.fuzzywuzzy.diffutils.FuzzySearch
@ -283,7 +284,7 @@ class YoutubeMusic constructor(
}
private suspend fun getYoutubeMusicResponse(query: String): String {
return httpClient.post("${corsApi}https://music.youtube.com/youtubei/v1/search?alt=json&key=$apiKey") {
return httpClient.postData("${corsApi}https://music.youtube.com/youtubei/v1/search?alt=json&key=$apiKey") {
contentType(ContentType.Application.Json)
headers {
append("referer", "https://music.youtube.com/search")

View File

@ -17,25 +17,33 @@
package com.shabinder.common.di.spotify
import com.shabinder.common.di.gaana.corsApi
import com.shabinder.common.di.utils.getData
import com.shabinder.common.models.NativeAtomicReference
import com.shabinder.common.models.spotify.Album
import com.shabinder.common.models.spotify.PagingObjectPlaylistTrack
import com.shabinder.common.models.spotify.Playlist
import com.shabinder.common.models.spotify.Track
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.descriptors.ClassSerialDescriptorBuilder
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
private val BASE_URL get() = "${corsApi}https://api.spotify.com/v1"
interface SpotifyRequests {
val httpClientRef: NativeAtomicReference<HttpClient>
val httpClient get() = httpClientRef.value
val httpClient:HttpClient get() = httpClientRef.value
suspend fun authenticateSpotifyClient(override: Boolean = false)
suspend fun getPlaylist(playlistID: String): Playlist {
return httpClient.get("$BASE_URL/playlists/$playlistID")
return httpClient.getData("$BASE_URL/playlists/$playlistID")
}
suspend fun getPlaylistTracks(
@ -43,26 +51,26 @@ interface SpotifyRequests {
offset: Int = 0,
limit: Int = 100
): PagingObjectPlaylistTrack {
return httpClient.get("$BASE_URL/playlists/$playlistID/tracks?offset=$offset&limit=$limit")
return httpClient.getData("$BASE_URL/playlists/$playlistID/tracks?offset=$offset&limit=$limit")
}
suspend fun getTrack(id: String?): Track {
return httpClient.get("$BASE_URL/tracks/$id")
return httpClient.getData("$BASE_URL/tracks/$id")
}
suspend fun getEpisode(id: String?): Track {
return httpClient.get("$BASE_URL/episodes/$id")
return httpClient.getData("$BASE_URL/episodes/$id")
}
suspend fun getShow(id: String?): Track {
return httpClient.get("$BASE_URL/shows/$id")
return httpClient.getData("$BASE_URL/shows/$id")
}
suspend fun getAlbum(id: String): Album {
return httpClient.get("$BASE_URL/albums/$id")
return httpClient.getData("$BASE_URL/albums/$id")
}
suspend fun getResponse(url: String): String {
return httpClient.get(url)
return httpClient.getData(url)
}
}

View File

@ -16,6 +16,51 @@
package com.shabinder.common.di.utils
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import kotlin.native.concurrent.SharedImmutable
import kotlin.native.concurrent.ThreadLocal
/*
* WorkAround: https://github.com/Kotlin/kotlinx.serialization/issues/1450
* */
@OptIn(InternalSerializationApi::class)
suspend inline fun <reified T: Any> HttpClient.getData(
urlString: String,
block: HttpRequestBuilder.() -> Unit = {}
): T {
val response = get<HttpResponse> {
url.takeFrom(urlString)
block()
}
val jsonBody = response.readText()
return json.decodeFromString(T::class.serializer(),jsonBody)
}
@OptIn(InternalSerializationApi::class)
suspend inline fun <reified T: Any> HttpClient.postData(
urlString: String,
block: HttpRequestBuilder.() -> Unit = {}
): T {
val response = post<HttpResponse> {
url.takeFrom(urlString)
block()
}
val jsonBody = response.readText()
return json.decodeFromString(T::class.serializer(),jsonBody)
}
@ThreadLocal
val json by lazy { Json {
isLenient = true
ignoreUnknownKeys = true
} }
/**
* Removing Illegal Chars from File Name
* **/

View File

@ -18,6 +18,7 @@ package com.shabinder.common.di.youtubeMp3
import co.touchlab.kermit.Kermit
import com.shabinder.common.di.gaana.corsApi
import com.shabinder.common.di.utils.postData
import io.ktor.client.HttpClient
import io.ktor.client.request.forms.FormDataContent
import io.ktor.client.request.post
@ -44,7 +45,7 @@ interface Yt1sMp3 {
* Body Form= q:yt video link ,vt:format=mp3
* */
private suspend fun getKey(videoID: String): String {
val response: JsonObject? = httpClient.post("${corsApi}https://yt1s.com/api/ajaxSearch/index") {
val response: JsonObject? = httpClient.postData("${corsApi}https://yt1s.com/api/ajaxSearch/index") {
body = FormDataContent(
Parameters.build {
append("q", "https://www.youtube.com/watch?v=$videoID")
@ -56,7 +57,7 @@ interface Yt1sMp3 {
}
private suspend fun getConvertedMp3Link(videoID: String, key: String): JsonObject? {
return httpClient.post("${corsApi}https://yt1s.com/api/ajaxConvert/convert") {
return httpClient.postData("${corsApi}https://yt1s.com/api/ajaxConvert/convert") {
body = FormDataContent(
Parameters.build {
append("vid", videoID)

View File

@ -31,18 +31,16 @@ actual class Dir actual constructor(
actual fun fileSeparator(): String = "/"
// TODO Error Handling
actual fun defaultDir(): String = defaultDirURL.path!!
actual fun defaultDir(): String = defaultDirURL.path!! + fileSeparator()
val defaultDirURL: NSURL by lazy {
createDirectories()
private val defaultDirURL: NSURL by lazy {
val musicDir = NSFileManager.defaultManager.URLForDirectory(NSMusicDirectory, NSUserDomainMask,null,true,null)!!
musicDir.URLByAppendingPathComponent("SpotiFlyer",true)!!
}
actual fun imageCacheDir(): String = imageCacheURL.path!!
actual fun imageCacheDir(): String = imageCacheURL.path!! + fileSeparator()
val imageCacheURL: NSURL by lazy {
createDirectories()
private val imageCacheURL: NSURL by lazy {
val cacheDir = NSFileManager.defaultManager.URLForDirectory(NSCachesDirectory, NSUserDomainMask,null,true,null)
cacheDir?.URLByAppendingPathComponent("SpotiFlyer",true)!!
}

View File

@ -24,6 +24,7 @@ import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
import com.shabinder.common.database.getLogger
import com.shabinder.common.di.Dir
import com.shabinder.common.di.FetchPlatformQueryResult
import com.shabinder.common.di.dispatcherIO
import com.shabinder.common.di.downloadTracks
import com.shabinder.common.list.SpotiFlyerList.State
import com.shabinder.common.list.store.SpotiFlyerListStore.Intent
@ -33,6 +34,7 @@ import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.methods
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.withContext
internal class SpotiFlyerListStoreProvider(
private val dir: Dir,
@ -85,6 +87,7 @@ internal class SpotiFlyerListStoreProvider(
throw Exception("An Error Occurred, Check your Link / Connection")
}
} catch (e:Exception) {
e.printStackTrace()
dispatch(Result.ErrorOccurred(e))
}
}

@ -1 +1 @@
Subproject commit f218336b5b31b365bbf34503b79f2c4f2b703d7d
Subproject commit 41c6aca4938d13cc7364b1200bf6987c852dfdc2