diff --git a/src/en/webcomics/build.gradle b/src/en/webcomics/build.gradle new file mode 100644 index 000000000..36208dd9b --- /dev/null +++ b/src/en/webcomics/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + appName = 'Tachiyomi: Webcomics' + pkgNameSuffix = 'en.webcomics' + extClass = '.Webcomics' + extVersionCode = 1 + libVersion = '1.2' +} + +apply from: "$rootDir/common.gradle" diff --git a/src/en/webcomics/res/mipmap-hdpi/ic_launcher.png b/src/en/webcomics/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..963a598f9 Binary files /dev/null and b/src/en/webcomics/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/en/webcomics/res/mipmap-mdpi/ic_launcher.png b/src/en/webcomics/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..5594e1daf Binary files /dev/null and b/src/en/webcomics/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/en/webcomics/res/mipmap-xhdpi/ic_launcher.png b/src/en/webcomics/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..c0dd86396 Binary files /dev/null and b/src/en/webcomics/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/en/webcomics/res/mipmap-xxhdpi/ic_launcher.png b/src/en/webcomics/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..acf32fecf Binary files /dev/null and b/src/en/webcomics/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/en/webcomics/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/webcomics/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..df9b90559 Binary files /dev/null and b/src/en/webcomics/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/en/webcomics/res/web_hi_res_512.png b/src/en/webcomics/res/web_hi_res_512.png new file mode 100644 index 000000000..347f81251 Binary files /dev/null and b/src/en/webcomics/res/web_hi_res_512.png differ diff --git a/src/en/webcomics/src/eu/kanade/tachiyomi/extension/en/webcomics/Webcomics.kt b/src/en/webcomics/src/eu/kanade/tachiyomi/extension/en/webcomics/Webcomics.kt new file mode 100644 index 000000000..2ad19ad8b --- /dev/null +++ b/src/en/webcomics/src/eu/kanade/tachiyomi/extension/en/webcomics/Webcomics.kt @@ -0,0 +1,164 @@ +package eu.kanade.tachiyomi.extension.en.webcomics + +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.HttpUrl +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.lang.UnsupportedOperationException + +class Webcomics : ParsedHttpSource() { + + override val name = "Webcomics" + + override val baseUrl = "http://www.webcomicsapp.com" + + override val lang = "en" + + override val supportsLatest = true + + override fun popularMangaSelector() = "section.mangas div div.col-md-3" + + override fun latestUpdatesSelector() = "section.mangas div div.col-md-3" + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", "http://www.webcomicsapp.com") + + override fun popularMangaRequest(page: Int) = GET("$baseUrl/popular.html", headers) + + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/latest.html", headers) + + private fun mangaFromElement(element: Element): SManga { + val manga = SManga.create() + element.select("a").first().let { + manga.setUrlWithoutDomain(it.attr("href")) + manga.title = it.select("h5").text() + } + return manga + } + + override fun popularMangaFromElement(element: Element) = mangaFromElement(element) + + override fun latestUpdatesFromElement(element: Element) = mangaFromElement(element) + + override fun popularMangaNextPageSelector() = null + + override fun latestUpdatesNextPageSelector() = null + + override fun mangaDetailsParse(document: Document): SManga { + val infoElement = document.select("section.book-info-left > .wrap") + + val manga = SManga.create() + manga.genre = infoElement.select(".labels > label").joinToString(", ") { it.text() } + manga.description = infoElement.select("p.p-description").text() + manga.thumbnail_url = infoElement.select("img").first()?.attr("src") + return manga + } + + override fun searchMangaNextPageSelector() = throw UnsupportedOperationException("Not used") + + override fun searchMangaFromElement(element: Element): SManga { + val infoElement = element.select(".col-md-5") + val manga = SManga.create() + infoElement.let { + manga.title = it.select(".wiki-book-title").text().trim() + manga.setUrlWithoutDomain(it.select("a").first().attr("href")) + } + return manga + } + + override fun searchMangaSelector() = ".wiki-book-list > .row" + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = HttpUrl.parse("$baseUrl/wiki.html?search=$query&page=$page")?.newBuilder() + (if (filters.isEmpty()) getFilterList() else filters).forEach { filter -> + when (filter) { + is GenreFilter -> { + val genre = getGenreList()[filter.state] + url?.addQueryParameter("category", genre) + } + } + } + return GET(url.toString(), headers) + } + + override fun searchMangaParse(response: Response): MangasPage { + val document = response.asJsoup() + var nextPage = true + val mangas = document.select(searchMangaSelector()).filter { + val shouldFilter = it.select(".col-md-2 > a").first().text() == "READ" + if (nextPage) { + nextPage = shouldFilter + } + shouldFilter + }.map { element -> + searchMangaFromElement(element) + } + + return MangasPage(mangas, if (nextPage) hasNextPage(document) else false) + } + + private fun hasNextPage(document: Document): Boolean { + return !document.select(".pagination .page-item.active + .page-item").isEmpty() + } + + override fun chapterListSelector() = "section.book-info-left > .wrap > ul > li" + + override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga) + + override fun chapterListParse(response: Response): List { + val document = response.asJsoup() + return document.select(chapterListSelector()).asReversed().map {chapterFromElement(it)} + } + + override fun chapterFromElement(element: Element): SChapter { + val urlElement = element.select("a") + + val chapter = SChapter.create() + chapter.setUrlWithoutDomain(urlElement.attr("href")) + chapter.name = urlElement.text().trim() + return chapter + } + + override fun mangaDetailsRequest(manga: SManga) = GET(baseUrl + "/" + manga.url, headers) + + override fun pageListRequest(chapter: SChapter) = GET(baseUrl + "/" + chapter.url, headers) + + override fun pageListParse(document: Document) = document + .select("section.book-reader .img-list > li > img") + .mapIndexed { + i, element -> + Page(i, "", element.attr("data-original")) + } + + override fun imageUrlParse(document: Document) = "" + + private class GenreFilter(genres: Array) : Filter.Select("Genre", genres) + + override fun getFilterList() = FilterList( + GenreFilter(getGenreList()) + ) + + // [...$('.row.wiki-book-nav .col-md-8 ul a')].map(el => `"${el.textContent.trim()}"`).join(',\n') + // http://www.webcomicsapp.com/wiki.html + private fun getGenreList() = arrayOf( + "All", + "Fantasy", + "Comedy", + "Drama", + "Modern", + "Action", + "Monster", + "Romance", + "Boys'Love", + "Harem", + "Thriller", + "Historical", + "Sci-fi", + "Slice of Life" + ) +}