diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/SourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/SourceManager.kt index cef43935df..681faf9dd3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/SourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/SourceManager.kt @@ -8,6 +8,8 @@ import eu.kanade.tachiyomi.data.source.base.Source import eu.kanade.tachiyomi.data.source.base.YamlOnlineSource import eu.kanade.tachiyomi.data.source.online.english.Batoto import eu.kanade.tachiyomi.data.source.online.english.Kissmanga +import eu.kanade.tachiyomi.data.source.online.english.Mangafox +import eu.kanade.tachiyomi.data.source.online.english.Mangahere import org.yaml.snakeyaml.Yaml import timber.log.Timber import java.io.File @@ -36,6 +38,8 @@ open class SourceManager(private val context: Context) { private fun createSource(id: Int): Source? = when (id) { BATOTO -> Batoto(context, id) KISSMANGA -> Kissmanga(context, id) + MANGAHERE -> Mangahere(context, id) + MANGAFOX -> Mangafox(context, id) else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt index c376350463..65e1a44f8c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt @@ -202,6 +202,7 @@ class Batoto(context: Context, override val id: Int) : ParsedOnlineSource(contex for ((i, element) in selectElement.select("option").withIndex()) { pages.add(Page(i, element.attr("value"))) } + pages.getOrNull(0)?.imageUrl = imageUrlParse(document) } else { // For webtoons in one page for ((i, element) in document.select("div > img").withIndex()) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Kissmanga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Kissmanga.kt index 75e9780443..2e2da4d8ad 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Kissmanga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Kissmanga.kt @@ -40,6 +40,8 @@ class Kissmanga(context: Context, override val id: Int) : ParsedOnlineSource(con } } + override fun popularMangaNextPageSelector() = "li > a:contains(› Next)" + override fun searchMangaRequest(page: MangasPage, query: String): Request { if (page.page == 1) { page.url = searchMangaInitialUrl(query) @@ -55,7 +57,7 @@ class Kissmanga(context: Context, override val id: Int) : ParsedOnlineSource(con return post(page.url, headers, form) } - override fun popularMangaNextPageSelector() = "li > a:contains(› Next)" + override fun searchMangaInitialUrl(query: String) = "$baseUrl/AdvanceSearch" override fun searchMangaSelector() = popularMangaSelector() @@ -65,26 +67,20 @@ class Kissmanga(context: Context, override val id: Int) : ParsedOnlineSource(con override fun searchMangaNextPageSelector() = null - override fun searchMangaInitialUrl(query: String) = "$baseUrl/AdvanceSearch" - override fun mangaDetailsParse(document: Document, manga: Manga) { val infoElement = document.select("div.barContent").first() 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 = parseStatus(infoElement.select("p:has(span:contains(Status:))").first()?.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") } - fun parseStatus(status: String?): Int { - if (status != null) { - when { - status.contains("Ongoing") -> return Manga.ONGOING - status.contains("Completed") -> return Manga.COMPLETED - } - } - return Manga.UNKNOWN + fun parseStatus(status: String) = when { + status.contains("Ongoing") -> Manga.ONGOING + status.contains("Completed") -> Manga.COMPLETED + else -> Manga.UNKNOWN } override fun chapterListSelector() = "table.listing tr:gt(1)" @@ -102,7 +98,8 @@ class Kissmanga(context: Context, override val id: Int) : ParsedOnlineSource(con override fun pageListRequest(chapter: Chapter) = post(baseUrl + chapter.url, headers) override fun pageListParse(response: Response, pages: MutableList) { - val p = Pattern.compile("lstImages.push\\(\"(.+?)\"") + //language=RegExp + val p = Pattern.compile("""lstImages.push\("(.+?)"""") val m = p.matcher(response.body().string()) var i = 0 diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.kt new file mode 100644 index 0000000000..4cbdc7179b --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.kt @@ -0,0 +1,122 @@ +package eu.kanade.tachiyomi.data.source.online.english + +import android.content.Context +import eu.kanade.tachiyomi.data.database.models.Chapter +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.source.EN +import eu.kanade.tachiyomi.data.source.Language +import eu.kanade.tachiyomi.data.source.base.ParsedOnlineSource +import eu.kanade.tachiyomi.data.source.model.Page +import okhttp3.Response +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.* + +class Mangafox(context: Context, override val id: Int) : ParsedOnlineSource(context) { + + override val name = "Mangafox" + + override val baseUrl = "http://mangafox.me" + + override val lang: Language get() = EN + + override fun popularMangaInitialUrl() = "$baseUrl/directory/" + + override fun popularMangaSelector() = "div#mangalist > ul.list > li" + + override fun popularMangaFromElement(element: Element, manga: Manga) { + element.select("a.title").first().let { + manga.setUrl(it.attr("href")) + manga.title = it.text() + } + } + + override fun popularMangaNextPageSelector() = "a:has(span.next)" + + override fun searchMangaInitialUrl(query: String) = + "$baseUrl/search.php?name_method=cw&advopts=1&order=za&sort=views&name=$query&page=1" + + override fun searchMangaSelector() = "table#listing > tbody > tr:gt(0)" + + override fun searchMangaFromElement(element: Element, manga: Manga) { + element.select("a.series_preview").first().let { + manga.setUrl(it.attr("href")) + manga.title = it.text() + } + } + + override fun searchMangaNextPageSelector() = "a:has(span.next)" + + override fun mangaDetailsParse(document: Document, manga: Manga) { + val infoElement = document.select("div#title").first() + val rowElement = infoElement.select("table > tbody > tr:eq(1)").first() + val sideInfoElement = document.select("#series_info").first() + + 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() + manga.status = sideInfoElement.select(".data").first()?.text().orEmpty().let { parseStatus(it) } + manga.thumbnail_url = sideInfoElement.select("div.cover > img").first()?.attr("src") + } + + private fun parseStatus(status: String) = when { + status.contains("Ongoing") -> Manga.ONGOING + status.contains("Completed") -> Manga.COMPLETED + else -> Manga.UNKNOWN + } + + override fun chapterListSelector() = "div#chapters li div" + + override fun chapterFromElement(element: Element, chapter: Chapter) { + val urlElement = element.select("a.tips").first() + + chapter.setUrl(urlElement.attr("href")) + chapter.name = urlElement.text() + chapter.date_upload = element.select("span.date").first()?.text()?.let { parseChapterDate(it) } ?: 0 + } + + 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(response: Response, pages: MutableList) { + val document = Jsoup.parse(response.body().string()) + + val url = response.request().url().toString().substringBeforeLast('/') + document.select("select.m").first().select("option:not([value=0])").forEach { + pages.add(Page(pages.size, "$url/${it.attr("value")}.html")) + } + pages.getOrNull(0)?.imageUrl = imageUrlParse(document) + } + + // Not used, overrides parent. + override fun pageListParse(document: Document, pages: MutableList) {} + + override fun imageUrlParse(document: Document) = document.getElementById("image").attr("src") + +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.kt new file mode 100644 index 0000000000..eb3d88818f --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.kt @@ -0,0 +1,113 @@ +package eu.kanade.tachiyomi.data.source.online.english + +import android.content.Context +import eu.kanade.tachiyomi.data.database.models.Chapter +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.source.EN +import eu.kanade.tachiyomi.data.source.Language +import eu.kanade.tachiyomi.data.source.base.ParsedOnlineSource +import eu.kanade.tachiyomi.data.source.model.Page +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.* + +class Mangahere(context: Context, override val id: Int) : ParsedOnlineSource(context) { + + override val name = "Mangahere" + + override val baseUrl = "http://www.mangahere.co" + + override val lang: Language get() = EN + + override fun popularMangaInitialUrl() = "$baseUrl/directory/" + + override fun popularMangaSelector() = "div.directory_list > ul > li" + + override fun popularMangaFromElement(element: Element, manga: Manga) { + element.select("div.title > a").first().let { + manga.setUrl(it.attr("href")) + manga.title = it.text() + } + } + + override fun popularMangaNextPageSelector() = "div.next-page > a.next" + + override fun searchMangaInitialUrl(query: String) = + "$baseUrl/search.php?name=$query&page=1&sort=views&order=za" + + override fun searchMangaSelector() = "div.result_search > dl" + + override fun searchMangaFromElement(element: Element, manga: Manga) { + element.select("a.manga_info").first().let { + manga.setUrl(it.attr("href")) + manga.title = it.text() + } + } + + override fun searchMangaNextPageSelector() = "div.next-page > a.next" + + override fun mangaDetailsParse(document: Document, manga: Manga) { + val detailElement = document.select(".manga_detail_top").first() + val infoElement = detailElement.select(".detail_topText").first() + + manga.author = infoElement.select("a[href^=http://www.mangahere.co/author/]").first()?.text() + manga.artist = infoElement.select("a[href^=http://www.mangahere.co/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.status = infoElement.select("li:eq(6)").first()?.text().orEmpty().let { parseStatus(it) } + manga.thumbnail_url = detailElement.select("img.img").first()?.attr("src") + } + + private fun parseStatus(status: String) = when { + status.contains("Ongoing") -> Manga.ONGOING + status.contains("Completed") -> Manga.COMPLETED + else -> Manga.UNKNOWN + } + + override fun chapterListSelector() = ".detail_list > ul:not([class]) > li" + + override fun chapterFromElement(element: Element, chapter: Chapter) { + val urlElement = element.select("a").first() + + chapter.setUrl(urlElement.attr("href")) + chapter.name = urlElement.text() + chapter.date_upload = element.select("span.right").first()?.text()?.let { parseChapterDate(it) } ?: 0 + } + + 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, pages: MutableList) { + document.select("select.wid60").first().getElementsByTag("option").forEach { + pages.add(Page(pages.size, it.attr("value"))) + } + pages.getOrNull(0)?.imageUrl = imageUrlParse(document) + } + + override fun imageUrlParse(document: Document) = document.getElementById("image").attr("src") + +} \ No newline at end of file