mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 01:04:31 +01:00
Build Fixes and updates
This commit is contained in:
parent
658cbbaf91
commit
b1cef3c265
@ -31,7 +31,7 @@ object Versions {
|
|||||||
const val koin = "3.0.1"
|
const val koin = "3.0.1"
|
||||||
|
|
||||||
// Logger
|
// Logger
|
||||||
const val kermit = "0.1.8"
|
const val kermit = "0.1.9"
|
||||||
|
|
||||||
// Internet
|
// Internet
|
||||||
const val ktor = "1.5.4"
|
const val ktor = "1.5.4"
|
||||||
|
@ -76,9 +76,8 @@ kotlin {
|
|||||||
|
|
||||||
// Extras
|
// Extras
|
||||||
implementation(Extras.kermit)
|
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("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-serialization-json:1.2.0")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt") {
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt") {
|
||||||
isForce = true
|
isForce = true
|
||||||
|
@ -34,7 +34,7 @@ import androidx.annotation.RequiresApi
|
|||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import com.shabinder.common.di.*
|
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.di.utils.ParallelExecutor
|
||||||
import com.shabinder.common.models.DownloadResult
|
import com.shabinder.common.models.DownloadResult
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
@ -183,7 +183,7 @@ class ForegroundService : Service(), CoroutineScope {
|
|||||||
try {
|
try {
|
||||||
val url = fetcher.youtubeMp3.getMp3DownloadLink(videoID)
|
val url = fetcher.youtubeMp3.getMp3DownloadLink(videoID)
|
||||||
if (url == null) {
|
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
|
val ytUrl = audioData.url!! //We Will catch NPE
|
||||||
enqueueDownload(ytUrl, track)
|
enqueueDownload(ytUrl, track)
|
||||||
} else enqueueDownload(url, track)
|
} else enqueueDownload(url, track)
|
||||||
|
@ -60,17 +60,15 @@ fun commonModule(enableNetworkLogs: Boolean) = module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ThreadLocal
|
@ThreadLocal
|
||||||
val kotlinxSerializer = KotlinxSerializer(
|
val globalJson = Json {
|
||||||
Json {
|
|
||||||
isLenient = true
|
isLenient = true
|
||||||
ignoreUnknownKeys = true
|
ignoreUnknownKeys = true
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
fun createHttpClient(enableNetworkLogs: Boolean = false) = HttpClient {
|
fun createHttpClient(enableNetworkLogs: Boolean = false) = HttpClient {
|
||||||
// https://github.com/Kotlin/kotlinx.serialization/issues/1450
|
// https://github.com/Kotlin/kotlinx.serialization/issues/1450
|
||||||
install(JsonFeature) {
|
install(JsonFeature) {
|
||||||
serializer = KotlinxSerializer()
|
serializer = KotlinxSerializer(globalJson)
|
||||||
}
|
}
|
||||||
// WorkAround for Freezing
|
// WorkAround for Freezing
|
||||||
// Use httpClient.getData / httpClient.postData Extensions
|
// Use httpClient.getData / httpClient.postData Extensions
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package com.shabinder.common.di.gaana
|
package com.shabinder.common.di.gaana
|
||||||
|
|
||||||
import com.shabinder.common.di.currentPlatform
|
import com.shabinder.common.di.currentPlatform
|
||||||
import com.shabinder.common.di.utils.getData
|
|
||||||
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
|
||||||
@ -25,7 +24,6 @@ import com.shabinder.common.models.gaana.GaanaArtistDetails
|
|||||||
import com.shabinder.common.models.gaana.GaanaArtistTracks
|
import com.shabinder.common.models.gaana.GaanaArtistTracks
|
||||||
import com.shabinder.common.models.gaana.GaanaPlaylist
|
import com.shabinder.common.models.gaana.GaanaPlaylist
|
||||||
import com.shabinder.common.models.gaana.GaanaSong
|
import com.shabinder.common.models.gaana.GaanaSong
|
||||||
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
|
||||||
|
|
||||||
@ -53,7 +51,7 @@ interface GaanaRequests {
|
|||||||
format: String = "JSON",
|
format: String = "JSON",
|
||||||
limit: Int = 2000
|
limit: Int = 2000
|
||||||
): GaanaPlaylist {
|
): GaanaPlaylist {
|
||||||
return httpClient.getData(
|
return httpClient.get(
|
||||||
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
|
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -70,7 +68,7 @@ interface GaanaRequests {
|
|||||||
format: String = "JSON",
|
format: String = "JSON",
|
||||||
limit: Int = 2000
|
limit: Int = 2000
|
||||||
): GaanaAlbum {
|
): GaanaAlbum {
|
||||||
return httpClient.getData(
|
return httpClient.get(
|
||||||
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
|
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -86,7 +84,7 @@ interface GaanaRequests {
|
|||||||
seokey: String,
|
seokey: String,
|
||||||
format: String = "JSON",
|
format: String = "JSON",
|
||||||
): GaanaSong {
|
): GaanaSong {
|
||||||
return httpClient.getData(
|
return httpClient.get(
|
||||||
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format"
|
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -102,7 +100,7 @@ interface GaanaRequests {
|
|||||||
seokey: String,
|
seokey: String,
|
||||||
format: String = "JSON",
|
format: String = "JSON",
|
||||||
): GaanaArtistDetails {
|
): GaanaArtistDetails {
|
||||||
return httpClient.getData(
|
return httpClient.get(
|
||||||
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format"
|
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -119,7 +117,7 @@ interface GaanaRequests {
|
|||||||
format: String = "JSON",
|
format: String = "JSON",
|
||||||
limit: Int = 50
|
limit: Int = 50
|
||||||
): GaanaArtistTracks {
|
): GaanaArtistTracks {
|
||||||
return httpClient.getData(
|
return httpClient.get(
|
||||||
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
|
"$BASE_URL/?type=$type&subtype=$subtype&seokey=$seokey&token=$TOKEN&format=$format&limit=$limit"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import com.shabinder.common.di.Dir
|
|||||||
import com.shabinder.common.di.TokenStore
|
import com.shabinder.common.di.TokenStore
|
||||||
import com.shabinder.common.di.createHttpClient
|
import com.shabinder.common.di.createHttpClient
|
||||||
import com.shabinder.common.di.finalOutputDir
|
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.SpotifyRequests
|
||||||
import com.shabinder.common.di.spotify.authenticateSpotify
|
import com.shabinder.common.di.spotify.authenticateSpotify
|
||||||
import com.shabinder.common.models.NativeAtomicReference
|
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 com.shabinder.common.models.spotify.Track
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.features.defaultRequest
|
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
|
import io.ktor.client.request.header
|
||||||
|
|
||||||
class SpotifyProvider(
|
class SpotifyProvider(
|
||||||
@ -59,9 +62,9 @@ class SpotifyProvider(
|
|||||||
defaultRequest {
|
defaultRequest {
|
||||||
header("Authorization", "Bearer ${token.access_token}")
|
header("Authorization", "Bearer ${token.access_token}")
|
||||||
}
|
}
|
||||||
/*install(JsonFeature) {
|
install(JsonFeature) {
|
||||||
serializer = kotlinxSerializer
|
serializer = KotlinxSerializer(globalJson)
|
||||||
}*/
|
}
|
||||||
}.also { httpClientRef.value = it }
|
}.also { httpClientRef.value = it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ package com.shabinder.common.di.providers
|
|||||||
|
|
||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import com.shabinder.common.di.gaana.corsApi
|
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.TrackDetails
|
||||||
import com.shabinder.common.models.YoutubeTrack
|
import com.shabinder.common.models.YoutubeTrack
|
||||||
import io.github.shabinder.fuzzywuzzy.diffutils.FuzzySearch
|
import io.github.shabinder.fuzzywuzzy.diffutils.FuzzySearch
|
||||||
@ -29,7 +28,6 @@ import io.ktor.http.ContentType
|
|||||||
import io.ktor.http.contentType
|
import io.ktor.http.contentType
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
import kotlinx.serialization.json.JsonElement
|
|
||||||
import kotlinx.serialization.json.buildJsonArray
|
import kotlinx.serialization.json.buildJsonArray
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
import kotlinx.serialization.json.contentOrNull
|
import kotlinx.serialization.json.contentOrNull
|
||||||
|
@ -18,8 +18,10 @@ 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.finalOutputDir
|
import com.shabinder.common.di.finalOutputDir
|
||||||
import com.shabinder.common.di.utils.removeIllegalChars
|
import com.shabinder.common.di.utils.removeIllegalChars
|
||||||
|
import com.shabinder.common.models.AllPlatforms
|
||||||
import com.shabinder.common.models.DownloadStatus
|
import com.shabinder.common.models.DownloadStatus
|
||||||
import com.shabinder.common.models.PlatformQueryResult
|
import com.shabinder.common.models.PlatformQueryResult
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
@ -35,7 +37,8 @@ class YoutubeProvider(
|
|||||||
private val logger: Kermit,
|
private val logger: Kermit,
|
||||||
private val dir: Dir,
|
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
|
* YT Album Art Schema
|
||||||
@ -92,7 +95,7 @@ class YoutubeProvider(
|
|||||||
)
|
)
|
||||||
result.apply {
|
result.apply {
|
||||||
try {
|
try {
|
||||||
val playlist = ytDownloader.getPlaylist(searchId)
|
val playlist = ytDownloader?.getPlaylist(searchId) ?: return null
|
||||||
val playlistDetails = playlist.details
|
val playlistDetails = playlist.details
|
||||||
val name = playlistDetails.title
|
val name = playlistDetails.title
|
||||||
subFolder = removeIllegalChars(name)
|
subFolder = removeIllegalChars(name)
|
||||||
@ -151,7 +154,7 @@ class YoutubeProvider(
|
|||||||
).apply {
|
).apply {
|
||||||
try {
|
try {
|
||||||
logger.i { searchId }
|
logger.i { searchId }
|
||||||
val video = ytDownloader.getVideo(searchId)
|
val video = ytDownloader?.getVideo(searchId) ?: return null
|
||||||
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
|
coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
|
||||||
val detail = video.videoDetails
|
val detail = video.videoDetails
|
||||||
val name = detail.title?.replace(detail.author?.toUpperCase() ?: "", "", true)
|
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)
|
return getAudioWithQuality(AudioQuality.high).getOrNull(0)
|
||||||
?: getAudioWithQuality(AudioQuality.medium).getOrNull(0)
|
?: getAudioWithQuality(AudioQuality.medium).getOrNull(0)
|
||||||
?: getAudioWithQuality(AudioQuality.low).getOrNull(0)
|
?: getAudioWithQuality(AudioQuality.low).getOrNull(0)
|
||||||
|
@ -16,13 +16,14 @@
|
|||||||
|
|
||||||
package com.shabinder.common.di.spotify
|
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.methods
|
||||||
import com.shabinder.common.models.spotify.TokenData
|
import com.shabinder.common.models.spotify.TokenData
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.features.auth.Auth
|
import io.ktor.client.features.auth.Auth
|
||||||
import io.ktor.client.features.auth.providers.basic
|
import io.ktor.client.features.auth.providers.basic
|
||||||
import io.ktor.client.features.json.JsonFeature
|
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.forms.FormDataContent
|
||||||
import io.ktor.client.request.post
|
import io.ktor.client.request.post
|
||||||
import io.ktor.http.Parameters
|
import io.ktor.http.Parameters
|
||||||
@ -53,7 +54,7 @@ private val spotifyAuthClient by lazy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
install(JsonFeature) {
|
install(JsonFeature) {
|
||||||
serializer = kotlinxSerializer
|
serializer = KotlinxSerializer(globalJson)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package com.shabinder.common.di.spotify
|
package com.shabinder.common.di.spotify
|
||||||
|
|
||||||
import com.shabinder.common.di.gaana.corsApi
|
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.NativeAtomicReference
|
||||||
import com.shabinder.common.models.spotify.Album
|
import com.shabinder.common.models.spotify.Album
|
||||||
import com.shabinder.common.models.spotify.PagingObjectPlaylistTrack
|
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 com.shabinder.common.models.spotify.Track
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.request.*
|
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"
|
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 authenticateSpotifyClient(override: Boolean = false)
|
||||||
|
|
||||||
suspend fun getPlaylist(playlistID: String): Playlist {
|
suspend fun getPlaylist(playlistID: String): Playlist {
|
||||||
return httpClient.getData("$BASE_URL/playlists/$playlistID")
|
return httpClient.get("$BASE_URL/playlists/$playlistID")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getPlaylistTracks(
|
suspend fun getPlaylistTracks(
|
||||||
@ -51,26 +43,26 @@ interface SpotifyRequests {
|
|||||||
offset: Int = 0,
|
offset: Int = 0,
|
||||||
limit: Int = 100
|
limit: Int = 100
|
||||||
): PagingObjectPlaylistTrack {
|
): 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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
suspend fun getResponse(url: String): String {
|
||||||
return httpClient.getData(url)
|
return httpClient.get(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,35 +26,6 @@ import kotlinx.serialization.serializer
|
|||||||
import kotlin.native.concurrent.SharedImmutable
|
import kotlin.native.concurrent.SharedImmutable
|
||||||
import kotlin.native.concurrent.ThreadLocal
|
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
|
@ThreadLocal
|
||||||
val json by lazy { Json {
|
val json by lazy { Json {
|
||||||
isLenient = true
|
isLenient = true
|
||||||
|
@ -18,7 +18,6 @@ package com.shabinder.common.di.youtubeMp3
|
|||||||
|
|
||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import com.shabinder.common.di.gaana.corsApi
|
import com.shabinder.common.di.gaana.corsApi
|
||||||
import com.shabinder.common.di.utils.postData
|
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.request.forms.FormDataContent
|
import io.ktor.client.request.forms.FormDataContent
|
||||||
import io.ktor.client.request.post
|
import io.ktor.client.request.post
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
package com.shabinder.common.di
|
package com.shabinder.common.di
|
||||||
|
|
||||||
import com.shabinder.common.di.providers.YoutubeMp3
|
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.di.utils.ParallelExecutor
|
||||||
import com.shabinder.common.models.AllPlatforms
|
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 io.github.shabinder.YoutubeDownloader
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
@ -75,7 +75,7 @@ suspend fun downloadTrack(
|
|||||||
youtubeMp3: YoutubeMp3
|
youtubeMp3: YoutubeMp3
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
val link = youtubeMp3.getMp3DownloadLink(videoID) ?: ytDownloader.getVideo(videoID).getData()?.url
|
val link = youtubeMp3.getMp3DownloadLink(videoID) ?: ytDownloader.getVideo(videoID).get()?.url
|
||||||
|
|
||||||
if (link == null) {
|
if (link == null) {
|
||||||
DownloadProgressFlow.emit(
|
DownloadProgressFlow.emit(
|
||||||
|
@ -63,7 +63,7 @@ suspend fun downloadTrack(
|
|||||||
|
|
||||||
fetcher.dir.logger.i { "LINK: $videoID -> $link" }
|
fetcher.dir.logger.i { "LINK: $videoID -> $link" }
|
||||||
if (link == null) {
|
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" }
|
fetcher.dir.logger.i { "LINK: $videoID -> $link" }
|
||||||
downloadFile(link).collect {
|
downloadFile(link).collect {
|
||||||
|
@ -135,5 +135,14 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
|
|||||||
isInternetAccessible()
|
isInternetAccessible()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
override val analytics = object: SpotiFlyerRoot.Analytics {
|
||||||
|
override fun appLaunchEvent() {}
|
||||||
|
|
||||||
|
override fun homeScreenVisit() {}
|
||||||
|
|
||||||
|
override fun listScreenVisit() {}
|
||||||
|
|
||||||
|
override fun donationDialogVisit() {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -28,19 +28,31 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("stdlib-js"))
|
implementation(kotlin("stdlib-js"))
|
||||||
implementation(Decompose.decompose)
|
|
||||||
implementation(Koin.core)
|
implementation(Koin.core)
|
||||||
implementation(Ktor.clientJs)
|
implementation(Extras.kermit)
|
||||||
|
implementation(Decompose.decompose)
|
||||||
implementation(MVIKotlin.mvikotlin)
|
implementation(MVIKotlin.mvikotlin)
|
||||||
implementation(MVIKotlin.coroutines)
|
implementation(MVIKotlin.coroutines)
|
||||||
implementation(MVIKotlin.mvikotlinMain)
|
implementation(MVIKotlin.mvikotlinMain)
|
||||||
implementation(MVIKotlin.mvikotlinLogging)
|
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:root"))
|
||||||
implementation(project(":common:main"))
|
implementation(project(":common:main"))
|
||||||
implementation(project(":common:list"))
|
implementation(project(":common:list"))
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:dependency-injection"))
|
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: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-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")
|
implementation("org.jetbrains:kotlin-styled:1.0.0-pre.115-kotlin-1.4.10")
|
||||||
|
@ -23,13 +23,11 @@ import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
|
|||||||
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
||||||
import com.shabinder.common.di.DownloadProgressFlow
|
import com.shabinder.common.di.DownloadProgressFlow
|
||||||
import com.shabinder.common.models.Actions
|
import com.shabinder.common.models.Actions
|
||||||
import com.shabinder.common.models.AllPlatforms
|
|
||||||
import com.shabinder.common.models.PlatformActions
|
import com.shabinder.common.models.PlatformActions
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
import extras.renderableChild
|
import extras.renderableChild
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import react.RBuilder
|
import react.RBuilder
|
||||||
import react.RComponent
|
import react.RComponent
|
||||||
import react.RProps
|
import react.RProps
|
||||||
@ -82,6 +80,23 @@ class App(props: AppProps): RComponent<AppProps, RState>(props) {
|
|||||||
|
|
||||||
override val isInternetAvailable: Boolean = true
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,9 +21,6 @@ import com.shabinder.common.di.initKoin
|
|||||||
import react.dom.render
|
import react.dom.render
|
||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import kotlinx.browser.window
|
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.KoinComponent
|
||||||
import org.koin.core.component.get
|
import org.koin.core.component.get
|
||||||
|
|
||||||
@ -38,7 +35,6 @@ fun main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object AppDependencies : KoinComponent {
|
object AppDependencies : KoinComponent {
|
||||||
val appScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
val logger: Kermit
|
val logger: Kermit
|
||||||
val directories: Dir
|
val directories: Dir
|
||||||
val fetchPlatformQueryResult: FetchPlatformQueryResult
|
val fetchPlatformQueryResult: FetchPlatformQueryResult
|
||||||
@ -47,8 +43,5 @@ object AppDependencies : KoinComponent {
|
|||||||
directories = get()
|
directories = get()
|
||||||
logger = get()
|
logger = get()
|
||||||
fetchPlatformQueryResult = get()
|
fetchPlatformQueryResult = get()
|
||||||
appScope.launch {
|
|
||||||
//fetchPlatformQueryResult.spotifyProvider.authenticateSpotifyClient(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package extras
|
package extras
|
||||||
|
|
||||||
|
import com.arkivanov.decompose.value.Value
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
@ -36,7 +37,7 @@ abstract class RenderableComponent<
|
|||||||
initialState: S
|
initialState: S
|
||||||
) : RComponent<RenderableComponent.Props<T>, RenderableComponent.State<S>>(props) {
|
) : 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 val model: T get() = props.model
|
||||||
protected var scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
protected var scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ abstract class RenderableComponent<
|
|||||||
if(!scope.isActive)
|
if(!scope.isActive)
|
||||||
scope = CoroutineScope(Dispatchers.Default)
|
scope = CoroutineScope(Dispatchers.Default)
|
||||||
scope.launch {
|
scope.launch {
|
||||||
stateFlow.collect {
|
stateFlow.subscribe {
|
||||||
setState { data = it }
|
setState { data = it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package home
|
package home
|
||||||
|
|
||||||
|
import com.arkivanov.decompose.value.Value
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.main.SpotiFlyerMain.State
|
import com.shabinder.common.main.SpotiFlyerMain.State
|
||||||
import extras.RenderableComponent
|
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() {
|
override fun RBuilder.render() {
|
||||||
styledDiv{
|
styledDiv{
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package list
|
package list
|
||||||
|
|
||||||
|
import com.arkivanov.decompose.value.Value
|
||||||
import com.shabinder.common.list.SpotiFlyerList
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
import com.shabinder.common.list.SpotiFlyerList.State
|
import com.shabinder.common.list.SpotiFlyerList.State
|
||||||
import extras.RenderableComponent
|
import extras.RenderableComponent
|
||||||
@ -39,7 +40,7 @@ class ListScreen(
|
|||||||
props: Props<SpotiFlyerList>,
|
props: Props<SpotiFlyerList>,
|
||||||
) : RenderableComponent<SpotiFlyerList, State>(props,initialState = State()) {
|
) : 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() {
|
override fun RBuilder.render() {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user