diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt b/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt index 51f3bc9d..9bd877e4 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/Anilist.kt @@ -14,6 +14,10 @@ import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.snackString import ani.dantotsu.toast import ani.dantotsu.util.Logger +import eu.kanade.tachiyomi.network.NetworkHelper +import okhttp3.Cookie +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import java.util.Calendar object Anilist { @@ -163,7 +167,8 @@ object Anilist { force: Boolean = false, useToken: Boolean = true, show: Boolean = false, - cache: Int? = null + cache: Int? = null, + extraHeaders: Map? = null ): T? { return try { if (show) Logger.log("Anilist Query: $query") @@ -182,10 +187,23 @@ object Anilist { if (token != null || force) { if (token != null && useToken) headers["Authorization"] = "Bearer $token" + val _cookies = + Injekt.get().cookieJar.manager?.getCookie("https://anilist.co/user/${Anilist.userid}") + val cookies = mutableMapOf() + _cookies?.split(";")?.forEach { + val pieces = it.split('=') + val name = it.split('=')[0].trim() + cookies[name] = + pieces.takeLast(pieces.size - 1).asReversed().joinToString("=").trim() + } + + Logger.log(extraHeaders.toString()) + Logger.log(cookies.toString()) val json = client.post( "https://graphql.anilist.co/", - headers, + (extraHeaders + ?: mapOf()) + headers + mapOf("Cookie" to "laravel_session=${cookies["laravel_session"]}"), data = data, cacheTime = cache ?: 10 ) diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt index 497b59e0..37fe410a 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt @@ -1,12 +1,19 @@ package ani.dantotsu.connections.anilist +import android.util.Log +import ani.dantotsu.client import ani.dantotsu.connections.anilist.Anilist.executeQuery import ani.dantotsu.connections.anilist.api.FuzzyDate import ani.dantotsu.connections.anilist.api.Query import ani.dantotsu.connections.anilist.api.ToggleLike import ani.dantotsu.currContext +import ani.dantotsu.util.Logger import com.google.gson.Gson +import eu.kanade.tachiyomi.network.NetworkHelper import kotlinx.serialization.json.JsonObject +import org.checkerframework.checker.regex.qual.Regex +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get class AnilistMutations { @@ -154,17 +161,47 @@ class AnilistMutations { } suspend fun saveUserAvatar(base64Avatar: String): JsonObject? { - val imageFormat = getImageFormat(base64Avatar) - val base64WithPrefix = "data:image/$imageFormat;base64,${base64Avatar.removePrefix("data:image/$imageFormat;base64,")}" - val query = "mutation(\$avatar: String) { SaveUserAvatar(avatar: \$avatar) { id avatar { large medium } } }" - val variables = """{"avatar":"$base64WithPrefix"}""" - return executeQuery(query, variables) + val cookies = mutableMapOf() + val _cookies = Injekt.get().cookieJar.manager + _cookies?.getCookie("https://anilist.co/user/${Anilist.userid}")?.split(";")?.forEach { + val pieces = it.split('=') + val name = it.split('=')[0].trim() + cookies[name] = pieces.takeLast(pieces.size - 1).asReversed().joinToString("=").trim() + } + val regexPattern = """window\.al_token\s*=\s*".*?"""".toRegex() + val xsrf = regexPattern.find( + client.get( + "https://anilist.co/settings", headers = mapOf( + "Authorization" to "Bearer ${Anilist.token}", + "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Cookie" to "laravel_session=${cookies["laravel_session"]}" + ) + ).text + )?.value?.substringAfter('"')?.substringBefore('"') + + /* +mutation($avatar:String){SaveUserAvatar(avatar:$avatar){id avatar{large medium}}} + */ + val query = + "mutation(\$avatar:String){SaveUserAvatar(avatar:\$avatar){id avatar{large medium}}}\n" + val variables = """{"avatar":"$base64Avatar"}""" + Log.d("IMAGE", "$query\n$variables") + val queryResult: JsonObject? = executeQuery( + query, + variables, + useToken = true, + extraHeaders = mapOf("schema" to "internal", "x-csrf-token" to (xsrf ?: ""), "content-type" to "application/json") + ) + Log.d("QUERY RESULT", queryResult.toString()) + return queryResult } suspend fun saveUserBanner(base64Banner: String): JsonObject? { val imageFormat = getImageFormat(base64Banner) - val base64WithPrefix = "data:image/$imageFormat;base64,${base64Banner.removePrefix("data:image/$imageFormat;base64,")}" - val query = "mutation(\$banner: String) { SaveUserBanner(banner: \$banner) { id bannerImage } }" + val base64WithPrefix = + "data:image/$imageFormat;base64,${base64Banner.removePrefix("data:image/$imageFormat;base64,")}" + val query = + "mutation(\$banner: String) { SaveUserBanner(banner: \$banner) { id bannerImage } }" val variables = """{"banner":"$base64WithPrefix"}""" return executeQuery(query, variables) } diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt index 2275c8f4..6022f320 100644 --- a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt @@ -129,6 +129,10 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene }) bindingProfileAppBar = ItemProfileAppBarBinding.bind(binding.root).apply { + val intent = Intent(context, CookieCatcher::class.java) + .putExtra("url", "https://anilist.co/user/${Anilist.userid}") + .putExtra("headers", "{\"Authorization: ${Anilist.token}\"") + ContextCompat.startActivity(context, intent, null) binding.profileProgressBar.visibility = View.GONE editProfileAvatar.visibility = View.GONE editProfileBanner.visibility = View.GONE @@ -296,7 +300,7 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene ) viewIds.forEach { viewId -> - findViewById(viewId).apply { + findViewById(viewId)?.apply { visibility = if (visibility == View.VISIBLE) View.GONE else View.VISIBLE } } @@ -335,7 +339,6 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene } private fun saveProfileImages() { - val avatarBitmap = (bindingProfileAppBar.profileUserAvatar.drawable as? BitmapDrawable)?.bitmap val bannerBitmap = (bindingProfileAppBar.profileBannerImage.drawable as? BitmapDrawable)?.bitmap if (avatarBitmap != null && bannerBitmap != null) { @@ -368,14 +371,19 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene private fun bitmapToBase64(bitmap: Bitmap): String { val outputStream = ByteArrayOutputStream() - bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) - val base64 = Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP) val imageFormat = when (bitmap.config) { Bitmap.Config.ARGB_8888 -> "png" Bitmap.Config.RGB_565 -> "png" Bitmap.Config.ALPHA_8 -> "png" else -> "jpeg" } + bitmap.compress( + if (imageFormat == "png") Bitmap.CompressFormat.PNG else Bitmap.CompressFormat.JPEG, + 100, + outputStream + ) + val base64 = Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP) + return "data:image/$imageFormat;base64,$base64" }