diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/OnlineSource.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/OnlineSource.kt index 4df59bc370..459859908e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/OnlineSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/OnlineSource.kt @@ -53,6 +53,11 @@ abstract class OnlineSource(context: Context) : Source { */ abstract val lang: Language + /** + * If the Source has Support for Latest Updates + */ + abstract val supportsLatest : Boolean + /** * Headers used for requests. */ @@ -96,6 +101,17 @@ abstract class OnlineSource(context: Context) : Source { page } + /** + * Returns an observable containing a page with a list of latest manga. + */ + open fun fetchLatestUpdates(page: MangasPage): Observable = client + .newCall(latestUpdatesRequest(page)) + .asObservable() + .map { response -> + latestUpdatesParse(response, page) + page + } + /** * Returns the request for the popular manga given the page. Override only if it's needed to * send different headers or request method like POST. @@ -109,11 +125,26 @@ abstract class OnlineSource(context: Context) : Source { return GET(page.url, headers) } + /** + * Returns the request for latest manga given the page. + */ + open protected fun latestUpdatesRequest(page: MangasPage): Request { + if (page.page == 1) { + page.url = latestUpdatesInitialUrl() + } + return GET(page.url, headers) + } + /** * Returns the absolute url of the first page to popular manga. */ abstract protected fun popularMangaInitialUrl(): String + /** + * Returns the absolute url of the first page to latest manga. + */ + abstract protected fun latestUpdatesInitialUrl(): String + /** * Parse the response from the site. It should add a list of manga and the absolute url to the * next page (if it has a next one) to [page]. @@ -123,6 +154,11 @@ abstract class OnlineSource(context: Context) : Source { */ abstract protected fun popularMangaParse(response: Response, page: MangasPage) + /** + * Same as [popularMangaParse], but for latest manga. + */ + abstract protected fun latestUpdatesParse(response: Response, page: MangasPage) + /** * Returns an observable containing a page with a list of manga. Normally it's not needed to * override this method. diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/ParsedOnlineSource.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/ParsedOnlineSource.kt index 639438d44e..5d10d77841 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/ParsedOnlineSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/ParsedOnlineSource.kt @@ -37,11 +37,33 @@ abstract class ParsedOnlineSource(context: Context) : OnlineSource(context) { } } + /** + * Parse the response from the site for latest updates and fills [page]. + */ + override fun latestUpdatesParse(response: Response, page: MangasPage) { + val document = response.asJsoup() + for (element in document.select(latestUpdatesSelector())) { + Manga.create(id).apply { + latestUpdatesFromElement(element, this) + page.mangas.add(this) + } + } + + latestUpdatesNextPageSelector()?.let { selector -> + page.nextPageUrl = document.select(selector).first()?.absUrl("href") + } + } + /** * Returns the Jsoup selector that returns a list of [Element] corresponding to each manga. */ abstract protected fun popularMangaSelector(): String + /** + * Returns the Jsoup selector similar to [popularMangaSelector], but for latest updates. + */ + abstract protected fun latestUpdatesSelector(): String + /** * Fills [manga] with the given [element]. Most sites only show the title and the url, it's * totally safe to fill only those two values. @@ -51,12 +73,22 @@ abstract class ParsedOnlineSource(context: Context) : OnlineSource(context) { */ abstract protected fun popularMangaFromElement(element: Element, manga: Manga) + /** + * Fills [manga] with the given [element]. For latest updates. + */ + abstract protected fun latestUpdatesFromElement(element: Element, manga: Manga) + /** * Returns the Jsoup selector that returns the tag linking to the next page, or null if * there's no next page. */ abstract protected fun popularMangaNextPageSelector(): String? + /** + * Returns the Jsoup selector that returns the tag, like [popularMangaNextPageSelector]. + */ + abstract protected fun latestUpdatesNextPageSelector(): String? + /** * Parse the response from the site and fills [page]. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/YamlOnlineSource.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/YamlOnlineSource.kt index 70ba85b5ff..75edc7d8d0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/YamlOnlineSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/YamlOnlineSource.kt @@ -34,6 +34,8 @@ class YamlOnlineSource(context: Context, mappings: Map<*, *>) : OnlineSource(con getLanguages().find { code == it.code }!! } + override val supportsLatest = map.supportsLatest.toBoolean() + override val client = when(map.client) { "cloudflare" -> network.cloudflareClient else -> network.client @@ -53,8 +55,20 @@ class YamlOnlineSource(context: Context, mappings: Map<*, *>) : OnlineSource(con } } + override fun latestUpdatesRequest(page: MangasPage): Request { + if (page.page == 1) { + page.url = latestUpdatesInitialUrl() + } + return when (map.latestupdates.method?.toLowerCase()) { + "post" -> POST(page.url, headers, map.latestupdates.createForm()) + else -> GET(page.url, headers) + } + } + override fun popularMangaInitialUrl() = map.popular.url + override fun latestUpdatesInitialUrl() = map.latestupdates.url + override fun popularMangaParse(response: Response, page: MangasPage) { val document = response.asJsoup() for (element in document.select(map.popular.manga_css)) { @@ -70,6 +84,21 @@ class YamlOnlineSource(context: Context, mappings: Map<*, *>) : OnlineSource(con } } + override fun latestUpdatesParse(response: Response, page: MangasPage) { + val document = response.asJsoup() + for (element in document.select(map.latestupdates.manga_css)) { + Manga.create(id).apply { + title = element.text() + setUrlWithoutDomain(element.attr("href")) + page.mangas.add(this) + } + } + + map.popular.next_url_css?.let { selector -> + page.nextPageUrl = document.select(selector).first()?.absUrl("href") + } + } + override fun searchMangaRequest(page: MangasPage, query: String, filters: List): Request { if (page.page == 1) { page.url = searchMangaInitialUrl(query, filters) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/YamlOnlineSourceMappings.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/YamlOnlineSourceMappings.kt index 970534de82..689e6180ad 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/YamlOnlineSourceMappings.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/YamlOnlineSourceMappings.kt @@ -25,11 +25,15 @@ class YamlSourceNode(uncheckedMap: Map<*, *>) { val lang: String by map + val supportsLatest: String by map + val client: String? get() = map["client"] as? String val popular = PopularNode(toMap(map["popular"])!!) + val latestupdates = LatestUpdatesNode(toMap(map["latest_updates"])!!) + val search = SearchNode(toMap(map["search"])!!) val manga = MangaNode(toMap(map["manga"])!!) @@ -73,6 +77,17 @@ class PopularNode(override val map: Map): RequestableNode { } + +class LatestUpdatesNode(override val map: Map): RequestableNode { + + val manga_css: String by map + + val next_url_css: String? + get() = map["next_url_css"] as? String + +} + + class SearchNode(override val map: Map): RequestableNode { val manga_css: String by map 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 40a2968809..edc70a9f1c 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 @@ -36,6 +36,8 @@ class Batoto(context: Context, override val id: Int) : ParsedOnlineSource(contex override val lang: Language get() = EN + override val supportsLatest = true + private val datePattern = Pattern.compile("(\\d+|A|An)\\s+(.*?)s? ago.*") private val dateFields = HashMap().apply { @@ -59,6 +61,8 @@ class Batoto(context: Context, override val id: Int) : ParsedOnlineSource(contex override fun popularMangaInitialUrl() = "$baseUrl/search_ajax?order_cond=views&order=desc&p=1" + override fun latestUpdatesInitialUrl() = "$baseUrl/search_ajax?order_cond=update&order=desc&p=1" + override fun popularMangaParse(response: Response, page: MangasPage) { val document = response.asJsoup() for (element in document.select(popularMangaSelector())) { @@ -73,8 +77,24 @@ class Batoto(context: Context, override val id: Int) : ParsedOnlineSource(contex } } + override fun latestUpdatesParse(response: Response, page: MangasPage) { + val document = response.asJsoup() + for (element in document.select(latestUpdatesSelector())) { + Manga.create(id).apply { + latestUpdatesFromElement(element, this) + page.mangas.add(this) + } + } + + page.nextPageUrl = document.select(latestUpdatesNextPageSelector()).first()?.let { + "$baseUrl/search_ajax?order_cond=update&order=desc&p=${page.page + 1}" + } + } + override fun popularMangaSelector() = "tr:has(a)" + override fun latestUpdatesSelector() = "tr:has(a)" + override fun popularMangaFromElement(element: Element, manga: Manga) { element.select("a[href^=http://bato.to]").first().let { manga.setUrlWithoutDomain(it.attr("href")) @@ -82,8 +102,14 @@ class Batoto(context: Context, override val id: Int) : ParsedOnlineSource(contex } } + override fun latestUpdatesFromElement(element: Element, manga: Manga) { + popularMangaFromElement(element, manga) + } + override fun popularMangaNextPageSelector() = "#show_more_row" + override fun latestUpdatesNextPageSelector() = "#show_more_row" + override fun searchMangaInitialUrl(query: String, filters: List) = "$baseUrl/search_ajax?name=${Uri.encode(query)}&order_cond=views&order=desc&p=1&genre_cond=and&genres=${getFilterParams(filters)}" private fun getFilterParams(filters: List): String = filters @@ -320,4 +346,5 @@ class Batoto(context: Context, override val id: Int) : ParsedOnlineSource(contex Filter("29", "Yaoi"), Filter("31", "Yuri") ) + } \ No newline at end of file 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 4abe1e8204..5bb4f2a4bc 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 @@ -27,12 +27,18 @@ class Kissmanga(context: Context, override val id: Int) : ParsedOnlineSource(con override val lang: Language get() = EN - override val client: OkHttpClient get() = network.cloudflareClient + override val supportsLatest = true + + override val client: OkHttpClient = network.cloudflareClient override fun popularMangaInitialUrl() = "$baseUrl/MangaList/MostPopular" + override fun latestUpdatesInitialUrl() = "http://kissmanga.com/MangaList/LatestUpdate" + override fun popularMangaSelector() = "table.listing tr:gt(1)" + override fun latestUpdatesSelector() = "table.listing tr:gt(1)" + override fun popularMangaFromElement(element: Element, manga: Manga) { element.select("td a:eq(0)").first().let { manga.setUrlWithoutDomain(it.attr("href")) @@ -40,8 +46,14 @@ class Kissmanga(context: Context, override val id: Int) : ParsedOnlineSource(con } } + override fun latestUpdatesFromElement(element: Element, manga: Manga) { + popularMangaFromElement(element, manga) + } + override fun popularMangaNextPageSelector() = "li > a:contains(› Next)" + override fun latestUpdatesNextPageSelector(): String = "ul.pager > li > a:contains(Next)" + override fun searchMangaRequest(page: MangasPage, query: String, filters: List): Request { if (page.page == 1) { page.url = searchMangaInitialUrl(query, filters) 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 index db79dc1e87..ba92a148a0 100644 --- 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 @@ -23,10 +23,16 @@ class Mangafox(context: Context, override val id: Int) : ParsedOnlineSource(cont override val lang: Language get() = EN + override val supportsLatest = true + override fun popularMangaInitialUrl() = "$baseUrl/directory/" + override fun latestUpdatesInitialUrl() = "$baseUrl/directory/?latest" + override fun popularMangaSelector() = "div#mangalist > ul.list > li" + override fun latestUpdatesSelector() = "div#mangalist > ul.list > li" + override fun popularMangaFromElement(element: Element, manga: Manga) { element.select("a.title").first().let { manga.setUrlWithoutDomain(it.attr("href")) @@ -34,8 +40,14 @@ class Mangafox(context: Context, override val id: Int) : ParsedOnlineSource(cont } } + override fun latestUpdatesFromElement(element: Element, manga: Manga) { + popularMangaFromElement(element, manga) + } + override fun popularMangaNextPageSelector() = "a:has(span.next)" + override fun latestUpdatesNextPageSelector() = "a:has(span.next)" + override fun searchMangaInitialUrl(query: String, filters: List) = "$baseUrl/search.php?name_method=cw&advopts=1&order=za&sort=views&name=$query&page=1&${filters.map { it.id + "=1" }.joinToString("&")}" @@ -157,4 +169,5 @@ class Mangafox(context: Context, override val id: Int) : ParsedOnlineSource(cont Filter("genres[Yaoi]", "Yaoi"), Filter("genres[Yuri]", "Yuri") ) + } \ 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 index f75c8ac88d..b040022eec 100644 --- 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 @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.data.source.online.english import android.content.Context -import android.util.Log import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.source.EN @@ -22,10 +21,16 @@ class Mangahere(context: Context, override val id: Int) : ParsedOnlineSource(con override val lang: Language get() = EN - override fun popularMangaInitialUrl() = "$baseUrl/directory/" + override val supportsLatest = true + + override fun popularMangaInitialUrl() = "$baseUrl/directory/?views.za" + + override fun latestUpdatesInitialUrl() = "$baseUrl/directory/?last_chapter_time.za" override fun popularMangaSelector() = "div.directory_list > ul > li" + override fun latestUpdatesSelector() = "div.directory_list > ul > li" + override fun popularMangaFromElement(element: Element, manga: Manga) { element.select("div.title > a").first().let { manga.setUrlWithoutDomain(it.attr("href")) @@ -33,8 +38,14 @@ class Mangahere(context: Context, override val id: Int) : ParsedOnlineSource(con } } + override fun latestUpdatesFromElement(element: Element, manga: Manga) { + popularMangaFromElement(element, manga) + } + override fun popularMangaNextPageSelector() = "div.next-page > a.next" + override fun latestUpdatesNextPageSelector() = "div.next-page > a.next" + override fun searchMangaInitialUrl(query: String, filters: List) = "$baseUrl/search.php?name=$query&page=1&sort=views&order=za&${filters.map { it.id + "=1" }.joinToString("&")}&advopts=1" override fun searchMangaSelector() = "div.result_search > dl:has(dt)" @@ -146,4 +157,5 @@ class Mangahere(context: Context, override val id: Int) : ParsedOnlineSource(con Filter("genres[Yaoi]", "Yaoi"), Filter("genres[Yuri]", "Yuri") ) + } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangasee.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangasee.kt index a53eee06c2..f191a0c10d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangasee.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangasee.kt @@ -22,6 +22,8 @@ class Mangasee(context: Context, override val id: Int) : ParsedOnlineSource(cont override val lang: Language get() = EN + override val supportsLatest = false + private val datePattern = Pattern.compile("(\\d+)\\s+(.*?)s? (from now|ago).*") private val dateFields = HashMap().apply { @@ -168,4 +170,21 @@ class Mangasee(context: Context, override val id: Int) : ParsedOnlineSource(cont Filter("Yaoi", "Yaoi"), Filter("Yuri", "Yuri") ) + + override fun latestUpdatesInitialUrl(): String { + throw UnsupportedOperationException("not implemented") + } + + override fun latestUpdatesNextPageSelector(): String { + throw UnsupportedOperationException("not implemented") + } + + override fun latestUpdatesFromElement(element: Element, manga: Manga) { + throw UnsupportedOperationException("not implemented") + } + + override fun latestUpdatesSelector(): String { + throw UnsupportedOperationException("not implemented") + } + } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Readmangatoday.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Readmangatoday.kt index f32b90a84e..8177eedf99 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Readmangatoday.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Readmangatoday.kt @@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.network.POST import eu.kanade.tachiyomi.data.source.EN import eu.kanade.tachiyomi.data.source.Language -import eu.kanade.tachiyomi.data.source.Source import eu.kanade.tachiyomi.data.source.model.MangasPage import eu.kanade.tachiyomi.data.source.model.Page import eu.kanade.tachiyomi.data.source.online.OnlineSource @@ -26,6 +25,8 @@ class Readmangatoday(context: Context, override val id: Int) : ParsedOnlineSourc override val lang: Language get() = EN + override val supportsLatest = false + override val client: OkHttpClient get() = network.cloudflareClient /** @@ -182,4 +183,21 @@ class Readmangatoday(context: Context, override val id: Int) : ParsedOnlineSourc Filter("36", "Yaoi"), Filter("37", "Yuri") ) + + override fun latestUpdatesInitialUrl(): String { + throw UnsupportedOperationException("not implemented") + } + + override fun latestUpdatesNextPageSelector(): String { + throw UnsupportedOperationException("not implemented") + } + + override fun latestUpdatesFromElement(element: Element, manga: Manga) { + throw UnsupportedOperationException("not implemented") + } + + override fun latestUpdatesSelector(): String { + throw UnsupportedOperationException("not implemented") + } + } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/german/WieManga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/german/WieManga.kt index f3f48ab172..6683891c76 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/german/WieManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/german/WieManga.kt @@ -15,83 +15,95 @@ import java.text.SimpleDateFormat class WieManga(context: Context, override val id: Int) : ParsedOnlineSource(context) { - override val name = "Wie Manga!" + override val name = "Wie Manga!" - override val baseUrl = "http://www.wiemanga.com" + override val baseUrl = "http://www.wiemanga.com" - override val lang: Language get() = DE + override val lang: Language get() = DE - override fun popularMangaInitialUrl() = "$baseUrl/list/Hot-Book/" + override val supportsLatest = true - override fun popularMangaSelector() = ".booklist td > div" + override fun popularMangaInitialUrl() = "$baseUrl/list/Hot-Book/" - override fun popularMangaFromElement(element: Element, manga: Manga) { - val image = element.select("dt img") - val title = element.select("dd a:first-child") + override fun latestUpdatesInitialUrl() = "$baseUrl/list/New-Update/" - manga.setUrlWithoutDomain(title.attr("href")) - manga.title = title.text() - manga.thumbnail_url = image.attr("src") + override fun popularMangaSelector() = ".booklist td > div" + + override fun latestUpdatesSelector() = ".booklist td > div" + + override fun popularMangaFromElement(element: Element, manga: Manga) { + val image = element.select("dt img") + val title = element.select("dd a:first-child") + + manga.setUrlWithoutDomain(title.attr("href")) + manga.title = title.text() + manga.thumbnail_url = image.attr("src") + } + + override fun latestUpdatesFromElement(element: Element, manga: Manga) { + popularMangaFromElement(element, manga) + } + + override fun popularMangaNextPageSelector() = null + + override fun latestUpdatesNextPageSelector() = null + + override fun searchMangaInitialUrl(query: String, filters: List) = "$baseUrl/search/?wd=$query" + + override fun searchMangaSelector() = ".searchresult td > div" + + override fun searchMangaFromElement(element: Element, manga: Manga) { + val image = element.select(".resultimg img") + val title = element.select(".resultbookname") + + manga.setUrlWithoutDomain(title.attr("href")) + manga.title = title.text() + manga.thumbnail_url = image.attr("src") + } + + override fun searchMangaNextPageSelector() = ".pagetor a.l" + + override fun mangaDetailsParse(document: Document, manga: Manga) { + val imageElement = document.select(".bookmessgae tr > td:nth-child(1)").first() + val infoElement = document.select(".bookmessgae tr > td:nth-child(2)").first() + + 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 + } + + override fun chapterListSelector() = ".chapterlist tr:not(:first-child)" + + override fun chapterFromElement(element: Element, chapter: Chapter) { + val urlElement = element.select(".col1 a").first() + val dateElement = element.select(".col3 a").first() + + chapter.setUrlWithoutDomain(urlElement.attr("href")) + chapter.name = urlElement.text() + chapter.date_upload = dateElement?.text()?.let { parseChapterDate(it) } ?: 0 + } + + private fun parseChapterDate(date: String): Long { + return SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(date).time + } + + override fun pageListParse(response: Response, pages: MutableList) { + val document = response.asJsoup() + + document.select("select#page").first().select("option").forEach { + pages.add(Page(pages.size, it.attr("value"))) } + } - override fun popularMangaNextPageSelector() = null + override fun pageListParse(document: Document, pages: MutableList) {} - override fun searchMangaInitialUrl(query: String, filters: List) = "$baseUrl/search/?wd=$query" - - override fun searchMangaSelector() = ".searchresult td > div" - - override fun searchMangaFromElement(element: Element, manga: Manga) { - val image = element.select(".resultimg img") - val title = element.select(".resultbookname") - - manga.setUrlWithoutDomain(title.attr("href")) - manga.title = title.text() - manga.thumbnail_url = image.attr("src") - } - - override fun searchMangaNextPageSelector() = ".pagetor a.l" - - override fun mangaDetailsParse(document: Document, manga: Manga) { - val imageElement = document.select(".bookmessgae tr > td:nth-child(1)").first() - val infoElement = document.select(".bookmessgae tr > td:nth-child(2)").first() - - 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 - } - - override fun chapterListSelector() = ".chapterlist tr:not(:first-child)" - - override fun chapterFromElement(element: Element, chapter: Chapter) { - val urlElement = element.select(".col1 a").first() - val dateElement = element.select(".col3 a").first() - - chapter.setUrlWithoutDomain(urlElement.attr("href")) - chapter.name = urlElement.text() - chapter.date_upload = dateElement?.text()?.let { parseChapterDate(it) } ?: 0 - } - - private fun parseChapterDate(date: String): Long { - return SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(date).time - } - - override fun pageListParse(response: Response, pages: MutableList) { - val document = response.asJsoup() - - document.select("select#page").first().select("option").forEach { - pages.add(Page(pages.size, it.attr("value"))) - } - } - - override fun pageListParse(document: Document, pages: MutableList) {} - - override fun imageUrlParse(document: Document) = document.select("img#comicpic").first().attr("src") + override fun imageUrlParse(document: Document) = document.select("img#comicpic").first().attr("src") } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mangachan.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mangachan.kt index 48e5cb9241..607ddc71c5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mangachan.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mangachan.kt @@ -21,12 +21,18 @@ class Mangachan(context: Context, override val id: Int) : ParsedOnlineSource(con override val lang: Language get() = RU + override val supportsLatest = true + override fun popularMangaInitialUrl() = "$baseUrl/mostfavorites" + override fun latestUpdatesInitialUrl() = "$baseUrl/manga/new" + override fun searchMangaInitialUrl(query: String, filters: List) = "$baseUrl/?do=search&subaction=search&story=$query" override fun popularMangaSelector() = "div.content_row" + override fun latestUpdatesSelector() = "div.content_row" + override fun popularMangaFromElement(element: Element, manga: Manga) { element.select("h2 > a").first().let { manga.setUrlWithoutDomain(it.attr("href")) @@ -34,8 +40,14 @@ class Mangachan(context: Context, override val id: Int) : ParsedOnlineSource(con } } + override fun latestUpdatesFromElement(element: Element, manga: Manga) { + popularMangaFromElement(element, manga) + } + override fun popularMangaNextPageSelector() = "a:contains(Вперед)" + override fun latestUpdatesNextPageSelector() = "a:contains(Вперед)" + override fun searchMangaSelector() = popularMangaSelector() override fun searchMangaFromElement(element: Element, manga: Manga) { @@ -91,4 +103,5 @@ class Mangachan(context: Context, override val id: Int) : ParsedOnlineSource(con override fun pageListParse(document: Document, pages: MutableList) { } override fun imageUrlParse(document: Document) = "" + } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mintmanga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mintmanga.kt index e6e821bbd8..5eb2773cd0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mintmanga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Mintmanga.kt @@ -22,13 +22,19 @@ class Mintmanga(context: Context, override val id: Int) : ParsedOnlineSource(con override val lang: Language get() = RU + override val supportsLatest = true + override fun popularMangaInitialUrl() = "$baseUrl/list?sortType=rate" + override fun latestUpdatesInitialUrl() = "$baseUrl/list?sortType=updated" + override fun searchMangaInitialUrl(query: String, filters: List) = "$baseUrl/search?q=$query&${filters.map { it.id + "=in" }.joinToString("&")}" override fun popularMangaSelector() = "div.desc" + override fun latestUpdatesSelector() = "div.desc" + override fun popularMangaFromElement(element: Element, manga: Manga) { element.select("h3 > a").first().let { manga.setUrlWithoutDomain(it.attr("href")) @@ -36,8 +42,14 @@ class Mintmanga(context: Context, override val id: Int) : ParsedOnlineSource(con } } + override fun latestUpdatesFromElement(element: Element, manga: Manga) { + popularMangaFromElement(element, manga) + } + override fun popularMangaNextPageSelector() = "a.nextLink" + override fun latestUpdatesNextPageSelector() = "a.nextLink" + override fun searchMangaSelector() = popularMangaSelector() override fun searchMangaFromElement(element: Element, manga: Manga) { @@ -151,4 +163,5 @@ class Mintmanga(context: Context, override val id: Int) : ParsedOnlineSource(con Filter("el_1315", "юри"), Filter("el_1336", "яой") ) + } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Readmanga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Readmanga.kt index a874ba09f2..555f9892af 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Readmanga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/russian/Readmanga.kt @@ -22,13 +22,19 @@ class Readmanga(context: Context, override val id: Int) : ParsedOnlineSource(con override val lang: Language get() = RU + override val supportsLatest = true + override fun popularMangaInitialUrl() = "$baseUrl/list?sortType=rate" + override fun latestUpdatesInitialUrl() = "$baseUrl/list?sortType=updated" + override fun searchMangaInitialUrl(query: String, filters: List) = "$baseUrl/search?q=$query&${filters.map { it.id + "=in" }.joinToString("&")}" override fun popularMangaSelector() = "div.desc" + override fun latestUpdatesSelector() = "div.desc" + override fun popularMangaFromElement(element: Element, manga: Manga) { element.select("h3 > a").first().let { manga.setUrlWithoutDomain(it.attr("href")) @@ -36,8 +42,14 @@ class Readmanga(context: Context, override val id: Int) : ParsedOnlineSource(con } } + override fun latestUpdatesFromElement(element: Element, manga: Manga) { + popularMangaFromElement(element, manga) + } + override fun popularMangaNextPageSelector() = "a.nextLink" + override fun latestUpdatesNextPageSelector() = "a.nextLink" + override fun searchMangaSelector() = popularMangaSelector() override fun searchMangaFromElement(element: Element, manga: Manga) { @@ -152,4 +164,5 @@ class Readmanga(context: Context, override val id: Int) : ParsedOnlineSource(con Filter("el_2149", "этти"), Filter("el_2123", "юри") ) + } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt index b1f79b57c1..f9c37e2b78 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt @@ -41,7 +41,7 @@ import java.util.concurrent.TimeUnit.MILLISECONDS * Uses R.layout.fragment_catalogue. */ @RequiresPresenter(CataloguePresenter::class) -class CatalogueFragment : BaseRxFragment(), FlexibleViewHolder.OnListItemClickListener { +open class CatalogueFragment : BaseRxFragment(), FlexibleViewHolder.OnListItemClickListener { /** * Spinner shown in the toolbar to change the selected source. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePager.kt index a10243a852..3019604140 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePager.kt @@ -4,19 +4,10 @@ import eu.kanade.tachiyomi.data.source.model.MangasPage import eu.kanade.tachiyomi.data.source.online.OnlineSource import eu.kanade.tachiyomi.data.source.online.OnlineSource.Filter import rx.Observable -import rx.subjects.PublishSubject -class CataloguePager(val source: OnlineSource, val query: String, val filters: List) { +open class CataloguePager(val source: OnlineSource, val query: String, val filters: List): Pager() { - private var lastPage: MangasPage? = null - - private val results = PublishSubject.create() - - fun results(): Observable { - return results.asObservable() - } - - fun requestNext(transformer: (Observable) -> Observable): Observable { + override fun requestNext(transformer: (Observable) -> Observable): Observable { val lastPage = lastPage val page = if (lastPage == null) @@ -34,8 +25,4 @@ class CataloguePager(val source: OnlineSource, val query: String, val filters: L .doOnNext { this@CataloguePager.lastPage = it } } - fun hasNextPage(): Boolean { - return lastPage == null || lastPage?.nextPageUrl != null - } - } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt index 37e3983aec..f3c5e0a0b9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt @@ -26,7 +26,7 @@ import java.util.NoSuchElementException /** * Presenter of [CatalogueFragment]. */ -class CataloguePresenter : BasePresenter() { +open class CataloguePresenter : BasePresenter() { /** * Source manager. @@ -73,7 +73,7 @@ class CataloguePresenter : BasePresenter() { /** * Pager containing a list of manga results. */ - private lateinit var pager: CataloguePager + private lateinit var pager: Pager /** * Subject that initializes a list of manga. @@ -140,7 +140,7 @@ class CataloguePresenter : BasePresenter() { } // Create a new pager. - pager = CataloguePager(source, query, filters) + pager = createPager(query, filters) // Prepare the pager. pagerSubscription?.let { remove(it) } @@ -305,7 +305,7 @@ class CataloguePresenter : BasePresenter() { * @param source the source to check. * @return true if the source is valid, false otherwise. */ - fun isValidSource(source: Source?): Boolean { + open fun isValidSource(source: Source?): Boolean { if (source == null) return false if (source is LoginSource) { @@ -327,7 +327,7 @@ class CataloguePresenter : BasePresenter() { /** * Returns a list of enabled sources ordered by language and name. */ - private fun getEnabledSources(): List { + open protected fun getEnabledSources(): List { val languages = prefs.enabledLanguages().getOrDefault() val hiddenCatalogues = prefs.hiddenCatalogues().getOrDefault() @@ -371,4 +371,8 @@ class CataloguePresenter : BasePresenter() { restartPager(filters = selectedFilters) } + open fun createPager(query: String, filters: List): Pager { + return CataloguePager(source, query, filters) + } + } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/Pager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/Pager.kt new file mode 100644 index 0000000000..26cb466f65 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/Pager.kt @@ -0,0 +1,25 @@ +package eu.kanade.tachiyomi.ui.catalogue + +import eu.kanade.tachiyomi.data.source.model.MangasPage +import rx.subjects.PublishSubject +import rx.Observable + +/** + * A general pager for source requests (latest updates, popular, search) + */ +abstract class Pager { + + protected var lastPage: MangasPage? = null + + protected val results = PublishSubject.create() + + fun results(): Observable { + return results.asObservable() + } + + fun hasNextPage(): Boolean { + return lastPage == null || lastPage?.nextPageUrl != null + } + + abstract fun requestNext(transformer: (Observable) -> Observable): Observable +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesFragment.kt new file mode 100644 index 0000000000..cff2ea3dbc --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesFragment.kt @@ -0,0 +1,29 @@ +package eu.kanade.tachiyomi.ui.latest_updates + +import eu.kanade.tachiyomi.ui.catalogue.CatalogueFragment +import nucleus.factory.RequiresPresenter +import android.view.* +import eu.kanade.tachiyomi.R + +/** + * Fragment that shows the manga from the catalogue. Inherit CatalogueFragment. + */ +@RequiresPresenter(LatestUpdatesPresenter::class) +class LatestUpdatesFragment : CatalogueFragment() { + + override fun onPrepareOptionsMenu(menu: Menu) { + super.onPrepareOptionsMenu(menu) + menu.findItem(R.id.action_search).isVisible = false + menu.findItem(R.id.action_set_filter).isVisible = false + + } + + companion object { + + fun newInstance(): LatestUpdatesFragment { + return LatestUpdatesFragment() + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPager.kt new file mode 100644 index 0000000000..6391f9d7c2 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPager.kt @@ -0,0 +1,28 @@ +package eu.kanade.tachiyomi.ui.latest_updates + +import eu.kanade.tachiyomi.data.source.model.MangasPage +import eu.kanade.tachiyomi.data.source.online.OnlineSource +import eu.kanade.tachiyomi.ui.catalogue.Pager +import rx.Observable + +/** + * LatestUpdatesPager inherited from the general Pager. + */ +class LatestUpdatesPager(val source: OnlineSource): Pager() { + + override fun requestNext(transformer: (Observable) -> Observable): Observable { + val lastPage = lastPage + + val page = if (lastPage == null) + MangasPage(1) + else + MangasPage(lastPage.page + 1).apply { url = lastPage.nextPageUrl!! } + + val observable = source.fetchLatestUpdates(page) + + return transformer(observable) + .doOnNext { results.onNext(it) } + .doOnNext { this@LatestUpdatesPager.lastPage = it } + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPresenter.kt new file mode 100644 index 0000000000..8df1196e6d --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPresenter.kt @@ -0,0 +1,26 @@ +package eu.kanade.tachiyomi.ui.latest_updates + +import eu.kanade.tachiyomi.data.source.Source +import eu.kanade.tachiyomi.data.source.online.OnlineSource +import eu.kanade.tachiyomi.ui.catalogue.CataloguePresenter +import eu.kanade.tachiyomi.ui.catalogue.Pager +import eu.kanade.tachiyomi.data.source.online.OnlineSource.Filter + +/** + * Presenter of [LatestUpdatesFragment]. Inherit CataloguePresenter. + */ +class LatestUpdatesPresenter : CataloguePresenter() { + + override fun createPager(query: String, filters: List): Pager { + return LatestUpdatesPager(source) + } + + override fun getEnabledSources(): List { + return super.getEnabledSources().filter { it.supportsLatest } + } + + override fun isValidSource(source: Source?): Boolean { + return super.isValidSource(source) && (source as OnlineSource).supportsLatest + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 54a3f78ebe..868d7a0ab9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.backup.BackupFragment import eu.kanade.tachiyomi.ui.base.activity.BaseActivity import eu.kanade.tachiyomi.ui.catalogue.CatalogueFragment +import eu.kanade.tachiyomi.ui.latest_updates.LatestUpdatesFragment import eu.kanade.tachiyomi.ui.download.DownloadFragment import eu.kanade.tachiyomi.ui.library.LibraryFragment import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersFragment @@ -58,9 +59,10 @@ class MainActivity : BaseActivity() { val id = item.itemId when (id) { R.id.nav_drawer_library -> setFragment(LibraryFragment.newInstance(), id) - R.id.nav_drawer_recently_read -> setFragment(RecentlyReadFragment.newInstance(), id) R.id.nav_drawer_recent_updates -> setFragment(RecentChaptersFragment.newInstance(), id) + R.id.nav_drawer_recently_read -> setFragment(RecentlyReadFragment.newInstance(), id) R.id.nav_drawer_catalogues -> setFragment(CatalogueFragment.newInstance(), id) + R.id.nav_drawer_latest_updates -> setFragment(LatestUpdatesFragment.newInstance(), id) R.id.nav_drawer_downloads -> setFragment(DownloadFragment.newInstance(), id) R.id.nav_drawer_settings -> { val intent = Intent(this, SettingsActivity::class.java) diff --git a/app/src/main/res/drawable-anydpi/ic_watch_later.xml b/app/src/main/res/drawable-anydpi/ic_watch_later.xml new file mode 100644 index 0000000000..e9f85c873b --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_watch_later.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/item_catalogue_grid.xml b/app/src/main/res/layout/item_catalogue_grid.xml index 4027c126c8..6e7fae66db 100644 --- a/app/src/main/res/layout/item_catalogue_grid.xml +++ b/app/src/main/res/layout/item_catalogue_grid.xml @@ -19,7 +19,8 @@ android:layout_height="match_parent" android:background="?android:attr/colorBackground" tools:background="?android:attr/colorBackground" - tools:src="@mipmap/ic_launcher" /> + tools:src="@mipmap/ic_launcher" + tools:ignore="ContentDescription" /> + - - + Download queue My library Recently read - Recent updates Catalogues + Library updates + Latest updates Categories Selected: %1$d Backup diff --git a/build.gradle b/build.gradle index 8f054debc4..dd9956688f 100644 --- a/build.gradle +++ b/build.gradle @@ -18,4 +18,4 @@ allprojects { jcenter() maven { url "https://jitpack.io" } } -} +} \ No newline at end of file