diff --git a/src/en/disasterscans/AndroidManifest.xml b/src/en/disasterscans/AndroidManifest.xml
deleted file mode 100644
index 3b3e56a7a..000000000
--- a/src/en/disasterscans/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <application>
-        <activity
-            android:name=".en.disasterscans.DisasterScansUrlActivity"
-            android:excludeFromRecents="true"
-            android:exported="true"
-            android:theme="@android:style/Theme.NoDisplay">
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.BROWSABLE" />
-
-                <data android:host="disasterscans.com" />
-                <data android:scheme="https" />
-
-                <data android:pathPattern="/comics/..*" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/src/en/disasterscans/build.gradle b/src/en/disasterscans/build.gradle
index cab0013f0..24c642934 100644
--- a/src/en/disasterscans/build.gradle
+++ b/src/en/disasterscans/build.gradle
@@ -1,7 +1,7 @@
 ext {
     extName = 'Disaster Scans'
     extClass = '.DisasterScans'
-    extVersionCode = 32
+    extVersionCode = 33
 }
 
 apply from: "$rootDir/common.gradle"
diff --git a/src/en/disasterscans/src/eu/kanade/tachiyomi/extension/en/disasterscans/DisasterScans.kt b/src/en/disasterscans/src/eu/kanade/tachiyomi/extension/en/disasterscans/DisasterScans.kt
index 393d16466..e1b18fe40 100644
--- a/src/en/disasterscans/src/eu/kanade/tachiyomi/extension/en/disasterscans/DisasterScans.kt
+++ b/src/en/disasterscans/src/eu/kanade/tachiyomi/extension/en/disasterscans/DisasterScans.kt
@@ -1,209 +1,129 @@
 package eu.kanade.tachiyomi.extension.en.disasterscans
 
-import android.app.Application
-import android.content.SharedPreferences
 import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.asObservableSuccess
-import eu.kanade.tachiyomi.network.interceptor.rateLimit
 import eu.kanade.tachiyomi.source.model.FilterList
 import eu.kanade.tachiyomi.source.model.MangasPage
 import eu.kanade.tachiyomi.source.model.Page
 import eu.kanade.tachiyomi.source.model.SChapter
 import eu.kanade.tachiyomi.source.model.SManga
-import eu.kanade.tachiyomi.source.online.HttpSource
+import eu.kanade.tachiyomi.source.online.ParsedHttpSource
 import eu.kanade.tachiyomi.util.asJsoup
+import kotlinx.serialization.Serializable
 import kotlinx.serialization.decodeFromString
 import kotlinx.serialization.json.Json
-import okhttp3.OkHttpClient
+import okhttp3.HttpUrl.Companion.toHttpUrl
 import okhttp3.Request
 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
 import uy.kohesive.injekt.injectLazy
 import java.text.SimpleDateFormat
 import java.util.Locale
 
