diff --git a/buildSrc/deps.versions.toml b/buildSrc/deps.versions.toml index ff4d0278..a3507216 100644 --- a/buildSrc/deps.versions.toml +++ b/buildSrc/deps.versions.toml @@ -7,7 +7,7 @@ koin = "3.1.2" kermit = "0.1.9" mokoParcelize = "0.7.1" ktor = "1.6.3" -kotlinxSerialization = "1.2.2" +kotlinxSerialization = "1.3.0" sqlDelight = "1.5.1" sqliteJdbcDriver = "3.34.0" slf4j = "1.7.31" diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Badges.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Badges.kt new file mode 100644 index 00000000..52d0f77d --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Badges.kt @@ -0,0 +1,13 @@ +package com.shabinder.common.models.soundcloud + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class Badges( + val pro: Boolean = false, + @SerialName("pro_unlimited") + val proUnlimited: Boolean = false, + val verified: Boolean = false +) \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/CreatorSubscription.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/CreatorSubscription.kt new file mode 100644 index 00000000..2c0fd45a --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/CreatorSubscription.kt @@ -0,0 +1,10 @@ +package com.shabinder.common.models.soundcloud + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class CreatorSubscription( + val product: Product = Product() +) \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Format.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Format.kt new file mode 100644 index 00000000..13b054ac --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Format.kt @@ -0,0 +1,14 @@ +package com.shabinder.common.models.soundcloud + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class Format( + @SerialName("mime_type") + val mimeType: String = "", + val protocol: String = "" +) { + val isProgressive get() = protocol == "progressive" +} \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Media.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Media.kt new file mode 100644 index 00000000..66ba4707 --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Media.kt @@ -0,0 +1,9 @@ +package com.shabinder.common.models.soundcloud + + +import kotlinx.serialization.Serializable + +@Serializable +data class Media( + val transcodings: List = emptyList() +) \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Product.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Product.kt new file mode 100644 index 00000000..b82c5cf9 --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Product.kt @@ -0,0 +1,10 @@ +package com.shabinder.common.models.soundcloud + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class Product( + val id: String = "" +) \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/PublisherMetadata.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/PublisherMetadata.kt new file mode 100644 index 00000000..d4b51b8a --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/PublisherMetadata.kt @@ -0,0 +1,24 @@ +package com.shabinder.common.models.soundcloud + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class PublisherMetadata( + @SerialName("album_title") + val albumTitle: String = "", + val artist: String = "", + @SerialName("contains_music") + val containsMusic: Boolean = false, + val id: Int = 0, + val isrc: String = "", + val publisher: String = "", + @SerialName("release_title") + val releaseTitle: String = "", + @SerialName("upc_or_ean") + val upcOrEan: String = "", + val urn: String = "", + @SerialName("writer_composer") + val writerComposer: String = "" +) \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/SoundCloudTrack.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/SoundCloudTrack.kt new file mode 100644 index 00000000..f660e4dd --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/SoundCloudTrack.kt @@ -0,0 +1,95 @@ +package com.shabinder.common.models.soundcloud + + +import com.shabinder.common.models.AudioFormat +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class SoundCloudTrack( + @SerialName("artwork_url") + val artworkUrl: String = "", + //val caption: Any = Any(), + @SerialName("comment_count") + val commentCount: Int = 0, + val commentable: Boolean = false, + @SerialName("created_at") + val createdAt: String = "", //2015-05-21T16:36:39Z + val description: String = "", + @SerialName("display_date") + val displayDate: String = "", + @SerialName("download_count") + val downloadCount: Int = 0, + val downloadable: Boolean = false, + val duration: Int = 0, //290116 + @SerialName("embeddable_by") + val embeddableBy: String = "", + @SerialName("full_duration") + val fullDuration: Int = 0, + val genre: String = "", + @SerialName("has_downloads_left") + val hasDownloadsLeft: Boolean = false, + val id: Int = 0, + val kind: String = "", + @SerialName("label_name") + val labelName: String = "", + @SerialName("last_modified") + val lastModified: String = "", + val license: String = "", + @SerialName("likes_count") + val likesCount: Int = 0, + val media: Media = Media(), // Important Data + @SerialName("monetization_model") + val monetizationModel: String = "", + val permalink: String = "", + @SerialName("permalink_url") + val permalinkUrl: String = "", + @SerialName("playback_count") + val playbackCount: Int = 0, + val policy: String = "", + val `public`: Boolean = false, + @SerialName("publisher_metadata") + val publisherMetadata: PublisherMetadata = PublisherMetadata(), + //@SerialName("purchase_title") + //val purchaseTitle: Any = Any(), + @SerialName("purchase_url") + val purchaseUrl: String = "", //"http://itunes.apple.com/us/album/sunrise-ep/id993328519" + @SerialName("release_date") + val releaseDate: String = "", + @SerialName("reposts_count") + val repostsCount: Int = 0, + //@SerialName("secret_token") + //val secretToken: Any = Any(), + val sharing: String = "", + val state: String = "", + @SerialName("station_permalink") + val stationPermalink: String = "", + @SerialName("station_urn") + val stationUrn: String = "", + val streamable: Boolean = false, + @SerialName("tag_list") + val tagList: String = "", + val title: String = "", + @SerialName("track_authorization") + val trackAuthorization: String = "", + @SerialName("track_format") + val trackFormat: String = "", + val uri: String = "", + val urn: String = "", + val user: User = User(), + @SerialName("user_id") + val userId: Int = 0, + //val visuals: Any = Any(), + @SerialName("waveform_url") + val waveformUrl: String = "" +) { + fun getDownloadableLink(): Pair? { + return (media.transcodings.firstOrNull { + it.quality == "hq" && (it.format.isProgressive || it.url.contains("progressive")) + } ?: media.transcodings.firstOrNull { + it.quality == "sq" && (it.format.isProgressive || it.url.contains("progressive")) + })?.let { + it.url to it.audioFormat + } + } +} \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Track.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Track.kt new file mode 100644 index 00000000..32237bad --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Track.kt @@ -0,0 +1,84 @@ +package com.shabinder.common.models.soundcloud + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class Track( + @SerialName("artwork_url") + val artworkUrl: String = "", + val caption: String = "", + @SerialName("comment_count") + val commentCount: Int = 0, + val commentable: Boolean = false, + @SerialName("created_at") + val createdAt: String = "", + val description: String = "", + @SerialName("display_date") + val displayDate: String = "", + @SerialName("download_count") + val downloadCount: Int = 0, + val downloadable: Boolean = false, + val duration: Int = 0, + @SerialName("embeddable_by") + val embeddableBy: String = "", + @SerialName("full_duration") + val fullDuration: Int = 0, + val genre: String = "", + @SerialName("has_downloads_left") + val hasDownloadsLeft: Boolean = false, + val id: Int = 0, + val kind: String = "", + @SerialName("label_name") + val labelName: String = "", + @SerialName("last_modified") + val lastModified: String = "", + val license: String = "", + @SerialName("likes_count") + val likesCount: Int = 0, + val media: Media = Media(), + @SerialName("monetization_model") + val monetizationModel: String = "", + val permalink: String = "", + @SerialName("permalink_url") + val permalinkUrl: String = "", + @SerialName("playback_count") + val playbackCount: Int = 0, + val policy: String = "", + val `public`: Boolean = false, + @SerialName("publisher_metadata") + val publisherMetadata: PublisherMetadata = PublisherMetadata(), + @SerialName("purchase_title") + val purchaseTitle:String = "", + @SerialName("purchase_url") + val purchaseUrl: String = "", + @SerialName("release_date") + val releaseDate: String = "", + @SerialName("reposts_count") + val repostsCount: Int = 0, + @SerialName("secret_token") + val secretToken: String = "", + val sharing: String = "", + val state: String = "", + @SerialName("station_permalink") + val stationPermalink: String = "", + @SerialName("station_urn") + val stationUrn: String = "", + val streamable: Boolean = false, + @SerialName("tag_list") + val tagList: String = "", + val title: String = "", + @SerialName("track_authorization") + val trackAuthorization: String = "", + @SerialName("track_format") + val trackFormat: String = "", + val uri: String = "", + val urn: String = "", + val user: User = User(), + @SerialName("user_id") + val userId: Int = 0, + val visuals: String = "", + @SerialName("waveform_url") + val waveformUrl: String = "" +) \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Transcoding.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Transcoding.kt new file mode 100644 index 00000000..0bd313de --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Transcoding.kt @@ -0,0 +1,22 @@ +package com.shabinder.common.models.soundcloud + + +import com.shabinder.common.models.AudioFormat +import kotlinx.serialization.Serializable + +@Serializable +data class Transcoding( + val duration: Int = 0, + val format: Format = Format(), + val preset: String = "", + val quality: String = "", //sq == 128kbps //hq == 256kbps + val snipped: Boolean = false, + val url: String = "" +) { + val audioFormat: AudioFormat = when { + preset.contains("mp3") -> AudioFormat.MP3 + preset.contains("aac") || preset.contains("m4a") -> AudioFormat.MP4 + preset.contains("flac") -> AudioFormat.FLAC + else -> AudioFormat.UNKNOWN + } +} \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/User.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/User.kt new file mode 100644 index 00000000..4ea07493 --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/User.kt @@ -0,0 +1,38 @@ +package com.shabinder.common.models.soundcloud + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class User( + @SerialName("avatar_url") + val avatarUrl: String = "", + val badges: Badges = Badges(), + val city: String = "", + @SerialName("country_code") + val countryCode: String = "", + @SerialName("first_name") + val firstName: String = "", + @SerialName("followers_count") + val followersCount: Int = 0, + @SerialName("full_name") + val fullName: String = "", + val id: Int = 0, + val kind: String = "", + @SerialName("last_modified") + val lastModified: String = "", + @SerialName("last_name") + val lastName: String = "", + val permalink: String = "", + @SerialName("permalink_url") + val permalinkUrl: String = "", + @SerialName("station_permalink") + val stationPermalink: String = "", + @SerialName("station_urn") + val stationUrn: String = "", + val uri: String = "", + val urn: String = "", + val username: String = "", + val verified: Boolean = false +) \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Visual.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Visual.kt new file mode 100644 index 00000000..9e28aede --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Visual.kt @@ -0,0 +1,14 @@ +package com.shabinder.common.models.soundcloud + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class Visual( + @SerialName("entry_time") + val entryTime: Int = 0, + val urn: String = "", + @SerialName("visual_url") + val visualUrl: String = "" +) \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Visuals.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Visuals.kt new file mode 100644 index 00000000..57ef84d2 --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/Visuals.kt @@ -0,0 +1,13 @@ +package com.shabinder.common.models.soundcloud + + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class Visuals( + val enabled: Boolean = false, + //val tracking: Any = Any(), + val urn: String = "", + val visuals: List = listOf() +) \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/resolvemodel/SoundCloudResolveResponseBase.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/resolvemodel/SoundCloudResolveResponseBase.kt new file mode 100644 index 00000000..583f96fc --- /dev/null +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/soundcloud/resolvemodel/SoundCloudResolveResponseBase.kt @@ -0,0 +1,159 @@ +package com.shabinder.common.models.soundcloud.resolvemodel + +import com.shabinder.common.models.soundcloud.Media +import com.shabinder.common.models.soundcloud.PublisherMetadata +import com.shabinder.common.models.soundcloud.Track +import com.shabinder.common.models.soundcloud.User +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.json.JsonClassDiscriminator +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic + +@Serializable +@JsonClassDiscriminator("kind") +sealed class SoundCloudResolveResponseBase { + abstract val kind: String + + @SerialName("playlist") + @Serializable + data class SoundCloudResolveResponsePlaylist( + @SerialName("artwork_url") + val artworkUrl: String = "", + @SerialName("calculated_artwork_url") + val calculatedArtworkUrl: String = "", //t500x500, t120x120 // "https://i1.sndcdn.com/artworks-pjsabv9w0EXW3lBJ-nvjDYg-large.jpg" // https://i1.sndcdn.com/artworks-pjsabv9w0EXW3lBJ-nvjDYg-t500x500.jpg + @SerialName("created_at") + val createdAt: String = "", + val description: String = "", + @SerialName("display_date") + val displayDate: String = "", + val duration: Int = 0, + override val kind: String = "", + @SerialName("embeddable_by") + val embeddableBy: String = "", + val genre: String = "", + val id: Int = 0, + @SerialName("is_album") + val isAlbum: Boolean = false, + @SerialName("label_name") + val labelName: String = "", + @SerialName("last_modified") + val lastModified: String = "", + val license: String = "", + @SerialName("likes_count") + val likesCount: Int = 0, + @SerialName("managed_by_feeds") + val managedByFeeds: Boolean = false, + val permalink: String = "", + @SerialName("permalink_url") + val permalinkUrl: String = "", + val `public`: Boolean = false, + @SerialName("published_at") + val publishedAt: String = "", + @SerialName("purchase_title") + val purchaseTitle: String = "", + @SerialName("purchase_url") + val purchaseUrl: String = "", + @SerialName("release_date") + val releaseDate: String = "", + @SerialName("reposts_count") + val repostsCount: Int = 0, + @SerialName("secret_token") + val secretToken: String = "", + @SerialName("set_type") + val setType: String = "", + val sharing: String = "", + @SerialName("tag_list") + val tagList: String = "", + val title: String = "", //"Top 50: Hip-hop & Rap" + @SerialName("track_count") + val trackCount: Int = 0, + val tracks: List = emptyList(), + val uri: String = "", + val user: User = User(), + @SerialName("user_id") + val userId: Int = 0 + ) : SoundCloudResolveResponseBase() + + + @SerialName("track") + @Serializable + data class SoundCloudResolveResponseTrack( + @SerialName("artwork_url") + val artworkUrl: String = "", + val caption: String = "", + @SerialName("comment_count") + val commentCount: Int = 0, + val commentable: Boolean = false, + @SerialName("created_at") + val createdAt: String = "", + val description: String = "", + @SerialName("display_date") + val displayDate: String = "", + @SerialName("download_count") + val downloadCount: Int = 0, + val downloadable: Boolean = false, + val duration: Int = 0, + @SerialName("embeddable_by") + val embeddableBy: String = "", + @SerialName("full_duration") + val fullDuration: Int = 0, + val genre: String = "", + @SerialName("has_downloads_left") + val hasDownloadsLeft: Boolean = false, + val id: Int = 0, + override val kind: String = "", + @SerialName("label_name") + val labelName: String = "", + @SerialName("last_modified") + val lastModified: String = "", + val license: String = "", + @SerialName("likes_count") + val likesCount: Int = 0, + val media: Media = Media(), + @SerialName("monetization_model") + val monetizationModel: String = "", + val permalink: String = "", + @SerialName("permalink_url") + val permalinkUrl: String = "", + @SerialName("playback_count") + val playbackCount: Int = 0, + val policy: String = "", + val `public`: Boolean = false, + @SerialName("publisher_metadata") + val publisherMetadata: PublisherMetadata = PublisherMetadata(), + @SerialName("purchase_title") + val purchaseTitle: String = "", + @SerialName("purchase_url") + val purchaseUrl: String = "", + @SerialName("release_date") + val releaseDate: String = "", + @SerialName("reposts_count") + val repostsCount: Int = 0, + @SerialName("secret_token") + val secretToken: String = "", + val sharing: String = "", + val state: String = "", + @SerialName("station_permalink") + val stationPermalink: String = "", + @SerialName("station_urn") + val stationUrn: String = "", + val streamable: Boolean = false, + @SerialName("tag_list") + val tagList: String = "", + val title: String = "", + @SerialName("track_authorization") + val trackAuthorization: String = "", + @SerialName("track_format") + val trackFormat: String = "", + val uri: String = "", + val urn: String = "", + val user: User = User(), + @SerialName("user_id") + val userId: Int = 0, + val visuals: String = "", + @SerialName("waveform_url") + val waveformUrl: String = "" + ) : SoundCloudResolveResponseBase() +} \ No newline at end of file diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/utils/Utils.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/utils/Utils.kt index 1bbad05f..cb685184 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/utils/Utils.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/utils/Utils.kt @@ -8,6 +8,7 @@ val globalJson by lazy { Json { isLenient = true ignoreUnknownKeys = true + coerceInputValues = true } } 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 index 91c050ff..615b7f4e 100644 --- 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 @@ -2,12 +2,15 @@ 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 com.shabinder.common.models.soundcloud.resolvemodel.SoundCloudResolveResponseBase +import com.shabinder.common.models.soundcloud.resolvemodel.SoundCloudResolveResponseBase.SoundCloudResolveResponsePlaylist +import com.shabinder.common.models.soundcloud.resolvemodel.SoundCloudResolveResponseBase.SoundCloudResolveResponseTrack 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.InternalSerializationApi import kotlinx.serialization.json.JsonObject interface SoundCloudRequests { @@ -16,62 +19,58 @@ interface SoundCloudRequests { suspend fun parseURL(url: String) { - getItem(url).let { item: JsonObject -> - when (item.getString("kind")) { - "track" -> { - + getResponseObj(url).let { item -> + when (item) { + is SoundCloudResolveResponseTrack -> { + getTrack(item) } - "playlist" -> { - - } - "user" -> { - + is SoundCloudResolveResponsePlaylist -> { + } + else -> throw SpotiFlyerException.FeatureNotImplementedYet() } } } @Suppress("NAME_SHADOWING") - suspend fun getTrack(track: JsonObject): TrackDetails? { + suspend fun getTrack(track: SoundCloudResolveResponseTrack): 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.policy == "BLOCK") + throw SpotiFlyerException.GeoLocationBlocked(extraInfo = "Use VPN to access ${track.title}") - if (track.getBoolean("streamable") == false) - throw SpotiFlyerException.LinkInvalid("\nSound Cloud Reports that $title is not streamable !\n") + if (!track.streamable) + throw SpotiFlyerException.LinkInvalid("\nSound Cloud Reports that ${track.title} is not streamable !\n") return null } - suspend fun getTrackInfo(track: JsonObject): JsonObject { - if (track.containsKey("media")) - return track + suspend fun getTrackInfo(res: SoundCloudResolveResponseTrack): SoundCloudResolveResponseTrack { + if (res.media.transcodings.isNotEmpty()) + return res - val infoURL = URLS.TRACK_INFO.buildURL(track.getString("id").requireNotNull()) + val infoURL = URLS.TRACK_INFO.buildURL(res.id.toString()) return httpClient.get(infoURL) { parameter("client_id", CLIENT_ID) } } - - suspend fun getItem(url: String, clientID: String = CLIENT_ID): JsonObject { + suspend fun getResponseObj(url: String, clientID: String = CLIENT_ID): SoundCloudResolveResponseBase { val itemURL = URLS.RESOLVE.buildURL(url) - val resp: JsonObject = try { + val resp: SoundCloudResolveResponseBase = try { httpClient.get(itemURL) { parameter("client_id", clientID) } } catch (e: ClientRequestException) { if (clientID != ALT_CLIENT_ID) - return getItem(url, ALT_CLIENT_ID) + return getResponseObj(url, ALT_CLIENT_ID) throw e } - val tracksPresent = resp.getString("kind").equals("playlist") && resp.containsKey("tracks") + val tracksPresent = (resp is SoundCloudResolveResponsePlaylist && resp.tracks.isNotEmpty()) if (!tracksPresent && clientID != ALT_CLIENT_ID) - return getItem(ALT_CLIENT_ID) + return getResponseObj(ALT_CLIENT_ID) return resp } @@ -90,7 +89,25 @@ interface SoundCloudRequests { ME({ "https://api-v2.soundcloud.com/me?oauth_token=$it" }), } - private const val CLIENT_ID = "a3e059563d7fd3372b49b37f00a00bcf" - private const val ALT_CLIENT_ID = "2t9loNQH90kzJcsFCODdigxfp325aq4z" + const val CLIENT_ID = "a3e059563d7fd3372b49b37f00a00bcf" + const val ALT_CLIENT_ID = "2t9loNQH90kzJcsFCODdigxfp325aq4z" + } +} + +@OptIn(InternalSerializationApi::class) +suspend inline fun SoundCloudRequests.doAuthenticatedRequest(url: String): T { + var clientID: String = SoundCloudRequests.CLIENT_ID + return try { + httpClient.get(url) { + parameter("client_id", clientID) + } + } catch (e: ClientRequestException) { + if (clientID != SoundCloudRequests.ALT_CLIENT_ID) { + clientID = SoundCloudRequests.ALT_CLIENT_ID + return httpClient.get(url) { + parameter("client_id", clientID) + } + } + throw e } } \ No newline at end of file diff --git a/common/providers/src/commonTest/kotlin/com/shabinder/common/providers/TestSpotifyTrackMatching.kt b/common/providers/src/commonTest/kotlin/com/shabinder/common/providers/TestSpotifyTrackMatching.kt index 8767770a..aa55719d 100644 --- a/common/providers/src/commonTest/kotlin/com/shabinder/common/providers/TestSpotifyTrackMatching.kt +++ b/common/providers/src/commonTest/kotlin/com/shabinder/common/providers/TestSpotifyTrackMatching.kt @@ -1,10 +1,15 @@ package com.shabinder.common.providers import com.shabinder.common.models.TrackDetails +import com.shabinder.common.models.soundcloud.SoundCloudTrack +import com.shabinder.common.models.soundcloud.resolvemodel.SoundCloudResolveResponseBase import com.shabinder.common.providers.utils.CommonUtils import com.shabinder.common.providers.utils.SpotifyUtils import com.shabinder.common.providers.utils.SpotifyUtils.toTrackDetailsList +import com.shabinder.common.utils.globalJson import io.github.shabinder.runBlocking +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.serializer import kotlin.test.Test class TestSpotifyTrackMatching { @@ -17,7 +22,19 @@ class TestSpotifyTrackMatching { private val spotifyToken: String? get() = null -// get() = "BQB41HqrLcrh5eRYaL97GvaH6tRe-1EktQ8VGTWUQuFnYVWBEoTcF7T_8ogqVn1GHl9HCcMiQ0HBT-ybC74" + + // get() = "BQB41HqrLcrh5eRYaL97GvaH6tRe-1EktQ8VGTWUQuFnYVWBEoTcF7T_8ogqVn1GHl9HCcMiQ0HBT-ybC74" + @OptIn(InternalSerializationApi::class) + @Test + fun testRandomThing() = runBlocking { + globalJson.decodeFromString(SoundCloudResolveResponseBase.serializer(), """{"artwork_url":null,"trackCount":12,"kind":"playlist"}""") + .also { + println(it) + println(it is SoundCloudResolveResponseBase.SoundCloudResolveResponsePlaylist) + println(it is SoundCloudResolveResponseBase.SoundCloudResolveResponseTrack) + } + } + @Test fun matchVideo() = runBlocking {