diff --git a/app/src/main/java/dev/brahmkshatriya/echo/extension/Convertors.kt b/app/src/main/java/dev/brahmkshatriya/echo/extension/Convertors.kt index 4b80059..0afc536 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/extension/Convertors.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/extension/Convertors.kt @@ -1,5 +1,7 @@ package dev.brahmkshatriya.echo.extension +import android.annotation.TargetApi +import android.os.Build import dev.brahmkshatriya.echo.common.models.Album import dev.brahmkshatriya.echo.common.models.Artist import dev.brahmkshatriya.echo.common.models.EchoMediaItem @@ -15,6 +17,8 @@ import kotlinx.serialization.json.int import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive +import java.time.Instant +import java.util.Date fun JsonElement.toMediaItemsContainer( @@ -72,6 +76,7 @@ fun JsonElement.toAlbum(): Album { id = data["ALB_ID"]?.jsonPrimitive?.content ?: "", title = data["ALB_TITLE"]?.jsonPrimitive?.content ?: "", cover = getCover(md5, "cover"), + tracks = jsonObject["SONGS"]?.jsonObject?.get("total")?.jsonPrimitive?.int, artists = listOf( Artist( id = artistObject?.get("ART_ID")?.jsonPrimitive?.content ?: "", @@ -96,6 +101,7 @@ fun JsonElement.toArtist(): Artist { ) } +@TargetApi(Build.VERSION_CODES.O) fun JsonElement.toTrack(): Track { val data = jsonObject["data"]?.jsonObject ?: jsonObject val md5 = data["ALB_PICTURE"]?.jsonPrimitive?.content ?: "" @@ -105,6 +111,8 @@ fun JsonElement.toTrack(): Track { id = data["SNG_ID"]!!.jsonPrimitive.content, title = data["SNG_TITLE"]!!.jsonPrimitive.content, cover = getCover(md5, "cover"), + duration = data["DURATION"]?.jsonPrimitive?.content?.toLong()?.times(1000), + releaseDate = Date.from(Instant.ofEpochSecond(data["DATE_ADD"]?.jsonPrimitive?.content?.toLong() ?: 0)).toString(), artists = listOf( Artist( id = artistObject?.get("ART_ID")?.jsonPrimitive?.content ?: "", diff --git a/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerApi.kt b/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerApi.kt index 7b60a6e..5d63e47 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerApi.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerApi.kt @@ -59,9 +59,11 @@ class DeezerApi { private val pass: String get() = DeezerCredentials.pass - private val client: OkHttpClient = OkHttpClient.Builder() - .proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved("uk.proxy.murglar.app", 3128))) - .addInterceptor { chain -> + private val proxy: Boolean + get() = DeezerUtils.proxy + + private val client: OkHttpClient = OkHttpClient.Builder().apply { + addInterceptor { chain -> val originalResponse = chain.proceed(chain.request()) if (originalResponse.header("Content-Encoding") == "gzip") { val gzipSource = GZIPInputStream(originalResponse.body?.byteStream()) @@ -70,7 +72,11 @@ class DeezerApi { } else { originalResponse } - }.build() + } + if(proxy) { + proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved("uk.proxy.murglar.app", 3128))) + } + }.build() private val json = Json { isLenient = true diff --git a/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerBase.kt b/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerBase.kt index 1f3330b..75e93bf 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerBase.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerBase.kt @@ -12,4 +12,5 @@ object DeezerCredentials { object DeezerUtils { var arl_expired: Boolean = false + var proxy: Boolean = false } diff --git a/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerExtension.kt b/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerExtension.kt index 46cd418..b804abd 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerExtension.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/extension/DeezerExtension.kt @@ -59,6 +59,12 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie "128", "Use 128kbps(overrides FLAC option)", false + ), + SettingSwitch( + "Use Proxy", + "proxy", + "Use Proxy against geo-blocking", + false ) ) @@ -73,6 +79,9 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie private val use128 get() = settings.getBoolean("128") ?: false + private val proxy + get() = settings.getBoolean("proxy") ?: false + private val arl: String get() = DeezerCredentials.arl @@ -85,6 +94,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override suspend fun getHomeTabs(): List { if (arl == "" || arlExpired) return emptyList() + DeezerUtils.proxy = proxy val jObject = DeezerApi().homePage() val resultObject = jObject["results"]!!.jsonObject val sections = resultObject["sections"]!!.jsonArray @@ -100,6 +110,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override fun getHomeFeed(tab: Tab?): PagedData = PagedData.Single { if(arl == "" || arlExpired) throw loginRequiredException + DeezerUtils.proxy = proxy val dataList = mutableListOf() val jsonData = json.decodeFromString(tab?.extras!!["sections"].toString()) jsonData.map { section -> @@ -126,6 +137,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override suspend fun getLibraryTabs(): List { if (arl == "" || arlExpired) return emptyList() + DeezerUtils.proxy = proxy val tabs = listOf(Tab("playlists", "Playlists"), Tab("albums", "Albums"), Tab("tracks", "Tracks"), Tab("artists", "Artists")) allTabs = "all" to tabs.mapNotNull { tab -> @@ -170,6 +182,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override fun getLibraryFeed(tab: Tab?) = PagedData.Single { if(arl == "" || arlExpired) throw loginRequiredException + DeezerUtils.proxy = proxy val tabId = tab?.id ?: "all" var list = listOf() when (tabId) { @@ -232,11 +245,13 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override suspend fun addTracksToPlaylist(playlist: Playlist, tracks: List, index: Int, new: List) { if(arl == "" || arlExpired) throw loginRequiredException + DeezerUtils.proxy = proxy DeezerApi().addToPlaylist(playlist, new) } override suspend fun createPlaylist(title: String, description: String?): Playlist { if(arl == "" || arlExpired) throw loginRequiredException + DeezerUtils.proxy = proxy val jsonObject = DeezerApi().createPlaylist(title, description) val id = jsonObject["results"]?.jsonPrimitive?.content ?: "" val playlist = Playlist( @@ -250,16 +265,19 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override suspend fun deletePlaylist(playlist: Playlist) { if(arl == "" || arlExpired) throw loginRequiredException + DeezerUtils.proxy = proxy DeezerApi().deletePlaylist(playlist.id) } override suspend fun editPlaylistMetadata(playlist: Playlist, title: String, description: String?) { if(arl == "" || arlExpired) throw loginRequiredException + DeezerUtils.proxy = proxy DeezerApi().updatePlaylist(playlist.id, title, description) } override suspend fun likeTrack(track: Track, liked: Boolean): Boolean { if(arl == "" || arlExpired) throw loginRequiredException + DeezerUtils.proxy = proxy if(liked) { DeezerApi().addFavoriteTrack(track.id) return true @@ -271,6 +289,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override suspend fun listEditablePlaylists(): List { if(arl == "" || arlExpired) throw loginRequiredException + DeezerUtils.proxy = proxy val playlistList = mutableListOf() val jsonObject = DeezerApi().getPlaylists() val resultObject = jsonObject["results"]!!.jsonObject @@ -289,6 +308,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override suspend fun moveTrackInPlaylist(playlist: Playlist, tracks: List, fromIndex: Int, toIndex: Int) { if(arl == "" || arlExpired) throw loginRequiredException + DeezerUtils.proxy = proxy val idArray = tracks.map { it.id }.toTypedArray() val newIdArray = moveElement(idArray, fromIndex, toIndex) DeezerApi().updatePlaylistOrder(playlist.id, newIdArray) @@ -296,12 +316,14 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override suspend fun removeTracksFromPlaylist(playlist: Playlist, tracks: List, indexes: List) { if(arl == "" || arlExpired) throw loginRequiredException + DeezerUtils.proxy = proxy DeezerApi().removeFromPlaylist(playlist, tracks, indexes) } //<============= Search =============> override suspend fun quickSearch(query: String?) = query?.run { + DeezerUtils.proxy = proxy try { val jsonObject = DeezerApi().searchSuggestions(query) val resultObject = jsonObject["results"]!!.jsonObject @@ -320,6 +342,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie private var oldSearch: Pair>? = null override fun searchFeed(query: String?, tab: Tab?) = PagedData.Single { if(arl == "" || arlExpired) throw loginRequiredException + DeezerUtils.proxy = proxy query ?: return@Single emptyList() val old = oldSearch?.takeIf { it.first == query && (tab == null || tab.id == "All") @@ -343,6 +366,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override suspend fun searchTabs(query: String?): List { if (arl == "" || arlExpired) return emptyList() + DeezerUtils.proxy = proxy query ?: return emptyList() val jsonObject = DeezerApi().search(query) val resultObject = jsonObject["results"]!!.jsonObject @@ -378,6 +402,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override suspend fun getStreamableVideo(streamable: Streamable) = throw Exception("not Used") override suspend fun loadTrack(track: Track) = coroutineScope { + DeezerUtils.proxy = false val api = DeezerApi() val jsonObject = if (track.extras["FILESIZE_MP3_MISC"] != "0" && track.extras["FILESIZE_MP3_MISC"] != null) { api.getMP3MediaUrl(track) @@ -398,10 +423,9 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie val code = client.newCall(request).execute().code if (code == 403) { val fallbackObject = fetchTrackData(track)["FALLBACK"]!!.jsonObject - val backId = fallbackObject["SNG_ID"]?.jsonPrimitive?.content ?: "" val backMd5Origin = fallbackObject["MD5_ORIGIN"]?.jsonPrimitive?.content ?: "" val backMediaVersion = fallbackObject["MEDIA_VERSION"]?.jsonPrimitive?.content ?: "" - url = generateTrackUrl(backId, backMd5Origin, backMediaVersion, 1) + url = generateTrackUrl(trackId, backMd5Origin, backMediaVersion, 1) } return url } @@ -421,7 +445,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie val newDataObject = fetchTrackData(newTrack) val md5Origin = newDataObject["MD5_ORIGIN"]?.jsonPrimitive?.content ?: "" val mediaVersion = newDataObject["MEDIA_VERSION"]?.jsonPrimitive?.content ?: "" - generateUrl(newTrack.id, md5Origin, mediaVersion) + generateUrl(track.id, md5Origin, mediaVersion) } else -> { val dataObject = jsonObject["data"]!!.jsonArray.first().jsonObject @@ -453,12 +477,14 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override fun getMediaItems(album: Album) = getMediaItems(album.artists.first()) override suspend fun loadAlbum(album: Album): Album { + DeezerUtils.proxy = proxy val jsonObject = DeezerApi().album(album) val resultsObject = jsonObject["results"]!!.jsonObject return resultsObject.toAlbum() } override fun loadTracks(album: Album): PagedData = PagedData.Single { + DeezerUtils.proxy = proxy val jsonObject = DeezerApi().album(album) val resultsObject = jsonObject["results"]!!.jsonObject val songsObject = resultsObject["SONGS"]!!.jsonObject @@ -472,6 +498,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie //<============= Playlist =============> override fun getMediaItems(playlist: Playlist) = PagedData.Single { + DeezerUtils.proxy = proxy val jsonObject = DeezerApi().playlist(playlist) val resultsObject = jsonObject["results"]!!.jsonObject val songsObject = resultsObject["SONGS"]!!.jsonObject @@ -483,12 +510,14 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie } override suspend fun loadPlaylist(playlist: Playlist): Playlist { + DeezerUtils.proxy = proxy val jsonObject = DeezerApi().playlist(playlist) val resultsObject = jsonObject["results"]!!.jsonObject return resultsObject.toPlaylist() } override fun loadTracks(playlist: Playlist): PagedData = PagedData.Single { + DeezerUtils.proxy = proxy val jsonObject = DeezerApi().playlist(playlist) val resultsObject = jsonObject["results"]!!.jsonObject val songsObject = resultsObject["SONGS"]!!.jsonObject @@ -502,6 +531,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie //<============= Artist =============> override fun getMediaItems(artist: Artist) = PagedData.Single { + DeezerUtils.proxy = proxy val dataList = mutableListOf() val jsonObject = DeezerApi().artist(artist) @@ -550,6 +580,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie } override suspend fun loadArtist(artist: Artist): Artist { + DeezerUtils.proxy = proxy val jsonObject = DeezerApi().artist(artist) val resultsObject = jsonObject["results"]!!.jsonObject["DATA"]!!.jsonObject return resultsObject.toArtist() @@ -565,6 +596,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie override val loginWebViewStopUrlRegex = "https://www\\.deezer\\.com/account/.*".toRegex() override suspend fun onLoginWebviewStop(url: String, data: String): List { + DeezerUtils.proxy = proxy if (data.contains("arl=")) { DeezerCredentials.arl = data.substringAfter("arl=").substringBefore(";") DeezerCredentials.sid = data.substringAfter("sid=").substringBefore(";") @@ -587,6 +619,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie ) override suspend fun onLogin(data: Map): List { + DeezerUtils.proxy = proxy DeezerCredentials.arl = data["arl"] ?: "" DeezerApi().getSid() val userList = DeezerApi().makeUser() @@ -594,6 +627,7 @@ class DeezerExtension : ExtensionClient, HomeFeedClient, TrackClient, SearchClie } override suspend fun onLogin(username: String, password: String): List { + DeezerUtils.proxy = proxy // Set shared credentials DeezerCredentials.email = username DeezerCredentials.pass = password