FMReader cleanup (#1633)
@ -1,12 +0,0 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
apply plugin: 'kotlin-android'
 | 
			
		||||
 | 
			
		||||
ext {
 | 
			
		||||
    appName = 'Tachiyomi: LHTranslation'
 | 
			
		||||
    pkgNameSuffix = 'en.lhtranslation'
 | 
			
		||||
    extClass = '.LHTranslation'
 | 
			
		||||
    extVersionCode = 4
 | 
			
		||||
    libVersion = '1.2'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$rootDir/common.gradle"
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 1.8 KiB  | 
| 
		 Before Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 2.4 KiB  | 
| 
		 Before Width: | Height: | Size: 3.8 KiB  | 
| 
		 Before Width: | Height: | Size: 5.9 KiB  | 
| 
		 Before Width: | Height: | Size: 12 KiB  | 
@ -1,260 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.en.lhtranslation
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.*
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
 | 
			
		||||
import eu.kanade.tachiyomi.util.asJsoup
 | 
			
		||||
import okhttp3.Headers
 | 
			
		||||
import okhttp3.HttpUrl
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import okhttp3.Response
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class LHTranslation : ParsedHttpSource() {
 | 
			
		||||
    // Ps. LHTranslation is really similar to rawLH
 | 
			
		||||
    override val name = "LHTranslation"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "https://lhtranslation.net"
 | 
			
		||||
 | 
			
		||||
    override val lang = "en"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request =
 | 
			
		||||
            GET("$baseUrl/manga-list.html?listType=pagination&page=$page&artist=&author=&group=&m_status=&name=&genre=&ungenre=&sort=views&sort_type=DESC", headers)
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val url = HttpUrl.parse("$baseUrl/manga-list.html?")!!.newBuilder()
 | 
			
		||||
            .addQueryParameter("name", query)
 | 
			
		||||
            .addQueryParameter("page", page.toString())
 | 
			
		||||
        (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
 | 
			
		||||
            when (filter) {
 | 
			
		||||
                is Status -> {
 | 
			
		||||
                    val status = arrayOf("", "1", "2")[filter.state]
 | 
			
		||||
                    url.addQueryParameter("m_status", status)
 | 
			
		||||
                }
 | 
			
		||||
                is TextField -> url.addQueryParameter(filter.key, filter.state)
 | 
			
		||||
                is GenreList -> {
 | 
			
		||||
 | 
			
		||||
                    var genre = String()
 | 
			
		||||
                    var ungenre = String()
 | 
			
		||||
 | 
			
		||||
                    filter.state.forEach {
 | 
			
		||||
                        if (it.isIncluded()) genre += ",${it.name}"
 | 
			
		||||
                        if (it.isExcluded()) ungenre += ",${it.name}"
 | 
			
		||||
                    }
 | 
			
		||||
                    url.addQueryParameter("genre", genre)
 | 
			
		||||
                    url.addQueryParameter("ungenre", ungenre)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return GET(url.toString(), headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request =
 | 
			
		||||
            GET("$baseUrl/manga-list.html?listType=pagination&page=$page&artist=&author=&group=&m_status=&name=&genre=&sort=last_update&sort_type=DESC")
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaParse(response: Response): MangasPage {
 | 
			
		||||
        val document = response.asJsoup()
 | 
			
		||||
        val mangas = mutableListOf<SManga>()
 | 
			
		||||
        var hasNextPage = true
 | 
			
		||||
 | 
			
		||||
        document.select(popularMangaSelector()).forEach{ mangas.add(popularMangaFromElement(it)) }
 | 
			
		||||
 | 
			
		||||
        // check if there's a next page
 | 
			
		||||
        document.select(popularMangaNextPageSelector()).first().text().let {
 | 
			
		||||
            val currentPage = it.substringAfter("Page ").substringBefore(" ")
 | 
			
		||||
            val lastPage = it.substringAfterLast(" ")
 | 
			
		||||
            if (currentPage == lastPage) hasNextPage = false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return MangasPage(mangas, hasNextPage)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaParse(response: Response) = popularMangaParse(response)
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = "div.media"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = popularMangaSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = popularMangaSelector()
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("h3 > a").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain("/" + it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga =
 | 
			
		||||
            popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = "div.col-lg-9 button.btn-info"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        val infoElement = document.select("div.row").first()
 | 
			
		||||
        val genres = infoElement.select("ul.manga-info li:nth-child(5) small a")?.map {
 | 
			
		||||
            it.text()
 | 
			
		||||
        }
 | 
			
		||||
        manga.author = infoElement.select("small a.btn.btn-xs.btn-info").first()?.text()
 | 
			
		||||
        manga.genre = genres?.joinToString(", ")
 | 
			
		||||
        manga.status = parseStatus(infoElement.select("a.btn.btn-xs.btn-success").first().text())
 | 
			
		||||
 | 
			
		||||
        manga.description = document.select("div.row > p")?.text()?.trim()
 | 
			
		||||
        val imgUrl = document.select("img.thumbnail").first()?.attr("src")
 | 
			
		||||
        if (imgUrl!!.startsWith("app/")) {
 | 
			
		||||
            manga.thumbnail_url = "$baseUrl/$imgUrl"
 | 
			
		||||
        } else {
 | 
			
		||||
            manga.thumbnail_url = imgUrl
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(element: String): Int = when {
 | 
			
		||||
        element.contains("Completed") -> SManga.COMPLETED
 | 
			
		||||
        element.contains("Ongoing") -> SManga.ONGOING
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = ".list-chapters .list-wrap p"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val urlElement = element.select(".titleLink a").first()
 | 
			
		||||
        val timeElement = element.select(".pubDate time").first()
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain("/" + urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text()
 | 
			
		||||
        chapter.date_upload = parseChapterDate(timeElement.text())
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseChapterDate(date: String): Long {
 | 
			
		||||
        val value = date.split(' ')[0].toInt()
 | 
			
		||||
        return when {
 | 
			
		||||
            "min(s) ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.MINUTE, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "hour(s) ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.HOUR_OF_DAY, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "day(s) ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.DATE, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "week(s) ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.DATE, value * 7 * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "month(s) ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.MONTH, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "year(s) ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.YEAR, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            else -> {
 | 
			
		||||
                return 0
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
        document.select(".chapter-img").forEach {
 | 
			
		||||
            val url = it.attr("src")
 | 
			
		||||
            if (url != "") {
 | 
			
		||||
                pages.add(Page(pages.size, "", url))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document) = ""
 | 
			
		||||
 | 
			
		||||
    override fun imageRequest(page: Page): Request {
 | 
			
		||||
        val imgHeader = Headers.Builder().apply {
 | 
			
		||||
            add("Referer", baseUrl)
 | 
			
		||||
        }.build()
 | 
			
		||||
        return GET(page.imageUrl!!, imgHeader)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class TextField(name: String, val key: String) : Filter.Text(name)
 | 
			
		||||
    private class Status : Filter.Select<String>("Status", arrayOf("Any", "Completed", "Ongoing"))
 | 
			
		||||
    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genre", genres)
 | 
			
		||||
    private class Genre(name: String, val id: String = name.replace(' ', '+')) : Filter.TriState(name)
 | 
			
		||||
 | 
			
		||||
    // TODO: Country
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            TextField("Author", "author"),
 | 
			
		||||
            TextField("Group", "group"),
 | 
			
		||||
            Status(),
 | 
			
		||||
            GenreList(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    // [...document.querySelectorAll("div.panel-body a")].map((el,i) => `Genre("${el.innerText.trim()}")`).join(',\n')
 | 
			
		||||
    //  on https://lhtranslation.net/search
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Genre("Action"),
 | 
			
		||||
            Genre("18+"),
 | 
			
		||||
            Genre("Adult"),
 | 
			
		||||
            Genre("Anime"),
 | 
			
		||||
            Genre("Comedy"),
 | 
			
		||||
            Genre("Comic"),
 | 
			
		||||
            Genre("Doujinshi"),
 | 
			
		||||
            Genre("Drama"),
 | 
			
		||||
            Genre("Ecchi"),
 | 
			
		||||
            Genre("Fantasy"),
 | 
			
		||||
            Genre("Gender Bender"),
 | 
			
		||||
            Genre("Harem"),
 | 
			
		||||
            Genre("Historical"),
 | 
			
		||||
            Genre("Horror"),
 | 
			
		||||
            Genre("Josei"),
 | 
			
		||||
            Genre("Live action"),
 | 
			
		||||
            Genre("Manhua"),
 | 
			
		||||
            Genre("Manhwa"),
 | 
			
		||||
            Genre("Martial Art"),
 | 
			
		||||
            Genre("Mature"),
 | 
			
		||||
            Genre("Mecha"),
 | 
			
		||||
            Genre("Mystery"),
 | 
			
		||||
            Genre("One shot"),
 | 
			
		||||
            Genre("Psychological"),
 | 
			
		||||
            Genre("Romance"),
 | 
			
		||||
            Genre("School Life"),
 | 
			
		||||
            Genre("Sci-fi"),
 | 
			
		||||
            Genre("Seinen"),
 | 
			
		||||
            Genre("Shoujo"),
 | 
			
		||||
            Genre("Shojou Ai"),
 | 
			
		||||
            Genre("Shounen"),
 | 
			
		||||
            Genre("Shounen Ai"),
 | 
			
		||||
            Genre("Slice of Life"),
 | 
			
		||||
            Genre("Smut"),
 | 
			
		||||
            Genre("Sports"),
 | 
			
		||||
            Genre("Supernatural"),
 | 
			
		||||
            Genre("Tragedy"),
 | 
			
		||||
            Genre("Adventure"),
 | 
			
		||||
            Genre("Yaoi")
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
apply plugin: 'kotlin-android'
 | 
			
		||||
 | 
			
		||||
ext {
 | 
			
		||||
    appName = 'Tachiyomi: Manhwa18'
 | 
			
		||||
    pkgNameSuffix = 'en.manhwa18'
 | 
			
		||||
    extClass = '.Manhwa18'
 | 
			
		||||
    extVersionCode = 1
 | 
			
		||||
    libVersion = '1.2'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$rootDir/common.gradle"
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 6.3 KiB  | 
| 
		 Before Width: | Height: | Size: 3.2 KiB  | 
| 
		 Before Width: | Height: | Size: 8.9 KiB  | 
| 
		 Before Width: | Height: | Size: 18 KiB  | 
| 
		 Before Width: | Height: | Size: 25 KiB  | 
| 
		 Before Width: | Height: | Size: 125 KiB  | 
@ -1,300 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.en.manhwa18
 | 
			
		||||
 | 
			
		||||
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.util.*
 | 
			
		||||
 | 
			
		||||
class Manhwa18 : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val name = "Manhwa18"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "https://manhwa18.com"
 | 
			
		||||
 | 
			
		||||
    override val lang = "en"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override val client: OkHttpClient = network.cloudflareClient
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = "div.row.top div.media"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int) =
 | 
			
		||||
        GET("$baseUrl/manga-list.html?page=$page&ungenre=raw&sort=last_update&sort_type=DESC")
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        val item = element.select("h3 a")
 | 
			
		||||
        manga.setUrlWithoutDomain("/" + item.attr("href"))
 | 
			
		||||
        manga.title = item.text()
 | 
			
		||||
        when {
 | 
			
		||||
            element.select("img").attr("src").contains("file-thumb") ->
 | 
			
		||||
                manga.thumbnail_url = "http:" + element.select("img").attr("src")
 | 
			
		||||
            element.select("img").attr("src").contains("app/manga/uploads") ->
 | 
			
		||||
                manga.thumbnail_url = baseUrl + element.select("img").attr("src")
 | 
			
		||||
            else -> manga.thumbnail_url = element.select("img").attr("src")
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = "a:not(.disabled):contains(»)"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int) =
 | 
			
		||||
        GET("$baseUrl/manga-list.html?page=$page&ungenre=raw&sort=views&sort_type=DESC")
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element) = latestUpdatesFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = latestUpdatesSelector()
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = latestUpdatesNextPageSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val url = HttpUrl.parse("$baseUrl/manga-list.html")!!.newBuilder()
 | 
			
		||||
        url.addQueryParameter("listType", "pagination")
 | 
			
		||||
        url.addQueryParameter("page", page.toString())
 | 
			
		||||
        url.addQueryParameter("name", query)
 | 
			
		||||
        filters.forEach { filter ->
 | 
			
		||||
            when (filter) {
 | 
			
		||||
                is AuthorFilter -> {
 | 
			
		||||
                    url.addQueryParameter("author", filter.state)
 | 
			
		||||
                }
 | 
			
		||||
                is StatusFilter -> {
 | 
			
		||||
                    if(filter.state != 0) {
 | 
			
		||||
                        url.addQueryParameter("m_status", filter.toUriPart())
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is SortBy -> {
 | 
			
		||||
                    url.addQueryParameter("sort", when {
 | 
			
		||||
                        filter.state?.index == 0 -> "name"
 | 
			
		||||
                        filter.state?.index == 1 -> "views"
 | 
			
		||||
                        else -> "last_update"
 | 
			
		||||
                    })
 | 
			
		||||
                    if (filter.state?.ascending == true)
 | 
			
		||||
                        url.addQueryParameter("sort_type", "ASC")
 | 
			
		||||
                }
 | 
			
		||||
                is GenreFilter -> {
 | 
			
		||||
                    val genreToExclude = mutableListOf<String>()
 | 
			
		||||
                    val genreToInclude = mutableListOf<String>()
 | 
			
		||||
                    filter.state.forEach { content ->
 | 
			
		||||
                        if (content.isExcluded())
 | 
			
		||||
                            genreToExclude.add(content.name)
 | 
			
		||||
                        else if (content.isIncluded())
 | 
			
		||||
                            genreToInclude.add(content.name)
 | 
			
		||||
                    }
 | 
			
		||||
                    if (genreToExclude.isNotEmpty()) {
 | 
			
		||||
                        genreToExclude.add("raw")
 | 
			
		||||
                        url.addQueryParameter("ungenre", genreToExclude
 | 
			
		||||
                            .joinToString(","))
 | 
			
		||||
                    } else url.addQueryParameter("ungenre", "raw")
 | 
			
		||||
                    if (genreToInclude.isNotEmpty()) {
 | 
			
		||||
                        url.addQueryParameter("genre", genreToInclude
 | 
			
		||||
                            .joinToString(","))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is TypeFilter -> {
 | 
			
		||||
                    if(filter.state != 0) {
 | 
			
		||||
                        url.addQueryParameter("genre", filter.toUriPart())
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return GET(url.build().toString(), headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = latestUpdatesSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element) = latestUpdatesFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = latestUpdatesNextPageSelector()
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsRequest(manga: SManga): Request {
 | 
			
		||||
        if (manga.url.startsWith("http")) {
 | 
			
		||||
            return GET(manga.url, headers)
 | 
			
		||||
        }
 | 
			
		||||
        return super.mangaDetailsRequest(manga)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val infoElement = document.select("ul.manga-info")
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        val status = infoElement.select("b:contains(status) + a").text()
 | 
			
		||||
        val genres = mutableListOf<String>()
 | 
			
		||||
        infoElement.select("b:contains(genre) + small a").forEach { element ->
 | 
			
		||||
            val genre = element.text()
 | 
			
		||||
            genres.add(genre)
 | 
			
		||||
        }
 | 
			
		||||
        val authors = mutableListOf<String>()
 | 
			
		||||
        infoElement.select("b:contains(author) + small a").forEach { element ->
 | 
			
		||||
            val author = element.text()
 | 
			
		||||
            authors.add(author)
 | 
			
		||||
        }
 | 
			
		||||
        manga.title = infoElement.select("h1").text()
 | 
			
		||||
        manga.author = authors.joinToString(", ")
 | 
			
		||||
        manga.artist = authors.joinToString(", ")
 | 
			
		||||
        manga.status = parseStatus(status)
 | 
			
		||||
        manga.genre = genres.joinToString(", ")
 | 
			
		||||
        manga.description = document.select("div.row:contains(description)").text()
 | 
			
		||||
            .substringAfter("Description ")
 | 
			
		||||
        when {
 | 
			
		||||
            document.select("img.thumbnail").attr("src").contains("file-thumb") ->
 | 
			
		||||
                manga.thumbnail_url = "http:" + document.select("img.thumbnail").attr("src")
 | 
			
		||||
            document.select("img.thumbnail").attr("src").contains("app/manga/uploads") ->
 | 
			
		||||
                manga.thumbnail_url = baseUrl + document.select("img.thumbnail").attr("src")
 | 
			
		||||
            else -> manga.thumbnail_url = document.select("img.thumbnail").attr("src")
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(status: String?) = when {
 | 
			
		||||
        status == null -> SManga.UNKNOWN
 | 
			
		||||
        status.contains("On going") -> SManga.ONGOING
 | 
			
		||||
        status.contains("Completed") -> SManga.COMPLETED
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListRequest(manga: SManga): Request {
 | 
			
		||||
        if (manga.url.startsWith("http")) {
 | 
			
		||||
            return GET(manga.url, headers)
 | 
			
		||||
        }
 | 
			
		||||
        return super.chapterListRequest(manga)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = "table tr"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        val timeElement = element.select("time").first()
 | 
			
		||||
        chapter.setUrlWithoutDomain("/" + element.select("a.chapter").attr("href"))
 | 
			
		||||
        chapter.name = element.select("a.chapter").text()
 | 
			
		||||
        chapter.date_upload = parseChapterDate(timeElement.text())
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseChapterDate(date: String): Long {
 | 
			
		||||
        val value = date.split(' ')[0].toInt()
 | 
			
		||||
        return when {
 | 
			
		||||
            "minutes" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.MINUTE, value * -1)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "hours" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.HOUR_OF_DAY, value * -1)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "days" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.DATE, value * -1)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "weeks" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.DATE, value * 7 * -1)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "months" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.MONTH, value * -1)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "years" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.YEAR, value * -1)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            else -> {
 | 
			
		||||
                return 0
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListRequest(chapter: SChapter): Request {
 | 
			
		||||
        if (chapter.url.startsWith("http")) {
 | 
			
		||||
            return GET(chapter.url, headers)
 | 
			
		||||
        }
 | 
			
		||||
        return super.pageListRequest(chapter)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
        document.select("img.chapter-img").forEach {
 | 
			
		||||
            val img = if (!it.attr("src").contains("app/manga/uploads"))
 | 
			
		||||
                it.attr("src")
 | 
			
		||||
            else
 | 
			
		||||
                "$baseUrl/" + it.attr("src")
 | 
			
		||||
            pages.add(Page(pages.size, "", img))
 | 
			
		||||
        }
 | 
			
		||||
        return pages
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document): String = throw  UnsupportedOperationException("Not used")
 | 
			
		||||
 | 
			
		||||
    private class AuthorFilter : Filter.Text("Author(s)")
 | 
			
		||||
 | 
			
		||||
    private class StatusFilter : UriPartFilter("Status", arrayOf(
 | 
			
		||||
            Pair("Any", ""),
 | 
			
		||||
            Pair("Completed", "1"),
 | 
			
		||||
            Pair("On going", "2"),
 | 
			
		||||
            Pair("Drop", "3")
 | 
			
		||||
    ))
 | 
			
		||||
 | 
			
		||||
    private class SortBy : Filter.Sort("Sorted By", arrayOf("A-Z", "Most vỉews", "Last updated"),
 | 
			
		||||
        Selection(1, false))
 | 
			
		||||
 | 
			
		||||
    private class GenreFilter(genres: List<Tag>) : Filter.Group<Tag>("Genres", genres)
 | 
			
		||||
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            AuthorFilter(),
 | 
			
		||||
            StatusFilter(),
 | 
			
		||||
            TypeFilter(),
 | 
			
		||||
            SortBy(),
 | 
			
		||||
            GenreFilter(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Tag("Action"),
 | 
			
		||||
            Tag("Anime"),
 | 
			
		||||
            Tag("Comedy"),
 | 
			
		||||
            Tag("Comic"),
 | 
			
		||||
            Tag("Doujinshi"),
 | 
			
		||||
            Tag("Drama"),
 | 
			
		||||
            Tag("Ecchi"),
 | 
			
		||||
            Tag("Fantasy"),
 | 
			
		||||
            Tag("Gender Bender"),
 | 
			
		||||
            Tag("Harem"),
 | 
			
		||||
            Tag("Historical"),
 | 
			
		||||
            Tag("Horror"),
 | 
			
		||||
            Tag("Josei"),
 | 
			
		||||
            Tag("Manhua"),
 | 
			
		||||
            Tag("Martial Art"),
 | 
			
		||||
            Tag("Mature"),
 | 
			
		||||
            Tag("Mecha"),
 | 
			
		||||
            Tag("Mystery"),
 | 
			
		||||
            Tag("One shot"),
 | 
			
		||||
            Tag("Psychological"),
 | 
			
		||||
            Tag("Romance"),
 | 
			
		||||
            Tag("School Life"),
 | 
			
		||||
            Tag("Sci-fi"),
 | 
			
		||||
            Tag("Seinen"),
 | 
			
		||||
            Tag("Shoujo"),
 | 
			
		||||
            Tag("Shojou Ai"),
 | 
			
		||||
            Tag("Shounen"),
 | 
			
		||||
            Tag("Shounen Ai"),
 | 
			
		||||
            Tag("Slice of Life"),
 | 
			
		||||
            Tag("Smut"),
 | 
			
		||||
            Tag("Sports"),
 | 
			
		||||
            Tag("Supernatural"),
 | 
			
		||||
            Tag("Tragedy"),
 | 
			
		||||
            Tag("Adventure"),
 | 
			
		||||
            Tag("Yaoi")
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private class TypeFilter : UriPartFilter("Types", arrayOf(
 | 
			
		||||
            Pair("<select>", ""),
 | 
			
		||||
            Pair("Adult 18", "adult"),
 | 
			
		||||
            Pair("Manhwa", "manhwa"),
 | 
			
		||||
            Pair("Hentai", "hentai")
 | 
			
		||||
    ))
 | 
			
		||||
 | 
			
		||||
    private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
 | 
			
		||||
        Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
 | 
			
		||||
        fun toUriPart() = vals[state].second
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class Tag(name: String) : Filter.TriState(name)
 | 
			
		||||
}
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
apply plugin: 'kotlin-android'
 | 
			
		||||
 | 
			
		||||
ext {
 | 
			
		||||
    appName = 'Tachiyomi: RawQQ'
 | 
			
		||||
    pkgNameSuffix = 'ja.rawqq'
 | 
			
		||||
    extClass = '.Rawqq'
 | 
			
		||||
    extVersionCode = 4
 | 
			
		||||
    libVersion = '1.2'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$rootDir/common.gradle"
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 9.4 KiB  | 
| 
		 Before Width: | Height: | Size: 4.8 KiB  | 
| 
		 Before Width: | Height: | Size: 15 KiB  | 
| 
		 Before Width: | Height: | Size: 32 KiB  | 
| 
		 Before Width: | Height: | Size: 54 KiB  | 
| 
		 Before Width: | Height: | Size: 312 KiB  | 
@ -1,242 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.ja.rawqq
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.*
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
 | 
			
		||||
import okhttp3.Headers
 | 
			
		||||
import okhttp3.HttpUrl
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class Rawqq : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val name = "RawQQ"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "http://rawqq.com"
 | 
			
		||||
 | 
			
		||||
    override val lang = "ja"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request =
 | 
			
		||||
            GET("$baseUrl/manga-list.html?listType=pagination&page=$page&artist=&author=&group=&m_status=&name=&genre=&ungenre=&sort=views&sort_type=DESC", headers)
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val url = HttpUrl.parse("$baseUrl/manga-list.html?")!!.newBuilder().addQueryParameter("name", query)
 | 
			
		||||
        (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
 | 
			
		||||
            when (filter) {
 | 
			
		||||
                is Status -> {
 | 
			
		||||
                    val status = arrayOf("", "1", "2")[filter.state]
 | 
			
		||||
                    url.addQueryParameter("m_status", status)
 | 
			
		||||
                }
 | 
			
		||||
                is TextField -> url.addQueryParameter(filter.key, filter.state)
 | 
			
		||||
                is GenreList -> {
 | 
			
		||||
 | 
			
		||||
                    var genre = mutableListOf<String>()
 | 
			
		||||
                    var ungenre = mutableListOf<String>()
 | 
			
		||||
 | 
			
		||||
                    filter.state.forEach {it ->
 | 
			
		||||
                        if (it.isIncluded()) genre.add(it.name)
 | 
			
		||||
                        if (it.isExcluded()) ungenre.add(it.name)
 | 
			
		||||
                    }
 | 
			
		||||
                    url.addQueryParameter("genre", genre.joinToString())
 | 
			
		||||
                    url.addQueryParameter("ungenre", ungenre.joinToString())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return GET(url.toString(), headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request =
 | 
			
		||||
            GET("$baseUrl/manga-list.html?listType=pagination&page=$page&artist=&author=&group=&m_status=&name=&genre=&sort=last_update&sort_type=DESC")
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = "div.media"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = popularMangaSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = popularMangaSelector()
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("h3 > a").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain("/" + it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga =
 | 
			
		||||
            popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = "a:contains(»)"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        val infoElement = document.select("div.row").first()
 | 
			
		||||
        manga.author = infoElement.select("small a.btn.btn-xs.btn-info").first()?.text()
 | 
			
		||||
        manga.status = parseStatus(infoElement.select("a.btn.btn-xs.btn-success").first().text())
 | 
			
		||||
 | 
			
		||||
        manga.description = document.select("div.content").first()?.text()
 | 
			
		||||
        val imgUrl = document.select("img.thumbnail").first()?.attr("src")
 | 
			
		||||
        if (imgUrl!!.startsWith("app/")) {
 | 
			
		||||
            manga.thumbnail_url = "$baseUrl/$imgUrl"
 | 
			
		||||
        } else {
 | 
			
		||||
            manga.thumbnail_url = imgUrl
 | 
			
		||||
        }
 | 
			
		||||
        var genres = mutableListOf<String>()
 | 
			
		||||
        infoElement.select("div.row small a.btn.btn-xs.btn-danger")?.forEach { it -> genres.add(it.text())}
 | 
			
		||||
        manga.genre = genres.joinToString(", ")
 | 
			
		||||
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(element: String): Int = when {
 | 
			
		||||
        element.contains("Completed") -> SManga.COMPLETED
 | 
			
		||||
        element.contains("Ongoing") -> SManga.ONGOING
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //override fun chapterListSelector() = " table.table.table-hover tbody tr"
 | 
			
		||||
    override fun chapterListSelector() = " div#list-chapters.list-wrap p"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val urlElement = element.select("span.title a").first()
 | 
			
		||||
        val timeElement = element.select("span.publishedDate time").first()
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain("/" + urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text()
 | 
			
		||||
        chapter.date_upload = parseChapterDate(timeElement.text())
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseChapterDate(date: String): Long {
 | 
			
		||||
        val value = date.split(' ')[0].toInt()
 | 
			
		||||
        return when {
 | 
			
		||||
            "hour(s) ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.HOUR_OF_DAY, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "day(s) ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.DATE, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "week(s) ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.DATE, value * 7 * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "month(s) ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.MONTH, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "year(s) ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.YEAR, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            else -> {
 | 
			
		||||
                return 0
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
        document.select("img.chapter-img").forEach {
 | 
			
		||||
            val url = it.attr("src")
 | 
			
		||||
            if (url != "") {
 | 
			
		||||
                pages.add(Page(pages.size, "", url))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document) = ""
 | 
			
		||||
 | 
			
		||||
    override fun imageRequest(page: Page): Request {
 | 
			
		||||
        if (page.imageUrl!!.contains("lhscanlation.club")) {
 | 
			
		||||
            val imgHeader = Headers.Builder().apply {
 | 
			
		||||
                add("Referer", "https://lhscan.net")
 | 
			
		||||
            }.build()
 | 
			
		||||
            return GET(page.imageUrl!!, imgHeader)
 | 
			
		||||
        }
 | 
			
		||||
        return GET(page.imageUrl!!)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class TextField(name: String, val key: String) : Filter.Text(name)
 | 
			
		||||
    private class Status : Filter.Select<String>("Status", arrayOf("Any", "Completed", "Ongoing"))
 | 
			
		||||
    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genre", genres)
 | 
			
		||||
    private class Genre(name: String, val id: String = name.replace(' ', '+')) : Filter.TriState(name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            TextField("Author", "author"),
 | 
			
		||||
            TextField("Group", "group"),
 | 
			
		||||
            Status(),
 | 
			
		||||
            GenreList(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Genre("4-Koma"),
 | 
			
		||||
            Genre("Action"),
 | 
			
		||||
            Genre("Adult"),
 | 
			
		||||
            Genre("Adventure"),
 | 
			
		||||
            Genre("Isekai"),
 | 
			
		||||
            Genre("Comedy"),
 | 
			
		||||
            Genre("Comic"),
 | 
			
		||||
            Genre("Cooking"),
 | 
			
		||||
            Genre("Doujinshi"),
 | 
			
		||||
            Genre("Drama"),
 | 
			
		||||
            Genre("Ecchi"),
 | 
			
		||||
            Genre("Fantasy"),
 | 
			
		||||
            Genre("Gender Bender"),
 | 
			
		||||
            Genre("Harem"),
 | 
			
		||||
            Genre("Historical"),
 | 
			
		||||
            Genre("Horror"),
 | 
			
		||||
            Genre("Josei"),
 | 
			
		||||
            Genre("Lolicon"),
 | 
			
		||||
            Genre("Manga"),
 | 
			
		||||
            Genre("Manhua"),
 | 
			
		||||
            Genre("Manhwa"),
 | 
			
		||||
            Genre("Martial Art"),
 | 
			
		||||
            Genre("Mature"),
 | 
			
		||||
            Genre("Mecha"),
 | 
			
		||||
            Genre("Medical"),
 | 
			
		||||
            Genre("Music"),
 | 
			
		||||
            Genre("Mystery"),
 | 
			
		||||
            Genre("One shot"),
 | 
			
		||||
            Genre("Psychological"),
 | 
			
		||||
            Genre("Romance"),
 | 
			
		||||
            Genre("School Life"),
 | 
			
		||||
            Genre("Sci-fi"),
 | 
			
		||||
            Genre("Seinen"),
 | 
			
		||||
            Genre("Shoujo"),
 | 
			
		||||
            Genre("Shojou Ai"),
 | 
			
		||||
            Genre("Shounen"),
 | 
			
		||||
            Genre("Shounen Ai"),
 | 
			
		||||
            Genre("Slice of Life"),
 | 
			
		||||
            Genre("Smut"),
 | 
			
		||||
            Genre("Sports"),
 | 
			
		||||
            Genre("Supernatural"),
 | 
			
		||||
            Genre("Tragedy"),
 | 
			
		||||
            Genre("Webtoon"),
 | 
			
		||||
            Genre("Yaoi"),
 | 
			
		||||
            Genre("Yuri")
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
apply plugin: 'kotlin-android'
 | 
			
		||||
 | 
			
		||||
ext {
 | 
			
		||||
    appName = 'Tachiyomi: RawLH'
 | 
			
		||||
    pkgNameSuffix = 'ja.rawlh'
 | 
			
		||||
    extClass = '.Rawlh'
 | 
			
		||||
    extVersionCode = 11
 | 
			
		||||
    libVersion = '1.2'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$rootDir/common.gradle"
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 1.8 KiB  | 
| 
		 Before Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 2.4 KiB  | 
| 
		 Before Width: | Height: | Size: 3.8 KiB  | 
| 
		 Before Width: | Height: | Size: 5.9 KiB  | 
| 
		 Before Width: | Height: | Size: 12 KiB  | 
@ -1,242 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.ja.rawlh
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.*
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
 | 
			
		||||
import okhttp3.Headers
 | 
			
		||||
import okhttp3.HttpUrl
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class Rawlh : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val name = "RawLH"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "http://lhscan.net"
 | 
			
		||||
 | 
			
		||||
    override val lang = "ja"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request =
 | 
			
		||||
            GET("$baseUrl/manga-list.html?listType=pagination&page=$page&artist=&author=&group=&m_status=&name=&genre=&ungenre=&sort=views&sort_type=DESC", headers)
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val url = HttpUrl.parse("$baseUrl/manga-list.html?")!!.newBuilder().addQueryParameter("name", query)
 | 
			
		||||
        (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
 | 
			
		||||
            when (filter) {
 | 
			
		||||
                is Status -> {
 | 
			
		||||
                    val status = arrayOf("", "1", "2")[filter.state]
 | 
			
		||||
                    url.addQueryParameter("m_status", status)
 | 
			
		||||
                }
 | 
			
		||||
                is TextField -> url.addQueryParameter(filter.key, filter.state)
 | 
			
		||||
                is GenreList -> {
 | 
			
		||||
 | 
			
		||||
                    var genre = String()
 | 
			
		||||
                    var ungenre = String()
 | 
			
		||||
 | 
			
		||||
                    filter.state.forEach {
 | 
			
		||||
                        if (it.isIncluded()) genre += ",${it.name}"
 | 
			
		||||
                        if (it.isExcluded()) ungenre += ",${it.name}"
 | 
			
		||||
                    }
 | 
			
		||||
                    url.addQueryParameter("genre", genre)
 | 
			
		||||
                    url.addQueryParameter("ungenre", ungenre)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return GET(url.toString(), headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request =
 | 
			
		||||
            GET("$baseUrl/manga-list.html?listType=pagination&page=$page&artist=&author=&group=&m_status=&name=&genre=&sort=last_update&sort_type=DESC")
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = "div.media"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = popularMangaSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = popularMangaSelector()
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("h3 > a").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain("/" + it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga =
 | 
			
		||||
            popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = "a:contains(»)"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        val infoElement = document.select("div.row").first()
 | 
			
		||||
        val genres = mutableListOf<String>()
 | 
			
		||||
        infoElement.select("li:contains(genre) a").forEach { element ->
 | 
			
		||||
            val genre = element.text()
 | 
			
		||||
            genres.add(genre)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        manga.author = infoElement.select("li:contains(author) a").text()
 | 
			
		||||
        manga.genre = genres.joinToString(", ")
 | 
			
		||||
        manga.status = parseStatus(infoElement.select("li:contains(status) a").first().text())
 | 
			
		||||
 | 
			
		||||
        manga.description = document.select("div.row:contains(description) > p").text()
 | 
			
		||||
        val imgUrl = document.select("img.thumbnail").first()?.attr("src")
 | 
			
		||||
        if (imgUrl!!.startsWith("app/")) {
 | 
			
		||||
            manga.thumbnail_url = "$baseUrl/$imgUrl"
 | 
			
		||||
        } else {
 | 
			
		||||
            manga.thumbnail_url = imgUrl
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(element: String): Int = when {
 | 
			
		||||
        element.contains("Completed") -> SManga.COMPLETED
 | 
			
		||||
        element.contains("On Going") -> SManga.ONGOING
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = " table.table.table-hover tbody tr"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val urlElement = element.select("td a").first()
 | 
			
		||||
        val timeElement = element.select("td time").first()
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain("/" + urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text()
 | 
			
		||||
        chapter.date_upload = parseChapterDate(timeElement.text())
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseChapterDate(date: String): Long {
 | 
			
		||||
        val value = date.split(' ')[0].toInt()
 | 
			
		||||
        return when {
 | 
			
		||||
            "hours ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.HOUR_OF_DAY, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "days ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.DATE, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "weeks ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.DATE, value * 7 * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "months ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.MONTH, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            "years ago" in date -> Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.YEAR, value * -1)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
            else -> {
 | 
			
		||||
                return 0
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
        document.select("img.chapter-img").forEach {
 | 
			
		||||
            val dataSrc = it.attr("data-src")
 | 
			
		||||
            if (dataSrc != "") {
 | 
			
		||||
                pages.add(Page(pages.size, "", dataSrc))
 | 
			
		||||
            }else{
 | 
			
		||||
                val src = it.attr("src")
 | 
			
		||||
                pages.add(Page(pages.size, "", src))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document) = ""
 | 
			
		||||
 | 
			
		||||
    override fun imageRequest(page: Page): Request {
 | 
			
		||||
        val imgHeader = Headers.Builder().apply {
 | 
			
		||||
            add("Referer", baseUrl)
 | 
			
		||||
        }.build()
 | 
			
		||||
        return GET(page.imageUrl!!, imgHeader)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class TextField(name: String, val key: String) : Filter.Text(name)
 | 
			
		||||
    private class Status : Filter.Select<String>("Status", arrayOf("Any", "Completed", "Ongoing"))
 | 
			
		||||
    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genre", genres)
 | 
			
		||||
    private class Genre(name: String) : Filter.TriState(name)
 | 
			
		||||
 | 
			
		||||
    // TODO: Country
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            TextField("Author", "author"),
 | 
			
		||||
            TextField("Group", "group"),
 | 
			
		||||
            Status(),
 | 
			
		||||
            GenreList(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    // [...document.querySelectorAll("div.panel-body a")].map((el,i) => `Genre("${el.innerText.trim()}")`).join(',\n')
 | 
			
		||||
    //  on https://lhscan.net/search
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Genre("4-Koma"),
 | 
			
		||||
            Genre("Action"),
 | 
			
		||||
            Genre("Adult"),
 | 
			
		||||
            Genre("Adventure"),
 | 
			
		||||
            Genre("Isekai"),
 | 
			
		||||
            Genre("Comedy"),
 | 
			
		||||
            Genre("Comic"),
 | 
			
		||||
            Genre("Cooking"),
 | 
			
		||||
            Genre("Doujinshi"),
 | 
			
		||||
            Genre("Drama"),
 | 
			
		||||
            Genre("Ecchi"),
 | 
			
		||||
            Genre("Fantasy"),
 | 
			
		||||
            Genre("Gender Bender"),
 | 
			
		||||
            Genre("Harem"),
 | 
			
		||||
            Genre("Historical"),
 | 
			
		||||
            Genre("Horror"),
 | 
			
		||||
            Genre("Josei"),
 | 
			
		||||
            Genre("Lolicon"),
 | 
			
		||||
            Genre("Manga"),
 | 
			
		||||
            Genre("Manhua"),
 | 
			
		||||
            Genre("Manhwa"),
 | 
			
		||||
            Genre("Martial Art"),
 | 
			
		||||
            Genre("Mature"),
 | 
			
		||||
            Genre("Mecha"),
 | 
			
		||||
            Genre("Medical"),
 | 
			
		||||
            Genre("Music"),
 | 
			
		||||
            Genre("Mystery"),
 | 
			
		||||
            Genre("One shot"),
 | 
			
		||||
            Genre("Psychological"),
 | 
			
		||||
            Genre("Romance"),
 | 
			
		||||
            Genre("School Life"),
 | 
			
		||||
            Genre("Sci-fi"),
 | 
			
		||||
            Genre("Seinen"),
 | 
			
		||||
            Genre("Shoujo"),
 | 
			
		||||
            Genre("Shojou Ai"),
 | 
			
		||||
            Genre("Shounen"),
 | 
			
		||||
            Genre("Shounen Ai"),
 | 
			
		||||
            Genre("Slice of Life"),
 | 
			
		||||
            Genre("Smut"),
 | 
			
		||||
            Genre("Sports"),
 | 
			
		||||
            Genre("Supernatural"),
 | 
			
		||||
            Genre("Tragedy"),
 | 
			
		||||
            Genre("Webtoon"),
 | 
			
		||||
            Genre("Yaoi"),
 | 
			
		||||
            Genre("Yuri")
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
@ -1,16 +0,0 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
apply plugin: 'kotlin-android'
 | 
			
		||||
 | 
			
		||||
ext {
 | 
			
		||||
    appName = 'Tachiyomi: TruyenTranhLH'
 | 
			
		||||
    pkgNameSuffix = 'vi.truyentranhlh'
 | 
			
		||||
    extClass = '.TruyenTranhLH'
 | 
			
		||||
    extVersionCode = 7
 | 
			
		||||
    libVersion = '1.2'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    compileOnly 'com.google.code.gson:gson:2.8.2'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$rootDir/common.gradle"
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 3.0 KiB  | 
| 
		 Before Width: | Height: | Size: 1.7 KiB  | 
| 
		 Before Width: | Height: | Size: 4.2 KiB  | 
| 
		 Before Width: | Height: | Size: 7.2 KiB  | 
| 
		 Before Width: | Height: | Size: 13 KiB  | 
| 
		 Before Width: | Height: | Size: 48 KiB  | 
@ -1,202 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.vi.truyentranhlh
 | 
			
		||||
 | 
			
		||||
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.Headers
 | 
			
		||||
import okhttp3.OkHttpClient
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import okhttp3.Response
 | 
			
		||||
import org.jsoup.helper.StringUtil
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class TruyenTranhLH : HttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val name = "TruyenTranhLH"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "https://truyentranhlh.net"
 | 
			
		||||
 | 
			
		||||
    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").joinToString { it.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) }
 | 
			
		||||
        val imgUrl = document.select("img.thumbnail").first()?.attr("src")
 | 
			
		||||
        if (imgUrl!!.startsWith("/app/")) {
 | 
			
		||||
            manga.thumbnail_url = "$baseUrl/$imgUrl"
 | 
			
		||||
        } else {
 | 
			
		||||
            manga.thumbnail_url = imgUrl
 | 
			
		||||
        }
 | 
			
		||||
        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>()
 | 
			
		||||
        document.select("div.chapter-content > img").forEach {
 | 
			
		||||
            pages.add(Page(pages.size, "", it.attr("src")))
 | 
			
		||||
        }
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageRequest(page: Page): Request {
 | 
			
		||||
        val imgHeader = Headers.Builder().apply {
 | 
			
		||||
            add("Referer", baseUrl)
 | 
			
		||||
        }.build()
 | 
			
		||||
        return GET(page.imageUrl!!, imgHeader)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(response: Response): String {
 | 
			
		||||
        return ""
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||