Skip to content

Commit

Permalink
Some small changes
Browse files Browse the repository at this point in the history
  • Loading branch information
LuftVerbot committed Jan 4, 2025
1 parent 19aacf5 commit ddc7aa3
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 78 deletions.
102 changes: 54 additions & 48 deletions ext/src/main/java/dev/brahmkshatriya/echo/extension/DeezerApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ class DeezerApi(private val session: DeezerSession) {
}
}

private val client: OkHttpClient get() = createOkHttpClient(useProxy = true)
private val clientLog: OkHttpClient get() = createOkHttpClient(useProxy = true , true)
private val clientNP: OkHttpClient get() = createOkHttpClient(useProxy = false)
private val client: OkHttpClient by lazy { createOkHttpClient(useProxy = true) }
private val clientLog: OkHttpClient by lazy { createOkHttpClient(useProxy = true , true) }
private val clientNP: OkHttpClient by lazy { createOkHttpClient(useProxy = false) }

private fun getHeaders(method: String? = ""): Headers {
return Headers.Builder().apply {
Expand All @@ -161,7 +161,11 @@ class DeezerApi(private val session: DeezerSession) {
}.build()
}

suspend fun callApi(method: String, params: JsonObject = buildJsonObject { }, gatewayInput: String? = ""): String = withContext(Dispatchers.IO) {
suspend fun callApi(
method: String,
params: JsonObject = buildJsonObject { },
gatewayInput: String? = ""
): String = withContext(Dispatchers.IO) {
val url = HttpUrl.Builder()
.scheme("https")
.host("www.deezer.com")
Expand Down Expand Up @@ -191,34 +195,32 @@ class DeezerApi(private val session: DeezerSession) {
}
.build()

val response = client.newCall(request).await()
val responseBody = response.body.string()
client.newCall(request).await().use { response ->
val responseBody = response.body.string()
if (!response.isSuccessful) throw Exception("API call failed with status ${response.code}: $responseBody")

if (!response.isSuccessful) {
throw Exception("API call failed with status code ${response.code}: $responseBody")
}

if (method == "deezer.getUserData") {
response.headers.forEach {
if (it.second.startsWith("sid=")) {
session.updateCredentials(sid = it.second.substringAfter("sid=").substringBefore(";"))
if (method == "deezer.getUserData") {
response.headers.forEach {
if (it.second.startsWith("sid=")) {
session.updateCredentials(sid = it.second.substringAfter("sid=").substringBefore(";"))
}
}
}
}

if (responseBody.contains("\"VALID_TOKEN_REQUIRED\":\"Invalid CSRF token\"")) {
if (email.isEmpty() && pass.isEmpty()) {
session.isArlExpired(true)
throw Exception("Please re-login (Best use User + Pass method)")
} else {
session.isArlExpired(false)
val userList = DeezerExtension().onLogin(email, pass)
DeezerExtension().onSetLoginUser(userList.first())
return@withContext callApi(method, params, gatewayInput)
if (responseBody.contains("\"VALID_TOKEN_REQUIRED\":\"Invalid CSRF token\"")) {
if (email.isEmpty() && pass.isEmpty()) {
session.isArlExpired(true)
throw Exception("Please re-login (Best use User + Pass method)")
} else {
session.isArlExpired(false)
val userList = DeezerExtension().onLogin(email, pass)
DeezerExtension().onSetLoginUser(userList.first())
return@withContext callApi(method, params, gatewayInput)
}
}
}

responseBody
responseBody
}
}

//<============= Login =============>
Expand Down Expand Up @@ -253,29 +255,33 @@ class DeezerApi(private val session: DeezerSession) {
}

suspend fun getArlByEmail(mail: String, password: String) {
// Get SID
getSid()

val clientId = "447462"
val clientSecret = "a83bf7f38ad2f137e444727cfc3775cf"
val md5Password = md5(password)

val params = mapOf(
"app_id" to clientId,
"login" to mail,
"password" to md5Password,
"hash" to md5(clientId + mail + md5Password + clientSecret)
)

// Get access token
val responseJson = getToken(params, sid)
val apiResponse = json.decodeFromString<JsonObject>(responseJson)
session.updateCredentials(token = apiResponse.jsonObject["access_token"]!!.jsonPrimitive.content)
try {
// Get SID
getSid()

val clientId = "447462"
val clientSecret = "a83bf7f38ad2f137e444727cfc3775cf"
val md5Password = md5(password)

val params = mapOf(
"app_id" to clientId,
"login" to mail,
"password" to md5Password,
"hash" to md5(clientId + mail + md5Password + clientSecret)
)

// Get ARL
val arlResponse = callApi("user.getArl")
val arlObject = json.decodeFromString<JsonObject>(arlResponse)
session.updateCredentials(arl = arlObject["results"]!!.jsonPrimitive.content)
// Get access token
val responseJson = getToken(params, sid)
val apiResponse = json.decodeFromString<JsonObject>(responseJson)
session.updateCredentials(token = apiResponse.jsonObject["access_token"]!!.jsonPrimitive.content)

// Get ARL
val arlResponse = callApi("user.getArl")
val arlObject = json.decodeFromString<JsonObject>(arlResponse)
session.updateCredentials(arl = arlObject["results"]!!.jsonPrimitive.content)
} catch (e: Exception) {
getArlByEmail(mail, password)
}
}

private fun md5(input: String): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,24 @@ object AudioStreamManager {
private var server: LocalAudioWebServer? = null
private val lock = Any()
private const val HOSTNAME = "127.0.0.1"
private const val PORT = 36958

@Volatile
private var usedPort = -1

private fun startServerIfNeeded() {
synchronized(lock) {
if (server == null) {
server = LocalAudioWebServer(HOSTNAME, PORT).apply {
start(SOCKET_READ_TIMEOUT, false)
try {
server = LocalAudioWebServer(HOSTNAME, 0).apply {
start(SOCKET_READ_TIMEOUT, false)
}
usedPort = server?.listeningPort ?: -1

println("LocalAudioWebServer started on port: $usedPort")
} catch (e: Exception) {
println("Failed to start LocalAudioWebServer: ${e.message}")
e.printStackTrace()
server = null
}
}
}
Expand All @@ -195,7 +206,7 @@ object AudioStreamManager {
}

fun getStreamUrlForTrack(trackId: String): String {
return "http://$HOSTNAME:$PORT/stream?trackId=$trackId"
return "http://$HOSTNAME:$usedPort/stream?trackId=$trackId"
}
}

29 changes: 11 additions & 18 deletions ext/src/main/java/dev/brahmkshatriya/echo/extension/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,32 @@ import javax.crypto.spec.SecretKeySpec

object Utils {
private const val SECRET = "g4el58wc0zvf9na1"
private val secretIvSpec = IvParameterSpec(byteArrayOf(0,1,2,3,4,5,6,7))

private val secretIvSpec = IvParameterSpec(ByteArray(8) { it.toByte() })
private val keySpecCache = ConcurrentHashMap<String, SecretKeySpec>()

private fun bitwiseXor(firstVal: Char, secondVal: Char, thirdVal: Char): Char {
return (firstVal.code xor secondVal.code xor thirdVal.code).toChar()
private val md5Digest: MessageDigest by lazy { MessageDigest.getInstance("MD5") }

private fun bitwiseXor(vararg values: Char): Char {
return values.fold(0) { acc, char -> acc xor char.code }.toChar()
}

fun createBlowfishKey(trackId: String): String {
val trackMd5Hex = trackId.toMD5()
val blowfishKey = StringBuilder()

for (i in 0..15) {
val nextChar = bitwiseXor(trackMd5Hex[i], trackMd5Hex[i + 16], SECRET[i])
blowfishKey.append(nextChar)
return buildString {
for (i in 0 until 16) {
append(bitwiseXor(trackMd5Hex[i], trackMd5Hex[i + 16], SECRET[i]))
}
}

return blowfishKey.toString()
}

private fun getSecretKeySpec(blowfishKey: String): SecretKeySpec {
return keySpecCache.computeIfAbsent(blowfishKey) {
SecretKeySpec(blowfishKey.toByteArray(), "Blowfish")
SecretKeySpec(blowfishKey.toByteArray(Charsets.ISO_8859_1), "Blowfish")
}
}

private fun bytesToHex(bytes: ByteArray): String {
return bytes.joinToString("") { String.format("%02X", it) }
}

private fun String.toMD5(): String {
val bytes = MessageDigest.getInstance("MD5").digest(this.toByteArray(Charsets.ISO_8859_1))
return bytesToHex(bytes).lowercase()
return md5Digest.digest(toByteArray(Charsets.ISO_8859_1)).joinToString("") { "%02x".format(it) }
}

fun decryptBlowfish(chunk: ByteArray, blowfishKey: String): ByteArray {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,6 @@ class DeezerSearchClient(private val api: DeezerApi, private val history: Boolea
return browseSections.mapNotNull { section ->
val id = section.jsonObject["module_id"]!!.jsonPrimitive.content
when (id) {
"67aa1c1b-7873-488d-88a0-55b6596cf4d6", "486313b7-e3c7-453d-ba79-27dc6bea20ce",
"1d8dfed4-582f-40e1-b29c-760b44c0301e", "ecb89e7c-1c07-4922-aa50-d29745576636",
"64ac680b-7c84-49a3-9077-38e9b653332e" -> {
parser.run {
section.toShelfItemsList(section.jsonObject["title"]?.jsonPrimitive?.content.orEmpty())
}
}

"8b2c6465-874d-4752-a978-1637ca0227b5" -> {
parser.run {
section.toShelfCategoryList(section.jsonObject["title"]?.jsonPrimitive?.content.orEmpty()) { target ->
Expand All @@ -113,6 +105,12 @@ class DeezerSearchClient(private val api: DeezerApi, private val history: Boolea
}
}

!in "6550abfd-15e4-47de-a5e8-a60e27fa152a", !in "c8b406d4-5293-4f59-a0f4-562eba496a0b" -> {
parser.run {
section.toShelfItemsList(section.jsonObject["title"]?.jsonPrimitive?.content.orEmpty())
}
}

else -> null
}
}
Expand Down

0 comments on commit ddc7aa3

Please sign in to comment.