diff --git a/art/SpotiFlyer.png b/art/SpotiFlyer.png new file mode 100644 index 00000000..21c6153b Binary files /dev/null and b/art/SpotiFlyer.png differ diff --git a/common/data-models/build.gradle.kts b/common/data-models/build.gradle.kts index 9b5d0f70..d5f215cb 100644 --- a/common/data-models/build.gradle.kts +++ b/common/data-models/build.gradle.kts @@ -50,9 +50,9 @@ kotlin { } commonMain { dependencies { - implementation("co.touchlab:stately-concurrency:$statelyVersion") - implementation("co.touchlab:stately-isolate:$statelyIsoVersion") - implementation("co.touchlab:stately-iso-collections:$statelyIsoVersion") + api("co.touchlab:stately-concurrency:$statelyVersion") + api("co.touchlab:stately-isolate:$statelyIsoVersion") + api("co.touchlab:stately-iso-collections:$statelyIsoVersion") implementation(Extras.youtubeDownloader) api(Internationalization.dep) } diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/SpotiFlyerException.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/SpotiFlyerException.kt index 960baf53..3415020d 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/SpotiFlyerException.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/SpotiFlyerException.kt @@ -12,6 +12,11 @@ sealed class SpotiFlyerException(override val message: String) : Exception(messa override val message: String = /*${Strings.mp3ConverterBusy()} */"CAUSE:$extraInfo" ) : SpotiFlyerException(message) + data class GeoLocationBlocked( + val extraInfo: String? = null, + override val message: String = "This Content is not Accessible from your Location, try using a VPN! \nCAUSE:$extraInfo" + ) : SpotiFlyerException(message) + data class UnknownReason( val exception: Throwable? = null, override val message: String = Strings.unknownError() diff --git a/common/providers/src/commonMain/kotlin/com.shabinder.common.providers/gaana/requests/GaanaRequests.kt b/common/providers/src/commonMain/kotlin/com.shabinder.common.providers/gaana/requests/GaanaRequests.kt index ab244d1e..933b06f4 100644 --- a/common/providers/src/commonMain/kotlin/com.shabinder.common.providers/gaana/requests/GaanaRequests.kt +++ b/common/providers/src/commonMain/kotlin/com.shabinder.common.providers/gaana/requests/GaanaRequests.kt @@ -25,11 +25,13 @@ import com.shabinder.common.models.gaana.GaanaSong import io.ktor.client.* import io.ktor.client.request.* -private const val TOKEN = "b2e6d7fbc136547a940516e9b77e5990" -private val BASE_URL get() = "${corsApi}https://api.gaana.com" - interface GaanaRequests { + companion object { + private const val TOKEN = "b2e6d7fbc136547a940516e9b77e5990" + private val BASE_URL get() = "${corsApi}https://api.gaana.com" + } + val httpClient: HttpClient /* diff --git a/common/providers/src/commonMain/kotlin/com.shabinder.common.providers/sound_cloud/SoundCloudProvider.kt b/common/providers/src/commonMain/kotlin/com.shabinder.common.providers/sound_cloud/SoundCloudProvider.kt new file mode 100644 index 00000000..2f551d17 --- /dev/null +++ b/common/providers/src/commonMain/kotlin/com.shabinder.common.providers/sound_cloud/SoundCloudProvider.kt @@ -0,0 +1,13 @@ +package com.shabinder.common.providers.sound_cloud + +import co.touchlab.kermit.Kermit +import com.shabinder.common.core_components.file_manager.FileManager + +class SoundCloudProvider( + private val logger: Kermit, + private val fileManager: FileManager, +) { + suspend fun query(fullURL: String) { + + } +} \ No newline at end of file diff --git a/common/providers/src/commonMain/kotlin/com.shabinder.common.providers/sound_cloud/requests/SoundCloudRequests.kt b/common/providers/src/commonMain/kotlin/com.shabinder.common.providers/sound_cloud/requests/SoundCloudRequests.kt new file mode 100644 index 00000000..238c782d --- /dev/null +++ b/common/providers/src/commonMain/kotlin/com.shabinder.common.providers/sound_cloud/requests/SoundCloudRequests.kt @@ -0,0 +1,96 @@ +package com.shabinder.common.providers.sound_cloud.requests + +import com.shabinder.common.models.SpotiFlyerException +import com.shabinder.common.models.TrackDetails +import com.shabinder.common.utils.requireNotNull +import io.github.shabinder.utils.getBoolean +import io.github.shabinder.utils.getString +import io.ktor.client.* +import io.ktor.client.features.* +import io.ktor.client.request.* +import kotlinx.serialization.json.JsonObject + +interface SoundCloudRequests { + + val httpClient: HttpClient + + + suspend fun parseURL(url: String) { + getItem(url).let { item: JsonObject -> + when (item.getString("kind")) { + "track" -> { + + } + "playlist" -> { + + } + "user" -> { + + } + } + } + } + + @Suppress("NAME_SHADOWING") + suspend fun getTrack(track: JsonObject): TrackDetails { + val track = getTrackInfo(track) + val title = track.getString("title") + + if (track.getString("policy") == "BLOCK") + throw SpotiFlyerException.GeoLocationBlocked(extraInfo = "Use VPN to access $title") + + if (track.getBoolean("streamable") == false) + throw SpotiFlyerException.LinkInvalid("\nSound Cloud Reports that $title is not streamable !\n") + + + } + + + suspend fun getTrackInfo(track: JsonObject): JsonObject { + if (track.containsKey("media")) + return track + + val infoURL = URLS.TRACK_INFO.buildURL(track.getString("id").requireNotNull()) + return httpClient.get(infoURL) { + parameter("client_id", CLIENT_ID) + } + } + + + suspend fun getItem(url: String, clientID: String = CLIENT_ID): JsonObject { + val itemURL = URLS.RESOLVE.buildURL(url) + val resp: JsonObject = try { + httpClient.get(itemURL) { + parameter("client_id", clientID) + } + } catch (e: ClientRequestException) { + if (clientID != ALT_CLIENT_ID) + return getItem(url, ALT_CLIENT_ID) + throw e + } + val tracksPresent = resp.getString("kind").equals("playlist") && resp.containsKey("tracks") + + if (!tracksPresent && clientID != ALT_CLIENT_ID) + return getItem(ALT_CLIENT_ID) + + return resp + } + + companion object { + private enum class URLS(val buildURL: (arg: String) -> String) { + RESOLVE({ "https://api-v2.soundcloud.com/resolve?url=$it}" }), + PLAYLIST_LIKED({ "https://api-v2.soundcloud.com/users/$it/playlists/liked_and_owned?limit=200" }), + FAVORITES({ "'https://api-v2.soundcloud.com/users/$it/track_likes?limit=200" }), + COMMENTED({ "https://api-v2.soundcloud.com/users/$it/comments" }), + TRACKS({ "https://api-v2.soundcloud.com/users/$it/tracks?limit=200" }), + ALL({ "https://api-v2.soundcloud.com/profile/soundcloud:users:$it?limit=200" }), + TRACK_INFO({ "https://api-v2.soundcloud.com/tracks/$it" }), + ORIGINAL_DOWNLOAD({ "https://api-v2.soundcloud.com/tracks/$it/download" }), + USER({ "https://api-v2.soundcloud.com/users/$it" }), + ME({ "https://api-v2.soundcloud.com/me?oauth_token=$it" }), + } + + private const val CLIENT_ID = "a3e059563d7fd3372b49b37f00a00bcf" + private const val ALT_CLIENT_ID = "2t9loNQH90kzJcsFCODdigxfp325aq4z" + } +} \ No newline at end of file