mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Remove internal sources
This commit is contained in:
		@@ -6,11 +6,6 @@ import eu.kanade.tachiyomi.source.model.Page
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SChapter
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SManga
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.HttpSource
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.english.*
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.german.WieManga
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.russian.Mangachan
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.russian.Mintmanga
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.russian.Readmanga
 | 
			
		||||
import rx.Observable
 | 
			
		||||
 | 
			
		||||
open class SourceManager(private val context: Context) {
 | 
			
		||||
@@ -48,17 +43,7 @@ open class SourceManager(private val context: Context) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun createInternalSources(): List<Source> = listOf(
 | 
			
		||||
            LocalSource(context),
 | 
			
		||||
            Batoto(),
 | 
			
		||||
            Mangahere(),
 | 
			
		||||
            Mangafox(),
 | 
			
		||||
            Kissmanga(),
 | 
			
		||||
            Readmanga(),
 | 
			
		||||
            Mintmanga(),
 | 
			
		||||
            Mangachan(),
 | 
			
		||||
            Readmangatoday(),
 | 
			
		||||
            Mangasee(),
 | 
			
		||||
            WieManga()
 | 
			
		||||
            LocalSource(context)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private inner class StubSource(override val id: Long) : Source {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source.online.english
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.Page
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SChapter
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SManga
 | 
			
		||||
import rx.Observable
 | 
			
		||||
 | 
			
		||||
class Batoto : Source {
 | 
			
		||||
 | 
			
		||||
    override val id: Long = 1
 | 
			
		||||
 | 
			
		||||
    override val name = "Batoto"
 | 
			
		||||
 | 
			
		||||
    override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
 | 
			
		||||
        return Observable.error(Exception("RIP Batoto"))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
 | 
			
		||||
        return Observable.error(Exception("RIP Batoto"))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
 | 
			
		||||
        return Observable.error(Exception("RIP Batoto"))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String {
 | 
			
		||||
        return "$name (EN)"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,253 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source.online.english
 | 
			
		||||
 | 
			
		||||
import com.squareup.duktape.Duktape
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.network.POST
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.*
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
 | 
			
		||||
import okhttp3.FormBody
 | 
			
		||||
import okhttp3.Headers
 | 
			
		||||
import okhttp3.OkHttpClient
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import okhttp3.Response
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.regex.Pattern
 | 
			
		||||
 | 
			
		||||
class Kissmanga : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val id: Long = 4
 | 
			
		||||
 | 
			
		||||
    override val name = "Kissmanga"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "http://kissmanga.com"
 | 
			
		||||
 | 
			
		||||
    override val lang = "en"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override val client: OkHttpClient = network.cloudflareClient
 | 
			
		||||
 | 
			
		||||
    override fun headersBuilder(): Headers.Builder {
 | 
			
		||||
        return Headers.Builder()
 | 
			
		||||
            .add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) Gecko/20100101 Firefox/60")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = "table.listing tr:gt(1)"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = "table.listing tr:gt(1)"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request {
 | 
			
		||||
        return GET("$baseUrl/MangaList/MostPopular?page=$page", headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request {
 | 
			
		||||
        return GET("http://kissmanga.com/MangaList/LatestUpdate?page=$page", headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("td a:eq(0)").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            val title = it.text()
 | 
			
		||||
            //check if cloudfire email obfuscation is affecting title name
 | 
			
		||||
            if (title.contains("[email protected]", true)) {
 | 
			
		||||
                try {
 | 
			
		||||
                    var str: String = it.html()
 | 
			
		||||
                    //get the  number
 | 
			
		||||
                    str = str.substringAfter("data-cfemail=\"")
 | 
			
		||||
                    str = str.substringBefore("\">[email")
 | 
			
		||||
                    val sb = StringBuilder()
 | 
			
		||||
                    //convert number to char
 | 
			
		||||
                    val r = Integer.valueOf(str.substring(0, 2), 16)!!
 | 
			
		||||
                    var i = 2
 | 
			
		||||
                    while (i < str.length) {
 | 
			
		||||
                        val c = (Integer.valueOf(str.substring(i, i + 2), 16) xor r).toChar()
 | 
			
		||||
                        sb.append(c)
 | 
			
		||||
                        i += 2
 | 
			
		||||
                    }
 | 
			
		||||
                    //replace the new word into the title
 | 
			
		||||
                    manga.title = title.replace("[email protected]", sb.toString(), true)
 | 
			
		||||
                } catch (e: Exception) {
 | 
			
		||||
                    //on error just default to obfuscated title
 | 
			
		||||
                    Timber.e("error parsing [email protected]", e)
 | 
			
		||||
                    manga.title = title
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                manga.title = title
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga {
 | 
			
		||||
        return popularMangaFromElement(element)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = "li > a:contains(› Next)"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector(): String = "ul.pager > li > a:contains(Next)"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val form = FormBody.Builder().apply {
 | 
			
		||||
            add("mangaName", query)
 | 
			
		||||
 | 
			
		||||
            for (filter in if (filters.isEmpty()) getFilterList() else filters) {
 | 
			
		||||
                when (filter) {
 | 
			
		||||
                    is Author -> add("authorArtist", filter.state)
 | 
			
		||||
                    is Status -> add("status", arrayOf("", "Completed", "Ongoing")[filter.state])
 | 
			
		||||
                    is GenreList -> filter.state.forEach { genre -> add("genres", genre.state.toString()) }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return POST("$baseUrl/AdvanceSearch", headers, form.build())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = popularMangaSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga {
 | 
			
		||||
        return popularMangaFromElement(element)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = null
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val infoElement = document.select("div.barContent").first()
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.author = infoElement.select("p:has(span:contains(Author:)) > a").first()?.text()
 | 
			
		||||
        manga.genre = infoElement.select("p:has(span:contains(Genres:)) > *:gt(0)").text()
 | 
			
		||||
        manga.description = infoElement.select("p:has(span:contains(Summary:)) ~ p").text()
 | 
			
		||||
        manga.status = infoElement.select("p:has(span:contains(Status:))").first()?.text().orEmpty().let { parseStatus(it) }
 | 
			
		||||
        manga.thumbnail_url = document.select(".rightBox:eq(0) img").first()?.attr("src")
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun parseStatus(status: String) = when {
 | 
			
		||||
        status.contains("Ongoing") -> SManga.ONGOING
 | 
			
		||||
        status.contains("Completed") -> SManga.COMPLETED
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = "table.listing tr:gt(1)"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text()
 | 
			
		||||
        chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let {
 | 
			
		||||
            SimpleDateFormat("MM/dd/yyyy").parse(it).time
 | 
			
		||||
        } ?: 0
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListRequest(chapter: SChapter) = POST(baseUrl + chapter.url, headers)
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(response: Response): List<Page> {
 | 
			
		||||
        val body = response.body()!!.string()
 | 
			
		||||
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
 | 
			
		||||
        // Kissmanga now encrypts the urls, so we need to execute these two scripts in JS.
 | 
			
		||||
        val ca = client.newCall(GET("$baseUrl/Scripts/ca.js", headers)).execute().body()!!.string()
 | 
			
		||||
        val lo = client.newCall(GET("$baseUrl/Scripts/lo.js", headers)).execute().body()!!.string()
 | 
			
		||||
 | 
			
		||||
        Duktape.create().use {
 | 
			
		||||
            it.evaluate(ca)
 | 
			
		||||
            it.evaluate(lo)
 | 
			
		||||
 | 
			
		||||
            // There are two functions in an inline script needed to decrypt the urls. We find and
 | 
			
		||||
            // execute them.
 | 
			
		||||
            var p = Pattern.compile("(var.*CryptoJS.*)")
 | 
			
		||||
            var m = p.matcher(body)
 | 
			
		||||
            while (m.find()) {
 | 
			
		||||
                it.evaluate(m.group(1))
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Finally find all the urls and decrypt them in JS.
 | 
			
		||||
            p = Pattern.compile("""lstImages.push\((.*)\);""")
 | 
			
		||||
            m = p.matcher(body)
 | 
			
		||||
 | 
			
		||||
            var i = 0
 | 
			
		||||
            while (m.find()) {
 | 
			
		||||
                val url = it.evaluate(m.group(1)) as String
 | 
			
		||||
                pages.add(Page(i++, "", url))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        throw Exception("Not used")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlRequest(page: Page) = GET(page.url)
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document) = ""
 | 
			
		||||
 | 
			
		||||
    private class Status : Filter.TriState("Completed")
 | 
			
		||||
    private class Author : Filter.Text("Author")
 | 
			
		||||
    private class Genre(name: String) : Filter.TriState(name)
 | 
			
		||||
    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
 | 
			
		||||
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            Author(),
 | 
			
		||||
            Status(),
 | 
			
		||||
            GenreList(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    // $("select[name=\"genres\"]").map((i,el) => `Genre("${$(el).next().text().trim()}", ${i})`).get().join(',\n')
 | 
			
		||||
    // on http://kissmanga.com/AdvanceSearch
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Genre("4-Koma"),
 | 
			
		||||
            Genre("Action"),
 | 
			
		||||
            Genre("Adult"),
 | 
			
		||||
            Genre("Adventure"),
 | 
			
		||||
            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 Arts"),
 | 
			
		||||
            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("Shotacon"),
 | 
			
		||||
            Genre("Shoujo"),
 | 
			
		||||
            Genre("Shoujo 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,231 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source.online.english
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.*
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
 | 
			
		||||
import okhttp3.HttpUrl
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.text.ParseException
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class Mangafox : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val id: Long = 3
 | 
			
		||||
 | 
			
		||||
    override val name = "Mangafox"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "http://mangafox.la"
 | 
			
		||||
 | 
			
		||||
    override val lang = "en"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = "div#mangalist > ul.list > li"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request {
 | 
			
		||||
        val pageStr = if (page != 1) "$page.htm" else ""
 | 
			
		||||
        return GET("$baseUrl/directory/$pageStr", headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = "div#mangalist > ul.list > li"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request {
 | 
			
		||||
        val pageStr = if (page != 1) "$page.htm" else ""
 | 
			
		||||
        return GET("$baseUrl/directory/$pageStr?latest")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("a.title").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() = "a:has(span.next)"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = "a:has(span.next)"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val url = HttpUrl.parse("$baseUrl/search.php?name_method=cw&author_method=cw&artist_method=cw&advopts=1")!!.newBuilder().addQueryParameter("name", query)
 | 
			
		||||
        (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
 | 
			
		||||
            when (filter) {
 | 
			
		||||
                is Status -> url.addQueryParameter(filter.id, filter.state.toString())
 | 
			
		||||
                is GenreList -> filter.state.forEach { genre -> url.addQueryParameter(genre.id, genre.state.toString()) }
 | 
			
		||||
                is TextField -> url.addQueryParameter(filter.key, filter.state)
 | 
			
		||||
                is Type -> url.addQueryParameter("type", if (filter.state == 0) "" else filter.state.toString())
 | 
			
		||||
                is OrderBy -> {
 | 
			
		||||
                    url.addQueryParameter("sort", arrayOf("name", "rating", "views", "total_chapters", "last_chapter_time")[filter.state!!.index])
 | 
			
		||||
                    url.addQueryParameter("order", if (filter.state?.ascending == true) "az" else "za")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        url.addQueryParameter("page", page.toString())
 | 
			
		||||
        return GET(url.toString(), headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = "div#mangalist > ul.list > li"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("a.title").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = "a:has(span.next)"
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val infoElement = document.select("div#title").first()
 | 
			
		||||
        val rowElement = infoElement.select("table > tbody > tr:eq(1)").first()
 | 
			
		||||
        val sideInfoElement = document.select("#series_info").first()
 | 
			
		||||
        val licensedElement = document.select("div.warning").first()
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.author = rowElement.select("td:eq(1)").first()?.text()
 | 
			
		||||
        manga.artist = rowElement.select("td:eq(2)").first()?.text()
 | 
			
		||||
        manga.genre = rowElement.select("td:eq(3)").first()?.text()
 | 
			
		||||
        manga.description = infoElement.select("p.summary").first()?.text()
 | 
			
		||||
        val isLicensed = licensedElement?.text()?.contains("licensed")
 | 
			
		||||
        if (isLicensed == true) {
 | 
			
		||||
            manga.status = SManga.LICENSED
 | 
			
		||||
        } else {
 | 
			
		||||
            manga.status = sideInfoElement.select(".data").first()?.text().orEmpty().let { parseStatus(it) }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        manga.thumbnail_url = sideInfoElement.select("div.cover > img").first()?.attr("src")
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(status: String) = when {
 | 
			
		||||
        status.contains("Ongoing") -> SManga.ONGOING
 | 
			
		||||
        status.contains("Completed") -> SManga.COMPLETED
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = "div#chapters li div"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val urlElement = element.select("a.tips").first()
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = element.select("span.title.nowrap").first()?.text()?.let { urlElement.text() + " - " + it } ?: urlElement.text()
 | 
			
		||||
        chapter.date_upload = element.select("span.date").first()?.text()?.let { parseChapterDate(it) } ?: 0
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseChapterDate(date: String): Long {
 | 
			
		||||
        return if ("Today" in date || " ago" in date) {
 | 
			
		||||
            Calendar.getInstance().apply {
 | 
			
		||||
                set(Calendar.HOUR_OF_DAY, 0)
 | 
			
		||||
                set(Calendar.MINUTE, 0)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
        } else if ("Yesterday" in date) {
 | 
			
		||||
            Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.DATE, -1)
 | 
			
		||||
                set(Calendar.HOUR_OF_DAY, 0)
 | 
			
		||||
                set(Calendar.MINUTE, 0)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
        } else {
 | 
			
		||||
            try {
 | 
			
		||||
                SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(date).time
 | 
			
		||||
            } catch (e: ParseException) {
 | 
			
		||||
                0L
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        val url = document.baseUri().substringBeforeLast('/')
 | 
			
		||||
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
        document.select("select.m").first()?.select("option:not([value=0])")?.forEach {
 | 
			
		||||
            pages.add(Page(pages.size, "$url/${it.attr("value")}.html"))
 | 
			
		||||
        }
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document): String {
 | 
			
		||||
        val url = document.getElementById("image").attr("src")
 | 
			
		||||
        return if ("compressed?token=" !in url) {
 | 
			
		||||
            url
 | 
			
		||||
        } else {
 | 
			
		||||
            "http://mangafox.me/media/logo.png"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class Status(val id: String = "is_completed") : Filter.TriState("Completed")
 | 
			
		||||
    private class Genre(name: String, val id: String = "genres[$name]") : Filter.TriState(name)
 | 
			
		||||
    private class TextField(name: String, val key: String) : Filter.Text(name)
 | 
			
		||||
    private class Type : Filter.Select<String>("Type", arrayOf("Any", "Japanese Manga", "Korean Manhwa", "Chinese Manhua"))
 | 
			
		||||
    private class OrderBy : Filter.Sort("Order by",
 | 
			
		||||
            arrayOf("Series name", "Rating", "Views", "Total chapters", "Last chapter"),
 | 
			
		||||
            Filter.Sort.Selection(2, false))
 | 
			
		||||
 | 
			
		||||
    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
 | 
			
		||||
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            TextField("Author", "author"),
 | 
			
		||||
            TextField("Artist", "artist"),
 | 
			
		||||
            Type(),
 | 
			
		||||
            Status(),
 | 
			
		||||
            OrderBy(),
 | 
			
		||||
            GenreList(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    // $('select.genres').map((i,el)=>`Genre("${$(el).next().text().trim()}", "${$(el).attr('name')}")`).get().join(',\n')
 | 
			
		||||
    // on http://mangafox.me/search.php
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Genre("Action"),
 | 
			
		||||
            Genre("Adult"),
 | 
			
		||||
            Genre("Adventure"),
 | 
			
		||||
            Genre("Comedy"),
 | 
			
		||||
            Genre("Doujinshi"),
 | 
			
		||||
            Genre("Drama"),
 | 
			
		||||
            Genre("Ecchi"),
 | 
			
		||||
            Genre("Fantasy"),
 | 
			
		||||
            Genre("Gender Bender"),
 | 
			
		||||
            Genre("Harem"),
 | 
			
		||||
            Genre("Historical"),
 | 
			
		||||
            Genre("Horror"),
 | 
			
		||||
            Genre("Josei"),
 | 
			
		||||
            Genre("Martial Arts"),
 | 
			
		||||
            Genre("Mature"),
 | 
			
		||||
            Genre("Mecha"),
 | 
			
		||||
            Genre("Mystery"),
 | 
			
		||||
            Genre("One Shot"),
 | 
			
		||||
            Genre("Psychological"),
 | 
			
		||||
            Genre("Romance"),
 | 
			
		||||
            Genre("School Life"),
 | 
			
		||||
            Genre("Sci-fi"),
 | 
			
		||||
            Genre("Seinen"),
 | 
			
		||||
            Genre("Shoujo"),
 | 
			
		||||
            Genre("Shoujo Ai"),
 | 
			
		||||
            Genre("Shounen"),
 | 
			
		||||
            Genre("Shounen Ai"),
 | 
			
		||||
            Genre("Slice of Life"),
 | 
			
		||||
            Genre("Smut"),
 | 
			
		||||
            Genre("Sports"),
 | 
			
		||||
            Genre("Supernatural"),
 | 
			
		||||
            Genre("Tragedy"),
 | 
			
		||||
            Genre("Webtoons"),
 | 
			
		||||
            Genre("Yaoi"),
 | 
			
		||||
            Genre("Yuri")
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,259 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source.online.english
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.*
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
 | 
			
		||||
import okhttp3.HttpUrl
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.security.SecureRandom
 | 
			
		||||
import java.security.cert.X509Certificate
 | 
			
		||||
import java.text.ParseException
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.*
 | 
			
		||||
import javax.net.ssl.SSLContext
 | 
			
		||||
import javax.net.ssl.X509TrustManager
 | 
			
		||||
 | 
			
		||||
class Mangahere : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val id: Long = 2
 | 
			
		||||
 | 
			
		||||
    override val name = "Mangahere"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "http://www.mangahere.cc"
 | 
			
		||||
 | 
			
		||||
    override val lang = "en"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    private val trustManager = object : X509TrustManager {
 | 
			
		||||
        override fun getAcceptedIssuers(): Array<X509Certificate> {
 | 
			
		||||
            return emptyArray()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val sslContext = SSLContext.getInstance("SSL").apply {
 | 
			
		||||
        init(null, arrayOf(trustManager), SecureRandom())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override val client = super.client.newBuilder()
 | 
			
		||||
            .sslSocketFactory(sslContext.socketFactory, trustManager)
 | 
			
		||||
            .build()
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = "div.directory_list > ul > li"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = "div.directory_list > ul > li"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request {
 | 
			
		||||
        return GET("$baseUrl/directory/$page.htm?views.za", headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request {
 | 
			
		||||
        return GET("$baseUrl/directory/$page.htm?last_chapter_time.za", headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun mangaFromElement(query: String, element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select(query).first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = if (it.hasAttr("title")) it.attr("title") else if (it.hasAttr("rel")) it.attr("rel") else it.text()
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        return mangaFromElement("div.title > a", element)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga {
 | 
			
		||||
        return popularMangaFromElement(element)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = "div.next-page > a.next"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = "div.next-page > a.next"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val url = HttpUrl.parse("$baseUrl/search.php?name_method=cw&author_method=cw&artist_method=cw&advopts=1")!!.newBuilder().addQueryParameter("name", query)
 | 
			
		||||
        (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
 | 
			
		||||
            when (filter) {
 | 
			
		||||
                is Status -> url.addQueryParameter("is_completed", arrayOf("", "1", "0")[filter.state])
 | 
			
		||||
                is GenreList -> filter.state.forEach { genre -> url.addQueryParameter(genre.id, genre.state.toString()) }
 | 
			
		||||
                is TextField -> url.addQueryParameter(filter.key, filter.state)
 | 
			
		||||
                is Type -> url.addQueryParameter("direction", arrayOf("", "rl", "lr")[filter.state])
 | 
			
		||||
                is OrderBy -> {
 | 
			
		||||
                    url.addQueryParameter("sort", arrayOf("name", "rating", "views", "total_chapters", "last_chapter_time")[filter.state!!.index])
 | 
			
		||||
                    url.addQueryParameter("order", if (filter.state?.ascending == true) "az" else "za")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        url.addQueryParameter("page", page.toString())
 | 
			
		||||
        return GET(url.toString(), headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = "div.result_search > dl:has(dt)"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga {
 | 
			
		||||
        return mangaFromElement("a.manga_info", element)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = "div.next-page > a.next"
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val detailElement = document.select(".manga_detail_top").first()
 | 
			
		||||
        val infoElement = detailElement.select(".detail_topText").first()
 | 
			
		||||
        val licensedElement = document.select(".mt10.color_ff00.mb10").first()
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.author = infoElement.select("a[href*=author/]").first()?.text()
 | 
			
		||||
        manga.artist = infoElement.select("a[href*=artist/]").first()?.text()
 | 
			
		||||
        manga.genre = infoElement.select("li:eq(3)").first()?.text()?.substringAfter("Genre(s):")
 | 
			
		||||
        manga.description = infoElement.select("#show").first()?.text()?.substringBeforeLast("Show less")
 | 
			
		||||
        manga.thumbnail_url = detailElement.select("img.img").first()?.attr("src")
 | 
			
		||||
 | 
			
		||||
        if (licensedElement?.text()?.contains("licensed") == true) {
 | 
			
		||||
            manga.status = SManga.LICENSED
 | 
			
		||||
        } else {
 | 
			
		||||
            manga.status = infoElement.select("li:eq(6)").first()?.text().orEmpty().let { parseStatus(it) }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(status: String) = when {
 | 
			
		||||
        status.contains("Ongoing") -> SManga.ONGOING
 | 
			
		||||
        status.contains("Completed") -> SManga.COMPLETED
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = ".detail_list > ul:not([class]) > li"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val parentEl = element.select("span.left").first()
 | 
			
		||||
 | 
			
		||||
        val urlElement = parentEl.select("a").first()
 | 
			
		||||
 | 
			
		||||
        var volume = parentEl.select("span.mr6")?.first()?.text()?.trim() ?: ""
 | 
			
		||||
        if (volume.length > 0) {
 | 
			
		||||
            volume = " - " + volume
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var title = parentEl?.textNodes()?.last()?.text()?.trim() ?: ""
 | 
			
		||||
        if (title.length > 0) {
 | 
			
		||||
            title = " - " + title
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text() + volume + title
 | 
			
		||||
        chapter.date_upload = element.select("span.right").first()?.text()?.let { parseChapterDate(it) } ?: 0
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseChapterDate(date: String): Long {
 | 
			
		||||
        return if ("Today" in date) {
 | 
			
		||||
            Calendar.getInstance().apply {
 | 
			
		||||
                set(Calendar.HOUR_OF_DAY, 0)
 | 
			
		||||
                set(Calendar.MINUTE, 0)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
        } else if ("Yesterday" in date) {
 | 
			
		||||
            Calendar.getInstance().apply {
 | 
			
		||||
                add(Calendar.DATE, -1)
 | 
			
		||||
                set(Calendar.HOUR_OF_DAY, 0)
 | 
			
		||||
                set(Calendar.MINUTE, 0)
 | 
			
		||||
                set(Calendar.SECOND, 0)
 | 
			
		||||
                set(Calendar.MILLISECOND, 0)
 | 
			
		||||
            }.timeInMillis
 | 
			
		||||
        } else {
 | 
			
		||||
            try {
 | 
			
		||||
                SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(date).time
 | 
			
		||||
            } catch (e: ParseException) {
 | 
			
		||||
                0L
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        val licensedError = document.select(".mangaread_error > .mt10").first()
 | 
			
		||||
        if (licensedError != null) {
 | 
			
		||||
            throw Exception(licensedError.text())
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
        document.select("select.wid60").first()?.getElementsByTag("option")?.forEach {
 | 
			
		||||
            if (!it.attr("value").contains("featured.html")) {
 | 
			
		||||
                pages.add(Page(pages.size, "http:" + it.attr("value")))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        pages.getOrNull(0)?.imageUrl = imageUrlParse(document)
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document) = document.getElementById("image").attr("src")
 | 
			
		||||
 | 
			
		||||
    private class Status : Filter.TriState("Completed")
 | 
			
		||||
    private class Genre(name: String, val id: String = "genres[$name]") : Filter.TriState(name)
 | 
			
		||||
    private class TextField(name: String, val key: String) : Filter.Text(name)
 | 
			
		||||
    private class Type : Filter.Select<String>("Type", arrayOf("Any", "Japanese Manga (read from right to left)", "Korean Manhwa (read from left to right)"))
 | 
			
		||||
    private class OrderBy : Filter.Sort("Order by",
 | 
			
		||||
            arrayOf("Series name", "Rating", "Views", "Total chapters", "Last chapter"),
 | 
			
		||||
            Filter.Sort.Selection(2, false))
 | 
			
		||||
 | 
			
		||||
    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
 | 
			
		||||
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            TextField("Author", "author"),
 | 
			
		||||
            TextField("Artist", "artist"),
 | 
			
		||||
            Type(),
 | 
			
		||||
            Status(),
 | 
			
		||||
            OrderBy(),
 | 
			
		||||
            GenreList(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    // [...document.querySelectorAll("select[id^='genres'")].map((el,i) => `Genre("${el.nextSibling.nextSibling.textContent.trim()}", "${el.getAttribute('name')}")`).join(',\n')
 | 
			
		||||
    // http://www.mangahere.co/advsearch.htm
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Genre("Action"),
 | 
			
		||||
            Genre("Adventure"),
 | 
			
		||||
            Genre("Comedy"),
 | 
			
		||||
            Genre("Doujinshi"),
 | 
			
		||||
            Genre("Drama"),
 | 
			
		||||
            Genre("Ecchi"),
 | 
			
		||||
            Genre("Fantasy"),
 | 
			
		||||
            Genre("Gender Bender"),
 | 
			
		||||
            Genre("Harem"),
 | 
			
		||||
            Genre("Historical"),
 | 
			
		||||
            Genre("Horror"),
 | 
			
		||||
            Genre("Josei"),
 | 
			
		||||
            Genre("Martial Arts"),
 | 
			
		||||
            Genre("Mature"),
 | 
			
		||||
            Genre("Mecha"),
 | 
			
		||||
            Genre("Mystery"),
 | 
			
		||||
            Genre("One Shot"),
 | 
			
		||||
            Genre("Psychological"),
 | 
			
		||||
            Genre("Romance"),
 | 
			
		||||
            Genre("School Life"),
 | 
			
		||||
            Genre("Sci-fi"),
 | 
			
		||||
            Genre("Seinen"),
 | 
			
		||||
            Genre("Shoujo"),
 | 
			
		||||
            Genre("Shoujo Ai"),
 | 
			
		||||
            Genre("Shounen"),
 | 
			
		||||
            Genre("Shounen Ai"),
 | 
			
		||||
            Genre("Slice of Life"),
 | 
			
		||||
            Genre("Sports"),
 | 
			
		||||
            Genre("Supernatural"),
 | 
			
		||||
            Genre("Tragedy"),
 | 
			
		||||
            Genre("Yaoi"),
 | 
			
		||||
            Genre("Yuri")
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,249 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source.online.english
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.network.POST
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.*
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
 | 
			
		||||
import okhttp3.FormBody
 | 
			
		||||
import okhttp3.Headers
 | 
			
		||||
import okhttp3.HttpUrl
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.regex.Pattern
 | 
			
		||||
 | 
			
		||||
class Mangasee : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val id: Long = 9
 | 
			
		||||
 | 
			
		||||
    override val name = "Mangasee"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "http://mangaseeonline.us"
 | 
			
		||||
 | 
			
		||||
    override val lang = "en"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    private val recentUpdatesPattern = Pattern.compile("(.*?)\\s(\\d+\\.?\\d*)\\s?(Completed)?")
 | 
			
		||||
 | 
			
		||||
    private val indexPattern = Pattern.compile("-index-(.*?)-")
 | 
			
		||||
 | 
			
		||||
    private val catalogHeaders = Headers.Builder().apply {
 | 
			
		||||
        add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
 | 
			
		||||
        add("Host", "mangaseeonline.us")
 | 
			
		||||
    }.build()
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = "div.requested > div.row"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request {
 | 
			
		||||
        val (body, requestUrl) = convertQueryToPost(page, "$baseUrl/search/request.php?sortBy=popularity&sortOrder=descending")
 | 
			
		||||
        return POST(requestUrl, catalogHeaders, body.build())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("a.resultLink").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = "button.requestMore"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = "div.requested > div.row"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val url = HttpUrl.parse("$baseUrl/search/request.php")!!.newBuilder()
 | 
			
		||||
        if (!query.isEmpty()) url.addQueryParameter("keyword", query)
 | 
			
		||||
        val genres = mutableListOf<String>()
 | 
			
		||||
        val genresNo = mutableListOf<String>()
 | 
			
		||||
        for (filter in if (filters.isEmpty()) getFilterList() else filters) {
 | 
			
		||||
            when (filter) {
 | 
			
		||||
                is Sort -> {
 | 
			
		||||
                    if (filter.state?.index != 0)
 | 
			
		||||
                        url.addQueryParameter("sortBy", if (filter.state?.index == 1) "dateUpdated" else "popularity")
 | 
			
		||||
                    if (filter.state?.ascending != true)
 | 
			
		||||
                        url.addQueryParameter("sortOrder", "descending")
 | 
			
		||||
                }
 | 
			
		||||
                is SelectField -> if (filter.state != 0) url.addQueryParameter(filter.key, filter.values[filter.state])
 | 
			
		||||
                is TextField -> if (!filter.state.isEmpty()) url.addQueryParameter(filter.key, filter.state)
 | 
			
		||||
                is GenreList -> filter.state.forEach { genre ->
 | 
			
		||||
                    when (genre.state) {
 | 
			
		||||
                        Filter.TriState.STATE_INCLUDE -> genres.add(genre.name)
 | 
			
		||||
                        Filter.TriState.STATE_EXCLUDE -> genresNo.add(genre.name)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (genres.isNotEmpty()) url.addQueryParameter("genre", genres.joinToString(","))
 | 
			
		||||
        if (genresNo.isNotEmpty()) url.addQueryParameter("genreNo", genresNo.joinToString(","))
 | 
			
		||||
 | 
			
		||||
        val (body, requestUrl) = convertQueryToPost(page, url.toString())
 | 
			
		||||
        return POST(requestUrl, catalogHeaders, body.build())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun convertQueryToPost(page: Int, url: String): Pair<FormBody.Builder, String> {
 | 
			
		||||
        val url = HttpUrl.parse(url)!!
 | 
			
		||||
        val body = FormBody.Builder().add("page", page.toString())
 | 
			
		||||
        for (i in 0..url.querySize() - 1) {
 | 
			
		||||
            body.add(url.queryParameterName(i), url.queryParameterValue(i))
 | 
			
		||||
        }
 | 
			
		||||
        val requestUrl = url.scheme() + "://" + url.host() + url.encodedPath()
 | 
			
		||||
        return Pair(body, requestUrl)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("a.resultLink").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = "button.requestMore"
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val detailElement = document.select("div.well > div.row").first()
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.author = detailElement.select("a[href^=/search/?author=]").first()?.text()
 | 
			
		||||
        manga.genre = detailElement.select("span.details > div.row > div:has(b:contains(Genre(s))) > a").map { it.text() }.joinToString()
 | 
			
		||||
        manga.description = detailElement.select("strong:contains(Description:) + div").first()?.text()
 | 
			
		||||
        manga.status = detailElement.select("a[href^=/search/?status=]").first()?.text().orEmpty().let { parseStatus(it) }
 | 
			
		||||
        manga.thumbnail_url = detailElement.select("div > img").first()?.absUrl("src")
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(status: String) = when {
 | 
			
		||||
        status.contains("Ongoing (Scan)") -> SManga.ONGOING
 | 
			
		||||
        status.contains("Complete (Scan)") -> SManga.COMPLETED
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = "div.chapter-list > a"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = element.select("span.chapterLabel").first().text()?.let { it } ?: ""
 | 
			
		||||
        chapter.date_upload = element.select("time").first()?.attr("datetime")?.let { parseChapterDate(it) } ?: 0
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseChapterDate(dateAsString: String): Long {
 | 
			
		||||
        return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(dateAsString).time
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        val fullUrl = document.baseUri()
 | 
			
		||||
        val url = fullUrl.substringBeforeLast('/')
 | 
			
		||||
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
 | 
			
		||||
        val series = document.select("input.IndexName").first().attr("value")
 | 
			
		||||
        val chapter = document.select("span.CurChapter").first().text()
 | 
			
		||||
        var index = ""
 | 
			
		||||
 | 
			
		||||
        val m = indexPattern.matcher(fullUrl)
 | 
			
		||||
        if (m.find()) {
 | 
			
		||||
            val indexNumber = m.group(1)
 | 
			
		||||
            index = "-index-$indexNumber"
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        document.select("div.ContainerNav").first().select("select.PageSelect > option").forEach {
 | 
			
		||||
            pages.add(Page(pages.size, "$url/$series-chapter-$chapter$index-page-${pages.size + 1}.html"))
 | 
			
		||||
        }
 | 
			
		||||
        pages.getOrNull(0)?.imageUrl = imageUrlParse(document)
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document): String = document.select("img.CurImage").attr("src")
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = "button.requestMore"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector(): String = "a.latestSeries"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request {
 | 
			
		||||
        val url = "http://mangaseeonline.net/home/latest.request.php"
 | 
			
		||||
        val (body, requestUrl) = convertQueryToPost(page, url)
 | 
			
		||||
        return POST(requestUrl, catalogHeaders, body.build())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("a.latestSeries").first().let {
 | 
			
		||||
            val chapterUrl = it.attr("href")
 | 
			
		||||
            val indexOfMangaUrl = chapterUrl.indexOf("-chapter-")
 | 
			
		||||
            val indexOfLastPath = chapterUrl.lastIndexOf("/")
 | 
			
		||||
            val mangaUrl = chapterUrl.substring(indexOfLastPath, indexOfMangaUrl)
 | 
			
		||||
            val defaultText = it.select("p.clamp2").text()
 | 
			
		||||
            val m = recentUpdatesPattern.matcher(defaultText)
 | 
			
		||||
            val title = if (m.matches()) m.group(1) else defaultText
 | 
			
		||||
            manga.setUrlWithoutDomain("/manga" + mangaUrl)
 | 
			
		||||
            manga.title = title
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class Sort : Filter.Sort("Sort", arrayOf("Alphabetically", "Date updated", "Popularity"), Filter.Sort.Selection(2, false))
 | 
			
		||||
    private class Genre(name: String) : Filter.TriState(name)
 | 
			
		||||
    private class TextField(name: String, val key: String) : Filter.Text(name)
 | 
			
		||||
    private class SelectField(name: String, val key: String, values: Array<String>, state: Int = 0) : Filter.Select<String>(name, values, state)
 | 
			
		||||
    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
 | 
			
		||||
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            TextField("Years", "year"),
 | 
			
		||||
            TextField("Author", "author"),
 | 
			
		||||
            SelectField("Scan Status", "status", arrayOf("Any", "Complete", "Discontinued", "Hiatus", "Incomplete", "Ongoing")),
 | 
			
		||||
            SelectField("Publish Status", "pstatus", arrayOf("Any", "Cancelled", "Complete", "Discontinued", "Hiatus", "Incomplete", "Ongoing", "Unfinished")),
 | 
			
		||||
            SelectField("Type", "type", arrayOf("Any", "Doujinshi", "Manga", "Manhua", "Manhwa", "OEL", "One-shot")),
 | 
			
		||||
            Sort(),
 | 
			
		||||
            GenreList(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    // [...document.querySelectorAll("label.triStateCheckBox input")].map(el => `Filter("${el.getAttribute('name')}", "${el.nextSibling.textContent.trim()}")`).join(',\n')
 | 
			
		||||
    // http://mangasee.co/advanced-search/
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Genre("Action"),
 | 
			
		||||
            Genre("Adult"),
 | 
			
		||||
            Genre("Adventure"),
 | 
			
		||||
            Genre("Comedy"),
 | 
			
		||||
            Genre("Doujinshi"),
 | 
			
		||||
            Genre("Drama"),
 | 
			
		||||
            Genre("Ecchi"),
 | 
			
		||||
            Genre("Fantasy"),
 | 
			
		||||
            Genre("Gender Bender"),
 | 
			
		||||
            Genre("Harem"),
 | 
			
		||||
            Genre("Hentai"),
 | 
			
		||||
            Genre("Historical"),
 | 
			
		||||
            Genre("Horror"),
 | 
			
		||||
            Genre("Josei"),
 | 
			
		||||
            Genre("Lolicon"),
 | 
			
		||||
            Genre("Martial Arts"),
 | 
			
		||||
            Genre("Mature"),
 | 
			
		||||
            Genre("Mecha"),
 | 
			
		||||
            Genre("Mystery"),
 | 
			
		||||
            Genre("Psychological"),
 | 
			
		||||
            Genre("Romance"),
 | 
			
		||||
            Genre("School Life"),
 | 
			
		||||
            Genre("Sci-fi"),
 | 
			
		||||
            Genre("Seinen"),
 | 
			
		||||
            Genre("Shotacon"),
 | 
			
		||||
            Genre("Shoujo"),
 | 
			
		||||
            Genre("Shoujo Ai"),
 | 
			
		||||
            Genre("Shounen"),
 | 
			
		||||
            Genre("Shounen Ai"),
 | 
			
		||||
            Genre("Slice of Life"),
 | 
			
		||||
            Genre("Smut"),
 | 
			
		||||
            Genre("Sports"),
 | 
			
		||||
            Genre("Supernatural"),
 | 
			
		||||
            Genre("Tragedy"),
 | 
			
		||||
            Genre("Yaoi"),
 | 
			
		||||
            Genre("Yuri")
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,224 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source.online.english
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.network.POST
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.*
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
 | 
			
		||||
import okhttp3.Headers
 | 
			
		||||
import okhttp3.OkHttpClient
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class Readmangatoday : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val id: Long = 8
 | 
			
		||||
 | 
			
		||||
    override val name = "ReadMangaToday"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "https://www.readmng.com"
 | 
			
		||||
 | 
			
		||||
    override val lang = "en"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override val client: OkHttpClient get() = network.cloudflareClient
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Search only returns data with this set
 | 
			
		||||
     */
 | 
			
		||||
    override fun headersBuilder() = Headers.Builder().apply {
 | 
			
		||||
        add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
 | 
			
		||||
        add("X-Requested-With", "XMLHttpRequest")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request {
 | 
			
		||||
        return GET("$baseUrl/hot-manga/$page", headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request {
 | 
			
		||||
        return GET("$baseUrl/latest-releases/$page", headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = "div.hot-manga > div.style-list > div.box"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = "div.hot-manga > div.style-grid > div.box"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("div.title > h2 > a").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.attr("title")
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga {
 | 
			
		||||
        return popularMangaFromElement(element)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = "div.hot-manga > ul.pagination > li > a:contains(»)"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = "div.hot-manga > ul.pagination > li > a:contains(»)"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val builder = okhttp3.FormBody.Builder()
 | 
			
		||||
        builder.add("manga-name", query)
 | 
			
		||||
        (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
 | 
			
		||||
            when (filter) {
 | 
			
		||||
                is TextField -> builder.add(filter.key, filter.state)
 | 
			
		||||
                is Type -> builder.add("type", arrayOf("all", "japanese", "korean", "chinese")[filter.state])
 | 
			
		||||
                is Status -> builder.add("status", arrayOf("both", "completed", "ongoing")[filter.state])
 | 
			
		||||
                is GenreList -> filter.state.forEach { genre ->
 | 
			
		||||
                    when (genre.state) {
 | 
			
		||||
                        Filter.TriState.STATE_INCLUDE -> builder.add("include[]", genre.id.toString())
 | 
			
		||||
                        Filter.TriState.STATE_EXCLUDE -> builder.add("exclude[]", genre.id.toString())
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return POST("$baseUrl/service/advanced_search", headers, builder.build())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = "div.style-list > div.box"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("div.title > h2 > a").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.attr("title")
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = "div.next-page > a.next"
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val detailElement = document.select("div.movie-meta").first()
 | 
			
		||||
        val genreElement = detailElement.select("dl.dl-horizontal > dd:eq(5) a")
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.author = document.select("ul.cast-list li.director > ul a").first()?.text()
 | 
			
		||||
        manga.artist = document.select("ul.cast-list li:not(.director) > ul a").first()?.text()
 | 
			
		||||
        manga.description = detailElement.select("li.movie-detail").first()?.text()
 | 
			
		||||
        manga.status = detailElement.select("dl.dl-horizontal > dd:eq(3)").first()?.text().orEmpty().let { parseStatus(it) }
 | 
			
		||||
        manga.thumbnail_url = detailElement.select("img.img-responsive").first()?.attr("src")
 | 
			
		||||
 | 
			
		||||
        var genres = mutableListOf<String>()
 | 
			
		||||
        genreElement?.forEach { genres.add(it.text()) }
 | 
			
		||||
        manga.genre = genres.joinToString(", ")
 | 
			
		||||
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(status: String) = when {
 | 
			
		||||
        status.contains("Ongoing") -> SManga.ONGOING
 | 
			
		||||
        status.contains("Completed") -> SManga.COMPLETED
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = "ul.chp_lst > li"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.select("span.val").text()
 | 
			
		||||
        chapter.date_upload = element.select("span.dte").first()?.text()?.let { parseChapterDate(it) } ?: 0
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseChapterDate(date: String): Long {
 | 
			
		||||
        val dateWords: List<String> = date.split(" ")
 | 
			
		||||
 | 
			
		||||
        if (dateWords.size == 3) {
 | 
			
		||||
            val timeAgo = Integer.parseInt(dateWords[0])
 | 
			
		||||
            val date: Calendar = Calendar.getInstance()
 | 
			
		||||
 | 
			
		||||
            if (dateWords[1].contains("Minute")) {
 | 
			
		||||
                date.add(Calendar.MINUTE, -timeAgo)
 | 
			
		||||
            } else if (dateWords[1].contains("Hour")) {
 | 
			
		||||
                date.add(Calendar.HOUR_OF_DAY, -timeAgo)
 | 
			
		||||
            } else if (dateWords[1].contains("Day")) {
 | 
			
		||||
                date.add(Calendar.DAY_OF_YEAR, -timeAgo)
 | 
			
		||||
            } else if (dateWords[1].contains("Week")) {
 | 
			
		||||
                date.add(Calendar.WEEK_OF_YEAR, -timeAgo)
 | 
			
		||||
            } else if (dateWords[1].contains("Month")) {
 | 
			
		||||
                date.add(Calendar.MONTH, -timeAgo)
 | 
			
		||||
            } else if (dateWords[1].contains("Year")) {
 | 
			
		||||
                date.add(Calendar.YEAR, -timeAgo)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return date.timeInMillis
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 0L
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
        document.select("ul.list-switcher-2 > li > select.jump-menu").first().getElementsByTag("option").forEach {
 | 
			
		||||
            pages.add(Page(pages.size, it.attr("value")))
 | 
			
		||||
        }
 | 
			
		||||
        pages.getOrNull(0)?.imageUrl = imageUrlParse(document)
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document) = document.select("#chapter_img").first().attr("src")
 | 
			
		||||
 | 
			
		||||
    private class Status : Filter.TriState("Completed")
 | 
			
		||||
    private class Genre(name: String, val id: Int) : Filter.TriState(name)
 | 
			
		||||
    private class TextField(name: String, val key: String) : Filter.Text(name)
 | 
			
		||||
    private class Type : Filter.Select<String>("Type", arrayOf("All", "Japanese Manga", "Korean Manhwa", "Chinese Manhua"))
 | 
			
		||||
    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
 | 
			
		||||
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            TextField("Author", "author-name"),
 | 
			
		||||
            TextField("Artist", "artist-name"),
 | 
			
		||||
            Type(),
 | 
			
		||||
            Status(),
 | 
			
		||||
            GenreList(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    // [...document.querySelectorAll("ul.manga-cat span")].map(el => `Genre("${el.nextSibling.textContent.trim()}", ${el.getAttribute('data-id')})`).join(',\n')
 | 
			
		||||
    // http://www.readmanga.today/advanced-search
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Genre("Action", 2),
 | 
			
		||||
            Genre("Adventure", 4),
 | 
			
		||||
            Genre("Comedy", 5),
 | 
			
		||||
            Genre("Doujinshi", 6),
 | 
			
		||||
            Genre("Drama", 7),
 | 
			
		||||
            Genre("Ecchi", 8),
 | 
			
		||||
            Genre("Fantasy", 9),
 | 
			
		||||
            Genre("Gender Bender", 10),
 | 
			
		||||
            Genre("Harem", 11),
 | 
			
		||||
            Genre("Historical", 12),
 | 
			
		||||
            Genre("Horror", 13),
 | 
			
		||||
            Genre("Josei", 14),
 | 
			
		||||
            Genre("Lolicon", 15),
 | 
			
		||||
            Genre("Martial Arts", 16),
 | 
			
		||||
            Genre("Mature", 17),
 | 
			
		||||
            Genre("Mecha", 18),
 | 
			
		||||
            Genre("Mystery", 19),
 | 
			
		||||
            Genre("One shot", 20),
 | 
			
		||||
            Genre("Psychological", 21),
 | 
			
		||||
            Genre("Romance", 22),
 | 
			
		||||
            Genre("School Life", 23),
 | 
			
		||||
            Genre("Sci-fi", 24),
 | 
			
		||||
            Genre("Seinen", 25),
 | 
			
		||||
            Genre("Shotacon", 26),
 | 
			
		||||
            Genre("Shoujo", 27),
 | 
			
		||||
            Genre("Shoujo Ai", 28),
 | 
			
		||||
            Genre("Shounen", 29),
 | 
			
		||||
            Genre("Shounen Ai", 30),
 | 
			
		||||
            Genre("Slice of Life", 31),
 | 
			
		||||
            Genre("Smut", 32),
 | 
			
		||||
            Genre("Sports", 33),
 | 
			
		||||
            Genre("Supernatural", 34),
 | 
			
		||||
            Genre("Tragedy", 35),
 | 
			
		||||
            Genre("Yaoi", 36),
 | 
			
		||||
            Genre("Yuri", 37)
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
@@ -1,122 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source.online.german
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.FilterList
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.Page
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SChapter
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SManga
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
 | 
			
		||||
class WieManga : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val id: Long = 10
 | 
			
		||||
 | 
			
		||||
    override val name = "Wie Manga!"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "http://www.wiemanga.com"
 | 
			
		||||
 | 
			
		||||
    override val lang = "de"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = ".booklist td > div"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = ".booklist td > div"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request {
 | 
			
		||||
        return GET("$baseUrl/list/Hot-Book/", headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request {
 | 
			
		||||
        return GET("$baseUrl/list/New-Update/", headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val image = element.select("dt img")
 | 
			
		||||
        val title = element.select("dd a:first-child")
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.setUrlWithoutDomain(title.attr("href"))
 | 
			
		||||
        manga.title = title.text()
 | 
			
		||||
        manga.thumbnail_url = image.attr("src")
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga {
 | 
			
		||||
        return popularMangaFromElement(element)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = null
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = null
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        return GET("$baseUrl/search/?wd=$query", headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = ".searchresult td > div"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val image = element.select(".resultimg img")
 | 
			
		||||
        val title = element.select(".resultbookname")
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.setUrlWithoutDomain(title.attr("href"))
 | 
			
		||||
        manga.title = title.text()
 | 
			
		||||
        manga.thumbnail_url = image.attr("src")
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = ".pagetor a.l"
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val imageElement = document.select(".bookmessgae tr > td:nth-child(1)").first()
 | 
			
		||||
        val infoElement = document.select(".bookmessgae tr > td:nth-child(2)").first()
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.author = infoElement.select("dd:nth-of-type(2) a").first()?.text()
 | 
			
		||||
        manga.artist = infoElement.select("dd:nth-of-type(3) a").first()?.text()
 | 
			
		||||
        manga.description = infoElement.select("dl > dt:last-child").first()?.text()?.replaceFirst("Beschreibung", "")
 | 
			
		||||
        manga.thumbnail_url = imageElement.select("img").first()?.attr("src")
 | 
			
		||||
 | 
			
		||||
        if (manga.author == "RSS")
 | 
			
		||||
            manga.author = null
 | 
			
		||||
 | 
			
		||||
        if (manga.artist == "RSS")
 | 
			
		||||
            manga.artist = null
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = ".chapterlist tr:not(:first-child)"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val urlElement = element.select(".col1 a").first()
 | 
			
		||||
        val dateElement = element.select(".col3 a").first()
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text()
 | 
			
		||||
        chapter.date_upload = dateElement?.text()?.let { parseChapterDate(it) } ?: 0
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseChapterDate(date: String): Long {
 | 
			
		||||
        return SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(date).time
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
 | 
			
		||||
        document.select("select#page").first().select("option").forEach {
 | 
			
		||||
            pages.add(Page(pages.size, it.attr("value")))
 | 
			
		||||
        }
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document) = document.select("img#comicpic").first().attr("src")
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,290 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source.online.russian
 | 
			
		||||
 | 
			
		||||
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.Request
 | 
			
		||||
import okhttp3.Response
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class Mangachan : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val id: Long = 7
 | 
			
		||||
 | 
			
		||||
    override val name = "Mangachan"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "http://mangachan.me"
 | 
			
		||||
 | 
			
		||||
    override val lang = "ru"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request =
 | 
			
		||||
            GET("$baseUrl/mostfavorites?offset=${20 * (page - 1)}", headers)
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        var pageNum = 1
 | 
			
		||||
        when {
 | 
			
		||||
            page <  1 -> pageNum = 1
 | 
			
		||||
            page >= 1 -> pageNum = page
 | 
			
		||||
        }
 | 
			
		||||
        val url = if (query.isNotEmpty()) {
 | 
			
		||||
            "$baseUrl/?do=search&subaction=search&story=$query&search_start=$pageNum"
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            var genres = ""
 | 
			
		||||
            var order = ""
 | 
			
		||||
            var statusParam = true
 | 
			
		||||
            var status = ""
 | 
			
		||||
            for (filter in if (filters.isEmpty()) getFilterList() else filters) {
 | 
			
		||||
                when (filter) {
 | 
			
		||||
                    is GenreList -> {
 | 
			
		||||
                        filter.state.forEach { f ->
 | 
			
		||||
                            if (!f.isIgnored()) {
 | 
			
		||||
                                genres += (if (f.isExcluded()) "-" else "") + f.id + '+'
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    is OrderBy -> {
 | 
			
		||||
                        if (filter.state!!.ascending && filter.state!!.index == 0) {
 | 
			
		||||
                            statusParam = false
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    is Status -> status = arrayOf("", "all_done", "end", "ongoing", "new_ch")[filter.state]
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (genres.isNotEmpty()) {
 | 
			
		||||
                for (filter in filters) {
 | 
			
		||||
                    when (filter) {
 | 
			
		||||
                        is OrderBy -> {
 | 
			
		||||
                            order = if (filter.state!!.ascending) {
 | 
			
		||||
                                arrayOf("", "&n=favasc", "&n=abcdesc", "&n=chasc")[filter.state!!.index]
 | 
			
		||||
                            } else {
 | 
			
		||||
                                arrayOf("&n=dateasc", "&n=favdesc", "&n=abcasc", "&n=chdesc")[filter.state!!.index]
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (statusParam) {
 | 
			
		||||
                    "$baseUrl/tags/${genres.dropLast(1)}$order?offset=${20 * (pageNum - 1)}&status=$status"
 | 
			
		||||
                } else {
 | 
			
		||||
                    "$baseUrl/tags/$status/${genres.dropLast(1)}/$order?offset=${20 * (pageNum - 1)}"
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                for (filter in filters) {
 | 
			
		||||
                    when (filter) {
 | 
			
		||||
                        is OrderBy -> {
 | 
			
		||||
                            order = if (filter.state!!.ascending) {
 | 
			
		||||
                                arrayOf("manga/new", "manga/new&n=favasc", "manga/new&n=abcdesc", "manga/new&n=chasc")[filter.state!!.index]
 | 
			
		||||
                            } else {
 | 
			
		||||
                                arrayOf("manga/new&n=dateasc", "mostfavorites", "catalog", "sortch")[filter.state!!.index]
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (statusParam) {
 | 
			
		||||
                    "$baseUrl/$order?offset=${20 * (pageNum - 1)}&status=$status"
 | 
			
		||||
                } else {
 | 
			
		||||
                    "$baseUrl/$order/$status?offset=${20 * (pageNum - 1)}"
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return GET(url, headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/newestch?page=$page")
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = "div.content_row"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = "ul.area_rightNews li"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = popularMangaSelector()
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.thumbnail_url = element.select("div.manga_images img").first().attr("src")
 | 
			
		||||
        element.select("h2 > a").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        element.select("a:nth-child(1)").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.text()
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = "a:contains(Вперед)"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = "a:contains(Далее)"
 | 
			
		||||
 | 
			
		||||
    private fun searchGenresNextPageSelector() = popularMangaNextPageSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaParse(response: Response): MangasPage {
 | 
			
		||||
        val document = response.asJsoup()
 | 
			
		||||
        var hasNextPage = false
 | 
			
		||||
 | 
			
		||||
        val mangas = document.select(searchMangaSelector()).map { element ->
 | 
			
		||||
            searchMangaFromElement(element)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val nextSearchPage = document.select(searchMangaNextPageSelector())
 | 
			
		||||
        if (nextSearchPage.isNotEmpty()) {
 | 
			
		||||
            val query = document.select("input#searchinput").first().attr("value")
 | 
			
		||||
            val pageNum = nextSearchPage.let { selector ->
 | 
			
		||||
                val onClick = selector.attr("onclick")
 | 
			
		||||
                onClick?.split("""\\d+""")
 | 
			
		||||
            }
 | 
			
		||||
            nextSearchPage.attr("href", "$baseUrl/?do=search&subaction=search&story=$query&search_start=$pageNum")
 | 
			
		||||
            hasNextPage = true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val nextGenresPage = document.select(searchGenresNextPageSelector())
 | 
			
		||||
        if (nextGenresPage.isNotEmpty()) {
 | 
			
		||||
            hasNextPage = true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return MangasPage(mangas, hasNextPage)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val infoElement = document.select("table.mangatitle").first()
 | 
			
		||||
        val descElement = document.select("div#description").first()
 | 
			
		||||
        val imgElement = document.select("img#cover").first()
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.author = infoElement.select("tr:eq(2) > td:eq(1)").text()
 | 
			
		||||
        manga.genre = infoElement.select("tr:eq(5) > td:eq(1)").text()
 | 
			
		||||
        manga.status = parseStatus(infoElement.select("tr:eq(4) > td:eq(1)").text())
 | 
			
		||||
        manga.description = descElement.textNodes().first().text()
 | 
			
		||||
        manga.thumbnail_url = imgElement.attr("src")
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(element: String): Int = when {
 | 
			
		||||
        element.contains("перевод завершен") -> SManga.COMPLETED
 | 
			
		||||
        element.contains("перевод продолжается") -> SManga.ONGOING
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = "table.table_cha tr:gt(1)"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href"))
 | 
			
		||||
        chapter.name = urlElement.text()
 | 
			
		||||
        chapter.date_upload = element.select("div.date").first()?.text()?.let {
 | 
			
		||||
            SimpleDateFormat("yyyy-MM-dd", Locale.US).parse(it).time
 | 
			
		||||
        } ?: 0
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(response: Response): List<Page> {
 | 
			
		||||
        val html = response.body()!!.string()
 | 
			
		||||
        val beginIndex = html.indexOf("fullimg\":[") + 10
 | 
			
		||||
        val endIndex = html.indexOf(",]", beginIndex)
 | 
			
		||||
        val trimmedHtml = html.substring(beginIndex, endIndex).replace("\"", "")
 | 
			
		||||
        val pageUrls = trimmedHtml.split(',')
 | 
			
		||||
 | 
			
		||||
        return pageUrls.mapIndexed { i, url -> Page(i, "", url) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        throw Exception("Not used")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document) = ""
 | 
			
		||||
 | 
			
		||||
    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Тэги", genres)
 | 
			
		||||
    private class Genre(name: String, val id: String = name.replace(' ', '_')) : Filter.TriState(name)
 | 
			
		||||
    private class Status : Filter.Select<String>("Статус", arrayOf("Все", "Перевод завершен", "Выпуск завершен", "Онгоинг", "Новые главы"))
 | 
			
		||||
    private class OrderBy : Filter.Sort("Сортировка",
 | 
			
		||||
            arrayOf("Дата", "Популярность", "Имя", "Главы"),
 | 
			
		||||
            Filter.Sort.Selection(1, false))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            Status(),
 | 
			
		||||
            OrderBy(),
 | 
			
		||||
            GenreList(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /* [...document.querySelectorAll("li.sidetag > a:nth-child(1)")]
 | 
			
		||||
    *  .map(el => `Genre("${el.getAttribute('href').substr(6)}")`).join(',\n')
 | 
			
		||||
    *  on http://mangachan.me/
 | 
			
		||||
    */
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Genre("18_плюс"),
 | 
			
		||||
            Genre("bdsm"),
 | 
			
		||||
            Genre("арт"),
 | 
			
		||||
            Genre("боевик"),
 | 
			
		||||
            Genre("боевые_искусства"),
 | 
			
		||||
            Genre("вампиры"),
 | 
			
		||||
            Genre("веб"),
 | 
			
		||||
            Genre("гарем"),
 | 
			
		||||
            Genre("гендерная_интрига"),
 | 
			
		||||
            Genre("героическое_фэнтези"),
 | 
			
		||||
            Genre("детектив"),
 | 
			
		||||
            Genre("дзёсэй"),
 | 
			
		||||
            Genre("додзинси"),
 | 
			
		||||
            Genre("драма"),
 | 
			
		||||
            Genre("игра"),
 | 
			
		||||
            Genre("инцест"),
 | 
			
		||||
            Genre("искусство"),
 | 
			
		||||
            Genre("история"),
 | 
			
		||||
            Genre("киберпанк"),
 | 
			
		||||
            Genre("кодомо"),
 | 
			
		||||
            Genre("комедия"),
 | 
			
		||||
            Genre("литРПГ"),
 | 
			
		||||
            Genre("махо-сёдзё"),
 | 
			
		||||
            Genre("меха"),
 | 
			
		||||
            Genre("мистика"),
 | 
			
		||||
            Genre("музыка"),
 | 
			
		||||
            Genre("научная_фантастика"),
 | 
			
		||||
            Genre("повседневность"),
 | 
			
		||||
            Genre("постапокалиптика"),
 | 
			
		||||
            Genre("приключения"),
 | 
			
		||||
            Genre("психология"),
 | 
			
		||||
            Genre("романтика"),
 | 
			
		||||
            Genre("самурайский_боевик"),
 | 
			
		||||
            Genre("сборник"),
 | 
			
		||||
            Genre("сверхъестественное"),
 | 
			
		||||
            Genre("сказка"),
 | 
			
		||||
            Genre("спорт"),
 | 
			
		||||
            Genre("супергерои"),
 | 
			
		||||
            Genre("сэйнэн"),
 | 
			
		||||
            Genre("сёдзё"),
 | 
			
		||||
            Genre("сёдзё-ай"),
 | 
			
		||||
            Genre("сёнэн"),
 | 
			
		||||
            Genre("сёнэн-ай"),
 | 
			
		||||
            Genre("тентакли"),
 | 
			
		||||
            Genre("трагедия"),
 | 
			
		||||
            Genre("триллер"),
 | 
			
		||||
            Genre("ужасы"),
 | 
			
		||||
            Genre("фантастика"),
 | 
			
		||||
            Genre("фурри"),
 | 
			
		||||
            Genre("фэнтези"),
 | 
			
		||||
            Genre("школа"),
 | 
			
		||||
            Genre("эротика"),
 | 
			
		||||
            Genre("юри"),
 | 
			
		||||
            Genre("яой"),
 | 
			
		||||
            Genre("ёнкома")
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
@@ -1,251 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source.online.russian
 | 
			
		||||
 | 
			
		||||
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 okhttp3.Response
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.*
 | 
			
		||||
import java.util.regex.Pattern
 | 
			
		||||
 | 
			
		||||
class Mintmanga : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val id: Long = 6
 | 
			
		||||
 | 
			
		||||
    override val name = "Mintmanga"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "http://mintmanga.com"
 | 
			
		||||
 | 
			
		||||
    override val lang = "ru"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request =
 | 
			
		||||
            GET("$baseUrl/list?sortType=rate&offset=${70 * (page - 1)}&max=70", headers)
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request =
 | 
			
		||||
            GET("$baseUrl/list?sortType=updated&offset=${70 * (page - 1)}&max=70", headers)
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = "div.tile"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = "div.tile"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.thumbnail_url = element.select("img.lazy").first()?.attr("data-original")
 | 
			
		||||
        element.select("h3 > a").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.attr("title")
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga =
 | 
			
		||||
            popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = "a.nextLink"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = "a.nextLink"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val url = HttpUrl.parse("$baseUrl/search/advanced")!!.newBuilder()
 | 
			
		||||
        (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
 | 
			
		||||
            when (filter) {
 | 
			
		||||
                is GenreList -> filter.state.forEach { genre ->
 | 
			
		||||
                    if (genre.state != Filter.TriState.STATE_IGNORE) {
 | 
			
		||||
                        url.addQueryParameter(genre.id, arrayOf("=", "=in", "=ex")[genre.state])
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is Category -> filter.state.forEach { category ->
 | 
			
		||||
                    if (category.state != Filter.TriState.STATE_IGNORE) {
 | 
			
		||||
                        url.addQueryParameter(category.id, arrayOf("=", "=in", "=ex")[category.state])
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!query.isEmpty()) {
 | 
			
		||||
            url.addQueryParameter("q", query)
 | 
			
		||||
        }
 | 
			
		||||
        return GET(url.toString().replace("=%3D", "="), headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = popularMangaSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    // max 200 results
 | 
			
		||||
    override fun searchMangaNextPageSelector(): Nothing? = null
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val infoElement = document.select("div.leftContent").first()
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.author = infoElement.select("span.elem_author").first()?.text()
 | 
			
		||||
        manga.genre = infoElement.select("span.elem_genre").text().replace(" ,", ",")
 | 
			
		||||
        manga.description = infoElement.select("div.manga-description").text()
 | 
			
		||||
        manga.status = parseStatus(infoElement.html())
 | 
			
		||||
        manga.thumbnail_url = infoElement.select("img").attr("data-full")
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(element: String): Int = when {
 | 
			
		||||
        element.contains("<h3>Запрещена публикация произведения по копирайту</h3>") -> SManga.LICENSED
 | 
			
		||||
        element.contains("<h1 class=\"names\"> Сингл") || element.contains("<b>Перевод:</b> завершен") -> SManga.COMPLETED
 | 
			
		||||
        element.contains("<b>Перевод:</b> продолжается") -> SManga.ONGOING
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = "div.chapters-link tbody tr"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
        val urlText = urlElement.text()
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mtr=1")
 | 
			
		||||
        if (urlText.endsWith(" новое")) {
 | 
			
		||||
            chapter.name = urlText.dropLast(6)
 | 
			
		||||
        } else {
 | 
			
		||||
            chapter.name = urlText
 | 
			
		||||
        }
 | 
			
		||||
        chapter.date_upload = element.select("td.hidden-xxs").last()?.text()?.let {
 | 
			
		||||
            SimpleDateFormat("dd/MM/yy", Locale.US).parse(it).time
 | 
			
		||||
        } ?: 0
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun prepareNewChapter(chapter: SChapter, manga: SManga) {
 | 
			
		||||
        val basic = Regex("""\s*([0-9]+)(\s-\s)([0-9]+)\s*""")
 | 
			
		||||
        val extra = Regex("""\s*([0-9]+\sЭкстра)\s*""")
 | 
			
		||||
        val single = Regex("""\s*Сингл\s*""")
 | 
			
		||||
        when {
 | 
			
		||||
            basic.containsMatchIn(chapter.name) -> {
 | 
			
		||||
                basic.find(chapter.name)?.let {
 | 
			
		||||
                    val number = it.groups[3]?.value!!
 | 
			
		||||
                    chapter.chapter_number = number.toFloat()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            extra.containsMatchIn(chapter.name) -> // Extra chapters doesn't contain chapter number
 | 
			
		||||
                chapter.chapter_number = -2f
 | 
			
		||||
            single.containsMatchIn(chapter.name) -> // Oneshoots, doujinshi and other mangas with one chapter
 | 
			
		||||
                chapter.chapter_number = 1f
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(response: Response): List<Page> {
 | 
			
		||||
        val html = response.body()!!.string()
 | 
			
		||||
        val beginIndex = html.indexOf("rm_h.init( [")
 | 
			
		||||
        val endIndex = html.indexOf("], 0, false);", beginIndex)
 | 
			
		||||
        val trimmedHtml = html.substring(beginIndex, endIndex)
 | 
			
		||||
 | 
			
		||||
        val p = Pattern.compile("'.*?','.*?',\".*?\"")
 | 
			
		||||
        val m = p.matcher(trimmedHtml)
 | 
			
		||||
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
 | 
			
		||||
        var i = 0
 | 
			
		||||
        while (m.find()) {
 | 
			
		||||
            val urlParts = m.group().replace("[\"\']+".toRegex(), "").split(',')
 | 
			
		||||
            val url = if (urlParts[1].isEmpty() && urlParts[2].startsWith("/static/")) {
 | 
			
		||||
                baseUrl + urlParts[2]
 | 
			
		||||
            } else {
 | 
			
		||||
                urlParts[1] + urlParts[0] + urlParts[2]
 | 
			
		||||
            }
 | 
			
		||||
            pages.add(Page(i++, "", url))
 | 
			
		||||
        }
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        throw Exception("Not used")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document) = ""
 | 
			
		||||
 | 
			
		||||
    override fun imageRequest(page: Page): Request {
 | 
			
		||||
        val imgHeader = Headers.Builder().apply {
 | 
			
		||||
            add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
 | 
			
		||||
            add("Referer", baseUrl)
 | 
			
		||||
        }.build()
 | 
			
		||||
        return GET(page.imageUrl!!, imgHeader)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class Genre(name: String, val id: String) : Filter.TriState(name)
 | 
			
		||||
    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
 | 
			
		||||
    private class Category(categories: List<Genre>) : Filter.Group<Genre>("Category", categories)
 | 
			
		||||
 | 
			
		||||
    /* [...document.querySelectorAll("tr.advanced_option:nth-child(1) > td:nth-child(3) span.js-link")]
 | 
			
		||||
    *  .map(el => `Genre("${el.textContent.trim()}", "${el.getAttribute('onclick')
 | 
			
		||||
    *  .substr(31,el.getAttribute('onclick').length-33)"})`).join(',\n')
 | 
			
		||||
    *  on http://mintmanga.com/search/advanced
 | 
			
		||||
    */
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            Category(getCategoryList()),
 | 
			
		||||
            GenreList(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private fun getCategoryList() = listOf(
 | 
			
		||||
            Genre("В цвете", "el_4614"),
 | 
			
		||||
            Genre("Веб", "el_1355"),
 | 
			
		||||
            Genre("Выпуск приостановлен", "el_5232"),
 | 
			
		||||
            Genre("Ёнкома", "el_2741"),
 | 
			
		||||
            Genre("Комикс западный", "el_1903"),
 | 
			
		||||
            Genre("Комикс русский", "el_2173"),
 | 
			
		||||
            Genre("Манхва", "el_1873"),
 | 
			
		||||
            Genre("Маньхуа", "el_1875"),
 | 
			
		||||
            Genre("Не Яой", "el_1874"),
 | 
			
		||||
            Genre("Ранобэ", "el_5688"),
 | 
			
		||||
            Genre("Сборник", "el_1348")
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Genre("арт", "el_2220"),
 | 
			
		||||
            Genre("бара", "el_1353"),
 | 
			
		||||
            Genre("боевик", "el_1346"),
 | 
			
		||||
            Genre("боевые искусства", "el_1334"),
 | 
			
		||||
            Genre("вампиры", "el_1339"),
 | 
			
		||||
            Genre("гарем", "el_1333"),
 | 
			
		||||
            Genre("гендерная интрига", "el_1347"),
 | 
			
		||||
            Genre("героическое фэнтези", "el_1337"),
 | 
			
		||||
            Genre("детектив", "el_1343"),
 | 
			
		||||
            Genre("дзёсэй", "el_1349"),
 | 
			
		||||
            Genre("додзинси", "el_1332"),
 | 
			
		||||
            Genre("драма", "el_1310"),
 | 
			
		||||
            Genre("игра", "el_5229"),
 | 
			
		||||
            Genre("история", "el_1311"),
 | 
			
		||||
            Genre("киберпанк", "el_1351"),
 | 
			
		||||
            Genre("комедия", "el_1328"),
 | 
			
		||||
            Genre("меха", "el_1318"),
 | 
			
		||||
            Genre("мистика", "el_1324"),
 | 
			
		||||
            Genre("научная фантастика", "el_1325"),
 | 
			
		||||
            Genre("омегаверс", "el_5676"),
 | 
			
		||||
            Genre("повседневность", "el_1327"),
 | 
			
		||||
            Genre("постапокалиптика", "el_1342"),
 | 
			
		||||
            Genre("приключения", "el_1322"),
 | 
			
		||||
            Genre("психология", "el_1335"),
 | 
			
		||||
            Genre("романтика", "el_1313"),
 | 
			
		||||
            Genre("самурайский боевик", "el_1316"),
 | 
			
		||||
            Genre("сверхъестественное", "el_1350"),
 | 
			
		||||
            Genre("сёдзё", "el_1314"),
 | 
			
		||||
            Genre("сёдзё-ай", "el_1320"),
 | 
			
		||||
            Genre("сёнэн", "el_1326"),
 | 
			
		||||
            Genre("сёнэн-ай", "el_1330"),
 | 
			
		||||
            Genre("спорт", "el_1321"),
 | 
			
		||||
            Genre("сэйнэн", "el_1329"),
 | 
			
		||||
            Genre("трагедия", "el_1344"),
 | 
			
		||||
            Genre("триллер", "el_1341"),
 | 
			
		||||
            Genre("ужасы", "el_1317"),
 | 
			
		||||
            Genre("фантастика", "el_1331"),
 | 
			
		||||
            Genre("фэнтези", "el_1323"),
 | 
			
		||||
            Genre("школа", "el_1319"),
 | 
			
		||||
            Genre("эротика", "el_1340"),
 | 
			
		||||
            Genre("этти", "el_1354"),
 | 
			
		||||
            Genre("юри", "el_1315"),
 | 
			
		||||
            Genre("яой", "el_1336")
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
@@ -1,247 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source.online.russian
 | 
			
		||||
 | 
			
		||||
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 okhttp3.Response
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.*
 | 
			
		||||
import java.util.regex.Pattern
 | 
			
		||||
 | 
			
		||||
class Readmanga : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    override val id: Long = 5
 | 
			
		||||
 | 
			
		||||
    override val name = "Readmanga"
 | 
			
		||||
 | 
			
		||||
    override val baseUrl = "http://readmanga.me"
 | 
			
		||||
 | 
			
		||||
    override val lang = "ru"
 | 
			
		||||
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaSelector() = "div.tile"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = "div.tile"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request =
 | 
			
		||||
            GET("$baseUrl/list?sortType=rate&offset=${70 * (page - 1)}&max=70", headers)
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request =
 | 
			
		||||
            GET("$baseUrl/list?sortType=updated&offset=${70 * (page - 1)}&max=70", headers)
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.thumbnail_url = element.select("img.lazy").first()?.attr("data-original")
 | 
			
		||||
        element.select("h3 > a").first().let {
 | 
			
		||||
            manga.setUrlWithoutDomain(it.attr("href"))
 | 
			
		||||
            manga.title = it.attr("title")
 | 
			
		||||
        }
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element): SManga =
 | 
			
		||||
            popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = "a.nextLink"
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = "a.nextLink"
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val url = HttpUrl.parse("$baseUrl/search/advanced")!!.newBuilder()
 | 
			
		||||
        (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
 | 
			
		||||
            when (filter) {
 | 
			
		||||
                is GenreList -> filter.state.forEach { genre ->
 | 
			
		||||
                    if (genre.state != Filter.TriState.STATE_IGNORE) {
 | 
			
		||||
                        url.addQueryParameter(genre.id, arrayOf("=", "=in", "=ex")[genre.state])
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                is Category -> filter.state.forEach { category ->
 | 
			
		||||
                    if (category.state != Filter.TriState.STATE_IGNORE) {
 | 
			
		||||
                        url.addQueryParameter(category.id, arrayOf("=", "=in", "=ex")[category.state])
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!query.isEmpty()) {
 | 
			
		||||
            url.addQueryParameter("q", query)
 | 
			
		||||
        }
 | 
			
		||||
        return GET(url.toString().replace("=%3D", "="), headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = popularMangaSelector()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    // max 200 results
 | 
			
		||||
    override fun searchMangaNextPageSelector(): Nothing? = null
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val infoElement = document.select("div.leftContent").first()
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.author = infoElement.select("span.elem_author").first()?.text()
 | 
			
		||||
        manga.genre = infoElement.select("span.elem_genre").text().replace(" ,", ",")
 | 
			
		||||
        manga.description = infoElement.select("div.manga-description").text()
 | 
			
		||||
        manga.status = parseStatus(infoElement.html())
 | 
			
		||||
        manga.thumbnail_url = infoElement.select("img").attr("data-full")
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun parseStatus(element: String): Int = when {
 | 
			
		||||
        element.contains("<h3>Запрещена публикация произведения по копирайту</h3>") -> SManga.LICENSED
 | 
			
		||||
        element.contains("<h1 class=\"names\"> Сингл") || element.contains("<b>Перевод:</b> завершен") -> SManga.COMPLETED
 | 
			
		||||
        element.contains("<b>Перевод:</b> продолжается") -> SManga.ONGOING
 | 
			
		||||
        else -> SManga.UNKNOWN
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = "div.chapters-link tbody tr"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val urlElement = element.select("a").first()
 | 
			
		||||
        val urlText = urlElement.text()
 | 
			
		||||
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mtr=1")
 | 
			
		||||
        if (urlText.endsWith(" новое")) {
 | 
			
		||||
            chapter.name = urlText.dropLast(6)
 | 
			
		||||
        } else {
 | 
			
		||||
            chapter.name = urlText
 | 
			
		||||
        }
 | 
			
		||||
        chapter.date_upload = element.select("td.hidden-xxs").last()?.text()?.let {
 | 
			
		||||
            SimpleDateFormat("dd/MM/yy", Locale.US).parse(it).time
 | 
			
		||||
        } ?: 0
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun prepareNewChapter(chapter: SChapter, manga: SManga) {
 | 
			
		||||
        val basic = Regex("""\s*([0-9]+)(\s-\s)([0-9]+)\s*""")
 | 
			
		||||
        val extra = Regex("""\s*([0-9]+\sЭкстра)\s*""")
 | 
			
		||||
        val single = Regex("""\s*Сингл\s*""")
 | 
			
		||||
        when {
 | 
			
		||||
            basic.containsMatchIn(chapter.name) -> {
 | 
			
		||||
                basic.find(chapter.name)?.let {
 | 
			
		||||
                    val number = it.groups[3]?.value!!
 | 
			
		||||
                    chapter.chapter_number = number.toFloat()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            extra.containsMatchIn(chapter.name) -> // Extra chapters doesn't contain chapter number
 | 
			
		||||
                chapter.chapter_number = -2f
 | 
			
		||||
            single.containsMatchIn(chapter.name) -> // Oneshoots, doujinshi and other mangas with one chapter
 | 
			
		||||
                chapter.chapter_number = 1f
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(response: Response): List<Page> {
 | 
			
		||||
        val html = response.body()!!.string()
 | 
			
		||||
        val beginIndex = html.indexOf("rm_h.init( [")
 | 
			
		||||
        val endIndex = html.indexOf("], 0, false);", beginIndex)
 | 
			
		||||
        val trimmedHtml = html.substring(beginIndex, endIndex)
 | 
			
		||||
 | 
			
		||||
        val p = Pattern.compile("'.*?','.*?',\".*?\"")
 | 
			
		||||
        val m = p.matcher(trimmedHtml)
 | 
			
		||||
 | 
			
		||||
        val pages = mutableListOf<Page>()
 | 
			
		||||
 | 
			
		||||
        var i = 0
 | 
			
		||||
        while (m.find()) {
 | 
			
		||||
            val urlParts = m.group().replace("[\"\']+".toRegex(), "").split(',')
 | 
			
		||||
            val url = if (urlParts[1].isEmpty() && urlParts[2].startsWith("/static/")) {
 | 
			
		||||
                baseUrl + urlParts[2]
 | 
			
		||||
            } else {
 | 
			
		||||
                urlParts[1] + urlParts[0] + urlParts[2]
 | 
			
		||||
            }
 | 
			
		||||
            pages.add(Page(i++, "", url))
 | 
			
		||||
        }
 | 
			
		||||
        return pages
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
        throw Exception("Not used")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(document: Document) = ""
 | 
			
		||||
 | 
			
		||||
    override fun imageRequest(page: Page): Request {
 | 
			
		||||
        val imgHeader = Headers.Builder().apply {
 | 
			
		||||
            add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
 | 
			
		||||
            add("Referer", baseUrl)
 | 
			
		||||
        }.build()
 | 
			
		||||
        return GET(page.imageUrl!!, imgHeader)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class Genre(name: String, val id: String) : Filter.TriState(name)
 | 
			
		||||
    private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
 | 
			
		||||
    private class Category(categories: List<Genre>) : Filter.Group<Genre>("Category", categories)
 | 
			
		||||
 | 
			
		||||
    /* [...document.querySelectorAll("tr.advanced_option:nth-child(1) > td:nth-child(3) span.js-link")]
 | 
			
		||||
    *  .map(el => `Genre("${el.textContent.trim()}", $"{el.getAttribute('onclick')
 | 
			
		||||
    *  .substr(31,el.getAttribute('onclick').length-33)"})`).join(',\n')
 | 
			
		||||
    *  on http://readmanga.me/search/advanced
 | 
			
		||||
    */
 | 
			
		||||
    override fun getFilterList() = FilterList(
 | 
			
		||||
            Category(getCategoryList()),
 | 
			
		||||
            GenreList(getGenreList())
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private fun getCategoryList() = listOf(
 | 
			
		||||
            Genre("В цвете", "el_7290"),
 | 
			
		||||
            Genre("Веб", "el_2160"),
 | 
			
		||||
            Genre("Выпуск приостановлен", "el_8033"),
 | 
			
		||||
            Genre("Ёнкома", "el_2161"),
 | 
			
		||||
            Genre("Комикс западный", "el_3515"),
 | 
			
		||||
            Genre("Манхва", "el_3001"),
 | 
			
		||||
            Genre("Маньхуа", "el_3002"),
 | 
			
		||||
            Genre("Ранобэ", "el_8575"),
 | 
			
		||||
            Genre("Сборник", "el_2157")
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    private fun getGenreList() = listOf(
 | 
			
		||||
            Genre("арт", "el_5685"),
 | 
			
		||||
            Genre("боевик", "el_2155"),
 | 
			
		||||
            Genre("боевые искусства", "el_2143"),
 | 
			
		||||
            Genre("вампиры", "el_2148"),
 | 
			
		||||
            Genre("гарем", "el_2142"),
 | 
			
		||||
            Genre("гендерная интрига", "el_2156"),
 | 
			
		||||
            Genre("героическое фэнтези", "el_2146"),
 | 
			
		||||
            Genre("детектив", "el_2152"),
 | 
			
		||||
            Genre("дзёсэй", "el_2158"),
 | 
			
		||||
            Genre("додзинси", "el_2141"),
 | 
			
		||||
            Genre("драма", "el_2118"),
 | 
			
		||||
            Genre("игра", "el_2154"),
 | 
			
		||||
            Genre("история", "el_2119"),
 | 
			
		||||
            Genre("киберпанк", "el_8032"),
 | 
			
		||||
            Genre("кодомо", "el_2137"),
 | 
			
		||||
            Genre("комедия", "el_2136"),
 | 
			
		||||
            Genre("махо-сёдзё", "el_2147"),
 | 
			
		||||
            Genre("меха", "el_2126"),
 | 
			
		||||
            Genre("мистика", "el_2132"),
 | 
			
		||||
            Genre("научная фантастика", "el_2133"),
 | 
			
		||||
            Genre("повседневность", "el_2135"),
 | 
			
		||||
            Genre("постапокалиптика", "el_2151"),
 | 
			
		||||
            Genre("приключения", "el_2130"),
 | 
			
		||||
            Genre("психология", "el_2144"),
 | 
			
		||||
            Genre("романтика", "el_2121"),
 | 
			
		||||
            Genre("самурайский боевик", "el_2124"),
 | 
			
		||||
            Genre("сверхъестественное", "el_2159"),
 | 
			
		||||
            Genre("сёдзё", "el_2122"),
 | 
			
		||||
            Genre("сёдзё-ай", "el_2128"),
 | 
			
		||||
            Genre("сёнэн", "el_2134"),
 | 
			
		||||
            Genre("сёнэн-ай", "el_2139"),
 | 
			
		||||
            Genre("спорт", "el_2129"),
 | 
			
		||||
            Genre("сэйнэн", "el_2138"),
 | 
			
		||||
            Genre("трагедия", "el_2153"),
 | 
			
		||||
            Genre("триллер", "el_2150"),
 | 
			
		||||
            Genre("ужасы", "el_2125"),
 | 
			
		||||
            Genre("фантастика", "el_2140"),
 | 
			
		||||
            Genre("фэнтези", "el_2131"),
 | 
			
		||||
            Genre("школа", "el_2127"),
 | 
			
		||||
            Genre("этти", "el_2149"),
 | 
			
		||||
            Genre("юри", "el_2123")
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user