diff --git a/src/zh/pufei/build.gradle b/src/zh/pufei/build.gradle
index 1868aed5f..ce32ddecf 100644
--- a/src/zh/pufei/build.gradle
+++ b/src/zh/pufei/build.gradle
@@ -5,7 +5,11 @@ ext {
     extName = 'Pufei Manhua'
     pkgNameSuffix = 'zh.pufei'
     extClass = '.Pufei'
-    extVersionCode = 9
+    extVersionCode = 10
 }
 
 apply from: "$rootDir/common.gradle"
+
+dependencies {
+    implementation 'com.github.stevenyomi:unpacker:12a09e3c1a' // 1.1
+}
diff --git a/src/zh/pufei/src/eu/kanade/tachiyomi/extension/zh/pufei/Pufei.kt b/src/zh/pufei/src/eu/kanade/tachiyomi/extension/zh/pufei/Pufei.kt
index 890712325..13f96cc02 100644
--- a/src/zh/pufei/src/eu/kanade/tachiyomi/extension/zh/pufei/Pufei.kt
+++ b/src/zh/pufei/src/eu/kanade/tachiyomi/extension/zh/pufei/Pufei.kt
@@ -5,6 +5,8 @@ import android.content.SharedPreferences
 import android.util.Base64
 import androidx.preference.ListPreference
 import androidx.preference.PreferenceScreen
+import com.github.stevenyomi.unpacker.ProgressiveParser
+import com.github.stevenyomi.unpacker.Unpacker
 import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.network.POST
 import eu.kanade.tachiyomi.network.asObservableSuccess
@@ -35,8 +37,8 @@ class Pufei : ParsedHttpSource(), ConfigurableSource {
     private val preferences: SharedPreferences =
         Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
 
-    private val domain = preferences.getString(MIRROR_PREF, "0")!!.toInt()
-        .coerceIn(0, MIRRORS.size - 1).let { MIRRORS[it] }
+    private val domain = preferences.getString(MIRROR_PREF, "0")!!
+        .toInt().coerceAtMost(MIRRORS.size - 1).let { MIRRORS[it] }
 
     override val baseUrl = "http://m.$domain"
     private val pcUrl = "http://www.$domain"
@@ -76,7 +78,7 @@ class Pufei : ParsedHttpSource(), ConfigurableSource {
         return MangasPage(mangas, false)
     }
 
-    private val searchCache = HashMap<String, String>(0)
+    private val searchCache = HashMap<String, String>()
 
     override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
         return if (query.isNotBlank()) {
@@ -168,17 +170,14 @@ class Pufei : ParsedHttpSource(), ConfigurableSource {
 
     override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers)
 
-    // Reference: https://github.com/evanw/packer/blob/master/packer.js
     override fun pageListParse(response: Response): List<Page> {
         val html = String(response.body!!.bytes(), GB2312).let(::ProgressiveParser)
         val base64 = html.substringBetween("cp=\"", "\"")
-        val packed = String(Base64.decode(base64, Base64.DEFAULT)).let(::ProgressiveParser)
-        packed.consumeUntil("p}('")
-        val imageList = packed.substringBetween("[", "]").replace("\\", "")
-        if (imageList.isEmpty()) return emptyList()
-        packed.consumeUntil("',")
-        val dictionary = packed.substringBetween("'", "'").split('|')
-        val result = unpack(imageList, dictionary).removeSurrounding("'").split("','")
+        val script = String(Base64.decode(base64, Base64.DEFAULT))
+        val result = Unpacker.unpack(script, "[", "]")
+            .ifEmpty { return emptyList() }
+            .replace("\\", "")
+            .removeSurrounding("\"").split("\",\"")
         // baseUrl/skin/2014mh/view.js (imgserver), mobileUrl/skin/main.js (IMH.reader)
         return result.mapIndexed { i, image ->
             val imageUrl = if (image.startsWith("http")) image else IMAGE_SERVER + image
@@ -194,14 +193,10 @@ class Pufei : ParsedHttpSource(), ConfigurableSource {
         ListPreference(screen.context).apply {
             key = MIRROR_PREF
             title = "使用镜像网站"
-            summary = "选择要使用的镜像网站,重启生效"
+            summary = "选择要使用的镜像网站,重启生效\n已选择:%s"
             entries = MIRRORS_DESCRIPTION
             entryValues = MIRROR_VALUES
             setDefaultValue("0")
-            setOnPreferenceChangeListener { _, newValue ->
-                preferences.edit().putString(MIRROR_PREF, newValue as String).apply()
-                true
-            }
         }.let { screen.addPreference(it) }
     }
 
diff --git a/src/zh/pufei/src/eu/kanade/tachiyomi/extension/zh/pufei/PufeiUtils.kt b/src/zh/pufei/src/eu/kanade/tachiyomi/extension/zh/pufei/PufeiUtils.kt
index 7c2f9dc0c..be0d23feb 100644
--- a/src/zh/pufei/src/eu/kanade/tachiyomi/extension/zh/pufei/PufeiUtils.kt
+++ b/src/zh/pufei/src/eu/kanade/tachiyomi/extension/zh/pufei/PufeiUtils.kt
@@ -26,39 +26,3 @@ internal val isNewDateLogic = AppInfo.getVersionCode() >= 81
 internal val dateFormat by lazy {
     SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ENGLISH)
 }
-
-internal class ProgressiveParser(private val text: String) {
-    private var startIndex = 0
-    fun consumeUntil(string: String) = with(text) { startIndex = indexOf(string, startIndex) + string.length }
-    fun substringBetween(left: String, right: String): String = with(text) {
-        val leftIndex = indexOf(left, startIndex) + left.length
-        val rightIndex = indexOf(right, leftIndex)
-        startIndex = rightIndex + right.length
-        return substring(leftIndex, rightIndex)
-    }
-}
-
-internal fun unpack(data: String, dictionary: List<String>): String {
-    val size = dictionary.size
-    return Regex("""\b\w+\b""").replace(data) {
-        with(it.value) {
-            val key = parseRadix62()
-            if (key >= size) return@replace this
-            val value = dictionary[key]
-            if (value.isEmpty()) return@replace this
-            return@replace value
-        }
-    }
-}
-
-private fun String.parseRadix62(): Int {
-    var result = 0
-    for (char in this) {
-        result = result * 62 + when {
-            char <= '9' -> char.code - '0'.code
-            char >= 'a' -> char.code - 'a'.code + 10
-            else -> char.code - 'A'.code + 36
-        }
-    }
-    return result
-}