-class DisasterScans : HttpSource() {
+class DisasterScans : ParsedHttpSource() {
 
     override val name = "Disaster Scans"
-
     override val lang = "en"
-
-    override val versionId = 2
-
+    override val versionId = 3
     override val baseUrl = "https://disasterscans.com"
+    override val supportsLatest = true
 
-    private val apiUrl = "https://api.disasterscans.com"
+    private val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US)
+    private val json by injectLazy<Json>()
 
-    override val supportsLatest = false
-
-    override val client: OkHttpClient = network.cloudflareClient.newBuilder()
-        .addInterceptor { chain ->
-            val request = chain.request()
-            val url = request.url
-            if (url.fragment == "thumbnail") {
-                val cdnUrl = preferences.getCdnUrl()
-                val requestUrl = url.toString().substringBefore("=") + "="
-                if (cdnUrl != requestUrl) {
-                    val fileId = url.queryParameterValues("fileId").first()
-                    return@addInterceptor chain.proceed(
-                        request.newBuilder()
-                            .url("$cdnUrl$fileId")
-                            .build(),
-                    )
-                }
-            }
-            return@addInterceptor chain.proceed(request)
-        }
-        .rateLimit(1)
-        .build()
-
-    private val json: Json by injectLazy()
-
-    private val preferences: SharedPreferences by lazy {
-        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
-    }
-
-    override fun popularMangaRequest(page: Int): Request {
-        return GET("$apiUrl/comics/search/comics", headers)
-    }
-
-    override fun popularMangaParse(response: Response): MangasPage {
-        val comics = response.parseAs<List<ApiSearchComic>>()
-
-        val cdnUrl = preferences.getCdnUrl()
-
-        return MangasPage(comics.map { it.toSManga(cdnUrl) }, false)
-    }
-
-    override fun fetchSearchManga(
-        page: Int,
-        query: String,
-        filters: FilterList,
-    ): Observable<MangasPage> {
-        return if (query.startsWith(PREFIX_SLUG)) {
-            val url = "/comics/${query.substringAfter(PREFIX_SLUG)}"
-            val manga = SManga.create().apply { this.url = url }
-            client.newCall(mangaDetailsRequest(manga))
-                .asObservableSuccess()
-                .map { mangaDetailsParse(it).apply { this.url = url } }
-                .map { MangasPage(listOf(it), false) }
-        } else {
-            client.newCall(searchMangaRequest(page, query, filters))
-                .asObservableSuccess()
-                .map { searchMangaParse(it, query) }
-        }
-    }
-
-    override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = popularMangaRequest(page)
-
-    private fun searchMangaParse(response: Response, query: String): MangasPage {
-        val comics = response.parseAs<List<ApiSearchComic>>()
-
-        val cdnUrl = preferences.getCdnUrl()
-
-        return comics
-            .filter { it.ComicTitle.contains(query, true) }
-            .map { it.toSManga(cdnUrl) }
-            .let { MangasPage(it, false) }
-    }
-
-    override fun mangaDetailsRequest(manga: SManga): Request {
-        return GET("$apiUrl${manga.url}", headers)
-    }
-
-    override fun mangaDetailsParse(response: Response): SManga {
-        val comic = response.parseAs<ApiComic>()
-
-        return comic.toSManga(json, preferences.getCdnUrl())
-    }
-
-    override fun getMangaUrl(manga: SManga) = "$baseUrl${manga.url}"
-
-    override fun chapterListRequest(manga: SManga): Request {
-        val url = "$apiUrl${manga.url.replace("comics", "chapters")}"
-        return GET(url, headers)
-    }
-
-    override fun chapterListParse(response: Response): List<SChapter> {
-        val chapters = response.parseAs<List<ApiChapter>>()
-
-        val mangaUrl = response.request.url.toString()
-            .substringAfter(apiUrl)
-            .replace("chapters", "comics")
-
-        return chapters.map { it.toSChapter(mangaUrl) }
-    }
-
-    override fun pageListParse(response: Response): List<Page> {
-        val document = response.asJsoup()
-
-        val chapterPages = document.select("#__NEXT_DATA__").html()
-            .parseAs<NextData<ApiChapterPages>>()
-            .props.pageProps.chapter.pages
-
-        val pages = chapterPages.parseAs<List<String>>()
-
-        val cdnUrl = updatedCdnUrl(document)
-
-        return pages.mapIndexed { idx, image ->
-            Page(idx, "", "$cdnUrl$image")
-        }
-    }
-
-    private fun updatedCdnUrl(document: Document): String {
-        val cdnUrlFromPage = document.selectFirst("main div.maxWidth img")
-            ?.attr("src")
-            ?.substringBefore("?")
-            ?.let { "$it?fileId=" }
-
-        return preferences.getCdnUrl()
-            .let {
-                if (it != cdnUrlFromPage && cdnUrlFromPage != null) {
-                    preferences.putCdnUrl(cdnUrlFromPage)
-                    cdnUrlFromPage
-                } else {
-                    it
-                }
-            }
-    }
-
-    private inline fun <reified T> String.parseAs(): T =
-        json.decodeFromString(this)
-
-    private inline fun <reified T> Response.parseAs(): T =
-        body.string().parseAs()
-
-    private fun SharedPreferences.getCdnUrl(): String {
-        return getString(cdnPref, fallbackCdnUrl) ?: fallbackCdnUrl
-    }
-
-    private fun SharedPreferences.putCdnUrl(url: String) {
-        edit().putString(cdnPref, url).commit()
-    }
-
-    companion object {
-        private const val fallbackCdnUrl = "https://f005.backblazeb2.com/b2api/v1/b2_download_file_by_id?fileId="
-        private const val cdnPref = "cdn_pref"
-        val titleSpecialCharactersRegex = "[^a-z0-9]+".toRegex()
-        val trailingHyphenRegex = "-+$".toRegex()
-        val dateFormat by lazy {
-            SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH)
-        }
-        const val PREFIX_SLUG = "slug:"
-    }
-
-    override fun searchMangaParse(response: Response) =
-        throw UnsupportedOperationException()
-
-    override fun imageUrlParse(response: Response) =
-        throw UnsupportedOperationException()
-
-    override fun latestUpdatesParse(response: Response) =
-        throw UnsupportedOperationException()
+    override fun popularMangaRequest(page: Int): Request =
+        GET("$baseUrl/home", headers)
 
     override fun latestUpdatesRequest(page: Int) =
