From fc7598bdcf8f2abdba7edd37fceda9633d273d02 Mon Sep 17 00:00:00 2001 From: nicki Date: Mon, 23 Oct 2023 11:45:32 -0500 Subject: [PATCH] more QoL fixes for MangaDex (#18672) * fix spelling for villainess in en translations * custom user agent + more md@h logging adds a custom user agent setting primarily intended for testing do note that spoofing to be a different browser might fetch you trouble as MangaDex's new hotlink/bot abusers can kick in action also add logging to figure out which MD@H node users are hitting as currently, the fallback was not observed to be changing even after a while; leading to user's keep failing to fetch images * more improvements make the `Extra` header more verbose use a `customUserAgent` sharedpreferences variable so that code looks cleaner move the useragent logic into an interceptor so that there is no need for restarting the app after every useragent change * completely switch to useragent interceptor removes builder header modification remove additional logging while testing (didn't really work as expected since I kept hitting uploads.mangadex.org anyhow, + the interceptor logs weren't useful otherwise) switch to hardcoding version code in header since it wasn't working as expected... add linting from android studio gradle release build * increment extVersionCode * oopsie be careful of comments * properly close unsuccessful requests to mdah node need to ensure this so that it doesn't crash (which has happened before) inb4 this is why the fallback url never works.... --- .../assets/i18n/messages_en.properties | 6 ++- src/all/mangadex/build.gradle | 2 +- .../extension/all/mangadex/MDConstants.kt | 7 +++ .../extension/all/mangadex/MangaDex.kt | 50 +++++++++++++++++-- .../all/mangadex/MdAtHomeReportInterceptor.kt | 4 ++ .../all/mangadex/MdUserAgentInterceptor.kt | 47 +++++++++++++++++ 6 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdUserAgentInterceptor.kt diff --git a/src/all/mangadex/assets/i18n/messages_en.properties b/src/all/mangadex/assets/i18n/messages_en.properties index 7d27c05e6..435187ebc 100644 --- a/src/all/mangadex/assets/i18n/messages_en.properties +++ b/src/all/mangadex/assets/i18n/messages_en.properties @@ -137,10 +137,14 @@ theme_time_travel=Time Travel theme_traditional_games=Traditional Games theme_vampires=Vampires theme_video_games=Video Games -theme_villainess=Vilania +theme_villainess=Villainess theme_virtual_reality=Virtual Reality theme_zombies=Zombies try_using_first_volume_cover=Attempt to use the first volume cover as cover try_using_first_volume_cover_summary=May need to manually refresh entries already in library. Otherwise, clear database to have new covers to show up. unable_to_process_chapter_request=Unable to process Chapter request. HTTP code: %d uploaded_by=Uploaded by %s +set_custom_useragent=Set custom User-Agent +set_custom_useragent_summary=Keep it as default +set_custom_useragent_dialog=\n\nSpecify a custom user agent\n After each modification, the application needs to be restarted.\n\nDefault value:\n%s +set_custom_useragent_error_invalid=Invalid User-Agent: %s diff --git a/src/all/mangadex/build.gradle b/src/all/mangadex/build.gradle index c6e70b1ee..b38d9e61a 100644 --- a/src/all/mangadex/build.gradle +++ b/src/all/mangadex/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'MangaDex' pkgNameSuffix = 'all.mangadex' extClass = '.MangaDexFactory' - extVersionCode = 189 + extVersionCode = 190 isNsfw = true } diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MDConstants.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MDConstants.kt index 69f79d63b..8fc471311 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MDConstants.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MDConstants.kt @@ -138,6 +138,13 @@ object MDConstants { return "${altTitlesInDescPref}_$dexLang" } + private const val customUserAgentPref = "customUserAgent" + fun getCustomUserAgentPrefKey(dexLang: String): String { + return "${customUserAgentPref}_$dexLang" + } + + val defaultUserAgent = "Tachiyomi " + System.getProperty("http.agent") + private const val tagGroupContent = "content" private const val tagGroupFormat = "format" private const val tagGroupGenre = "genre" diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDex.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDex.kt index f3f7aadbf..06b72b1d0 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDex.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDex.kt @@ -2,11 +2,14 @@ package eu.kanade.tachiyomi.extension.all.mangadex import android.app.Application import android.content.SharedPreferences +import android.os.Build +import android.widget.Toast import androidx.preference.EditTextPreference import androidx.preference.ListPreference import androidx.preference.MultiSelectListPreference import androidx.preference.PreferenceScreen import androidx.preference.SwitchPreferenceCompat +import eu.kanade.tachiyomi.AppInfo import eu.kanade.tachiyomi.extension.all.mangadex.dto.AggregateDto import eu.kanade.tachiyomi.extension.all.mangadex.dto.AggregateVolume import eu.kanade.tachiyomi.extension.all.mangadex.dto.AtHomeDto @@ -56,13 +59,23 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St private val helper = MangaDexHelper(lang) - final override fun headersBuilder() = Headers.Builder() - .add("Referer", "$baseUrl/") - .add("User-Agent", "Tachiyomi " + System.getProperty("http.agent")) + final override fun headersBuilder(): Headers.Builder { + val extraHeader = "Android/${Build.VERSION.RELEASE} " + + "Tachiyomi/${AppInfo.getVersionName()} " + + "MangaDex/1.4.190" + + val builder = super.headersBuilder().apply { + set("Referer", "$baseUrl/") + set("Extra", extraHeader) + } + + return builder + } override val client = network.client.newBuilder() .rateLimit(3) .addInterceptor(MdAtHomeReportInterceptor(network.client, headers)) + .addInterceptor(MdUserAgentInterceptor(preferences, dexLang)) .build() init { @@ -745,6 +758,30 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St } } + val userAgentPref = EditTextPreference(screen.context).apply { + key = MDConstants.getCustomUserAgentPrefKey(dexLang) + title = helper.intl["set_custom_useragent"] + summary = helper.intl["set_custom_useragent_summary"] + dialogMessage = helper.intl.format( + "set_custom_useragent_dialog", + MDConstants.defaultUserAgent, + ) + + setDefaultValue(MDConstants.defaultUserAgent) + + setOnPreferenceChangeListener { _, newValue -> + try { + Headers.Builder().add("User-Agent", newValue as String) + summary = newValue + true + } catch (e: Throwable) { + val errorMessage = helper.intl.format("set_custom_useragent_error_invalid", e.message) + Toast.makeText(screen.context, errorMessage, Toast.LENGTH_LONG).show() + false + } + } + } + screen.addPreference(coverQualityPref) screen.addPreference(tryUsingFirstVolumeCoverPref) screen.addPreference(dataSaverPref) @@ -754,6 +791,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St screen.addPreference(originalLanguagePref) screen.addPreference(blockedGroupsPref) screen.addPreference(blockedUploaderPref) + screen.addPreference(userAgentPref) } override fun getFilterList(): FilterList = @@ -828,6 +866,12 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St private val SharedPreferences.altTitlesInDesc get() = getBoolean(MDConstants.getAltTitlesInDescPrefKey(dexLang), false) + private val SharedPreferences.customUserAgent + get() = getString( + MDConstants.getCustomUserAgentPrefKey(dexLang), + MDConstants.defaultUserAgent, + ) + /** * Previous versions of the extension allowed invalid UUID values to be stored in the * preferences. This method clear invalid UUIDs in case the user have updated from diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdAtHomeReportInterceptor.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdAtHomeReportInterceptor.kt index 48c79056b..41d2559eb 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdAtHomeReportInterceptor.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdAtHomeReportInterceptor.kt @@ -36,6 +36,8 @@ class MdAtHomeReportInterceptor( return response } + Log.e("MangaDex", "Connecting to MD@Home node at $url") + val result = ImageReportDto( url = url, success = response.isSuccessful, @@ -61,6 +63,8 @@ class MdAtHomeReportInterceptor( return response } + response.close() + Log.e("MangaDex", "Error connecting to MD@Home node, fallback to uploads server") val fallbackUrl = MDConstants.cdnUrl.toHttpUrl().newBuilder() diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdUserAgentInterceptor.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdUserAgentInterceptor.kt new file mode 100644 index 000000000..e20adce6e --- /dev/null +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdUserAgentInterceptor.kt @@ -0,0 +1,47 @@ +package eu.kanade.tachiyomi.extension.all.mangadex + +import android.content.SharedPreferences +import okhttp3.Interceptor +import okhttp3.Response +import java.io.IOException + +/** + * Interceptor to set custom useragent for MangaDex + */ +class MdUserAgentInterceptor( + private val preferences: SharedPreferences, + private val dexLang: String, +) : Interceptor { + + private val SharedPreferences.customUserAgent + get() = getString( + MDConstants.getCustomUserAgentPrefKey(dexLang), + MDConstants.defaultUserAgent, + ) + + private fun getUserAgent(): String? { + return preferences.customUserAgent + } + + override fun intercept(chain: Interceptor.Chain): Response { + try { + val originalRequest = chain.request() + + val newUserAgent = getUserAgent() ?: return chain.proceed(originalRequest) + + val originalHeaders = originalRequest.headers + + val modifiedHeaders = originalHeaders.newBuilder() + .set("User-Agent", newUserAgent) + .build() + + return chain.proceed( + originalRequest.newBuilder() + .headers(modifiedHeaders) + .build(), + ) + } catch (e: Exception) { + throw IOException("MdUserAgentInterceptor failed with error: ${e.message}") + } + } +}