diff --git a/src/fr/japscan/build.gradle b/src/fr/japscan/build.gradle
index 6d0c10670..f8935eae7 100644
--- a/src/fr/japscan/build.gradle
+++ b/src/fr/japscan/build.gradle
@@ -6,7 +6,7 @@ ext {
     extName = 'Japscan'
     pkgNameSuffix = 'fr.japscan'
     extClass = '.Japscan'
-    extVersionCode = 36
+    extVersionCode = 37
 }
 
 apply from: "$rootDir/common.gradle"
diff --git a/src/fr/japscan/src/eu/kanade/tachiyomi/extension/fr/japscan/Japscan.kt b/src/fr/japscan/src/eu/kanade/tachiyomi/extension/fr/japscan/Japscan.kt
index a805210d0..bd9ed93bf 100644
--- a/src/fr/japscan/src/eu/kanade/tachiyomi/extension/fr/japscan/Japscan.kt
+++ b/src/fr/japscan/src/eu/kanade/tachiyomi/extension/fr/japscan/Japscan.kt
@@ -87,7 +87,7 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
 
     override fun popularMangaNextPageSelector(): String? = null
 
-    override fun popularMangaSelector() = "#top_mangas_week li > span"
+    override fun popularMangaSelector() = "#top_mangas_week li"
 
     override fun popularMangaFromElement(element: Element): SManga {
         val manga = SManga.create()
@@ -117,7 +117,7 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
 
     override fun latestUpdatesNextPageSelector(): String? = null
 
-    override fun latestUpdatesSelector() = "#chapters > div > h3.text-truncate"
+    override fun latestUpdatesSelector() = "#chapters h3.text-truncate, #chapters_list h3.text-truncate"
 
     override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
 
@@ -207,12 +207,13 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
     }
 
     override fun mangaDetailsParse(document: Document): SManga {
-        val infoElement = document.select("div#main > .card > .card-body").first()
+        val infoElement = document.selectFirst("#main .card-body")
 
         val manga = SManga.create()
-        manga.thumbnail_url = "$baseUrl/${infoElement.select(".d-flex > div.m-2:eq(0) > img").attr("src")}"
+        manga.thumbnail_url = infoElement.select("img").attr("abs:src")
 
-        infoElement.select(".d-flex > div.m-2:eq(1) > p.mb-2").forEachIndexed { _, el ->
+        val infoRows = infoElement.select(".row, .d-flex")
+        infoRows.select("p").forEach { el ->
             when (el.select("span").text().trim()) {
                 "Auteur(s):" -> manga.author = el.text().replace("Auteur(s):", "").trim()
                 "Artiste(s):" -> manga.artist = el.text().replace("Artiste(s):", "").trim()
@@ -222,7 +223,7 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
                 }
             }
         }
-        manga.description = infoElement.select("> p").text().orEmpty()
+        manga.description = infoElement.select("div:contains(Synopsis) + p").text().orEmpty()
 
         return manga
     }
@@ -257,6 +258,10 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
         }
     }
 
+    private val decodingStringsRe: Regex = Regex("""'([\dA-Z]{62})'""", RegexOption.IGNORE_CASE)
+
+    private val sortedLookupString: List<Char> = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray().toList()
+
     override fun pageListParse(document: Document): List<Page> {
         /*
             JapScan stores chapter metadata in a `#data` element, and in the `data-data` attribute.
@@ -273,16 +278,11 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
         Log.d("japscan", "ZJS at $zjsurl")
         val zjs = client.newCall(GET(baseUrl + zjsurl, headers)).execute().body!!.string()
 
-        val stringLookupTables = zjs
-            .substringAfter("= [")
-            .substringBefore("];")
-            .split(",")
-            .mapNotNull {
-                it.trim().removeSurrounding("'").takeIf { unquoted ->
-                    unquoted.length == 62 &&
-                        unquoted.toCharArray().sorted().joinToString("") == "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
-                }
+        val stringLookupTables = decodingStringsRe.findAll(zjs).mapNotNull {
+            it.groupValues[1].takeIf {
+                it.toCharArray().sorted() == sortedLookupString
             }
+        }.toList()
 
         if (stringLookupTables.size != 2) {
             throw Exception("Attendait 2 chaînes de recherche dans ZJS, a trouvé ${stringLookupTables.size}")
@@ -297,6 +297,12 @@ class Japscan : ConfigurableSource, ParsedHttpSource() {
             val lookupTable = stringLookupTables[i].zip(stringLookupTables[otherIndice]).toMap()
             try {
                 val unscrambledData = scrambledData.map { lookupTable[it] ?: it }.joinToString("")
+                if (!unscrambledData.startsWith("ey")) {
+                    // `ey` is the Base64 representation of a curly bracket. Since we're expecting a
+                    // JSON object, we're counting this attempt as failed if it doesn't start with a
+                    // curly bracket.
+                    continue
+                }
                 val decoded = Base64.decode(unscrambledData, Base64.DEFAULT).toString(Charsets.UTF_8)
 
                 val data = json.parseToJsonElement(decoded).jsonObject