From acacecf4bd3dec2ada6a086e63731a11d0fd9812 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nam=20Nguy=E1=BB=85n?= <namnguyendeveloper@gmail.com>
Date: Mon, 23 Oct 2017 00:18:43 +0700
Subject: [PATCH] Fix: Academyvn (#109)

Add:
- Blogtruyen
- Truyentranhlh
- Iutruyentranh
---
 .../extension/vi/academyvn/Academyvn.kt       |   2 +-
 src/vi/blogtruyen/build.gradle                |  13 ++
 .../extension/vi/blogtruyen/Blogtruyen.kt     | 214 ++++++++++++++++++
 src/vi/iutruyentranh/build.gradle             |  13 ++
 .../vi/iutruyentranh/Iutruyentranh.kt         | 201 ++++++++++++++++
 src/vi/truyentranhlh/build.gradle             |  18 ++
 .../vi/truyentranhlh/Truyentranhlh.kt         | 191 ++++++++++++++++
 7 files changed, 651 insertions(+), 1 deletion(-)
 create mode 100644 src/vi/blogtruyen/build.gradle
 create mode 100644 src/vi/blogtruyen/src/eu/kanade/tachiyomi/extension/vi/blogtruyen/Blogtruyen.kt
 create mode 100644 src/vi/iutruyentranh/build.gradle
 create mode 100644 src/vi/iutruyentranh/src/eu/kanade/tachiyomi/extension/vi/iutruyentranh/Iutruyentranh.kt
 create mode 100644 src/vi/truyentranhlh/build.gradle
 create mode 100644 src/vi/truyentranhlh/src/eu/kanade/tachiyomi/extension/vi/truyentranhlh/Truyentranhlh.kt

diff --git a/src/vi/academyvn/src/eu/kanade/tachiyomi/extension/vi/academyvn/Academyvn.kt b/src/vi/academyvn/src/eu/kanade/tachiyomi/extension/vi/academyvn/Academyvn.kt
index ecceae088..e0487d878 100644
--- a/src/vi/academyvn/src/eu/kanade/tachiyomi/extension/vi/academyvn/Academyvn.kt
+++ b/src/vi/academyvn/src/eu/kanade/tachiyomi/extension/vi/academyvn/Academyvn.kt
@@ -14,7 +14,7 @@ class Academyvn : ParsedHttpSource() {
 
     override val name = "Academy VN"
 
-    override val baseUrl = "http://truyen.academyvn.com"
+    override val baseUrl = "http://hocvientruyentranh.com"
 
     override val lang = "vi"
 
diff --git a/src/vi/blogtruyen/build.gradle b/src/vi/blogtruyen/build.gradle
new file mode 100644
index 000000000..12aaf3eaa
--- /dev/null
+++ b/src/vi/blogtruyen/build.gradle
@@ -0,0 +1,13 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+
+ext {
+    appName = 'Tachiyomi: BlogTruyen'
+    pkgNameSuffix = "vi.blogtruyen"
+    extClass = '.Blogtruyen'
+    extVersionCode = 1
+    extVersionSuffix = 1
+    libVersion = '1.0'
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/vi/blogtruyen/src/eu/kanade/tachiyomi/extension/vi/blogtruyen/Blogtruyen.kt b/src/vi/blogtruyen/src/eu/kanade/tachiyomi/extension/vi/blogtruyen/Blogtruyen.kt
new file mode 100644
index 000000000..f00120e75
--- /dev/null
+++ b/src/vi/blogtruyen/src/eu/kanade/tachiyomi/extension/vi/blogtruyen/Blogtruyen.kt
@@ -0,0 +1,214 @@
+package eu.kanade.tachiyomi.source.online.vietnamese
+
+import android.util.Log
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.source.model.*
+import eu.kanade.tachiyomi.source.online.ParsedHttpSource
+import okhttp3.HttpUrl
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import java.text.SimpleDateFormat
+
+
+class Blogtruyen : ParsedHttpSource() {
+
+    override val name = "BlogTruyen"
+
+    override val baseUrl = "http://blogtruyen.com"
+
+    override val lang = "vi"
+
+    override val supportsLatest = true
+
+    override val client: OkHttpClient = network.cloudflareClient
+
+    override fun popularMangaSelector() = "div.list span.tiptip.fs-12.ellipsis"
+
+    override fun latestUpdatesSelector() = popularMangaSelector()
+
+    override fun popularMangaRequest(page: Int): Request {
+        return GET("$baseUrl/ajax/Search/AjaxLoadListManga?key=tatca&orderBy=3&p=$page", headers)
+    }
+
+    override fun latestUpdatesRequest(page: Int): Request {
+        return GET("$baseUrl/ajax/Search/AjaxLoadListManga?key=tatca&orderBy=5&p=$page", headers)
+    }
+
+    override fun popularMangaFromElement(element: Element): SManga {
+        val manga = SManga.create()
+        element.select("a").first().let {
+            manga.setUrlWithoutDomain(it.attr("href"))
+            manga.title = it.text()
+        }
+        return manga
+    }
+
+    override fun latestUpdatesFromElement(element: Element): SManga {
+        return popularMangaFromElement(element)
+    }
+
+    override fun popularMangaNextPageSelector() = "div.paging:last-child:not(.current_page)"
+
+    override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
+
+    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+        var temp = "$baseUrl/timkiem/nangcao/1/0"
+        val genres = mutableListOf<String>()
+        val genresEx = mutableListOf<String>()
+        var aut = ""
+        (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
+            when (filter) {
+                is GenreList -> filter.state.forEach {
+                    genre ->
+                    when (genre.state) {
+                        Filter.TriState.STATE_INCLUDE -> genres.add(genre.name.toLowerCase())
+                        Filter.TriState.STATE_EXCLUDE -> genresEx.add(genre.name.toLowerCase())
+                    }
+                }
+                is Author -> {
+                    if (!filter.state.isEmpty()) {
+                        aut = filter.state
+                    }
+                }
+            }
+        }
+        if (genres.isNotEmpty()) temp = temp + "/" + genres.joinToString(",")
+        else temp = temp + "/-1"
+        if (genresEx.isNotEmpty()) temp = temp + "/" + genresEx.joinToString(",")
+        else temp = temp + "/-1"
+        val url = HttpUrl.parse(temp)!!.newBuilder()
+        url.addQueryParameter("txt", query)
+        if (!aut.isEmpty()) url.addQueryParameter("aut", aut)
+        url.addQueryParameter("p", page.toString())
+        Log.i("tachiyomi", url.toString())
+        return GET(url.toString().replace("m.", ""), headers)
+    }
+
+    override fun searchMangaSelector() = "div.list > p:gt(0) > span:eq(0)"
+
+    override fun searchMangaFromElement(element: Element): SManga {
+        return popularMangaFromElement(element)
+    }
+
+    override fun searchMangaNextPageSelector() = "ul.pagination i.glyphicon.glyphicon-step-forward.red"
+
+    override fun mangaDetailsParse(document: Document): SManga {
+        val infoElement = document.select("div.description").first()
+
+        val manga = SManga.create()
+        manga.author = infoElement.select("p:contains(Tác giả) > a").first()?.text()
+        manga.genre = infoElement.select("p:contains(Thể loại) > span.category > a").text()
+        manga.description = document.select("div.detail > div.content").text()
+        manga.status = infoElement.select("p:contains(Trạng thái) > span.color-red").first()?.text().orEmpty().let { parseStatus(it) }
+        manga.thumbnail_url = document.select("div.thumbnail > img").first()?.attr("src")
+        return manga
+    }
+
+    fun parseStatus(status: String) = when {
+        status.contains("Đang tiến hành") -> SManga.ONGOING
+        status.contains("Đã hoàn thành") -> SManga.COMPLETED
+        else -> SManga.UNKNOWN
+    }
+
+    override fun chapterListSelector() = "div.list-wrap > p"
+
+    override fun chapterFromElement(element: Element): SChapter {
+        val urlElement = element.select("span > a").first()
+
+        val chapter = SChapter.create()
+        chapter.setUrlWithoutDomain(urlElement.attr("href"))
+        chapter.name = urlElement.attr("title")
+        chapter.date_upload = element.select("span.publishedDate").first()?.text()?.let {
+            SimpleDateFormat("dd/MM/yyyy HH:mm").parse(it).time
+        } ?: 0
+        return chapter
+    }
+
+    override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers)
+
+    override fun pageListParse(document: Document): List<Page> {
+        val pages = mutableListOf<Page>()
+        var i = 0
+        document.select("article#content > img").forEach {
+            pages.add(Page(i++, "", it.attr("src")))
+        }
+        return pages
+    }
+
+    override fun imageUrlRequest(page: Page) = GET(page.url)
+
+    override fun imageUrlParse(document: Document) = ""
+
+    var status = arrayOf("Sao cũng được", "Đang tiến hành", "Đã hoàn thành", "Tạm ngưng")
+
+    private class Status : Filter.Select<String>("Status", arrayOf("Sao cũng được", "Đang tiến hành", "Đã hoàn thành", "Tạm ngưng"))
+    private class Author : Filter.Text("Tác giả")
+    private class Genre(name: String, val id: Int) : Filter.TriState(name)
+    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Thể loại", genres)
+
+    override fun getFilterList() = FilterList(
+            Status(),
+            GenreList(getGenreList()),
+            Author()
+    )
+
+    private fun getGenreList() = listOf(
+            Genre("16+", 54),
+            Genre("18+", 45),
+            Genre("Action", 1),
+            Genre("Adult", 2),
+            Genre("Adventure", 3),
+            Genre("Anime", 4),
+            Genre("Comedy", 5),
+            Genre("Comic", 6),
+            Genre("Doujinshi", 7),
+            Genre("Drama", 49),
+            Genre("Ecchi", 48),
+            Genre("Even BT", 60),
+            Genre("Fantasy", 50),
+            Genre("Game", 61),
+            Genre("Gender Bender", 51),
+            Genre("Harem", 12),
+            Genre("Historical", 13),
+            Genre("Horror", 14),
+            Genre("Josei", 15),
+            Genre("Live Action", 16),
+            Genre("Magic", 46),
+            Genre("Manga", 55),
+            Genre("Manhua", 17),
+            Genre("Manhwa", 18),
+            Genre("Martial Arts", 19),
+            Genre("Mature", 20),
+            Genre("Mecha", 21),
+            Genre("Mystery", 22),
+            Genre("Nấu ăn", 56),
+            Genre("NTR", 61),
+            Genre("One shot", 23),
+            Genre("Psychological", 24),
+            Genre("Romance", 25),
+            Genre("School Life", 26),
+            Genre("Sci-fi", 27),
+            Genre("Seinen", 28),
+            Genre("Shoujo", 29),
+            Genre("Shoujo Ai", 30),
+            Genre("Shounen", 31),
+            Genre("Shounen Ai", 32),
+            Genre("Slice of Life", 33),
+            Genre("Smut", 34),
+            Genre("Soft Yaoi", 35),
+            Genre("Soft Yuri", 36),
+            Genre("Sports", 37),
+            Genre("Supernatural", 38),
+            Genre("Tạp chí truyện tranh", 39),
+            Genre("Tragedy", 40),
+            Genre("Trap", 58),
+            Genre("Trinh thám", 57),
+            Genre("Truyện scan", 41),
+            Genre("Video clip", 53),
+            Genre("VnComic", 42),
+            Genre("Webtoon", 52),
+            Genre("Yuri", 59)
+    )
+}
\ No newline at end of file
diff --git a/src/vi/iutruyentranh/build.gradle b/src/vi/iutruyentranh/build.gradle
new file mode 100644
index 000000000..6c544800a
--- /dev/null
+++ b/src/vi/iutruyentranh/build.gradle
@@ -0,0 +1,13 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+
+ext {
+    appName = 'Tachiyomi: IuTruyenTranh'
+    pkgNameSuffix = "vi.iutruyentranh"
+    extClass = '.Iutruyentranh'
+    extVersionCode = 1
+    extVersionSuffix = 1
+    libVersion = '1.0'
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/vi/iutruyentranh/src/eu/kanade/tachiyomi/extension/vi/iutruyentranh/Iutruyentranh.kt b/src/vi/iutruyentranh/src/eu/kanade/tachiyomi/extension/vi/iutruyentranh/Iutruyentranh.kt
new file mode 100644
index 000000000..72eb0c5f1
--- /dev/null
+++ b/src/vi/iutruyentranh/src/eu/kanade/tachiyomi/extension/vi/iutruyentranh/Iutruyentranh.kt
@@ -0,0 +1,201 @@
+package eu.kanade.tachiyomi.source.online.vietnamese
+
+import android.util.Log
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.source.model.*
+import eu.kanade.tachiyomi.source.online.ParsedHttpSource
+import okhttp3.HttpUrl
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import java.text.SimpleDateFormat
+
+/**
+ * Created by Nam Nguyen on 29/4/2017.
+ */
+
+class Iutruyentranh : ParsedHttpSource() {
+
+    override val name = "IuTruyenTranh"
+
+    override val baseUrl = "http://iutruyentranh.com"
+
+    override val lang = "vi"
+
+    override val supportsLatest = true
+
+    override val client: OkHttpClient = network.cloudflareClient
+
+    override fun popularMangaSelector() = "div.bbottom h4.media-heading"
+
+    override fun latestUpdatesSelector() = "h4.media-heading"
+
+    override fun popularMangaRequest(page: Int): Request {
+        return GET("$baseUrl/genre/$page?popular", headers)
+    }
+
+    override fun latestUpdatesRequest(page: Int): Request {
+        return GET("$baseUrl/latest/$page", headers)
+    }
+
+    override fun popularMangaFromElement(element: Element): SManga {
+        val manga = SManga.create()
+        element.select("a").first().let {
+            manga.setUrlWithoutDomain(it.attr("href"))
+            manga.title = it.text()
+        }
+        return manga
+    }
+
+    override fun latestUpdatesFromElement(element: Element): SManga {
+        return popularMangaFromElement(element)
+    }
+
+    override fun popularMangaNextPageSelector() = "ul.pagination > li:contains(...»)"
+
+    override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
+
+    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+        val url = HttpUrl.parse("$baseUrl/search/$page?")!!.newBuilder().addQueryParameter("name", query)
+        val genres = mutableListOf<String>()
+        val genresEx = mutableListOf<String>()
+        (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
+            when (filter) {
+                is Author -> url.addQueryParameter("autart", filter.state)
+                is GenreList -> filter.state.forEach { genre ->
+                    when (genre.state) {
+                        Filter.TriState.STATE_INCLUDE -> genres.add(genre.name.toLowerCase())
+                        Filter.TriState.STATE_EXCLUDE -> genresEx.add(genre.name.toLowerCase())
+                    }
+                }
+            }
+        }
+        if (genres.isNotEmpty()) url.addQueryParameter("genres", genres.joinToString(","))
+        if (genresEx.isNotEmpty()) url.addQueryParameter("genres-exclude", genresEx.joinToString(","))
+
+        Log.i("tachiyomi", url.toString())
+        return GET(url.toString(), headers)
+    }
+
+    override fun searchMangaSelector() = popularMangaSelector()
+
+    override fun searchMangaFromElement(element: Element): SManga {
+        return popularMangaFromElement(element)
+    }
+
+    override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
+
+    override fun mangaDetailsParse(document: Document): SManga {
+        val infoElement = document.select("section.manga article").first()
+
+        val manga = SManga.create()
+        manga.author = infoElement.select("span[itemprop=author]").first()?.text()
+        manga.genre = infoElement.select("a[itemprop=genre]").text()
+        manga.description = infoElement.select("p.box.box-danger").text()
+        manga.status = infoElement.select("a[rel=nofollow]").last()?.text().orEmpty().let { parseStatus(it) }
+        manga.thumbnail_url = infoElement.select("img[class^=thumbnail]").first()?.attr("src")
+        return manga
+    }
+
+    fun parseStatus(status: String) = when {
+        status.contains("Đang tiến hành") -> SManga.ONGOING
+        status.contains("Đã hoàn thành") -> SManga.COMPLETED
+        else -> SManga.UNKNOWN
+    }
+
+    override fun chapterListSelector() = "ul.list-unstyled > table > tbody > tr"
+
+    override fun chapterFromElement(element: Element): SChapter {
+        val urlElement = element.select("a").first()
+
+        val chapter = SChapter.create()
+        chapter.setUrlWithoutDomain(urlElement.attr("href") + "&load=all")
+        chapter.name = urlElement.select("b").text()
+        chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let {
+            SimpleDateFormat("dd/MM/yyyy").parse(it).time
+        } ?: 0
+        return chapter
+    }
+
+    override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers)
+
+    override fun pageListParse(document: Document): List<Page> {
+        val pages = mutableListOf<Page>()
+        var i = 0
+        document.select("img.img").forEach {
+            pages.add(Page(i++, "", it.attr("src")))
+        }
+        return pages
+    }
+
+    override fun imageUrlRequest(page: Page) = GET(page.url)
+
+    override fun imageUrlParse(document: Document) = ""
+
+    private class Author : Filter.Text("Tác giả")
+    private class Genre(name: String) : Filter.TriState(name)
+    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Thể loại", genres)
+
+    override fun getFilterList() = FilterList(
+            Author(),
+            GenreList(getGenreList())
+    )
+
+    private fun getGenreList() = listOf(
+            Genre("Action"),
+            Genre("Adult"),
+            Genre("Adventure"),
+            Genre("Anime"),
+            Genre("Bishounen"),
+            Genre("Comedy"),
+            Genre("Cookin"),
+            Genre("Demons"),
+            Genre("Doujinshi"),
+            Genre("Drama"),
+            Genre("Ecchi"),
+            Genre("Fantasy"),
+            Genre("Gender Bender"),
+            Genre("Harem"),
+            Genre("Hentai"),
+            Genre("Historical"),
+            Genre("Horror"),
+            Genre("Josei"),
+            Genre("Live action"),
+            Genre("Magic"),
+            Genre("Manhua"),
+            Genre("Manhwa"),
+            Genre("Martial Arts"),
+            Genre("Mature"),
+            Genre("Mecha"),
+            Genre("Medical"),
+            Genre("Military"),
+            Genre("Mystery"),
+            Genre("One shot"),
+            Genre("Oneshot"),
+            Genre("Other"),
+            Genre("Psychological"),
+            Genre("Romance"),
+            Genre("School Life"),
+            Genre("Sci fi"),
+            Genre("Seinen"),
+            Genre("Shotacon"),
+            Genre("Shoujo"),
+            Genre("Shoujo Ai"),
+            Genre("Shoujoai"),
+            Genre("Shounen"),
+            Genre("Shounen Ai"),
+            Genre("Shounenai"),
+            Genre("Slice of Life"),
+            Genre("Smut"),
+            Genre("Sports"),
+            Genre("Super power"),
+            Genre("Superma"),
+            Genre("Supernatural"),
+            Genre("Tragedy"),
+            Genre("Vampire"),
+            Genre("Webtoon"),
+            Genre("Yaoi"),
+            Genre("Yuri")
+    )
+}
\ No newline at end of file
diff --git a/src/vi/truyentranhlh/build.gradle b/src/vi/truyentranhlh/build.gradle
new file mode 100644
index 000000000..063c220de
--- /dev/null
+++ b/src/vi/truyentranhlh/build.gradle
@@ -0,0 +1,18 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+
+ext {
+    appName = 'Tachiyomi: TruyenTranhLh'
+    pkgNameSuffix = "vi.truyentranhlh"
+    extClass = '.Truyentrnahlh'
+    extVersionCode = 1
+    extVersionSuffix = 1
+    libVersion = '1.0'
+}
+
+dependencies {
+    provided "com.google.code.gson:gson:2.8.0"
+    provided "com.github.salomonbrys.kotson:kotson:2.5.0"
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/vi/truyentranhlh/src/eu/kanade/tachiyomi/extension/vi/truyentranhlh/Truyentranhlh.kt b/src/vi/truyentranhlh/src/eu/kanade/tachiyomi/extension/vi/truyentranhlh/Truyentranhlh.kt
new file mode 100644
index 000000000..cfeb55a63
--- /dev/null
+++ b/src/vi/truyentranhlh/src/eu/kanade/tachiyomi/extension/vi/truyentranhlh/Truyentranhlh.kt
@@ -0,0 +1,191 @@
+package eu.kanade.tachiyomi.source.online.vietnamese
+
+import com.google.gson.JsonElement
+import com.google.gson.JsonParser
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.source.model.*
+import eu.kanade.tachiyomi.source.online.HttpSource
+import eu.kanade.tachiyomi.util.asJsoup
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.Response
+import org.jsoup.nodes.Element
+import java.util.*
+
+class Truyentranhlh : HttpSource() {
+
+    override val name = "TruyenTranhLH"
+
+    override val baseUrl = "http://truyentranhlh.com"
+
+    override val lang = "vi"
+
+    override val supportsLatest = true
+
+    override val client: OkHttpClient = network.cloudflareClient
+
+    fun popularMangaSelector() = "div.media-body > h3"
+
+    fun latestUpdatesSelector() = popularMangaSelector()
+
+    override fun popularMangaRequest(page: Int): Request {
+        return GET("$baseUrl/manga-list.html?listType=pagination&page=$page&artist=&author=&group=&name=&genre=&sort=views&sort_type=DESC", headers)
+    }
+
+    override fun popularMangaParse(response: Response): MangasPage {
+        val document = response.asJsoup()
+
+        val mangas = document.select(popularMangaSelector()).map { element ->
+            popularMangaFromElement(element)
+        }
+
+        val hasNextPage = popularMangaNextPageSelector()?.let { selector ->
+            document.select(selector).first()
+        } != null
+
+        return MangasPage(mangas, hasNextPage)
+    }
+
+
+    override fun latestUpdatesRequest(page: Int): Request {
+        return GET("$baseUrl/manga-list.html?listType=pagination&page=$page&artist=&author=&group=&name=&genre=&sort=last_update&sort_type=DESC", headers)
+    }
+
+    override fun latestUpdatesParse(response: Response): MangasPage {
+        val document = response.asJsoup()
+
+        val mangas = document.select(latestUpdatesSelector()).map { element ->
+            latestUpdatesFromElement(element)
+        }
+
+        val hasNextPage = latestUpdatesNextPageSelector()?.let { selector ->
+            document.select(selector).first()
+        } != null
+
+        return MangasPage(mangas, hasNextPage)
+    }
+
+    fun popularMangaFromElement(element: Element): SManga {
+        val manga = SManga.create()
+        element.select("a").first().let {
+            manga.setUrlWithoutDomain("/" + it.attr("href"))
+            manga.title = it.text()
+        }
+        return manga
+    }
+
+    fun latestUpdatesFromElement(element: Element): SManga {
+        return popularMangaFromElement(element)
+    }
+
+    fun popularMangaNextPageSelector() = "i.glyphicon.glyphicon-chevron-right"
+
+    fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
+
+    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+        return GET("$baseUrl/app/manga/controllers/search.single.php?q=" + query, headers)
+    }
+
+    override fun searchMangaParse(response: Response): MangasPage {
+        var jsonData = response.asJsoup().text()
+        jsonData = jsonData.substring(1, jsonData.length - 1)
+        val elementArray = JsonParser().parse(jsonData)
+                .asJsonObject
+                .getAsJsonArray("data")
+        val mangas = elementArray.map { element ->
+            searchMangaFromElement(element)
+        }
+        return MangasPage(mangas, false)
+    }
+
+    fun searchMangaFromElement(element: JsonElement): SManga {
+        val result = element.asJsonObject
+        val manga = SManga.create()
+        manga.title = result.get("primary").toString().replace("\"", "")
+        manga.url = "/" + result.get("onclick").toString().replace("\"window.location='", "").replace("'\"", "")
+        return manga
+    }
+
+    override fun mangaDetailsParse(response: Response): SManga {
+        val document = response.asJsoup()
+        val infoElement = document.select("ul.manga-info").first()
+
+        val manga = SManga.create()
+        manga.author = infoElement.select("a.btn.btn-xs.btn-info").first()?.text()
+        manga.genre = infoElement.select("a.btn.btn-xs.btn-danger").text()
+        manga.description = document.select("h3:contains(Sơ lược) + p").text()
+        manga.status = infoElement.select("a.btn.btn-xs.btn-success").last()?.text().orEmpty().let { parseStatus(it) }
+        manga.thumbnail_url = document.select("img.thumbnail").first()?.attr("src")
+        return manga
+    }
+
+    fun parseStatus(status: String) = when {
+        status.contains("Chưa hoàn thành") -> SManga.ONGOING
+        status.contains("Đã hoàn thành") -> SManga.COMPLETED
+        else -> SManga.UNKNOWN
+    }
+
+    override fun chapterListParse(response: Response): List<SChapter> {
+        val document = response.asJsoup()
+        return document.select(chapterListSelector()).map { chapterFromElement(it) }
+    }
+
+    fun chapterListSelector() = "table.table.table-hover > tbody > tr"
+
+    fun chapterFromElement(element: Element): SChapter {
+        val urlElement = element.select("td > a").first()
+
+        val chapter = SChapter.create()
+        chapter.setUrlWithoutDomain(cleanUrl(urlElement.attr("href")))
+        chapter.name = urlElement.select("b").text()
+        chapter.date_upload = element.select("td > i > time").first()?.text()?.let { parseChapterDate(it) } ?: 0
+        return chapter
+    }
+
+    fun cleanUrl(url: String): String {
+        val index = url.lastIndexOf(baseUrl)
+        if (index != -1) return url.substring(index)
+        return "/" + url
+    }
+
+    private fun parseChapterDate(date: String): Long {
+        val dateWords: List<String> = date.split(" ")
+        if (dateWords.size == 3) {
+            val timeAgo = Integer.parseInt(dateWords[0])
+            val dates: Calendar = Calendar.getInstance()
+            if (dateWords[1].contains("phút")) {
+                dates.add(Calendar.MINUTE, -timeAgo)
+            } else if (dateWords[1].contains("giờ")) {
+                dates.add(Calendar.HOUR_OF_DAY, -timeAgo)
+            } else if (dateWords[1].contains("ngày")) {
+                dates.add(Calendar.DAY_OF_YEAR, -timeAgo)
+            } else if (dateWords[1].contains("tuần")) {
+                dates.add(Calendar.WEEK_OF_YEAR, -timeAgo)
+            } else if (dateWords[1].contains("tháng")) {
+                dates.add(Calendar.MONTH, -timeAgo)
+            } else if (dateWords[1].contains("năm")) {
+                dates.add(Calendar.YEAR, -timeAgo)
+            }
+            return dates.timeInMillis
+        }
+        return 0L
+    }
+
+    override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers)
+
+    override fun pageListParse(response: Response): List<Page> {
+        val document = response.asJsoup()
+        val pages = mutableListOf<Page>()
+        var i = 0
+        document.select("div.chapter-content > img").forEach {
+            pages.add(Page(i++, "", it.attr("src")))
+        }
+        return pages
+    }
+
+    override fun imageUrlRequest(page: Page) = GET(page.url)
+
+    override fun imageUrlParse(response: Response): String {
+        return ""
+    }
+}
\ No newline at end of file