diff --git a/src/en/ohjoysextoy/build.gradle b/src/en/ohjoysextoy/build.gradle new file mode 100644 index 000000000..d15c137cb --- /dev/null +++ b/src/en/ohjoysextoy/build.gradle @@ -0,0 +1,8 @@ +ext { + extName = "Oh Joy Sex Toy" + extClass = ".OhJoySexToy" + extVersionCode = 1 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/en/ohjoysextoy/res/mipmap-hdpi/ic_launcher.png b/src/en/ohjoysextoy/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..821b81ef1 Binary files /dev/null and b/src/en/ohjoysextoy/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/en/ohjoysextoy/res/mipmap-mdpi/ic_launcher.png b/src/en/ohjoysextoy/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..a7762eca5 Binary files /dev/null and b/src/en/ohjoysextoy/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/en/ohjoysextoy/res/mipmap-xhdpi/ic_launcher.png b/src/en/ohjoysextoy/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..d0b4bc682 Binary files /dev/null and b/src/en/ohjoysextoy/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/en/ohjoysextoy/res/mipmap-xxhdpi/ic_launcher.png b/src/en/ohjoysextoy/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..9a20be238 Binary files /dev/null and b/src/en/ohjoysextoy/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/en/ohjoysextoy/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/ohjoysextoy/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..272be03c1 Binary files /dev/null and b/src/en/ohjoysextoy/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/en/ohjoysextoy/src/eu/kanade/tachiyomi/extension/en/ohjoysextoy/OhJoySexToy.kt b/src/en/ohjoysextoy/src/eu/kanade/tachiyomi/extension/en/ohjoysextoy/OhJoySexToy.kt new file mode 100644 index 000000000..339116377 --- /dev/null +++ b/src/en/ohjoysextoy/src/eu/kanade/tachiyomi/extension/en/ohjoysextoy/OhJoySexToy.kt @@ -0,0 +1,133 @@ +package eu.kanade.tachiyomi.extension.en.ohjoysextoy + +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.model.UpdateStrategy +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.util.asJsoup +import keiyoushi.utils.tryParse +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale + +private val MULTI_SPACE_REGEX = "\\s{6,}".toRegex() + +class OhJoySexToy : ParsedHttpSource() { + + override val name = "Oh Joy Sex Toy" + override val baseUrl = "https://www.ohjoysextoy.com" + override val lang = "en" + override val supportsLatest = true + + private val dateFormat = SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH) + + // Browse + + override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/category/comic/page/$page/", headers) + + override fun popularMangaSelector(): String = ".comicthumbwrap" + + override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { + setUrlWithoutDomain(element.selectFirst(".comicarchiveframe > a")!!.absUrl("href")) + title = element.selectFirst(".comicthumbdate")!!.text().substringBefore(" by") + thumbnail_url = element.selectFirst(".comicarchiveframe > a > img")?.absUrl("src") + } + + override fun popularMangaNextPageSelector(): String = ".pagenav-left a" + + // Latest + + override fun latestUpdatesRequest(page: Int): Request = GET(baseUrl, headers) + + override fun latestUpdatesSelector(): String = "#MattsRecentComicsBar > ul > div" + + override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply { + setUrlWithoutDomain(element.selectFirst(".comicarchiveframe > a")!!.absUrl("href")) + title = element.selectFirst(".comicthumbdate")!!.text().substringBefore(" by") + thumbnail_url = element.selectFirst(".comicarchiveframe > a > img")?.absUrl("src") + } + + override fun latestUpdatesNextPageSelector(): String? = null + + // Search + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/?s=$query", headers) + + override fun searchMangaSelector(): String = "h2.post-title" + + override fun searchMangaFromElement(element: Element): SManga = SManga.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href")) + title = element.selectFirst("a")!!.text().substringBefore(" by") + } + + override fun searchMangaNextPageSelector(): String? = null + + // etc + + override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { + thumbnail_url = document.selectFirst("meta[property=\"og:image\"]") + ?.absUrl("content") + status = SManga.COMPLETED + title = document.selectFirst("meta[property=\"og:title\"]")!! + .attr("content") + .substringBefore(" by") + author = document.selectFirst("meta[property=\"og:title\"]") + ?.attr("content") + ?.substringAfter("by ", "") + description = parseDescription(document) + genre = document.select("meta[property=\"article:section\"]:not(:first-of-type)") + .eachAttr("content") + .joinToString() + update_strategy = UpdateStrategy.ONLY_FETCH_ONCE + setUrlWithoutDomain( + document.selectFirst("meta[property=\"og:url\"]")!!.absUrl("content"), + ) + } + + private fun parseDescription(document: Document): String { + val desc = document.selectFirst("meta[property=\"og:description\"]") + ?.attr("content") + ?.split(MULTI_SPACE_REGEX) + ?.get(0) + "..." + + val authorCredits = document.select(".entry div.ui-tabs div a") + .joinToString("\n") { link -> + "${link.text()}: ${link.absUrl("href")}" + } + + return listOf(desc, authorCredits, "(Full description and credits in WebView)").joinToString("\n\n") + } + + override fun chapterListRequest(manga: SManga): Request = GET("$baseUrl${manga.url}", headers) + + override fun chapterListParse(response: Response): List<SChapter> { + val document = response.asJsoup() + val dateString = document.selectFirst(".post-date")?.text() + + return listOf( + SChapter.create().apply { + name = document.title() + scanlator = document.selectFirst(".post-author a")?.text() + date_upload = dateFormat.tryParse(dateString) + setUrlWithoutDomain(response.request.url.toString()) + }, + ) + } + + override fun chapterListSelector(): String = throw UnsupportedOperationException() + + override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException() + + override fun pageListParse(document: Document): List<Page> { + return document.select("div.comicpane img") + .mapIndexed { index, img -> Page(index = index, imageUrl = img.absUrl("src")) } + } + + override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException() +}