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}") + } + } +}