-        throw UnsupportedOperationException()
+        popularMangaRequest(page)
+
+    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
+        GET("$baseUrl/comics", headers)
+
+    override fun popularMangaSelector(): String = "div:has(span:contains(POPULAR)) + section a:has(img)"
+
+    override fun latestUpdatesSelector(): String = "div:has(span:contains(LATEST)) + section a:has(img)"
+
+    override fun searchMangaSelector(): String = ".grid a"
+
+    private fun mangaFromElement(element: Element): SManga = SManga.create().apply {
+        setUrlWithoutDomain(element.absUrl("href"))
+        thumbnail_url = element.selectFirst("img")?.absUrl("src")
+    }
+
+    override fun popularMangaFromElement(element: Element): SManga = mangaFromElement(element).apply {
+        title = element.selectFirst("h5")!!.text()
+    }
+
+    override fun latestUpdatesFromElement(element: Element): SManga = mangaFromElement(element).apply {
+        title = element.parent()?.selectFirst("div a")!!.text()
+    }
+
+    override fun searchMangaFromElement(element: Element): SManga = mangaFromElement(element).apply {
+        title = element.selectFirst("h1")!!.text()
+    }
+
+    override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
+        val response = client.newCall(searchMangaRequest(page, query, filters)).execute()
+        val mangaList = response.asJsoup().select(searchMangaSelector())
+            .map { searchMangaFromElement(it) }
+            .filter { it.title.lowercase().contains(query.lowercase()) }
+        return Observable.just(MangasPage(mangaList, false))
+    }
+
+    override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
+        author = document.selectFirst("span:contains(Author) + span")!!.text()
+
+        document.selectFirst("section div div")?.children()?.also { infoRows ->
+            infoRows[0].selectFirst("h1")?.text()?.let { title = it }
+            description = infoRows[2].text()
+
+            with(infoRows[1].select("span")) {
+                status = when (this.removeAt(0)?.text()?.lowercase()) {
+                    "ongoing" -> SManga.ONGOING
+                    else -> SManga.UNKNOWN
+                }
+                genre = this.joinToString { text() }
+            }
+        }
+    }
+
+    @Serializable
+    class ChapterDTO(val chapterID: Int, val ChapterNumber: String, val ChapterName: String, val chapterDate: String)
+    private val chapterDataRegex = Regex("""\\"chapters\\":(\[.*]),\\"param\\":\\"(\S+)\\"\}""")
+
+    override fun chapterListParse(response: Response): List<SChapter> {
+        chapterDataRegex.find(response.body.string())?.destructured?.also { (chapterData, mangaId) ->
+            return json.decodeFromString<List<ChapterDTO>>(chapterData).map { chapter ->
+                SChapter.create().apply {
+                    name = "Chapter ${chapter.ChapterNumber} - ${chapter.ChapterName}"
+                    setUrlWithoutDomain(
+                        baseUrl.toHttpUrl().newBuilder().apply {
+                            addPathSegment("comics")
+                            addPathSegment(mangaId)
+                            addPathSegment("${chapter.chapterID}-chapter-${chapter.ChapterNumber}")
+                        }.build().toString(),
+                    )
+
+                    date_upload = try {
+                        dateFormat.parse(chapter.chapterDate)?.time ?: 0
+                    } catch (_: Exception) {
+                        0
+                    }
+                }
+            }
+        }
+        return listOf()
+    }
+
+    override fun pageListParse(document: Document): List<Page> =
+        document.select("section img").mapIndexed { index, img -> Page(index, imageUrl = img.absUrl("src")) }
+
+    override fun popularMangaNextPageSelector(): String? = null
+    override fun latestUpdatesNextPageSelector(): String? = null
+    override fun searchMangaNextPageSelector(): String? = null
+    override fun imageUrlParse(document: Document): String = ""
+    override fun chapterListSelector(): String = ""
+    override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException()
 }
