Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.base64Encode
import com.lagradost.cloudstream3.splitUrlParameters
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.APP_STRING
import com.lagradost.cloudstream3.utils.AppContextUtils.splitQuery
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
import java.net.URI
import java.security.SecureRandom

data class AuthLoginPage(
Expand Down Expand Up @@ -172,10 +171,8 @@ abstract class AuthAPI {
get() = unixTimeMS

fun splitRedirectUrl(redirectUrl: String): Map<String, String> {
return splitQuery(
URI(
redirectUrl.replace(APP_STRING, "https").replace("/#", "?")
).toURL()
return splitUrlParameters(
redirectUrl.replace(APP_STRING, "https").replace("/#", "?")
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,9 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import okhttp3.Cache
import java.io.File
import java.net.URL
import java.net.URLDecoder
import java.util.concurrent.Executor
import java.util.concurrent.Executors


object AppContextUtils {
fun RecyclerView.isRecyclerScrollable(): Boolean {
val layoutManager =
Expand Down Expand Up @@ -630,16 +627,17 @@ object AppContextUtils {
}
}

fun splitQuery(url: URL): Map<String, String> {
val queryPairs: MutableMap<String, String> = LinkedHashMap()
val query: String = url.query
val pairs = query.split("&").toTypedArray()
for (pair in pairs) {
val idx = pair.indexOf("=")
queryPairs[URLDecoder.decode(pair.substring(0, idx), "UTF-8")] =
URLDecoder.decode(pair.substring(idx + 1), "UTF-8")
}
return queryPairs
// Deprecate after next stable
/* @Deprecated(
message = "Use splitUrlParameters instead.",
replaceWith = ReplaceWith(
expression = "splitUrlParameters(url.toString())",
imports = ["com.lagradost.cloudstream3.splitUrlParameters"],
),
level = DeprecationLevel.WARNING,
) */
fun splitQuery(url: java.net.URL): Map<String, String> {
return com.lagradost.cloudstream3.splitUrlParameters(url.toString())
}

/**| S1:E2 Hello World
Expand Down Expand Up @@ -896,4 +894,4 @@ object AppContextUtils {
} else null
return currentAudioFocusRequest
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -758,18 +758,64 @@ fun MainAPI.fixUrl(url: String): String {
}
}

/** Sort the urls based on quality
/**
* Sort the urls based on quality
*
* @param urls Set of [ExtractorLink]
* */
*/
fun sortUrls(urls: Set<ExtractorLink>): List<ExtractorLink> {
return urls.sortedBy { t -> -t.quality }
}

/** Capitalize the first letter of string.
/**
* Splits the parameters of a [Url] into a map of key-value pairs.
*
* Unlike a manual `split("&")` / `split("=")` implementation, this relies on Ktor's
* built-in parameters parser ([Url.parameters]), which already handles URL-decoding,
* malformed pairs, and parameters without a value.
*
* Note: if a parameter key appears multiple times (e.g. `?a=1&a=2`), only the **first**
* value is kept, since the return type is `Map<String, String>`. Use [Url.parameters]
* directly if you need all values for repeated keys.
*
* @param url the [Url] whose parameters should be extracted.
* @return a map of decoded parameter names to their first decoded value.
*
* @sample
* splitUrlParameters(Url("https://example.com/path?foo=bar&baz=qux"))
* // returns {"foo": "bar", "baz": "qux"}
*/
@Prerelease
fun splitUrlParameters(url: Url): Map<String, String> {
return url.parameters.entries().associate { (key, values) -> key to values.firstOrNull().orEmpty() }
}

/**
* Splits the parameters of a raw URL [String] into a map of key-value pairs.
*
* Convenience overload for callers that have a URL as plain text rather than a parsed
* [Url] instance. Internally parses [url] with Ktor's [Url] constructor and delegates
* to [splitUrlParameters].
*
* @param url the URL string whose parameters should be extracted.
* @return a map of decoded parameter names to their first decoded value.
*
* @sample
* splitUrlParameters("https://example.com/path?foo=bar&baz=qux")
* // returns {"foo": "bar", "baz": "qux"}
*/
@Prerelease
fun splitUrlParameters(url: String): Map<String, String> {
return splitUrlParameters(Url(url))
}

/**
* Capitalize the first letter of string.
*
* @param str String to be capitalized
* @return non-nullable String
* @see capitalizeStringNullable
* */
*/
fun capitalizeString(str: String): String {
return capitalizeStringNullable(str) ?: str
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.lagradost.cloudstream3

import io.ktor.http.Url
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class SplitUrlParametersTest {

@Test
fun splitsBasicParameters() {
val url = Url("https://example.com/path?foo=bar&baz=qux")
val result = splitUrlParameters(url)
assertEquals(mapOf("foo" to "bar", "baz" to "qux"), result)
}

@Test
fun decodesUrlEncodedKeysAndValues() {
val url = Url("https://example.com/path?na%20me=hello%20world&sp%26ec=a%2Bb")
val result = splitUrlParameters(url)
assertEquals(mapOf("na me" to "hello world", "sp&ec" to "a+b"), result)
}

@Test
fun returnsEmptyMapWhenThereAreNoParameters() {
val url = Url("https://example.com/path")
val result = splitUrlParameters(url)
assertTrue(result.isEmpty())
}

@Test
fun keepsOnlyFirstValueForRepeatedKeys() {
val url = Url("https://example.com/path?a=1&a=2&a=3")
val result = splitUrlParameters(url)
assertEquals(mapOf("a" to "1"), result)
}

@Test
fun handlesParameterWithNoValue() {
val url = Url("https://example.com/path?flag&foo=bar")
val result = splitUrlParameters(url)
assertEquals("bar", result["foo"])
assertEquals("", result["flag"])
}

@Test
fun stringOverloadSplitsBasicParameters() {
val result = splitUrlParameters("https://example.com/path?foo=bar&baz=qux")
assertEquals(mapOf("foo" to "bar", "baz" to "qux"), result)
}

@Test
fun stringOverloadDecodesUrlEncodedKeysAndValues() {
val result = splitUrlParameters("https://example.com/path?na%20me=hello%20world&sp%26ec=a%2Bb")
assertEquals(mapOf("na me" to "hello world", "sp&ec" to "a+b"), result)
}

@Test
fun stringOverloadReturnsEmptyMapWhenThereAreNoParameters() {
val result = splitUrlParameters("https://example.com/path")
assertTrue(result.isEmpty())
}

@Test
fun stringOverloadKeepsOnlyFirstValueForRepeatedKeys() {
val result = splitUrlParameters("https://example.com/path?a=1&a=2&a=3")
assertEquals(mapOf("a" to "1"), result)
}

@Test
fun stringOverloadHandlesParameterWithNoValue() {
val result = splitUrlParameters("https://example.com/path?flag&foo=bar")
assertEquals("bar", result["foo"])
assertEquals("", result["flag"])
}
}