Skip to content

Commit

Permalink
More Proxies & Code rework
Browse files Browse the repository at this point in the history
## Clear App Data before use!!

- Added more proxy options
  • Loading branch information
LuftVerbot committed Oct 24, 2024
1 parent 5f69246 commit 3325da5
Show file tree
Hide file tree
Showing 14 changed files with 824 additions and 600 deletions.
676 changes: 85 additions & 591 deletions ext/src/main/java/dev/brahmkshatriya/echo/extension/DeezerApi.kt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package dev.brahmkshatriya.echo.extension
import dev.brahmkshatriya.echo.common.clients.AlbumClient
import dev.brahmkshatriya.echo.common.clients.ArtistClient
import dev.brahmkshatriya.echo.common.clients.ArtistFollowClient
import dev.brahmkshatriya.echo.common.clients.ExtensionClient
import dev.brahmkshatriya.echo.common.clients.HomeFeedClient
import dev.brahmkshatriya.echo.common.clients.LibraryClient
import dev.brahmkshatriya.echo.common.clients.LoginClient
Expand Down Expand Up @@ -68,11 +67,13 @@ class DeezerExtension : HomeFeedClient, TrackClient, TrackLikeClient, RadioClien

override val settingItems: List<Setting>
get() = listOf(
SettingSwitch(
SettingList(
"Use Proxy",
"proxy",
"Use proxy to prevent GEO-Blocking",
false
mutableListOf("No Proxy", "UK", "RU 1", "RU 2"),
mutableListOf("", "uk.proxy.murglar.app", "ru1.proxy.murglar.app", "ru2.proxy.murglar.app"),
0
),
SettingSwitch(
"Enable Logging",
Expand Down Expand Up @@ -313,7 +314,7 @@ class DeezerExtension : HomeFeedClient, TrackClient, TrackLikeClient, RadioClien
}

suspend fun channelFeed(target: String): List<Shelf> {
val jsonObject = api.channelPage(target)
val jsonObject = api.page(target.substringAfter("/"))
val channelPageResults = jsonObject["results"]!!.jsonObject
val channelSections = channelPageResults["sections"]!!.jsonArray
return coroutineScope {
Expand Down
3 changes: 0 additions & 3 deletions ext/src/main/java/dev/brahmkshatriya/echo/extension/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import dev.brahmkshatriya.echo.common.models.Streamable.Media.Companion.toMedia
import io.ktor.utils.io.ByteChannel
import io.ktor.utils.io.writeFully
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.ConnectionPool
import okhttp3.OkHttpClient
import okhttp3.Protocol
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package dev.brahmkshatriya.echo.extension.api

import dev.brahmkshatriya.echo.common.models.Album
import dev.brahmkshatriya.echo.extension.DeezerApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put

class DeezerAlbum(private val deezerApi: DeezerApi, private val json: Json) {

suspend fun album(album: Album, language: String): JsonObject {
val jsonData = deezerApi.callApi(
method = "deezer.pageAlbum",
params = buildJsonObject {
put("alb_id", album.id)
put("header", true)
put("lang", language.substringBefore("-"))
}
)
return json.decodeFromString<JsonObject>(jsonData)
}

suspend fun getAlbums(userId: String): JsonObject {
val jsonData = deezerApi.callApi(
method = "deezer.pageProfile",
params = buildJsonObject {
put("user_id", userId)
put("tab", "albums")
put("nb", 50)
}
)
return json.decodeFromString<JsonObject>(jsonData)
}

suspend fun addFavoriteAlbum(id: String) {
deezerApi.callApi(
method = "album.addFavorite",
params = buildJsonObject {
put("ALB_ID", id)
}
)
}

suspend fun removeFavoriteAlbum(id: String) {
deezerApi.callApi(
method = "album.deleteFavorite",
params = buildJsonObject {
put("ALB_ID", id)
}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package dev.brahmkshatriya.echo.extension.api

import dev.brahmkshatriya.echo.extension.DeezerApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonObject

class DeezerArtist(private val deezerApi: DeezerApi, private val json: Json) {

suspend fun artist(id: String, language: String): JsonObject {
val jsonData = deezerApi.callApi(
method = "deezer.pageArtist",
params = buildJsonObject {
put("art_id", id)
put ("lang", language.substringBefore("-"))
}
)
return json.decodeFromString<JsonObject>(jsonData)
}

suspend fun getArtists(userId: String): JsonObject {
val jsonData = deezerApi.callApi(
method = "deezer.pageProfile",
params = buildJsonObject {
put("nb", 40)
put ("tab", "artists")
put("user_id", userId)
}
)
return json.decodeFromString<JsonObject>(jsonData)
}

suspend fun followArtist(id: String) {
deezerApi.callApi(
method = "artist.addFavorite",
params = buildJsonObject {
put("ART_ID", id)
putJsonObject("CTXT") {
put("id", id)
put("t", "artist_smartradio")
}
}
)
}

suspend fun unfollowArtist(id: String) {
deezerApi.callApi(
method = "artist.deleteFavorite",
params = buildJsonObject {
put("ART_ID", id)
putJsonObject("CTXT") {
put("id", id)
put("t", "artist_smartradio")
}
}
)
}
}
123 changes: 123 additions & 0 deletions ext/src/main/java/dev/brahmkshatriya/echo/extension/api/DeezerLogin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package dev.brahmkshatriya.echo.extension.api

import dev.brahmkshatriya.echo.common.models.ImageHolder.Companion.toImageHolder
import dev.brahmkshatriya.echo.common.models.User
import dev.brahmkshatriya.echo.extension.DeezerApi
import dev.brahmkshatriya.echo.extension.DeezerSession
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import java.math.BigInteger
import java.security.MessageDigest

class DeezerLogin(
private val deezerApi: DeezerApi,
private val json: Json,
private val session: DeezerSession,
private val client: OkHttpClient
) {

suspend fun makeUser(email: String = "", pass: String = "", arl: String, sid: String): List<User> {
val userList = mutableListOf<User>()
val jsonData = deezerApi.callApi("deezer.getUserData")
val jObject = json.decodeFromString<JsonObject>(jsonData)
val userResults = jObject["results"]!!
val userObject = userResults.jsonObject["USER"]!!
val token = userResults.jsonObject["checkForm"]!!.jsonPrimitive.content
val userId = userObject.jsonObject["USER_ID"]!!.jsonPrimitive.content
val licenseToken = userObject.jsonObject["OPTIONS"]!!.jsonObject["license_token"]!!.jsonPrimitive.content
val name = userObject.jsonObject["BLOG_NAME"]!!.jsonPrimitive.content
val cover = userObject.jsonObject["USER_PICTURE"]!!.jsonPrimitive.content
val user = User(
id = userId,
name = name,
cover = "https://e-cdns-images.dzcdn.net/images/user/$cover/100x100-000000-80-0-0.jpg".toImageHolder(),
extras = mapOf(
"arl" to arl,
"user_id" to userId,
"sid" to sid,
"token" to token,
"license_token" to licenseToken,
"email" to email,
"pass" to pass
)
)
userList.add(user)
return userList
}

suspend fun getArlByEmail(mail: String, password: String, sid: 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)

// Get ARL
val arlResponse = deezerApi.callApi("user.getArl")
val arlObject = json.decodeFromString<JsonObject>(arlResponse)
session.updateCredentials(arl = arlObject["results"]!!.jsonPrimitive.content)
}

private fun md5(input: String): String {
val md = MessageDigest.getInstance("MD5")
val digest = md.digest(input.toByteArray())
return BigInteger(1, digest).toString(16).padStart(32, '0')
}

private fun getToken(params: Map<String, String>, sid: String): String {
val url = "https://connect.deezer.com/oauth/user_auth.php"
val httpUrl = url.toHttpUrlOrNull()!!.newBuilder().apply {
params.forEach { (key, value) -> addQueryParameter(key, value) }
}.build()

val request = Request.Builder()
.url(httpUrl)
.get()
.headers(
Headers.headersOf(
"Cookie", "sid=$sid",
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
)
)
.build()

client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw Exception("Unexpected code $response")
return response.body.string()
}
}

fun getSid() {
val url = "https://www.deezer.com/"
val request = Request.Builder()
.url(url)
.get()
.build()

val response = client.newCall(request).execute()
response.headers.forEach {
if (it.second.startsWith("sid=")) {
session.updateCredentials(sid = it.second.substringAfter("sid=").substringBefore(";"))
}
}
}
}
100 changes: 100 additions & 0 deletions ext/src/main/java/dev/brahmkshatriya/echo/extension/api/DeezerMedia.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package dev.brahmkshatriya.echo.extension.api

import dev.brahmkshatriya.echo.common.models.Track
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.add
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody

class DeezerMedia(private val json: Json, private val clientNP: OkHttpClient) {

fun getMP3MediaUrl(track: Track, language: String, arl: String, sid: String, licenseToken: String): JsonObject {
val headers = Headers.Builder().apply {
add("Accept-Encoding", "gzip")
add("Accept-Language", language.substringBefore("-"))
add("Cache-Control", "max-age=0")
add("Connection", "Keep-alive")
add("Content-Type", "application/json; charset=utf-8")
add("Cookie", "arl=$arl&sid=$sid")
add("Host", "media.deezer.com")
add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36")
}.build()

val url = HttpUrl.Builder()
.scheme("https")
.host("media.deezer.com")
.addPathSegment("v1")
.addPathSegment("get_url")
.build()

val requestBody = json.encodeToString(
buildJsonObject {
put("license_token", licenseToken)
putJsonArray("media") {
add(buildJsonObject {
put("type", "FULL")
putJsonArray("formats") {
add(buildJsonObject {
put("cipher", "BF_CBC_STRIPE")
put("format", "MP3_MISC")
})
}
})
}
putJsonArray("track_tokens") { add(track.extras["TRACK_TOKEN"]) }
}
).toRequestBody("application/json; charset=utf-8".toMediaType())

val request = Request.Builder()
.url(url)
.post(requestBody)
.headers(headers)
.build()

val response = clientNP.newCall(request).execute()
val responseBody = response.body.string()

return json.decodeFromString<JsonObject>(responseBody)
}

fun getMediaUrl(track: Track, quality: String): JsonObject {
val url = HttpUrl.Builder()
.scheme("https")
.host("dzmedia.fly.dev")
.addPathSegment("get_url")
.build()

val formats = when (quality) {
"128" -> arrayOf("MP3_128", "MP3_64", "MP3_MISC")
"flac" -> arrayOf("FLAC", "MP3_320", "MP3_128", "MP3_64", "MP3_MISC")
else -> arrayOf("MP3_320", "MP3_128", "MP3_64", "MP3_MISC")
}

val requestBody = json.encodeToString(
buildJsonObject {
put("formats", buildJsonArray { formats.forEach { add(it) } })
put ("ids", buildJsonArray{ add(track.id.toLong()) })
}
).toRequestBody("application/json; charset=utf-8".toMediaType())

val request = Request.Builder()
.url(url)
.post(requestBody)
.build()

val response = clientNP.newCall(request).execute()
val responseBody = response.body.string()

return json.decodeFromString<JsonObject>(responseBody)
}
}
Loading

0 comments on commit 3325da5

Please sign in to comment.