diff --git a/src/en/dynasty/build.gradle b/src/en/dynasty/build.gradle new file mode 100644 index 000000000..63c49a3cd --- /dev/null +++ b/src/en/dynasty/build.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + appName = 'Tachiyomi: Dynasty' + pkgNameSuffix = "en.dynasty" + extClass = '.DynastyAnthologies; .DynastyIssues; .DynastySeries; .DynastyDoujins' + extVersionCode = 1 + extVersionSuffix = 1 + libVersion = '1.2' +} + +apply from: "$rootDir/common.gradle" diff --git a/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyAnthologies.kt b/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyAnthologies.kt new file mode 100644 index 000000000..3708dd9a2 --- /dev/null +++ b/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyAnthologies.kt @@ -0,0 +1,28 @@ +package eu.kanade.tachiyomi.extension.en.dynasty + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.Request +import org.jsoup.nodes.Document + +class DynastyAnthologies : DynastyScans() { + + override val name = "Dynasty-Anthologies" + + override fun popularMangaInitialUrl() = "$baseUrl/anthologies?view=cover" + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + return GET("$baseUrl/search?q=$query&classes[]=Series&sort=", headers) + } + + override fun mangaDetailsParse(document: Document): SManga { + val manga = SManga.create() + manga.thumbnail_url = baseUrl + document.select("div.span2 > img").attr("src") + parseHeader(document, manga) + parseGenres(document, manga) + parseDescription(document, manga) + return manga + } + +} diff --git a/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyDoujins.kt b/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyDoujins.kt new file mode 100644 index 000000000..3e86bbcd1 --- /dev/null +++ b/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyDoujins.kt @@ -0,0 +1,31 @@ +package eu.kanade.tachiyomi.extension.en.dynasty + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.Request +import org.jsoup.nodes.Document + +class DynastyDoujins : DynastyScans() { + + override val name = "Dynasty-Doujins" + + override fun popularMangaInitialUrl() = "$baseUrl/doujins?view=cover" + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + return GET("$baseUrl/search?q=$query&classes[]=Series&sort=", headers) + } + + override fun mangaDetailsParse(document: Document): SManga { + val manga = SManga.create() + super.mangaDetailsParse(document) + parseThumbnail(manga) + manga.author = ".." + manga.status = SManga.UNKNOWN + parseGenres(document, manga) + return manga + } + + override fun chapterListSelector() = "div.span9 > dl.chapter-list > dd" + +} diff --git a/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyIssues.kt b/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyIssues.kt new file mode 100644 index 000000000..d86986a77 --- /dev/null +++ b/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyIssues.kt @@ -0,0 +1,28 @@ +package eu.kanade.tachiyomi.extension.en.dynasty + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.Request +import org.jsoup.nodes.Document + +class DynastyIssues : DynastyScans() { + + override val name = "Dynasty-Issues" + + override fun popularMangaInitialUrl() = "$baseUrl/issues?view=cover" + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + return GET("$baseUrl/search?q=$query&classes[]=Series&sort=", headers) + } + + override fun mangaDetailsParse(document: Document): SManga { + val manga = SManga.create() + manga.thumbnail_url = baseUrl + document.select("div.span2 > img").attr("src") + parseHeader(document, manga) + parseGenres(document, manga) + parseDescription(document, manga) + return manga + } + +} diff --git a/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyScans.kt b/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyScans.kt new file mode 100644 index 000000000..5e023f407 --- /dev/null +++ b/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastyScans.kt @@ -0,0 +1,235 @@ +package eu.kanade.tachiyomi.extension.en.dynasty + +import android.net.Uri +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Request +import okhttp3.Response +import org.json.JSONArray +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import org.jsoup.nodes.Node +import org.jsoup.nodes.TextNode +import java.text.SimpleDateFormat +import java.util.* +import java.util.regex.Pattern + +abstract class DynastyScans : ParsedHttpSource() { + + override val baseUrl = "http://dynasty-scans.com" + + abstract fun popularMangaInitialUrl(): String + + override val lang = "en" + + override val supportsLatest = false + + var parent: List = ArrayList() + + var list = InternalList(ArrayList(), "") + + var imgList = InternalList(ArrayList(), "") + + var _valid: Validate = Validate(false, -1) + + override fun popularMangaRequest(page: Int): Request { + return GET(popularMangaInitialUrl(), headers) + } + + override fun popularMangaSelector() = "ul.thumbnails > li.span2" + + override fun popularMangaFromElement(element: Element): SManga { + val manga = SManga.create() + manga.setUrlWithoutDomain(element.select("a").attr("href")) + manga.title = element.select("div.caption").text() + return manga + } + + override fun popularMangaParse(response: Response): MangasPage { + val mangas = response.asJsoup().select(popularMangaSelector()).map { element -> + popularMangaFromElement(element) + } + return MangasPage(mangas, false) + } + + override fun searchMangaSelector() = "a.name" + + override fun searchMangaFromElement(element: Element): SManga { + val manga = SManga.create() + manga.setUrlWithoutDomain(element.attr("href")) + manga.title = element.text() + return manga + } + + override fun searchMangaNextPageSelector() = "div.pagination > ul > li.active + li > a" + + private fun buildListfromResponse(): List { + return client.newCall(Request.Builder().headers(headers) + .url(popularMangaInitialUrl()).build()).execute().asJsoup() + .select("div#main").filter { it.hasText() }.first().childNodes() + } + + protected fun parseThumbnail(manga: SManga) { + if (_valid.isManga) manga.thumbnail_url = baseUrl + imgList[_valid.pos].substringBefore('?') + } + + protected fun parseHeader(document: Document, manga: SManga): Boolean { + val elements = document.select("div.tags > h2.tag-title").first().getElementsByTag("a") + if (elements.isEmpty()) { + return false + } + if (elements.lastIndex == 0) { + manga.author = elements[0].text() + } else { + manga.artist = elements[0].text() + manga.author = elements[1].text() + } + manga.status = document.select("div.tags > h2.tag-title > small").text().let { + when { + it.contains("Ongoing") -> SManga.ONGOING + it.contains("Completed") -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + } + return true + } + + protected fun parseGenres(document: Document, manga: SManga) { + manga.genre = "" + val glist = document.select("div.tags > div.tag-tags").first().getElementsByTag("a") + if (!glist.isEmpty()) { + for (g in glist) { + val s = g.text() + manga.genre += if (glist.last() == (g)) s else "$s, " + } + } + } + + protected fun parseDescription(document: Document, manga: SManga) { + manga.description = document.select("div.tags > div.row div.description").text() + } + + private fun getValid(manga: SManga): Validate { + if (parent.isEmpty()) parent = buildListfromResponse() + if (list.isEmpty()) list = InternalList(parent, "href") + if (imgList.isEmpty()) imgList = InternalList(parent, "src") + val pos = list.indexOf(manga.url.substringBeforeLast("/") + "/" + Uri.encode(manga.url.substringAfterLast("/"))) + return Validate((pos > -1), pos) + } + + override fun mangaDetailsParse(document: Document): SManga { + val manga = SManga.create() + _valid = getValid(manga) + return manga + } + + override fun chapterListSelector() = "div.span10 > dl.chapter-list > dd" + + override fun chapterListParse(response: Response): List { + return super.chapterListParse(response).asReversed() + } + + override fun chapterFromElement(element: Element): SChapter { + val chapter = SChapter.create() + val nodes = InternalList(element.childNodes(), "text") + + chapter.setUrlWithoutDomain(element.select("a.name").attr("href")) + chapter.name = nodes[0] + if (nodes.contains(" by ")) { + chapter.name += " by ${nodes[nodes.indexOfPartial(" by ") + 1]}" + if (nodes.contains(" and ")) { + chapter.name += " and ${nodes[nodes.indexOfPartial(" and ") + 1]}" + } + } + chapter.date_upload = nodes[nodes.indexOfPartial("released")].let { + SimpleDateFormat("MMM dd yy", Locale.ENGLISH).parse(it.substringAfter("released ").replace("\'", "")).time + } + return chapter + } + + override fun pageListParse(document: Document): List { + val pages = mutableListOf() + try { + val script = document.select("script").last() + val p = Pattern.compile("(?s)(pages)\\s??=\\s??\\[(.*?)\\]") + val m = p.matcher(script.html()) + var imageUrls = JSONArray() + while (m.find()) + imageUrls = JSONArray("[" + m.group(2) + "]") + + (0..imageUrls.length() - 1) + .map { imageUrls.getJSONObject(it) } + .map { baseUrl + it.get("image") } + .forEach { pages.add(Page(pages.size, "", it)) } + } catch (e: Exception) { + e.printStackTrace() + } + return pages + } + + class InternalList : ArrayList { + + constructor(nodes: List, type: String) { + if (type == "text") { + for (node in nodes) { + if (node is TextNode) { + if (node.text() != " " && !node.text().contains("\n")) { + this.add(node.text()) + } + } else if (node is Element) this.add(node.text()) + } + } + if (type == "src") { + nodes + .filter { it is Element && it.hasClass("thumbnails") } + .flatMap { it.childNodes() } + .filterIsInstance() + .filter { it.hasClass("span2") } + .forEach { this.add(it.child(0).child(0).attr(type)) } + } + if (type == "href") { + nodes + .filter { it is Element && it.hasClass("thumbnails") } + .flatMap { it.childNodes() } + .filterIsInstance() + .filter { it.hasClass("span2") } + .forEach { this.add(it.child(0).attr(type)) } + } + } + + fun indexOfPartial(partial: String): Int { + return (0..this.lastIndex).firstOrNull { this[it].contains(partial) } + ?: -1 + } + + fun getItem(partial: String): String { + return (0..this.lastIndex) + .firstOrNull { super.get(it).contains(partial) } + ?.let { super.get(it) } + ?: "" + } + } + + class Validate(_isManga: Boolean, _pos: Int) { + val isManga = _isManga + val pos = _pos + } + + override fun popularMangaNextPageSelector() = "" + override fun latestUpdatesSelector() = "" + override fun latestUpdatesNextPageSelector() = "" + override fun imageUrlParse(document: Document): String = "" + override fun latestUpdatesFromElement(element: Element): SManga { + return popularMangaFromElement(element) + } + + override fun latestUpdatesRequest(page: Int): Request { + return popularMangaRequest(page) + } + +} \ No newline at end of file diff --git a/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastySeries.kt b/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastySeries.kt new file mode 100644 index 000000000..fab5f9bf3 --- /dev/null +++ b/src/en/dynasty/src/eu/kanade/tachiyomi/extension/en/dynasty/DynastySeries.kt @@ -0,0 +1,28 @@ +package eu.kanade.tachiyomi.extension.en.dynasty + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.Request +import org.jsoup.nodes.Document + +class DynastySeries : DynastyScans() { + + override val name = "Dynasty-Series" + + override fun popularMangaInitialUrl() = "$baseUrl/series?view=cover" + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + return GET("$baseUrl/search?q=$query&classes[]=Series&sort=", headers) + } + + override fun mangaDetailsParse(document: Document): SManga { + val manga = SManga.create() + manga.thumbnail_url = baseUrl + document.select("div.span2 > img").attr("src") + parseHeader(document, manga) + parseGenres(document, manga) + parseDescription(document, manga) + return manga + } + +} \ No newline at end of file