From ef6c1015d9504af721f2a54ffeb89114fa775724 Mon Sep 17 00:00:00 2001
From: stevenyomi <95685115+stevenyomi@users.noreply.github.com>
Date: Fri, 19 May 2023 18:15:56 +0800
Subject: [PATCH] WNACG: auto update domains (#16436)

* WNACG: auto update domains

* mark updated if unchanged

* Dedupe

* Fix

* Reorder parameters
---
 src/zh/wnacg/build.gradle                     |   2 +-
 .../extension/zh/wnacg/Preferences.kt         | 126 ++++++++++++++----
 .../tachiyomi/extension/zh/wnacg/wnacg.kt     |  19 ++-
 3 files changed, 112 insertions(+), 35 deletions(-)

diff --git a/src/zh/wnacg/build.gradle b/src/zh/wnacg/build.gradle
index 00488f84c..0e50b8a2e 100644
--- a/src/zh/wnacg/build.gradle
+++ b/src/zh/wnacg/build.gradle
@@ -5,7 +5,7 @@ ext {
     extName = 'WNACG'
     pkgNameSuffix = 'zh.wnacg'
     extClass = '.wnacg'
-    extVersionCode = 12
+    extVersionCode = 13
     isNsfw = true
 }
 
diff --git a/src/zh/wnacg/src/eu/kanade/tachiyomi/extension/zh/wnacg/Preferences.kt b/src/zh/wnacg/src/eu/kanade/tachiyomi/extension/zh/wnacg/Preferences.kt
index 26ebb0ba9..70525cac5 100644
--- a/src/zh/wnacg/src/eu/kanade/tachiyomi/extension/zh/wnacg/Preferences.kt
+++ b/src/zh/wnacg/src/eu/kanade/tachiyomi/extension/zh/wnacg/Preferences.kt
@@ -1,43 +1,113 @@
 package eu.kanade.tachiyomi.extension.zh.wnacg
 
+import android.app.Application
 import android.content.Context
 import android.content.SharedPreferences
-import androidx.preference.EditTextPreference
+import androidx.preference.ListPreference
+import eu.kanade.tachiyomi.network.GET
+import okhttp3.Interceptor
+import okhttp3.Response
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+import java.io.IOException
+import kotlin.random.Random
 
-private const val BASE_URL = "http://www.htmanga3.top"
+private const val DEFAULT_LIST = "https://www.htmanga3.top,https://www.htmanga4.top,https://www.htmanga5.top"
 
-fun getPreferencesInternal(context: Context, preferences: SharedPreferences) = arrayOf(
-    EditTextPreference(context).apply {
-        key = OVERRIDE_BASE_URL_PREF
+fun getPreferencesInternal(
+    context: Context,
+    preferences: SharedPreferences,
+    isUrlUpdated: Boolean,
+) = arrayOf(
+    ListPreference(context).apply {
+        key = URL_INDEX_PREF
         title = "网址"
-        summary = "默认网址为:$BASE_URL\n" +
-            "可以尝试的网址有:\n" +
-            "    http://www.htmanga4.top\n" +
-            "    http://www.htmanga5.top\n" +
-            "可以到 www.wnacglink.top 查看最新网址。\n" +
-            "如果默认网址失效,可以在此填写新的网址。重启生效。"
+        summary = if (isUrlUpdated) "%s\n网址已自动更新,请重启应用。" else "%s\n正常情况下会自动更新。重启生效。"
 
-        setOnPreferenceChangeListener { _, _ ->
-            preferences.edit().putString(DEFAULT_BASE_URL_PREF, BASE_URL).apply()
-            true
-        }
+        val options = preferences.urlList
+        val count = options.size
+        entries = options.toTypedArray()
+        entryValues = Array(count, Int::toString)
     },
 )
 
 val SharedPreferences.baseUrl: String
     get() {
-        val overrideBaseUrl = getString(OVERRIDE_BASE_URL_PREF, "")!!
-        if (overrideBaseUrl.isNotEmpty()) {
-            val defaultBaseUrl = getString(DEFAULT_BASE_URL_PREF, "")!!
-            if (defaultBaseUrl == BASE_URL) return overrideBaseUrl
-            // Outdated
-            edit()
-                .remove(OVERRIDE_BASE_URL_PREF)
-                .remove(DEFAULT_BASE_URL_PREF)
-                .apply()
-        }
-        return BASE_URL
+        val list = urlList
+        return list.getOrNull(urlIndex) ?: list[0]
     }
 
-private const val OVERRIDE_BASE_URL_PREF = "overrideBaseUrl"
-private const val DEFAULT_BASE_URL_PREF = "defaultBaseUrl"
+val SharedPreferences.urlIndex get() = getString(URL_INDEX_PREF, "-1")!!.toInt()
+val SharedPreferences.urlList get() = getString(URL_LIST_PREF, DEFAULT_LIST)!!.split(",")
+
+fun getCiBaseUrl() = DEFAULT_LIST.replace(",", ", ")
+
+fun getSharedPreferences(id: Long): SharedPreferences {
+    val preferences: SharedPreferences = Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
+    if (preferences.getString(DEFAULT_LIST_PREF, "")!! == DEFAULT_LIST) return preferences
+    preferences.edit()
+        .remove("overrideBaseUrl")
+        .putString(DEFAULT_LIST_PREF, DEFAULT_LIST)
+        .setUrlList(DEFAULT_LIST, preferences.urlIndex)
+        .apply()
+    return preferences
+}
+
+fun SharedPreferences.Editor.setUrlList(urlList: String, oldIndex: Int): SharedPreferences.Editor {
+    putString(URL_LIST_PREF, urlList)
+    val maxIndex = urlList.count { it == ',' }
+    if (oldIndex in 0..maxIndex) return this
+    val newIndex = Random.nextInt(0, maxIndex + 1)
+    return putString(URL_INDEX_PREF, newIndex.toString())
+}
+
+class UpdateUrlInterceptor(private val preferences: SharedPreferences) : Interceptor {
+    private val baseUrl = preferences.baseUrl
+    var isUpdated = false
+
+    override fun intercept(chain: Interceptor.Chain): Response {
+        val request = chain.request()
+        if (!request.url.toString().startsWith(baseUrl)) return chain.proceed(request)
+
+        val failedResponse = try {
+            val response = chain.proceed(request)
+            if (response.isSuccessful) return response
+            Result.success(response)
+        } catch (e: Throwable) {
+            if (chain.call().isCanceled()) throw e
+            Result.failure(e)
+        }
+
+        if (isUpdated || updateUrl(chain)) {
+            failedResponse.onSuccess(Response::close)
+            throw IOException("网址已自动更新,请重启应用")
+        }
+        return failedResponse.getOrThrow()
+    }
+
+    @Synchronized
+    private fun updateUrl(chain: Interceptor.Chain): Boolean {
+        if (isUpdated) return true
+        val response = try {
+            chain.proceed(GET("https://stevenyomi.github.io/source-domains/wnacg.txt"))
+        } catch (_: Throwable) {
+            return false
+        }
+        if (!response.isSuccessful) {
+            response.close()
+            return false
+        }
+        val newList = response.body.string()
+        if (newList != preferences.getString(URL_LIST_PREF, "")!!) {
+            preferences.edit()
+                .setUrlList(newList, preferences.urlIndex)
+                .apply()
+        }
+        isUpdated = true
+        return true
+    }
+}
+
+private const val DEFAULT_LIST_PREF = "defaultBaseUrl"
+private const val URL_LIST_PREF = "baseUrlList"
+private const val URL_INDEX_PREF = "baseUrlIndex"
diff --git a/src/zh/wnacg/src/eu/kanade/tachiyomi/extension/zh/wnacg/wnacg.kt b/src/zh/wnacg/src/eu/kanade/tachiyomi/extension/zh/wnacg/wnacg.kt
index 49bcaadd6..a2e6e4a3b 100644
--- a/src/zh/wnacg/src/eu/kanade/tachiyomi/extension/zh/wnacg/wnacg.kt
+++ b/src/zh/wnacg/src/eu/kanade/tachiyomi/extension/zh/wnacg/wnacg.kt
@@ -1,6 +1,5 @@
 package eu.kanade.tachiyomi.extension.zh.wnacg
 
-import android.app.Application
 import androidx.preference.PreferenceScreen
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.source.ConfigurableSource
@@ -17,8 +16,6 @@ import okhttp3.Response
 import org.jsoup.nodes.Document
 import org.jsoup.nodes.Element
 import rx.Observable
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
 
 // URL can be found at https://www.wnacglink.top/
 class wnacg : ParsedHttpSource(), ConfigurableSource {
@@ -26,8 +23,18 @@ class wnacg : ParsedHttpSource(), ConfigurableSource {
     override val lang = "zh"
     override val supportsLatest = false
 
-    private val preferences = Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)!!
-    override val baseUrl = preferences.baseUrl
+    private val preferences = getSharedPreferences(id)
+
+    override val baseUrl = when (System.getenv("CI")) {
+        "true" -> getCiBaseUrl()
+        else -> preferences.baseUrl
+    }
+
+    private val updateUrlInterceptor = UpdateUrlInterceptor(preferences)
+
+    override val client = network.client.newBuilder()
+        .addInterceptor(updateUrlInterceptor)
+        .build()
 
     override fun popularMangaSelector() = ".gallary_item"
     override fun latestUpdatesSelector() = throw Exception("Not used")
@@ -155,6 +162,6 @@ class wnacg : ParsedHttpSource(), ConfigurableSource {
     // <<< Filters <<<
 
     override fun setupPreferenceScreen(screen: PreferenceScreen) {
-        getPreferencesInternal(screen.context, preferences).forEach(screen::addPreference)
+        getPreferencesInternal(screen.context, preferences, updateUrlInterceptor.isUpdated).forEach(screen::addPreference)
     }
 }