fixed all issues regarding #10040 (#10042)

This commit is contained in:
THE_ORONCO 2021-12-09 11:44:16 +01:00 committed by GitHub
parent bd43b9f982
commit ef491c08ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 162 additions and 101 deletions

View File

@ -5,7 +5,7 @@ ext {
extName = 'Kill Six Billion Demons' extName = 'Kill Six Billion Demons'
pkgNameSuffix = 'en.killsixbilliondemons' pkgNameSuffix = 'en.killsixbilliondemons'
extClass = '.KillSixBillionDemons' extClass = '.KillSixBillionDemons'
extVersionCode = 3 extVersionCode = 4
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.extension.en.killsixbilliondemons package eu.kanade.tachiyomi.extension.en.killsixbilliondemons
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
@ -11,12 +10,15 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import java.text.SimpleDateFormat
import java.util.Locale
/** /**
* original creator:
* @author Aria Moradi <aria.moradi007@gmail.com> * @author Aria Moradi <aria.moradi007@gmail.com>
* fixed some bugs and added title pages:
* @author THE_ORONCO <the_oronco@posteo.net>
*/ */
class KillSixBillionDemons : HttpSource() { class KillSixBillionDemons : HttpSource() {
@ -29,138 +31,197 @@ class KillSixBillionDemons : HttpSource() {
override val supportsLatest: Boolean = false override val supportsLatest: Boolean = false
// list of books private val authorKSBD = "Abbadon"
private val bookSelector: String = "#chapter option:contains(book)"
override fun popularMangaParse(response: Response): MangasPage { private val pagesOrder = "?order=ASC"
val document = response.asJsoup() private val urlDateFormat = SimpleDateFormat("yyyy/MM", Locale.US)
private val descriptionKSBD =
val mangas = document.select(popularMangaSelector()).map { element -> "Q: What is this all about?\nThis is a webcomic! Its graphic novel style, meaning its meant to be read in large chunks, but you can subject yourself to the agony of reading it a couple pages a week!\n" +
popularMangaFromElement(element) "\nQ: Do you have a twitter/tumble machine? Just who the hell draws this thing anyway?\n" +
} "A mysterious comics goblin named Abbadon draws this mess. My twitter is @orbitaldropkick, my tumblr is orbitaldropkick.tumblr.com. If youre feeling dangerous, you can e-mail me at ksbdabbadon@gmail.com\n" +
"\nQ: A webcomic, eh? When does it update?\n" +
return MangasPage(mangas, false) "Tuesday and Friday evenings (and occasionally weekends). Sometimes it will be up quite late on those days.\n" +
} "\nQ: Whos this YISUN guy that keeps getting talked about?\n" +
"Someone has not read their Psalms and Spasms recently!\n" +
"\nQ: Whats this about suggestions?\n" +
"KSBD will periodically take suggestions, mostly on characters to stick in the background. You can also stick fanart, character ideas, concepts, and literature in the Submit section up above. You need tumblr for this. If you want to suggest directly, the best way to do it is through the comments section below the comic! A huge chunk of minor characters have been named and inspired by reader comments so far.\n" +
"\nQ: Can I buy this book in a more traditional format?\n" +
"You absolutely can. You can get your hands on a print copy of the first and second books from Image comics in your local comics shop or anywhere else you can get comics. It looks fantastic in print and if you dont like reading stuff online I highly recommend it."
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
return GET(baseUrl) return GET(baseUrl, headers)
} }
private fun popularMangaSelector(): String { // list of books
return "#chapter option:contains(book)" override fun popularMangaParse(response: Response): MangasPage {
return generateKSBDMangasPage()
} }
private fun popularMangaFromElement(element: Element): SManga { /**
return SManga.create().apply { * @return the MangasPage containing the different books of Kill Six Billion Demons as manga
title = element.text().substringBefore(" (") */
thumbnail_url = "https://dummyimage.com/768x994/000/ffffff.jpg&text=$title" private fun generateKSBDMangasPage(): MangasPage {
artist = "Abbadon" return MangasPage(fetchBooksAsMangas(), false)
author = "Abbadon" }
status = SManga.UNKNOWN
url = title // this url is not useful at all but must set to something unique or the app breaks! /**
* This fetches the different books of Kill Six Billion Demons as different manga.
* @return a list of all books in form of multiple manga
*/
private fun fetchBooksAsMangas(): List<SManga> {
val doc = client.newCall(GET(baseUrl, headers)).execute().asJsoup()
val bookElements = doc.select(bookSelector)
return bookElements.map { bookElement ->
val bookOverviewUrl = bookElement.attr("value")
val bookTitle = bookElement.text().substringBefore(" (")
SManga.create().apply {
title = bookTitle
setUrlWithoutDomain(bookOverviewUrl)
artist = authorKSBD
author = authorKSBD
description = descriptionKSBD
thumbnail_url = fetchThumbnailUrl(bookOverviewUrl)
status = fetchStatusForBook(bookTitle)
}
} }
} }
/**
* This fetches the Thumbnail given the url to a book overview. In ascending order the first
* image will always be the cover of the given book.
*
* @param bookOverviewUrl url to the book overview
* @return url to the cover of the book
*/
private fun fetchThumbnailUrl(bookOverviewUrl: String): String {
val overviewDoc =
client.newCall(GET(bookOverviewUrl + pagesOrder, headers)).execute().asJsoup()
return overviewDoc.selectFirst(".comic-thumbnail-in-archive a img").attr("src")
}
private fun String.lowercase(): String {
return this.toLowerCase(Locale.ROOT)
}
/**
* Get the SManga status for a given book by checking if the title of the newest page contains
* the title of the the given book.
*
* @param bookTitle name of the book the status should be fetched for
* @return the status of the book (as Enum value of SManga because chapters are mangas)
*/
private fun fetchStatusForBook(bookTitle: String): Int {
val bookTitleWithoutBook = bookTitle.substringAfter(": ")
val newestPage = client.newCall(GET(baseUrl, headers)).execute().asJsoup()
val postTitle = newestPage.selectFirst(".post-title")?.text() ?: ""
// title is "<book name> <page(s)>"
return if (postTitle.lowercase().contains(bookTitleWithoutBook.lowercase())) SManga.UNKNOWN
else SManga.COMPLETED
}
// latest Updates not used // latest Updates not used
override fun latestUpdatesParse(response: Response): MangasPage = throw Exception("Not used") override fun latestUpdatesParse(response: Response): MangasPage = throw Exception("Not used")
override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used") override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used")
// books dont change around here, but still write the data again to avoid bugs in backup restore
override fun fetchMangaDetails(manga: SManga): Observable<SManga> { override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(popularMangaRequest(1)) return Observable.just(fetchBooksAsMangas().find { manga.title == it.title })
.asObservableSuccess()
.map { response ->
popularMangaParse(response).mangas.find { manga.title == it.title }
}
} }
override fun mangaDetailsParse(response: Response): SManga = throw Exception("Not used") override fun mangaDetailsParse(response: Response): SManga = throw Exception("Not used")
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> { override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return client.newCall(chapterListRequest(manga)) return Observable.just(
.asObservableSuccess() fetchChapterListTR(
.map { response -> baseUrl + manga.url + pagesOrder,
chapterListParse(response, manga) mutableListOf()
} )
)
} }
override fun chapterListRequest(manga: SManga): Request = popularMangaRequest(1) /**
* Though this is recursive this will be optimized by the compiler into a for loop equivalent
* thing. This has to be done this way because the maximum number of further chapter overview
* pages that will be shown on one chapter overview page will at maximum be 5 enven though there
* might be more.
*
* @param currentUrl url of the current page / the page this algorithm will start the recursion on
* @param foundChapters a list of all found chapters (should be empty)
* @return a list of all chapters that were found under "currentUrl" and following pages
*/
private tailrec fun fetchChapterListTR(
currentUrl: String,
foundChapters: MutableList<SChapter>
): MutableList<SChapter> {
val numberOfPreviousChapters = foundChapters.size
val currentPage = client.newCall(GET(currentUrl, headers)).execute().asJsoup()
val chaptersOnCurrentPage = currentPage.select(".post-content")
.mapIndexed { index, chapterElement ->
val chapterTitle: String = chapterElement.select(".post-title a").text()
val chapterUrl: String =
chapterElement.select(".comic-thumbnail-in-archive a").attr("href")
val imageUrl =
chapterElement.select(".comic-thumbnail-in-archive a img").attr("src")
SChapter.create().apply {
setUrlWithoutDomain(chapterUrl)
name = chapterTitle
chapter_number = numberOfPreviousChapters + index + 1f
date_upload = extractDateFromImageUrl(imageUrl)
}
}
foundChapters.addAll(chaptersOnCurrentPage)
val potentialNextPageUrl = currentPage.select(".paginav-next a").attr("href")
return if (potentialNextPageUrl.isEmpty()) {
foundChapters
} else {
fetchChapterListTR(potentialNextPageUrl, foundChapters)
}
}
/**
* @param imageUrl Url the date should be got from
* @return date of the image upload as a long
*/
private fun extractDateFromImageUrl(imageUrl: String): Long {
val dateRegex = "[0-9]{4}/[0-9]{2}".toRegex()
val dateString = dateRegex.find(imageUrl)
return if (dateString?.value != null) {
return urlDateFormat.parse(dateString.value)?.time ?: 0L
} else 0L
}
override fun chapterListRequest(manga: SManga): Request = throw Exception("Not used")
override fun chapterListParse(response: Response): List<SChapter> = throw Exception("Not used") override fun chapterListParse(response: Response): List<SChapter> = throw Exception("Not used")
private fun chapterListParse(response: Response, manga: SManga): List<SChapter> {
val document = response.asJsoup()
val options = document.select(chapterListSelector())
val chapters = mutableListOf<SChapter>()
var bookNum = 0
val targetBookNum = manga.title.split(":")[0].split(" ")[1].toInt()
for (element in options) {
val text = element.text()
if (text.startsWith("Book")) {
bookNum += 1
continue
}
if (bookNum > targetBookNum)
break
if (bookNum == targetBookNum) {
chapters.add(
SChapter.create().apply {
url = element.attr("value")
val textSplit = text.split(" ")
name = "Chapter ${textSplit[0]}"
}
)
}
}
return chapters.reversed()
}
private fun chapterListSelector(): String {
return "#chapter option"
}
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> { override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
val wordpressPages = mutableListOf<Document>() val chapterDoc = client.newCall(GET(baseUrl + chapter.url, headers)).execute().asJsoup()
// get the first page and add ir to the list val pages = chapterDoc.select("#comic img")
val firstPageURL = chapter.url + "?order=ASC" // change the url to ask Wordpress to reverse the posts .mapIndexed { index, imageElement ->
val firstPage = client.newCall(GET(firstPageURL)).execute().asJsoup() val imageUrl = imageElement.attr("src")
wordpressPages.add(firstPage) Page(index + 1, "", imageUrl)
val otherPages = firstPage.select("#paginav a")
for (i in 0 until (otherPages.size - 1)) // ignore the last one (last page button)
wordpressPages.add(client.newCall(GET(otherPages[i].attr("href"))).execute().asJsoup())
val chapterPages = mutableListOf<Page>()
var pageNum = 1
wordpressPages.forEach { wordpressPage ->
wordpressPage.select(".post-content .entry a:has(img)").forEach { postImage ->
chapterPages.add(
Page(pageNum, postImage.attr("href"), postImage.select("img").attr("src"))
)
pageNum++
} }
}
return Observable.just(chapterPages) return Observable.just(pages)
} }
override fun imageUrlParse(response: Response): String = throw Exception("Not used") override fun imageUrlParse(response: Response): String = throw Exception("Not used")
override fun pageListParse(response: Response): List<Page> = throw Exception("Not used") override fun pageListParse(response: Response): List<Page> = throw Exception("Not used")
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> = throw Exception("Search functionality is not available.") override fun fetchSearchManga(
page: Int,
query: String,
filters: FilterList
): Observable<MangasPage> = throw Exception("Search functionality is not available.")
override fun searchMangaParse(response: Response): MangasPage = throw Exception("Not used") override fun searchMangaParse(response: Response): MangasPage = throw Exception("Not used")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = throw Exception("Not used") override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
throw Exception("Not used")
} }