Build Fixes and updates

This commit is contained in:
shabinder 2021-05-16 02:07:51 +05:30
parent 658cbbaf91
commit b1cef3c265
21 changed files with 88 additions and 94 deletions

View File

@ -31,7 +31,7 @@ object Versions {
const val koin = "3.0.1"
// Logger
const val kermit = "0.1.8"
const val kermit = "0.1.9"
// Internet
const val ktor = "1.5.4"

View File

@ -76,9 +76,8 @@ kotlin {
// Extras
implementation(Extras.kermit)
implementation("co.touchlab:stately-common:1.1.6")
implementation("co.touchlab:stately-common:1.1.7")
implementation("dev.icerock.moko:parcelize:0.6.1")
// implementation("io.github.reactivecircus.cache4k:cache4k:0.2.0-SNAPSHOT") // Local Maven
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt") {
isForce = true

View File

@ -34,7 +34,7 @@ import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import co.touchlab.kermit.Kermit
import com.shabinder.common.di.*
import com.shabinder.common.di.providers.getData
import com.shabinder.common.di.providers.get
import com.shabinder.common.di.utils.ParallelExecutor
import com.shabinder.common.models.DownloadResult
import com.shabinder.common.models.DownloadStatus
@ -183,7 +183,7 @@ class ForegroundService : Service(), CoroutineScope {
try {
val url = fetcher.youtubeMp3.getMp3DownloadLink(videoID)
if (url == null) {
val audioData: Format = ytDownloader.getVideo(videoID).getData() ?: throw Exception("Java YT Dependency Error")
val audioData: Format = ytDownloader?.getVideo(videoID)?.get() ?: throw Exception("Java YT Dependency Error")
val ytUrl = audioData.url!! //We Will catch NPE
enqueueDownload(ytUrl, track)
} else enqueueDownload(url, track)

View File

@ -60,17 +60,15 @@ fun commonModule(enableNetworkLogs: Boolean) = module {
}
@ThreadLocal
val kotlinxSerializer = KotlinxSerializer(
Json {
isLenient = true
ignoreUnknownKeys = true
}
)
val globalJson = Json {
isLenient = true
ignoreUnknownKeys = true
}
fun createHttpClient(enableNetworkLogs: Boolean = false) = HttpClient {
// https://github.com/Kotlin/kotlinx.serialization/issues/1450
install(JsonFeature) {
serializer = KotlinxSerializer()
serializer = KotlinxSerializer(globalJson)
}
// WorkAround for Freezing
// Use httpClient.getData / httpClient.postData Extensions

View File

@ -17,7 +17,6 @@
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
@ -25,7 +24,6 @@ import com.shabinder.common.models.gaana.GaanaArtistDetails
import com.shabinder.common.models.gaana.GaanaArtistTracks
import com.shabinder.common.models.gaana.GaanaPlaylist
import com.shabinder.common.models.gaana.GaanaSong
import com.shabinder.common.models.methods
import io.ktor.client.HttpClient
import io.ktor.client.request.get
@ -53,7 +51,7 @@ interface GaanaRequests {
format: String = "JSON",
limit: Int = 2000
): GaanaPlaylist {
return httpClient.getData(
return httpClient.get(
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
)
}
@ -70,7 +68,7 @@ interface GaanaRequests {
format: String = "JSON",
limit: Int = 2000
): GaanaAlbum {
return httpClient.getData(
return httpClient.get(
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
)
}
@ -86,7 +84,7 @@ interface GaanaRequests {
seokey: String,
format: String = "JSON",
): GaanaSong {
return httpClient.getData(
return httpClient.get(
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format"
)
}
@ -102,7 +100,7 @@ interface GaanaRequests {
seokey: String,
format: String = "JSON",
): GaanaArtistDetails {
return httpClient.getData(
return httpClient.get(
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format"
)
}
@ -119,7 +117,7 @@ interface GaanaRequests {
format: String = "JSON",
limit: Int = 50
): GaanaArtistTracks {
return httpClient.getData(
return httpClient.get(
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
)
}

View File

@ -21,6 +21,7 @@ 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.globalJson
import com.shabinder.common.di.spotify.SpotifyRequests
import com.shabinder.common.di.spotify.authenticateSpotify
import com.shabinder.common.models.NativeAtomicReference
@ -32,6 +33,8 @@ import com.shabinder.common.models.spotify.Source
import com.shabinder.common.models.spotify.Track
import io.ktor.client.HttpClient
import io.ktor.client.features.defaultRequest
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.client.request.header
class SpotifyProvider(
@ -59,9 +62,9 @@ class SpotifyProvider(
defaultRequest {
header("Authorization", "Bearer ${token.access_token}")
}
/*install(JsonFeature) {
serializer = kotlinxSerializer
}*/
install(JsonFeature) {
serializer = KotlinxSerializer(globalJson)
}
}.also { httpClientRef.value = it }
}
}

View File

@ -18,7 +18,6 @@ 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 io.github.shabinder.fuzzywuzzy.diffutils.FuzzySearch
@ -29,7 +28,6 @@ import io.ktor.http.ContentType
import io.ktor.http.contentType
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.contentOrNull

View File

@ -18,8 +18,10 @@ 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.finalOutputDir
import com.shabinder.common.di.utils.removeIllegalChars
import com.shabinder.common.models.AllPlatforms
import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.PlatformQueryResult
import com.shabinder.common.models.TrackDetails
@ -35,7 +37,8 @@ class YoutubeProvider(
private val logger: Kermit,
private val dir: Dir,
) {
val ytDownloader: YoutubeDownloader = YoutubeDownloader()
// Youtube Downloader isn't fully compatible with JS Yet
val ytDownloader: YoutubeDownloader? = if(currentPlatform == AllPlatforms.Js) null else YoutubeDownloader()
/*
* YT Album Art Schema
@ -92,7 +95,7 @@ class YoutubeProvider(
)
result.apply {
try {
val playlist = ytDownloader.getPlaylist(searchId)
val playlist = ytDownloader?.getPlaylist(searchId) ?: return null
val playlistDetails = playlist.details
val name = playlistDetails.title
subFolder = removeIllegalChars(name)
@ -151,7 +154,7 @@ class YoutubeProvider(
).apply {
try {
logger.i { searchId }
val video = ytDownloader.getVideo(searchId)
val video = ytDownloader?.getVideo(searchId) ?: return null
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
val detail = video.videoDetails
val name = detail.title?.replace(detail.author?.toUpperCase() ?: "", "", true)
@ -193,7 +196,7 @@ class YoutubeProvider(
}
}
fun YoutubeVideo.getData(): Format? {
fun YoutubeVideo.get(): Format? {
return getAudioWithQuality(AudioQuality.high).getOrNull(0)
?: getAudioWithQuality(AudioQuality.medium).getOrNull(0)
?: getAudioWithQuality(AudioQuality.low).getOrNull(0)

View File

@ -16,13 +16,14 @@
package com.shabinder.common.di.spotify
import com.shabinder.common.di.kotlinxSerializer
import com.shabinder.common.di.globalJson
import com.shabinder.common.models.methods
import com.shabinder.common.models.spotify.TokenData
import io.ktor.client.HttpClient
import io.ktor.client.features.auth.Auth
import io.ktor.client.features.auth.providers.basic
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.client.request.forms.FormDataContent
import io.ktor.client.request.post
import io.ktor.http.Parameters
@ -53,7 +54,7 @@ private val spotifyAuthClient by lazy {
}
}
install(JsonFeature) {
serializer = kotlinxSerializer
serializer = KotlinxSerializer(globalJson)
}
}
}

View File

@ -17,7 +17,6 @@
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
@ -25,13 +24,6 @@ import com.shabinder.common.models.spotify.Playlist
import com.shabinder.common.models.spotify.Track
import io.ktor.client.HttpClient
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"
@ -43,7 +35,7 @@ interface SpotifyRequests {
suspend fun authenticateSpotifyClient(override: Boolean = false)
suspend fun getPlaylist(playlistID: String): Playlist {
return httpClient.getData("$BASE_URL/playlists/$playlistID")
return httpClient.get("$BASE_URL/playlists/$playlistID")
}
suspend fun getPlaylistTracks(
@ -51,26 +43,26 @@ interface SpotifyRequests {
offset: Int = 0,
limit: Int = 100
): PagingObjectPlaylistTrack {
return httpClient.getData("$BASE_URL/playlists/$playlistID/tracks?offset=$offset&limit=$limit")
return httpClient.get("$BASE_URL/playlists/$playlistID/tracks?offset=$offset&limit=$limit")
}
suspend fun getTrack(id: String?): Track {
return httpClient.getData("$BASE_URL/tracks/$id")
return httpClient.get("$BASE_URL/tracks/$id")
}
suspend fun getEpisode(id: String?): Track {
return httpClient.getData("$BASE_URL/episodes/$id")
return httpClient.get("$BASE_URL/episodes/$id")
}
suspend fun getShow(id: String?): Track {
return httpClient.getData("$BASE_URL/shows/$id")
return httpClient.get("$BASE_URL/shows/$id")
}
suspend fun getAlbum(id: String): Album {
return httpClient.getData("$BASE_URL/albums/$id")
return httpClient.get("$BASE_URL/albums/$id")
}
suspend fun getResponse(url: String): String {
return httpClient.getData(url)
return httpClient.get(url)
}
}

View File

@ -26,35 +26,6 @@ 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

View File

@ -18,7 +18,6 @@ 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

View File

@ -17,13 +17,13 @@
package com.shabinder.common.di
import com.shabinder.common.di.providers.YoutubeMp3
import com.shabinder.common.di.providers.getData
import com.shabinder.common.di.providers.get
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 io.github.shabinder.YoutubeDownloader
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
@ -75,7 +75,7 @@ suspend fun downloadTrack(
youtubeMp3: YoutubeMp3
) {
try {
val link = youtubeMp3.getMp3DownloadLink(videoID) ?: ytDownloader.getVideo(videoID).getData()?.url
val link = youtubeMp3.getMp3DownloadLink(videoID) ?: ytDownloader.getVideo(videoID).get()?.url
if (link == null) {
DownloadProgressFlow.emit(

View File

@ -63,7 +63,7 @@ suspend fun downloadTrack(
fetcher.dir.logger.i { "LINK: $videoID -> $link" }
if (link == null) {
link = fetcher.youtubeProvider.ytDownloader.getVideo(videoID).getData()?.url ?: return
link = fetcher.youtubeProvider.ytDownloader.getVideo(videoID).get()?.url ?: return
}
fetcher.dir.logger.i { "LINK: $videoID -> $link" }
downloadFile(link).collect {

View File

@ -135,5 +135,14 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
isInternetAccessible()
}
}
override val analytics = object: SpotiFlyerRoot.Analytics {
override fun appLaunchEvent() {}
override fun homeScreenVisit() {}
override fun listScreenVisit() {}
override fun donationDialogVisit() {}
}
}
)

View File

@ -28,19 +28,31 @@ repositories {
dependencies {
implementation(kotlin("stdlib-js"))
implementation(Decompose.decompose)
implementation(Koin.core)
implementation(Ktor.clientJs)
implementation(Extras.kermit)
implementation(Decompose.decompose)
implementation(MVIKotlin.mvikotlin)
implementation(MVIKotlin.coroutines)
implementation(MVIKotlin.mvikotlinMain)
implementation(MVIKotlin.mvikotlinLogging)
implementation(Ktor.auth)
implementation(Ktor.clientJs)
implementation(Ktor.clientJson)
implementation(Ktor.clientCore)
implementation(Ktor.clientLogging)
implementation(Ktor.clientSerialization)
implementation(project(":common:root"))
implementation(project(":common:main"))
implementation(project(":common:list"))
implementation(project(":common:database"))
implementation(project(":common:data-models"))
implementation(project(":common:dependency-injection"))
implementation("co.touchlab:stately-common:1.1.7")
implementation("dev.icerock.moko:parcelize:0.6.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0") {
// https://youtrack.jetbrains.com/issue/KTOR-2670
isForce = true
}
implementation("org.jetbrains:kotlin-react:17.0.1-pre.148-kotlin-1.4.30")
implementation("org.jetbrains:kotlin-react-dom:17.0.1-pre.148-kotlin-1.4.30")
implementation("org.jetbrains:kotlin-styled:1.0.0-pre.115-kotlin-1.4.10")

View File

@ -23,13 +23,11 @@ import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
import com.shabinder.common.di.DownloadProgressFlow
import com.shabinder.common.models.Actions
import com.shabinder.common.models.AllPlatforms
import com.shabinder.common.models.PlatformActions
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.database.Database
import extras.renderableChild
import kotlinx.coroutines.Dispatchers
import react.RBuilder
import react.RComponent
import react.RProps
@ -82,6 +80,23 @@ class App(props: AppProps): RComponent<AppProps, RState>(props) {
override val isInternetAvailable: Boolean = true
}
override val analytics = object: SpotiFlyerRoot.Analytics{
override fun appLaunchEvent() {
// TODO("Not yet implemented")
}
override fun homeScreenVisit() {
// TODO("Not yet implemented")
}
override fun listScreenVisit() {
// TODO("Not yet implemented")
}
override fun donationDialogVisit() {
// TODO("Not yet implemented")
}
}
}
)

View File

@ -21,9 +21,6 @@ import com.shabinder.common.di.initKoin
import react.dom.render
import kotlinx.browser.document
import kotlinx.browser.window
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
@ -38,7 +35,6 @@ fun main() {
}
object AppDependencies : KoinComponent {
val appScope = CoroutineScope(Dispatchers.Default)
val logger: Kermit
val directories: Dir
val fetchPlatformQueryResult: FetchPlatformQueryResult
@ -47,8 +43,5 @@ object AppDependencies : KoinComponent {
directories = get()
logger = get()
fetchPlatformQueryResult = get()
appScope.launch {
//fetchPlatformQueryResult.spotifyProvider.authenticateSpotifyClient(true)
}
}
}

View File

@ -16,6 +16,7 @@
package extras
import com.arkivanov.decompose.value.Value
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
@ -36,7 +37,7 @@ abstract class RenderableComponent<
initialState: S
) : RComponent<RenderableComponent.Props<T>, RenderableComponent.State<S>>(props) {
protected abstract val stateFlow: Flow<S>
protected abstract val stateFlow: Value<S>
protected val model: T get() = props.model
protected var scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
@ -48,7 +49,7 @@ abstract class RenderableComponent<
if(!scope.isActive)
scope = CoroutineScope(Dispatchers.Default)
scope.launch {
stateFlow.collect {
stateFlow.subscribe {
setState { data = it }
}
}

View File

@ -16,6 +16,7 @@
package home
import com.arkivanov.decompose.value.Value
import com.shabinder.common.main.SpotiFlyerMain
import com.shabinder.common.main.SpotiFlyerMain.State
import extras.RenderableComponent
@ -55,7 +56,7 @@ class HomeScreen(
}
}
override val stateFlow: Flow<SpotiFlyerMain.State> = model.models
override val stateFlow: Value<SpotiFlyerMain.State> = model.models
override fun RBuilder.render() {
styledDiv{

View File

@ -16,6 +16,7 @@
package list
import com.arkivanov.decompose.value.Value
import com.shabinder.common.list.SpotiFlyerList
import com.shabinder.common.list.SpotiFlyerList.State
import extras.RenderableComponent
@ -39,7 +40,7 @@ class ListScreen(
props: Props<SpotiFlyerList>,
) : RenderableComponent<SpotiFlyerList, State>(props,initialState = State()) {
override val stateFlow: Flow<SpotiFlyerList.State> = model.models
override val stateFlow: Value<SpotiFlyerList.State> = model.models
override fun RBuilder.render() {