From 9e00ce42fee6122e85f983716ff884b05b34dc13 Mon Sep 17 00:00:00 2001
From: e-shl <35057681+e-shl@users.noreply.github.com>
Date: Sun, 19 Dec 2021 17:05:21 +0500
Subject: [PATCH] New [RU] Hentailib (#10135)

* New [RU] Hentailib

* -mangalib

* option change language in latest

* icon

* icon microperfect
---
 src/ru/libhentai/AndroidManifest.xml          |  25 +
 src/ru/libhentai/build.gradle                 |  17 +
 .../libhentai/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1886 bytes
 .../libhentai/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1578 bytes
 .../res/mipmap-xhdpi/ic_launcher.png          | Bin 0 -> 2549 bytes
 .../res/mipmap-xxhdpi/ic_launcher.png         | Bin 0 -> 3670 bytes
 .../res/mipmap-xxxhdpi/ic_launcher.png        | Bin 0 -> 5052 bytes
 src/ru/libhentai/res/web_hi_res_512.png       | Bin 0 -> 10925 bytes
 .../extension/ru/libhentai/LibHentai.kt       | 889 ++++++++++++++++++
 .../ru/libhentai/LibHentaiActivity.kt         |  41 +
 10 files changed, 972 insertions(+)
 create mode 100644 src/ru/libhentai/AndroidManifest.xml
 create mode 100644 src/ru/libhentai/build.gradle
 create mode 100644 src/ru/libhentai/res/mipmap-hdpi/ic_launcher.png
 create mode 100644 src/ru/libhentai/res/mipmap-mdpi/ic_launcher.png
 create mode 100644 src/ru/libhentai/res/mipmap-xhdpi/ic_launcher.png
 create mode 100644 src/ru/libhentai/res/mipmap-xxhdpi/ic_launcher.png
 create mode 100644 src/ru/libhentai/res/mipmap-xxxhdpi/ic_launcher.png
 create mode 100644 src/ru/libhentai/res/web_hi_res_512.png
 create mode 100644 src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentai.kt
 create mode 100644 src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentaiActivity.kt

diff --git a/src/ru/libhentai/AndroidManifest.xml b/src/ru/libhentai/AndroidManifest.xml
new file mode 100644
index 000000000..c33cfb934
--- /dev/null
+++ b/src/ru/libhentai/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="eu.kanade.tachiyomi.extension">
+
+    <application>
+        <activity
+            android:name=".ru.libhentai.LibHentaiActivity"
+            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" />
+
+                <!-- LibHentaiActivity sites can be added here. -->
+                <data
+                    android:host="hentailib.me"
+                    android:pathPattern="/..*/v..*"
+                    android:scheme="https" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/src/ru/libhentai/build.gradle b/src/ru/libhentai/build.gradle
new file mode 100644
index 000000000..78fa3a4e6
--- /dev/null
+++ b/src/ru/libhentai/build.gradle
@@ -0,0 +1,17 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlinx-serialization'
+
+ext {
+    extName = 'HentaiLib'
+    pkgNameSuffix = 'ru.libhentai'
+    extClass = '.LibHentai'
+    extVersionCode = 1
+    isNsfw = true
+}
+
+dependencies {
+    implementation project(path: ':lib-ratelimit')
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/ru/libhentai/res/mipmap-hdpi/ic_launcher.png b/src/ru/libhentai/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..41cb9dd7a2abd57e448c2a5f3232d5cd05bbde10
GIT binary patch
literal 1886
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
YKXUD{nfr<4-2eap07*qoM6N<$f;uu$vH$=8

literal 0
HcmV?d00001

diff --git a/src/ru/libhentai/res/mipmap-mdpi/ic_launcher.png b/src/ru/libhentai/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..40d1a9c7844e3a332f21ea6202a7527c3aec431f
GIT binary patch
literal 1578
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

literal 0
HcmV?d00001

diff --git a/src/ru/libhentai/res/mipmap-xhdpi/ic_launcher.png b/src/ru/libhentai/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..ca16a46fd4f52ac8a78839acad9ca5ae6719a76b
GIT binary patch
literal 2549
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
LNkvXXu0mjfN_n$n

literal 0
HcmV?d00001

diff --git a/src/ru/libhentai/res/mipmap-xxhdpi/ic_launcher.png b/src/ru/libhentai/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..01ba951d014e4a838e80248cd40785b71acb2f38
GIT binary patch
literal 3670
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
EKSv;pMgRZ+

literal 0
HcmV?d00001

diff --git a/src/ru/libhentai/res/mipmap-xxxhdpi/ic_launcher.png b/src/ru/libhentai/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..8171958cd35ac6f9ee62cab100454dfa4125c3ab
GIT binary patch
literal 5052
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
b##4S;PpL`QD$Eu9l>>GMoNX$t$;tl*(VaMO

literal 0
HcmV?d00001

diff --git a/src/ru/libhentai/res/web_hi_res_512.png b/src/ru/libhentai/res/web_hi_res_512.png
new file mode 100644
index 0000000000000000000000000000000000000000..ace364704002a01c29b3553e202ad4a4c95e9ca6
GIT binary patch
literal 10925
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
OPd;8W_uNO)fBzqCRPF8n

literal 0
HcmV?d00001

diff --git a/src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentai.kt b/src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentai.kt
new file mode 100644
index 000000000..5c9ef8e0b
--- /dev/null
+++ b/src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentai.kt
@@ -0,0 +1,889 @@
+package eu.kanade.tachiyomi.extension.ru.libhentai
+
+import android.app.Application
+import android.content.SharedPreferences
+import android.widget.Toast
+import androidx.preference.ListPreference
+import androidx.preference.PreferenceScreen
+import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.POST
+import eu.kanade.tachiyomi.network.asObservableSuccess
+import eu.kanade.tachiyomi.source.ConfigurableSource
+import eu.kanade.tachiyomi.source.model.Filter
+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.util.asJsoup
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.JsonArray
+import kotlinx.serialization.json.JsonElement
+import kotlinx.serialization.json.JsonObject
+import kotlinx.serialization.json.contentOrNull
+import kotlinx.serialization.json.int
+import kotlinx.serialization.json.intOrNull
+import kotlinx.serialization.json.jsonArray
+import kotlinx.serialization.json.jsonObject
+import kotlinx.serialization.json.jsonPrimitive
+import okhttp3.Headers
+import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.Response
+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
+import java.util.concurrent.TimeUnit
+
+class LibHentai : ConfigurableSource, HttpSource() {
+
+    private val json: Json by injectLazy()
+
+    private val preferences: SharedPreferences by lazy {
+        Injekt.get<Application>().getSharedPreferences("source_${id}_2", 0x0000)
+    }
+
+    override val name: String = "Hentailib"
+
+    override val lang = "ru"
+
+    override val supportsLatest = true
+
+    override val client: OkHttpClient = network.cloudflareClient.newBuilder()
+        .connectTimeout(10, TimeUnit.SECONDS)
+        .readTimeout(30, TimeUnit.SECONDS)
+        .addNetworkInterceptor(RateLimitInterceptor(3))
+        .build()
+
+    override val baseUrl = "https://hentailib.me"
+
+    override fun headersBuilder() = Headers.Builder().apply {
+        add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
+        add("Accept", "image/webp,*/*;q=0.8")
+        add("Referer", baseUrl)
+    }
+
+    override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers)
+
+    private val latestUpdatesSelector = "div.updates__item"
+
+    override fun latestUpdatesParse(response: Response): MangasPage {
+        val elements = response.asJsoup().select(latestUpdatesSelector)
+        val latestMangas = elements?.map { latestUpdatesFromElement(it) }
+        if (latestMangas != null)
+            return MangasPage(latestMangas, false) // TODO: use API
+        return MangasPage(emptyList(), false)
+    }
+
+    private fun latestUpdatesFromElement(element: Element): SManga {
+        val manga = SManga.create()
+        element.select("div.cover").first().let { img ->
+            manga.thumbnail_url = img.attr("data-src").replace("_thumb", "_250x350")
+        }
+
+        element.select("a").first().let { link ->
+            manga.setUrlWithoutDomain(link.attr("href"))
+            manga.title = if (titleLanguage.equals("rus") || element.select(".updates__name_rus").isNullOrEmpty()) { element.select("h4").first().text() } else element.select(".updates__name_rus").first().text()
+        }
+        return manga
+    }
+
+    private var csrfToken: String = ""
+
+    private fun catalogHeaders() = Headers.Builder()
+        .apply {
+            add("Accept", "application/json, text/plain, */*")
+            add("X-Requested-With", "XMLHttpRequest")
+            add("x-csrf-token", csrfToken)
+        }
+        .build()
+
+    override fun popularMangaRequest(page: Int) = GET("$baseUrl/login", headers)
+
+    override fun fetchPopularManga(page: Int): Observable<MangasPage> {
+        if (csrfToken.isEmpty()) {
+            return client.newCall(popularMangaRequest(page))
+                .asObservableSuccess()
+                .flatMap { response ->
+                    // Obtain token
+                    val resBody = response.body!!.string()
+                    csrfToken = "_token\" content=\"(.*)\"".toRegex().find(resBody)!!.groups[1]!!.value
+                    return@flatMap fetchPopularMangaFromApi(page)
+                }
+        }
+        return fetchPopularMangaFromApi(page)
+    }
+
+    private fun fetchPopularMangaFromApi(page: Int): Observable<MangasPage> {
+        return client.newCall(POST("$baseUrl/filterlist?dir=desc&sort=views&page=$page", catalogHeaders()))
+            .asObservableSuccess()
+            .map { response ->
+                popularMangaParse(response)
+            }
+    }
+
+    override fun popularMangaParse(response: Response): MangasPage {
+        val resBody = response.body!!.string()
+        val result = json.decodeFromString<JsonObject>(resBody)
+        val items = result["items"]!!.jsonObject
+        val popularMangas = items["data"]?.jsonArray?.map { popularMangaFromElement(it) }
+
+        if (popularMangas != null) {
+            val hasNextPage = items["next_page_url"]?.jsonPrimitive?.contentOrNull != null
+            return MangasPage(popularMangas, hasNextPage)
+        }
+        return MangasPage(emptyList(), false)
+    }
+
+    private fun popularMangaFromElement(el: JsonElement) = SManga.create().apply {
+        val slug = el.jsonObject["slug"]!!.jsonPrimitive.content
+        val cover = el.jsonObject["cover"]!!.jsonPrimitive.content
+        title = if (titleLanguage.equals("rus")) el.jsonObject["rus_name"]!!.jsonPrimitive.content else el.jsonObject["name"]!!.jsonPrimitive.content
+        thumbnail_url = "$COVER_URL/huploads/cover/$slug/cover/${cover}_250x350.jpg"
+        url = "/$slug"
+    }
+
+    override fun mangaDetailsParse(response: Response): SManga {
+        val document = response.asJsoup()
+
+        if (document.select("body[data-page=home]").isNotEmpty())
+            throw Exception("Can't open manga. Try log in via WebView")
+
+        val manga = SManga.create()
+
+        val body = document.select("div.media-info-list").first()
+        val rawCategory = body.select("div.media-info-list__title:contains(Тип) + div").text()
+        val category = when {
+            rawCategory == "Комикс западный" -> "Комикс"
+            rawCategory.isNotBlank() -> rawCategory
+            else -> "Манга"
+        }
+        var rawAgeStop = body.select("div.media-info-list__title:contains(Возрастной рейтинг) + div").text()
+        if (rawAgeStop.isEmpty()) {
+            rawAgeStop = "0+"
+        }
+
+        val ratingValue = document.select(".media-rating.media-rating_lg div.media-rating__value").text().toFloat() * 2
+        val ratingVotes = document.select(".media-rating.media-rating_lg div.media-rating__votes").text()
+        val ratingStar = when {
+            ratingValue > 9.5 -> "★★★★★"
+            ratingValue > 8.5 -> "★★★★✬"
+            ratingValue > 7.5 -> "★★★★☆"
+            ratingValue > 6.5 -> "★★★✬☆"
+            ratingValue > 5.5 -> "★★★☆☆"
+            ratingValue > 4.5 -> "★★✬☆☆"
+            ratingValue > 3.5 -> "★★☆☆☆"
+            ratingValue > 2.5 -> "★✬☆☆☆"
+            ratingValue > 1.5 -> "★☆☆☆☆"
+            ratingValue > 0.5 -> "✬☆☆☆☆"
+            else -> "☆☆☆☆☆"
+        }
+        val genres = document.select(".media-tags > a").map { it.text().capitalize() }
+        manga.title = if (titleLanguage.equals("rus")) document.select(".media-name__main").text() else document.select(".media-name__alt").text()
+        manga.thumbnail_url = document.select(".media-sidebar__cover > img").attr("src")
+        manga.author = body.select("div.media-info-list__title:contains(Автор) + div").text()
+        manga.artist = body.select("div.media-info-list__title:contains(Художник) + div").text()
+        manga.status = if (document.html().contains("Манга удалена по просьбе правообладателей") ||
+            document.html().contains("Данный тайтл лицензирован на территории РФ.")
+        ) {
+            SManga.LICENSED
+        } else
+            when (
+                body.select("div.media-info-list__title:contains(Статус перевода) + div")
+                    .text()
+                    .toLowerCase(Locale.ROOT)
+            ) {
+                "продолжается" -> SManga.ONGOING
+                "завершен" -> SManga.COMPLETED
+                else -> SManga.UNKNOWN
+            }
+        manga.genre = genres.plusElement(category).plusElement(rawAgeStop).joinToString { it.trim() }
+        val altSelector = document.select(".media-info-list__item_alt-names .media-info-list__value div")
+        var altName = ""
+        if (altSelector.isNotEmpty()) {
+            altName = "Альтернативные названия:\n" + altSelector.map { it.text() }.joinToString(" / ") + "\n\n"
+        }
+        val mediaNameLanguage = if (titleLanguage.equals("rus")) document.select(".media-name__alt").text() else document.select(".media-name__main").text()
+        manga.description = mediaNameLanguage + "\n" + ratingStar + " " + ratingValue + " (голосов: " + ratingVotes + ")\n" + altName + document.select(".media-description__text").text()
+        return manga
+    }
+
+    override fun chapterListParse(response: Response): List<SChapter> {
+        val document = response.asJsoup()
+        if (document.html().contains("Манга удалена по просьбе правообладателей") ||
+            document.html().contains("Данный тайтл лицензирован на территории РФ.")
+        ) {
+            return emptyList()
+        }
+        val dataStr = document
+            .toString()
+            .substringAfter("window.__DATA__ = ")
+            .substringBefore("window._SITE_COLOR_")
+            .substringBeforeLast(";")
+
+        val data = json.decodeFromString<JsonObject>(dataStr)
+        val chaptersList = data["chapters"]!!.jsonObject["list"]?.jsonArray
+        val slug = data["manga"]!!.jsonObject["slug"]!!.jsonPrimitive.content
+        val branches = data["chapters"]!!.jsonObject["branches"]!!.jsonArray.reversed()
+        val sortingList = preferences.getString(SORTING_PREF, "ms_mixing")
+
+        val chapters: List<SChapter>? = if (branches.isNotEmpty() && !sortingList.equals("ms_mixing")) {
+            sortChaptersByTranslator(sortingList, chaptersList, slug, branches)
+        } else {
+            chaptersList
+                ?.filter { it.jsonObject["status"]?.jsonPrimitive?.intOrNull != 2 }
+                ?.map { chapterFromElement(it, sortingList, slug) }
+        }
+
+        return chapters ?: emptyList()
+    }
+
+    private fun sortChaptersByTranslator
+    (sortingList: String?, chaptersList: JsonArray?, slug: String, branches: List<JsonElement>): List<SChapter>? {
+        var chapters: List<SChapter>? = null
+        when (sortingList) {
+            "ms_combining" -> {
+                val tempChaptersList = mutableListOf<SChapter>()
+                for (currentBranch in branches.withIndex()) {
+                    val teamId = branches[currentBranch.index].jsonObject["id"]!!.jsonPrimitive.int
+                    chapters = chaptersList
+                        ?.filter { it.jsonObject["branch_id"]?.jsonPrimitive?.intOrNull == teamId && it.jsonObject["status"]?.jsonPrimitive?.intOrNull != 2 }
+                        ?.map { chapterFromElement(it, sortingList, slug, teamId, branches) }
+                    chapters?.let { tempChaptersList.addAll(it) }
+                }
+                chapters = tempChaptersList
+            }
+            "ms_largest" -> {
+                val sizesChaptersLists = mutableListOf<Int>()
+                for (currentBranch in branches.withIndex()) {
+                    val teamId = branches[currentBranch.index].jsonObject["id"]!!.jsonPrimitive.int
+                    val chapterSize = chaptersList
+                        ?.filter { it.jsonObject["branch_id"]?.jsonPrimitive?.intOrNull == teamId }!!.size
+                    sizesChaptersLists.add(chapterSize)
+                }
+                val max = sizesChaptersLists.indexOfFirst { it == sizesChaptersLists.maxOrNull() ?: 0 }
+                val teamId = branches[max].jsonObject["id"]!!.jsonPrimitive.int
+
+                chapters = chaptersList
+                    ?.filter { it.jsonObject["branch_id"]?.jsonPrimitive?.intOrNull == teamId && it.jsonObject["status"]?.jsonPrimitive?.intOrNull != 2 }
+                    ?.map { chapterFromElement(it, sortingList, slug, teamId, branches) }
+            }
+            "ms_active" -> {
+                for (currentBranch in branches.withIndex()) {
+                    val teams = branches[currentBranch.index].jsonObject["teams"]!!.jsonArray
+                    for (currentTeam in teams.withIndex()) {
+                        if (teams[currentTeam.index].jsonObject["is_active"]!!.jsonPrimitive.int == 1) {
+                            val teamId = branches[currentBranch.index].jsonObject["id"]!!.jsonPrimitive.int
+                            chapters = chaptersList
+                                ?.filter { it.jsonObject["branch_id"]?.jsonPrimitive?.intOrNull == teamId && it.jsonObject["status"]?.jsonPrimitive?.intOrNull != 2 }
+                                ?.map { chapterFromElement(it, sortingList, slug, teamId, branches) }
+                            break
+                        }
+                    }
+                }
+                chapters ?: throw Exception("Активный перевод не назначен на сайте")
+            }
+        }
+
+        return chapters
+    }
+
+    private fun chapterFromElement
+    (chapterItem: JsonElement, sortingList: String?, slug: String, teamIdParam: Int? = null, branches: List<JsonElement>? = null): SChapter {
+        val chapter = SChapter.create()
+
+        val volume = chapterItem.jsonObject["chapter_volume"]!!.jsonPrimitive.int
+        val number = chapterItem.jsonObject["chapter_number"]!!.jsonPrimitive.content
+        val teamId = if (teamIdParam != null) "?bid=$teamIdParam" else ""
+
+        val url = "$baseUrl/$slug/v$volume/c$number$teamId"
+
+        chapter.setUrlWithoutDomain(url)
+
+        val nameChapter = chapterItem.jsonObject["chapter_name"]?.jsonPrimitive?.contentOrNull
+        val fullNameChapter = "Том $volume. Глава $number"
+
+        if (!sortingList.equals("ms_mixing")) {
+            chapter.scanlator = branches?.let { getScanlatorTeamName(it, chapterItem) } ?: chapterItem.jsonObject["username"]!!.jsonPrimitive.content
+        }
+        chapter.name = if (nameChapter.isNullOrBlank()) fullNameChapter else "$fullNameChapter - $nameChapter"
+        chapter.date_upload = SimpleDateFormat("yyyy-MM-dd", Locale.US)
+            .parse(chapterItem.jsonObject["chapter_created_at"]!!.jsonPrimitive.content.substringBefore(" "))?.time ?: 0L
+
+        return chapter
+    }
+
+    private fun getScanlatorTeamName(branches: List<JsonElement>, chapterItem: JsonElement): String? {
+        var scanlatorData: String? = null
+        for (currentBranch in branches.withIndex()) {
+            val branch = branches[currentBranch.index].jsonObject
+            val teams = branch["teams"]!!.jsonArray
+            if (chapterItem.jsonObject["branch_id"]!!.jsonPrimitive.int == branch["id"]!!.jsonPrimitive.int) {
+                for (currentTeam in teams.withIndex()) {
+                    val team = teams[currentTeam.index].jsonObject
+                    val scanlatorId = chapterItem.jsonObject["chapter_scanlator_id"]!!.jsonPrimitive.int
+                    scanlatorData = if ((scanlatorId == team.jsonObject["id"]!!.jsonPrimitive.int) ||
+                        (scanlatorId == 0 && team["is_active"]!!.jsonPrimitive.int == 1)
+                    ) team["name"]!!.jsonPrimitive.content else branch["teams"]!!.jsonArray[0].jsonObject["name"]!!.jsonPrimitive.content
+                }
+            }
+        }
+        return scanlatorData
+    }
+
+    override fun prepareNewChapter(chapter: SChapter, manga: SManga) {
+        """Глава\s(\d+)""".toRegex().find(chapter.name)?.let {
+            val number = it.groups[1]?.value!!
+            chapter.chapter_number = number.toFloat()
+        }
+    }
+
+    override fun pageListParse(response: Response): List<Page> {
+        val document = response.asJsoup()
+
+        val redirect = document.html()
+        if (!redirect.contains("window.__info")) {
+            if (redirect.contains("hold-transition login-page")) {
+                throw Exception("Для просмотра 18+ контента необходима авторизация через WebView")
+            } else if (redirect.contains("header__logo")) {
+                throw Exception("Лицензировано - Главы не доступны")
+            }
+        }
+
+        val chapInfo = document
+            .select("script:containsData(window.__info)")
+            .first()
+            .html()
+            .split("window.__info = ")
+            .last()
+            .trim()
+            .split(";")
+            .first()
+
+        val chapInfoJson = json.decodeFromString<JsonObject>(chapInfo)
+        val servers = chapInfoJson["servers"]!!.jsonObject.toMap()
+        val defaultServer: String = chapInfoJson["img"]!!.jsonObject["server"]!!.jsonPrimitive.content
+        val autoServer = setOf("secondary", "fourth", defaultServer, "compress")
+        val imgUrl: String = chapInfoJson["img"]!!.jsonObject["url"]!!.jsonPrimitive.content
+
+        val serverToUse = when (this.server) {
+            null -> autoServer
+            "auto" -> autoServer
+            else -> listOf(this.server)
+        }
+
+        // Get pages
+        val pagesArr = document
+            .select("script:containsData(window.__pg)")
+            .first()
+            .html()
+            .trim()
+            .removePrefix("window.__pg = ")
+            .removeSuffix(";")
+
+        val pagesJson = json.decodeFromString<JsonArray>(pagesArr)
+        val pages = mutableListOf<Page>()
+
+        pagesJson.forEach { page ->
+            val keys = servers.keys.filter { serverToUse.indexOf(it) >= 0 }.sortedBy { serverToUse.indexOf(it) }
+            val serversUrls = keys.map {
+                servers[it]?.jsonPrimitive?.contentOrNull + imgUrl + page.jsonObject["u"]!!.jsonPrimitive.content
+            }.joinToString(separator = ",,") { it }
+            pages.add(Page(page.jsonObject["p"]!!.jsonPrimitive.int, serversUrls))
+        }
+
+        return pages
+    }
+
+    private fun checkImage(url: String): Boolean {
+        val response = client.newCall(Request.Builder().url(url).head().headers(headers).build()).execute()
+        return response.isSuccessful && (response.header("content-length", "0")?.toInt()!! > 320)
+    }
+
+    override fun fetchImageUrl(page: Page): Observable<String> {
+        if (page.imageUrl != null) {
+            return Observable.just(page.imageUrl)
+        }
+
+        val urls = page.url.split(",,")
+        if (urls.size == 1) {
+            return Observable.just(urls[0])
+        }
+
+        return Observable.from(urls).filter { checkImage(it) }.first()
+    }
+
+    override fun imageUrlParse(response: Response): String = ""
+
+    private fun searchMangaByIdRequest(id: String): Request {
+        return GET("$baseUrl/$id", headers)
+    }
+
+    override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
+        return if (query.startsWith(PREFIX_SLUG_SEARCH)) {
+            val realQuery = query.removePrefix(PREFIX_SLUG_SEARCH)
+            client.newCall(searchMangaByIdRequest(realQuery))
+                .asObservableSuccess()
+                .map { response ->
+                    val details = mangaDetailsParse(response)
+                    details.url = "/$realQuery"
+                    MangasPage(listOf(details), false)
+                }
+        } else {
+            client.newCall(searchMangaRequest(page, query, filters))
+                .asObservableSuccess()
+                .map { response ->
+                    searchMangaParse(response)
+                }
+        }
+    }
+
+    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+        if (csrfToken.isEmpty()) {
+            val tokenResponse = client.newCall(popularMangaRequest(page)).execute()
+            val resBody = tokenResponse.body!!.string()
+            csrfToken = "_token\" content=\"(.*)\"".toRegex().find(resBody)!!.groups[1]!!.value
+        }
+        val url = "$baseUrl/filterlist?page=$page".toHttpUrlOrNull()!!.newBuilder()
+        if (query.isNotEmpty()) {
+            url.addQueryParameter("name", query)
+        }
+        (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
+            when (filter) {
+                is CategoryList -> filter.state.forEach { category ->
+                    if (category.state) {
+                        url.addQueryParameter("types[]", category.id)
+                    }
+                }
+                is FormatList -> filter.state.forEach { forma ->
+                    if (forma.state != Filter.TriState.STATE_IGNORE) {
+                        url.addQueryParameter(if (forma.isIncluded()) "format[include][]" else "format[exclude][]", forma.id)
+                    }
+                }
+                is StatusList -> filter.state.forEach { status ->
+                    if (status.state) {
+                        url.addQueryParameter("status[]", status.id)
+                    }
+                }
+                is StatusTitleList -> filter.state.forEach { title ->
+                    if (title.state) {
+                        url.addQueryParameter("manga_status[]", title.id)
+                    }
+                }
+                is GenreList -> filter.state.forEach { genre ->
+                    if (genre.state != Filter.TriState.STATE_IGNORE) {
+                        url.addQueryParameter(if (genre.isIncluded()) "genres[include][]" else "genres[exclude][]", genre.id)
+                    }
+                }
+                is OrderBy -> {
+                    url.addQueryParameter("dir", if (filter.state!!.ascending) "asc" else "desc")
+                    url.addQueryParameter("sort", arrayOf("rate", "name", "views", "created_at", "last_chapter_at", "chap_count")[filter.state!!.index])
+                }
+                is TagList -> filter.state.forEach { tag ->
+                    if (tag.state != Filter.TriState.STATE_IGNORE) {
+                        url.addQueryParameter(if (tag.isIncluded()) "tags[include][]" else "tags[exclude][]", tag.id)
+                    }
+                }
+            }
+        }
+        return POST(url.toString(), catalogHeaders())
+    }
+
+    // Hack search method to add some results from search popup
+    override fun searchMangaParse(response: Response): MangasPage {
+        val searchRequest = response.request.url.queryParameter("name")
+        val mangas = mutableListOf<SManga>()
+
+        if (!searchRequest.isNullOrEmpty()) {
+            val popupSearchHeaders = headers
+                .newBuilder()
+                .add("Accept", "application/json, text/plain, */*")
+                .add("X-Requested-With", "XMLHttpRequest")
+                .build()
+
+            // +200ms
+            val popup = client.newCall(
+                GET("$baseUrl/search?query=$searchRequest", popupSearchHeaders)
+            )
+                .execute().body!!.string()
+
+            val jsonList = json.decodeFromString<JsonArray>(popup)
+            jsonList.forEach {
+                mangas.add(popularMangaFromElement(it))
+            }
+        }
+        val searchedMangas = popularMangaParse(response)
+
+        // Filtered out what find in popup search
+        mangas.addAll(
+            searchedMangas.mangas.filter { search ->
+                mangas.find { search.title == it.title } == null
+            }
+        )
+
+        return MangasPage(mangas, searchedMangas.hasNextPage)
+    }
+
+    private class SearchFilter(name: String, val id: String) : Filter.TriState(name)
+    private class CheckFilter(name: String, val id: String) : Filter.CheckBox(name)
+
+    private class CategoryList(categories: List<CheckFilter>) : Filter.Group<CheckFilter>("Тип", categories)
+    private class FormatList(formas: List<SearchFilter>) : Filter.Group<SearchFilter>("Формат выпуска", formas)
+    private class StatusList(statuses: List<CheckFilter>) : Filter.Group<CheckFilter>("Статус перевода", statuses)
+    private class StatusTitleList(titles: List<CheckFilter>) : Filter.Group<CheckFilter>("Статус тайтла", titles)
+    private class GenreList(genres: List<SearchFilter>) : Filter.Group<SearchFilter>("Жанры", genres)
+    private class TagList(tags: List<SearchFilter>) : Filter.Group<SearchFilter>("Теги", tags)
+    private class AgeList(ages: List<CheckFilter>) : Filter.Group<CheckFilter>("Возрастное ограничение", ages)
+
+    override fun getFilterList() = FilterList(
+        OrderBy(),
+        CategoryList(getCategoryList()),
+        FormatList(getFormatList()),
+        GenreList(getGenreList()),
+        TagList(getTagList()),
+        StatusList(getStatusList()),
+        StatusTitleList(getStatusTitleList())
+    )
+
+    private class OrderBy : Filter.Sort(
+        "Сортировка",
+        arrayOf("Рейтинг", "Имя", "Просмотры", "Дате добавления", "Дате обновления", "Кол-во глав"),
+        Selection(0, false)
+    )
+
+    /*
+    * Use console
+    * Object.entries(__FILTER_ITEMS__.types).map(([k, v]) => `SearchFilter("${v.label}", "${v.id}")`).join(',\n')
+    * on /manga-list
+    */
+    private fun getCategoryList() = listOf(
+        CheckFilter("Манга", "1"),
+        CheckFilter("OEL-манга", "4"),
+        CheckFilter("Манхва", "5"),
+        CheckFilter("Маньхуа", "6"),
+        CheckFilter("Руманга", "8"),
+        CheckFilter("Комикс западный", "9")
+    )
+
+    private fun getFormatList() = listOf(
+        SearchFilter("4-кома (Ёнкома)", "1"),
+        SearchFilter("Сборник", "2"),
+        SearchFilter("Додзинси", "3"),
+        SearchFilter("Сингл", "4"),
+        SearchFilter("В цвете", "5"),
+        SearchFilter("Веб", "6")
+    )
+
+    /*
+    * Use console
+    * Object.entries(__FILTER_ITEMS__.status).map(([k, v]) => `SearchFilter("${v.label}", "${v.id}")`).join(',\n')
+    * on /manga-list
+    */
+    private fun getStatusList() = listOf(
+        CheckFilter("Продолжается", "1"),
+        CheckFilter("Завершен", "2"),
+        CheckFilter("Заморожен", "3"),
+        CheckFilter("Заброшен", "4")
+    )
+
+    private fun getStatusTitleList() = listOf(
+        CheckFilter("Онгоинг", "1"),
+        CheckFilter("Завершён", "2"),
+        CheckFilter("Анонс", "3"),
+        CheckFilter("Приостановлен", "4"),
+        CheckFilter("Выпуск прекращён", "5"),
+    )
+
+    /*
+    * Use console
+    * __FILTER_ITEMS__.genres.map(it => `SearchFilter("${it.name}", "${it.id}")`).join(',\n')
+    * on /manga-list
+    */
+    private fun getGenreList() = listOf(
+        SearchFilter("арт", "32"),
+        SearchFilter("боевик", "34"),
+        SearchFilter("боевые искусства", "35"),
+        SearchFilter("вампиры", "36"),
+        SearchFilter("гарем", "37"),
+        SearchFilter("гендерная интрига", "38"),
+        SearchFilter("героическое фэнтези", "39"),
+        SearchFilter("детектив", "40"),
+        SearchFilter("дзёсэй", "41"),
+        SearchFilter("драма", "43"),
+        SearchFilter("игра", "44"),
+        SearchFilter("исекай", "79"),
+        SearchFilter("история", "45"),
+        SearchFilter("киберпанк", "46"),
+        SearchFilter("кодомо", "76"),
+        SearchFilter("комедия", "47"),
+        SearchFilter("махо-сёдзё", "48"),
+        SearchFilter("меха", "49"),
+        SearchFilter("мистика", "50"),
+        SearchFilter("научная фантастика", "51"),
+        SearchFilter("омегаверс", "77"),
+        SearchFilter("повседневность", "52"),
+        SearchFilter("постапокалиптика", "53"),
+        SearchFilter("приключения", "54"),
+        SearchFilter("психология", "55"),
+        SearchFilter("романтика", "56"),
+        SearchFilter("самурайский боевик", "57"),
+        SearchFilter("сверхъестественное", "58"),
+        SearchFilter("сёдзё", "59"),
+        SearchFilter("сёдзё-ай", "60"),
+        SearchFilter("сёнэн", "61"),
+        SearchFilter("сёнэн-ай", "62"),
+        SearchFilter("спорт", "63"),
+        SearchFilter("сэйнэн", "64"),
+        SearchFilter("трагедия", "65"),
+        SearchFilter("триллер", "66"),
+        SearchFilter("ужасы", "67"),
+        SearchFilter("фантастика", "68"),
+        SearchFilter("фэнтези", "69"),
+        SearchFilter("школа", "70"),
+        SearchFilter("эротика", "71"),
+        SearchFilter("этти", "72"),
+        SearchFilter("юри", "73"),
+        SearchFilter("яой", "74")
+    )
+
+    private fun getTagList() = listOf(
+        SearchFilter("3D", "1"),
+        SearchFilter("Defloration", "287"),
+        SearchFilter("FPP(Вид от первого лица)", "289"),
+        SearchFilter("Footfuck", "5"),
+        SearchFilter("Handjob", "6"),
+        SearchFilter("Lactation", "7"),
+        SearchFilter("Living clothes", "284"),
+        SearchFilter("Mind break", "9"),
+        SearchFilter("Scat", "13"),
+        SearchFilter("Selfcest", "286"),
+        SearchFilter("Shemale", "220"),
+        SearchFilter("Tomboy", "14"),
+        SearchFilter("Unbirth", "283"),
+        SearchFilter("X-Ray", "15"),
+        SearchFilter("Алкоголь", "16"),
+        SearchFilter("Анал", "17"),
+        SearchFilter("Андроид", "18"),
+        SearchFilter("Анилингус", "19"),
+        SearchFilter("Анимация (GIF)", "350"),
+        SearchFilter("Арт", "20"),
+        SearchFilter("Ахэгао", "2"),
+        SearchFilter("БДСМ", "22"),
+        SearchFilter("Бакуню", "21"),
+        SearchFilter("Бара", "293"),
+        SearchFilter("Без проникновения", "336"),
+        SearchFilter("Без текста", "23"),
+        SearchFilter("Без трусиков", "24"),
+        SearchFilter("Без цензуры", "25"),
+        SearchFilter("Беременность", "26"),
+        SearchFilter("Бикини", "27"),
+        SearchFilter("Близнецы", "28"),
+        SearchFilter("Боди-арт", "29"),
+        SearchFilter("Больница", "30"),
+        SearchFilter("Большая грудь", "31"),
+        SearchFilter("Большая попка", "32"),
+        SearchFilter("Борьба", "33"),
+        SearchFilter("Буккакэ", "34"),
+        SearchFilter("В бассейне", "35"),
+        SearchFilter("В ванной", "36"),
+        SearchFilter("В государственном учреждении", "37"),
+        SearchFilter("В общественном месте", "38"),
+        SearchFilter("В очках", "8"),
+        SearchFilter("В первый раз", "39"),
+        SearchFilter("В транспорте", "40"),
+        SearchFilter("Вампиры", "41"),
+        SearchFilter("Вибратор", "42"),
+        SearchFilter("Втроём", "43"),
+        SearchFilter("Гипноз", "44"),
+        SearchFilter("Глубокий минет", "45"),
+        SearchFilter("Горячий источник", "46"),
+        SearchFilter("Групповой секс", "47"),
+        SearchFilter("Гуро", "307"),
+        SearchFilter("Гяру и Гангуро", "48"),
+        SearchFilter("Двойное проникновение", "49"),
+        SearchFilter("Девочки-волшебницы", "50"),
+        SearchFilter("Девушка-туалет", "51"),
+        SearchFilter("Демон", "52"),
+        SearchFilter("Дилдо", "53"),
+        SearchFilter("Домохозяйка", "54"),
+        SearchFilter("Дыра в стене", "55"),
+        SearchFilter("Жестокость", "56"),
+        SearchFilter("Золотой дождь", "57"),
+        SearchFilter("Зомби", "58"),
+        SearchFilter("Зоофилия", "351"),
+        SearchFilter("Зрелые женщины", "59"),
+        SearchFilter("Избиение", "223"),
+        SearchFilter("Измена", "60"),
+        SearchFilter("Изнасилование", "61"),
+        SearchFilter("Инопланетяне", "62"),
+        SearchFilter("Инцест", "63"),
+        SearchFilter("Исполнение желаний", "64"),
+        SearchFilter("Историческое", "65"),
+        SearchFilter("Камера", "66"),
+        SearchFilter("Кляп", "288"),
+        SearchFilter("Колготки", "67"),
+        SearchFilter("Косплей", "68"),
+        SearchFilter("Кримпай", "3"),
+        SearchFilter("Куннилингус", "69"),
+        SearchFilter("Купальники", "70"),
+        SearchFilter("ЛГБТ", "343"),
+        SearchFilter("Латекс и кожа", "71"),
+        SearchFilter("Магия", "72"),
+        SearchFilter("Маленькая грудь", "73"),
+        SearchFilter("Мастурбация", "74"),
+        SearchFilter("Медсестра", "221"),
+        SearchFilter("Мейдочка", "75"),
+        SearchFilter("Мерзкий дядька", "76"),
+        SearchFilter("Милф", "77"),
+        SearchFilter("Много девушек", "78"),
+        SearchFilter("Много спермы", "79"),
+        SearchFilter("Молоко", "80"),
+        SearchFilter("Монашка", "353"),
+        SearchFilter("Монстродевушки", "81"),
+        SearchFilter("Монстры", "82"),
+        SearchFilter("Мочеиспускание", "83"),
+        SearchFilter("На природе", "84"),
+        SearchFilter("Наблюдение", "85"),
+        SearchFilter("Насекомые", "285"),
+        SearchFilter("Небритая киска", "86"),
+        SearchFilter("Небритые подмышки", "87"),
+        SearchFilter("Нетораре", "88"),
+        SearchFilter("Нэтори", "11"),
+        SearchFilter("Обмен телами", "89"),
+        SearchFilter("Обычный секс", "90"),
+        SearchFilter("Огромная грудь", "91"),
+        SearchFilter("Огромный член", "92"),
+        SearchFilter("Омораси", "93"),
+        SearchFilter("Оральный секс", "94"),
+        SearchFilter("Орки", "95"),
+        SearchFilter("Остановка времени", "296"),
+        SearchFilter("Пайзури", "96"),
+        SearchFilter("Парень пассив", "97"),
+        SearchFilter("Переодевание", "98"),
+        SearchFilter("Пирсинг", "308"),
+        SearchFilter("Пляж", "99"),
+        SearchFilter("Повседневность", "100"),
+        SearchFilter("Подвязки", "282"),
+        SearchFilter("Подглядывание", "101"),
+        SearchFilter("Подчинение", "102"),
+        SearchFilter("Похищение", "103"),
+        SearchFilter("Превозмогание", "104"),
+        SearchFilter("Принуждение", "105"),
+        SearchFilter("Прозрачная одежда", "106"),
+        SearchFilter("Проституция", "107"),
+        SearchFilter("Психические отклонения", "108"),
+        SearchFilter("Публично", "109"),
+        SearchFilter("Пытки", "224"),
+        SearchFilter("Пьяные", "110"),
+        SearchFilter("Рабы", "356"),
+        SearchFilter("Рабыни", "111"),
+        SearchFilter("С Сюжетом", "337"),
+        SearchFilter("Сuminside", "4"),
+        SearchFilter("Секс-игрушки", "112"),
+        SearchFilter("Сексуально возбуждённая", "113"),
+        SearchFilter("Сибари", "114"),
+        SearchFilter("Спортивная форма", "117"),
+        SearchFilter("Спортивное тело", "335"),
+        SearchFilter("Спящие", "118"),
+        SearchFilter("Страпон", "119"),
+        SearchFilter("Суккуб", "120"),
+        SearchFilter("Темнокожие", "121"),
+        SearchFilter("Тентакли", "122"),
+        SearchFilter("Толстушки", "123"),
+        SearchFilter("Трагедия", "124"),
+        SearchFilter("Трап", "125"),
+        SearchFilter("Ужасы", "126"),
+        SearchFilter("Униформа", "127"),
+        SearchFilter("Учитель и ученик", "352"),
+        SearchFilter("Ушастые", "128"),
+        SearchFilter("Фантазии", "129"),
+        SearchFilter("Фемдом", "130"),
+        SearchFilter("Фестиваль", "131"),
+        SearchFilter("Фетиш", "132"),
+        SearchFilter("Фистинг", "133"),
+        SearchFilter("Фурри", "134"),
+        SearchFilter("Футанари", "136"),
+        SearchFilter("Футанари имеет парня", "137"),
+        SearchFilter("Цельный купальник", "138"),
+        SearchFilter("Цундэрэ", "139"),
+        SearchFilter("Чикан", "140"),
+        SearchFilter("Чулки", "141"),
+        SearchFilter("Шлюха", "142"),
+        SearchFilter("Эксгибиционизм", "143"),
+        SearchFilter("Эльф", "144"),
+        SearchFilter("Юные", "145"),
+        SearchFilter("Яндэрэ", "146")
+    )
+
+    companion object {
+        const val PREFIX_SLUG_SEARCH = "slug:"
+        private const val SERVER_PREF = "MangaLibImageServer"
+        private const val SERVER_PREF_Title = "Сервер изображений"
+
+        private const val SORTING_PREF = "MangaLibSorting"
+        private const val SORTING_PREF_Title = "Способ выбора переводчиков"
+
+        private const val LANGUAGE_PREF = "MangaLibTitleLanguage"
+        private const val LANGUAGE_PREF_Title = "Выбор языка на обложке"
+
+        private const val COVER_URL = "https://staticlib.me"
+    }
+
+    private var server: String? = preferences.getString(SERVER_PREF, null)
+    private var titleLanguage: String? = preferences.getString(LANGUAGE_PREF, null)
+    override fun setupPreferenceScreen(screen: PreferenceScreen) {
+        val serverPref = ListPreference(screen.context).apply {
+            key = SERVER_PREF
+            title = SERVER_PREF_Title
+            entries = arrayOf("Основной", "Второй (тестовый)", "Третий (эконом трафика)", "Авто")
+            entryValues = arrayOf("secondary", "fourth", "compress", "auto")
+            summary = "%s"
+            setDefaultValue("auto")
+            setOnPreferenceChangeListener { _, newValue ->
+                server = newValue.toString()
+                true
+            }
+        }
+
+        val sortingPref = ListPreference(screen.context).apply {
+            key = SORTING_PREF
+            title = SORTING_PREF_Title
+            entries = arrayOf(
+                "Полный список (без повторных переводов)", "Все переводы (друг за другом)",
+                "Наибольшее число глав", "Активный перевод"
+            )
+            entryValues = arrayOf("ms_mixing", "ms_combining", "ms_largest", "ms_active")
+            summary = "%s"
+            setDefaultValue("ms_mixing")
+            setOnPreferenceChangeListener { _, newValue ->
+                val selected = newValue as String
+                preferences.edit().putString(SORTING_PREF, selected).commit()
+            }
+        }
+        val titleLanguagePref = ListPreference(screen.context).apply {
+            key = LANGUAGE_PREF
+            title = LANGUAGE_PREF_Title
+            entries = arrayOf("Английский (транскрипция)", "Русский")
+            entryValues = arrayOf("eng", "rus")
+            summary = "%s"
+            setDefaultValue("eng")
+            setOnPreferenceChangeListener { _, newValue ->
+                titleLanguage = newValue.toString()
+                val warning = "Если язык обложки не изменился очистите базу данных в приложении (Настройки -> Дополнительно -> Очистить базу данных)"
+                Toast.makeText(screen.context, warning, Toast.LENGTH_LONG).show()
+                true
+            }
+        }
+        screen.addPreference(serverPref)
+        screen.addPreference(sortingPref)
+        screen.addPreference(titleLanguagePref)
+    }
+}
diff --git a/src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentaiActivity.kt b/src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentaiActivity.kt
new file mode 100644
index 000000000..4b29428c0
--- /dev/null
+++ b/src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentaiActivity.kt
@@ -0,0 +1,41 @@
+package eu.kanade.tachiyomi.extension.ru.libhentai
+
+import android.app.Activity
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import kotlin.system.exitProcess
+
+/**
+ * Springboard that accepts https://hentailib.me/xxx intents and redirects them to
+ * the main tachiyomi process. The idea is to not install the intent filter unless
+ * you have this extension installed, but still let the main tachiyomi app control
+ * things.
+ */
+class LibHentaiActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        val pathSegments = intent?.data?.pathSegments
+        if (pathSegments != null && pathSegments.size > 0) {
+            val titleid = pathSegments[0]
+            val mainIntent = Intent().apply {
+                action = "eu.kanade.tachiyomi.SEARCH"
+                putExtra("query", "${LibHentai.PREFIX_SLUG_SEARCH}$titleid")
+                putExtra("filter", packageName)
+            }
+
+            try {
+                startActivity(mainIntent)
+            } catch (e: ActivityNotFoundException) {
+                Log.e("LibHentaiActivity", e.toString())
+            }
+        } else {
+            Log.e("LibHentaiActivity", "could not parse uri from intent $intent")
+        }
+
+        finish()
+        exitProcess(0)
+    }
+}