diff --git a/src/en/disasterscans/src/eu/kanade/tachiyomi/extension/en/disasterscans/DisasterScansDto.kt b/src/en/disasterscans/src/eu/kanade/tachiyomi/extension/en/disasterscans/DisasterScansDto.kt
deleted file mode 100644
index 490674e42..000000000
--- a/src/en/disasterscans/src/eu/kanade/tachiyomi/extension/en/disasterscans/DisasterScansDto.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-package eu.kanade.tachiyomi.extension.en.disasterscans
-
-import eu.kanade.tachiyomi.source.model.SChapter
-import eu.kanade.tachiyomi.source.model.SManga
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.decodeFromString
-import kotlinx.serialization.json.Json
-
-@Serializable
-data class ApiSearchComic(
-    val id: String,
-    val ComicTitle: String,
-    val CoverImage: String,
-) {
-    fun toSManga(cdnUrl: String) = SManga.create().apply {
-        title = ComicTitle
-        thumbnail_url = "$cdnUrl$CoverImage#thumbnail"
-        url = "/comics/$id-${ComicTitle.titleToSlug()}"
-    }
-}
-
-@Serializable
-data class ApiComic(
-    val id: String,
-    val ComicTitle: String,
-    val Description: String,
-    val CoverImage: String,
-    val Status: String,
-    val Genres: String,
-    val Author: String,
-    val Artist: String,
-) {
-    fun toSManga(json: Json, cdnUrl: String) = SManga.create().apply {
-        title = ComicTitle
-        thumbnail_url = "$cdnUrl$CoverImage#thumbnail"
-        url = "/comics/$id-${ComicTitle.titleToSlug()}"
-        description = Description
-        author = Author
-        artist = Artist
-        genre = json.decodeFromString<List<String>>(Genres).joinToString()
-        status = Status.parseStatus()
-    }
-}
-
-@Serializable
-data class ApiChapter(
-    val chapterID: Int,
-    val chapterNumber: String,
-    val ChapterName: String,
-    val chapterDate: String,
-) {
-    fun toSChapter(mangaUrl: String) = SChapter.create().apply {
-        url = "$mangaUrl/$chapterID-chapter-$chapterNumber"
-        chapter_number = chapterNumber.toFloat()
-        name = "Chapter $chapterNumber"
-        if (ChapterName.isNotEmpty()) {
-            name += ": $ChapterName"
-        }
-        date_upload = chapterDate.parseDate()
-    }
-}
-
-@Serializable
-data class NextData<T>(
-    val props: Props<T>,
-) {
-    @Serializable
-    data class Props<T>(val pageProps: T)
-}
-
-@Serializable
-data class ApiChapterPages(
-    val chapter: ApiPages,
-) {
-    @Serializable
-    data class ApiPages(val pages: String)
-}
-
-private fun String.titleToSlug() = this.trim()
-    .lowercase()
-    .replace(DisasterScans.titleSpecialCharactersRegex, "-")
-    .replace(DisasterScans.trailingHyphenRegex, "")
-
-private fun String.parseDate(): Long {
-    return runCatching {
-        DisasterScans.dateFormat.parse(this)!!.time
-    }.getOrDefault(0L)
-}
-
-private fun String.parseStatus(): Int {
-    return when {
-        contains("ongoing", true) -> SManga.ONGOING
-        contains("completed", true) -> SManga.COMPLETED
-        else -> SManga.UNKNOWN
-    }
-}
diff --git a/src/en/disasterscans/src/eu/kanade/tachiyomi/extension/en/disasterscans/DisasterScansUrlActivity.kt b/src/en/disasterscans/src/eu/kanade/tachiyomi/extension/en/disasterscans/DisasterScansUrlActivity.kt
deleted file mode 100644
index 63fdd53a6..000000000
--- a/src/en/disasterscans/src/eu/kanade/tachiyomi/extension/en/disasterscans/DisasterScansUrlActivity.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package eu.kanade.tachiyomi.extension.en.disasterscans
-
-import android.app.Activity
-import android.content.ActivityNotFoundException
-import android.content.Intent
-import android.os.Bundle
-import android.util.Log
-import kotlin.system.exitProcess
-
-class DisasterScansUrlActivity : Activity() {
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        val pathSegments = intent?.data?.pathSegments
-        if (pathSegments != null && pathSegments.size > 1) {
-            val slug = pathSegments[1]
-            val mainIntent = Intent().apply {
-                action = "eu.kanade.tachiyomi.SEARCH"
-                putExtra("query", "${DisasterScans.PREFIX_SLUG}$slug")
-                putExtra("filter", packageName)
-            }
-
-            try {
-                startActivity(mainIntent)
-            } catch (e: ActivityNotFoundException) {
-                Log.e("DisasterScansUrl", e.toString())
-            }
-        } else {
-            Log.e("DisasterScansUrl", "could not parse uri from intent $intent")
-        }
-
-        finish()
-        exitProcess(0)
-    }
-}