diff --git a/src/all/wpmanga/build.gradle b/src/all/wpmanga/build.gradle new file mode 100644 index 000000000..35806f856 --- /dev/null +++ b/src/all/wpmanga/build.gradle @@ -0,0 +1,16 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + appName = 'Tachiyomi: WPManga (Many sources)' + pkgNameSuffix = "all.wpmanga" + extClass = '.WpMangaFactory' + extVersionCode = 1 + extVersionSuffix = 1 + libVersion = '1.2' +} +dependencies { + provided "com.google.code.gson:gson:2.8.0" + provided "com.github.salomonbrys.kotson:kotson:2.5.0" +} +apply from: "$rootDir/common.gradle" diff --git a/src/all/wpmanga/res/mipmap-hdpi/ic_launcher.png b/src/all/wpmanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..e2f015fdd Binary files /dev/null and b/src/all/wpmanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/wpmanga/res/mipmap-mdpi/ic_launcher.png b/src/all/wpmanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..89d8c9131 Binary files /dev/null and b/src/all/wpmanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/wpmanga/res/mipmap-xhdpi/ic_launcher.png b/src/all/wpmanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..5a5efe0a3 Binary files /dev/null and b/src/all/wpmanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/wpmanga/res/mipmap-xxhdpi/ic_launcher.png b/src/all/wpmanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..723de063e Binary files /dev/null and b/src/all/wpmanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/wpmanga/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/wpmanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..c57835705 Binary files /dev/null and b/src/all/wpmanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/wpmanga/res/web_hi_res_512.png b/src/all/wpmanga/res/web_hi_res_512.png new file mode 100644 index 000000000..c8845b98b Binary files /dev/null and b/src/all/wpmanga/res/web_hi_res_512.png differ diff --git a/src/all/wpmanga/src/eu/kanade/tachiyomi/extension/all/wpmanga/WpManga.kt b/src/all/wpmanga/src/eu/kanade/tachiyomi/extension/all/wpmanga/WpManga.kt new file mode 100644 index 000000000..71509889c --- /dev/null +++ b/src/all/wpmanga/src/eu/kanade/tachiyomi/extension/all/wpmanga/WpManga.kt @@ -0,0 +1,178 @@ +package eu.kanade.tachiyomi.extension.all.wpmanga + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.FilterList +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 okhttp3.Request +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.* + + +open class WpManga(override val name: String, override val baseUrl: String, override val lang: String) : ParsedHttpSource() { + + override val supportsLatest = false + + override fun popularMangaSelector() = "div[id^=manga-item]" + + override fun popularMangaRequest(page: Int): Request = GET(baseUrl, headers) + + override fun latestUpdatesNextPageSelector(): String? = throw Exception("Not used") + + override fun latestUpdatesFromElement(element: Element): SManga = throw Exception("Not used") + + override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used") + + override fun latestUpdatesSelector(): String = throw Exception("Not used") + + override fun popularMangaNextPageSelector() = null + + + override fun popularMangaFromElement(element: Element): SManga { + val manga = SManga.create() + element.select("a").first().let { + manga.setUrlWithoutDomain(it.attr("href")) + manga.title = it.attr("title") + } + element.select("img").first()?.let { + manga.thumbnail_url = it.absUrl("src").substringBefore("?resize").substringBefore("?fit") + } + return manga + } + + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + return GET("$baseUrl/?s=$query&post_type=wp-manga", headers) + } + + override fun searchMangaSelector() = "div.post-title" + + override fun searchMangaFromElement(element: Element): SManga { + val manga = SManga.create() + element.select("a").first().let { + manga.setUrlWithoutDomain(it.attr("href")) + manga.title = it.text() + } + element.select("img").first()?.let { + manga.thumbnail_url = it.absUrl("src").substringBefore("?resize").substringBefore("?fit") + } + + return manga + } + + override fun searchMangaNextPageSelector() = null + + override fun mangaDetailsParse(document: Document): SManga { + val infoElement = document.select("div.tab-summary").first() + val manga = SManga.create() + manga.author = infoElement.select("div.author-content a")?.first()?.text() + manga.artist = infoElement.select("div.artist-content a")?.first()?.text() + manga.genre = infoElement.select("div.genres-content a")?.first()?.text() + var genres = mutableListOf() + + infoElement.select("div.genres-content a").orEmpty().forEach { id -> + genres.add(id.text()) + } + manga.genre = genres.joinToString(", ") + manga.description = document.select("div.summary__content")?.first()?.text() + manga.status = document.select("div.post-status div.post-content_item:contains(status) div.summary-content").first()?.text().orEmpty().let { parseStatus(it) } + manga.thumbnail_url = document.select("div.summary_image img")?.first()?.absUrl("src")?.substringBefore("?resize")?.substringBefore("?fit") + return manga + } + + private fun parseStatus(status: String) = when { + status.contains("Ongoing", true) -> SManga.ONGOING + status.contains("Completed", true) -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + + override fun chapterListSelector() = "div.listing-chapters_wrap li.wp-manga-chapter" + + + override fun chapterFromElement(element: Element): SChapter { + val urlElement = element.select("a").first() + val dateElement = element.select("span").first() + val chapter = SChapter.create() + chapter.setUrlWithoutDomain(urlElement.attr("href") + "?style=list") + chapter.name = urlElement.text() + chapter.date_upload = dateElement.text()?.let { parseChapterDate(it) } ?: 0 + return chapter + } + + open fun parseChapterDate(date: String): Long? { + val lcDate = date.toLowerCase() + if (lcDate.endsWith(" ago")) + parseRelativeDate(lcDate)?.let { return it } + + //Handle 'yesterday' and 'today' + var relativeDate: Calendar? = null + if (lcDate.startsWith("yesterday")) { + relativeDate = Calendar.getInstance() + relativeDate.add(Calendar.DAY_OF_MONTH, -1) //yesterday + } else if (lcDate.startsWith("today")) { + relativeDate = Calendar.getInstance() + } + + relativeDate?.timeInMillis?.let { + return it + } + + return DATE_FORMAT_1.parse(date).time + + } + + /** + * Parses dates in this form: + * `11 days ago` + */ + private fun parseRelativeDate(date: String): Long? { + val trimmedDate = date.split(" ") + + if (trimmedDate[2] != "ago") return null + + val number = trimmedDate[0].toIntOrNull() ?: return null + val unit = trimmedDate[1].removeSuffix("s") // Remove 's' suffix + + val now = Calendar.getInstance() + + // Map English unit to Java unit + val javaUnit = when (unit) { + "year", "yr" -> Calendar.YEAR + "month" -> Calendar.MONTH + "week", "wk" -> Calendar.WEEK_OF_MONTH + "day" -> Calendar.DAY_OF_MONTH + "hour", "hr" -> Calendar.HOUR + "minute", "min" -> Calendar.MINUTE + "second", "sec" -> Calendar.SECOND + else -> return null + } + + now.add(javaUnit, -number) + + return now.timeInMillis + } + + override fun pageListParse(document: Document): List { + val doc = document.select("div.page-break img"); + + val pages = mutableListOf() + doc.forEach { + // Create dummy element to resolve relative URL + val absUrl = it.select("img").attr("src") + pages.add(Page(pages.size, "", absUrl)) + } + return pages + } + + override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used") + + companion object { + + private val DATE_FORMAT_1 = SimpleDateFormat("MMM dd, yyyy", Locale.US) + + } +} \ No newline at end of file diff --git a/src/all/wpmanga/src/eu/kanade/tachiyomi/extension/all/wpmanga/WpMangaFactory.kt b/src/all/wpmanga/src/eu/kanade/tachiyomi/extension/all/wpmanga/WpMangaFactory.kt new file mode 100644 index 000000000..b8b19169f --- /dev/null +++ b/src/all/wpmanga/src/eu/kanade/tachiyomi/extension/all/wpmanga/WpMangaFactory.kt @@ -0,0 +1,20 @@ +package eu.kanade.tachiyomi.extension.all.wpmanga + +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceFactory + +class WpMangaFactory : SourceFactory { + override fun createSources(): List = getAllWpManga() +} + +fun getAllWpManga(): List { + return listOf( + TrashScanlations(), + ZeroScans() + ) +} + +class TrashScanlations : WpManga("Trash Scanlations", "https://trashscanlations.com/", "en") + +class ZeroScans : WpManga("Zero Scans", "https://zeroscans.com/", "en") +