diff --git a/src/all/hennojin/AndroidManifest.xml b/src/all/hennojin/AndroidManifest.xml new file mode 100644 index 000000000..30deb7f79 --- /dev/null +++ b/src/all/hennojin/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/src/all/hennojin/build.gradle b/src/all/hennojin/build.gradle new file mode 100644 index 000000000..07a0cc725 --- /dev/null +++ b/src/all/hennojin/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + extName = 'Hennojin' + pkgNameSuffix = 'all.hennojin' + extClass = '.HennojinFactory' + extVersionCode = 1 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/all/hennojin/res/mipmap-hdpi/ic_launcher.png b/src/all/hennojin/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..9949e221d Binary files /dev/null and b/src/all/hennojin/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/hennojin/res/mipmap-mdpi/ic_launcher.png b/src/all/hennojin/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..001f6db22 Binary files /dev/null and b/src/all/hennojin/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/hennojin/res/mipmap-xhdpi/ic_launcher.png b/src/all/hennojin/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..0cef5d015 Binary files /dev/null and b/src/all/hennojin/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/hennojin/res/mipmap-xxhdpi/ic_launcher.png b/src/all/hennojin/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..3d22714a7 Binary files /dev/null and b/src/all/hennojin/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/hennojin/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/hennojin/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..686a2e978 Binary files /dev/null and b/src/all/hennojin/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/hennojin/res/web_hi_res_512.png b/src/all/hennojin/res/web_hi_res_512.png new file mode 100644 index 000000000..a5079a5a0 Binary files /dev/null and b/src/all/hennojin/res/web_hi_res_512.png differ diff --git a/src/all/hennojin/src/eu/kanade/tachiyomi/extension/all/hennojin/Hennojin.kt b/src/all/hennojin/src/eu/kanade/tachiyomi/extension/all/hennojin/Hennojin.kt new file mode 100644 index 000000000..b46c27fea --- /dev/null +++ b/src/all/hennojin/src/eu/kanade/tachiyomi/extension/all/hennojin/Hennojin.kt @@ -0,0 +1,137 @@ +package eu.kanade.tachiyomi.extension.all.hennojin + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.asObservableSuccess +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.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale + +class Hennojin(override val lang: String, suffix: String) : ParsedHttpSource() { + override val baseUrl = "https://hennojin.com/home/$suffix" + + override val name = "Hennojin" + + // Popular is latest + override val supportsLatest = false + + private val httpUrl by lazy { baseUrl.toHttpUrl() } + + override fun latestUpdatesSelector() = popularMangaSelector() + + override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector() + + override fun latestUpdatesRequest(page: Int) = + popularMangaRequest(page) + + override fun latestUpdatesFromElement(element: Element) = + popularMangaFromElement(element) + + override fun popularMangaSelector() = ".grid-items .layer-content" + + override fun popularMangaNextPageSelector() = ".paginate .next" + + override fun popularMangaRequest(page: Int) = + httpUrl.request { addEncodedPathSegments("page/$page") } + + override fun popularMangaFromElement(element: Element) = + SManga.create().apply { + element.selectFirst(".title_link > a").let { + title = it.text() + setUrlWithoutDomain(it.attr("href")) + } + thumbnail_url = element.selectFirst("img").attr("src") + } + + override fun searchMangaSelector() = popularMangaSelector() + + override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = + httpUrl.request { + addEncodedPathSegments("page/$page") + addQueryParameter("keyword", query) + addQueryParameter("_wpnonce", WP_NONCE) + } + + override fun searchMangaFromElement(element: Element) = + popularMangaFromElement(element) + + override fun mangaDetailsRequest(manga: SManga) = + GET("https://hennojin.com" + manga.url, headers) + + override fun mangaDetailsParse(document: Document) = + SManga.create().apply { + description = document.selectFirst( + ".manga-subtitle + p + p" + )?.html()?.replace("
", "\n") + genre = document.select( + ".tags-list a[href*=/parody/]," + + ".tags-list a[href*=/tags/]," + + ".tags-list a[href*=/character/]" + )?.joinToString { it.text() } + artist = document.select( + ".tags-list a[href*=/artist/]" + )?.joinToString { it.text() } + author = document.select( + ".tags-list a[href*=/group/]" + )?.joinToString { it.text() } ?: artist + status = SManga.COMPLETED + } + + override fun fetchChapterList(manga: SManga) = + Request.Builder().url(manga.thumbnail_url!!) + .head().build().run(client::newCall) + .asObservableSuccess().map { res -> + SChapter.create().apply { + name = "Chapter" + url = manga.reader + date_upload = res.date + chapter_number = -1f + }.let(::listOf) + }!! + + override fun pageListRequest(chapter: SChapter) = + GET("https://hennojin.com" + chapter.url, headers) + + override fun pageListParse(document: Document) = + document.select(".slideshow-container > img") + .mapIndexed { idx, img -> Page(idx, "", img.absUrl("src")) } + + private inline fun HttpUrl.request( + block: HttpUrl.Builder.() -> HttpUrl.Builder + ) = GET(newBuilder().block().toString(), headers) + + private inline val Response.date: Long + get() = headers["Last-Modified"]?.run(httpDate::parse)?.time ?: 0L + + private inline val SManga.reader: String + get() = "/home/manga-reader/?manga=$title&view=multi" + + override fun chapterListSelector() = + throw UnsupportedOperationException("Not used") + + override fun chapterFromElement(element: Element) = + throw UnsupportedOperationException("Not used") + + override fun imageUrlParse(document: Document) = + throw UnsupportedOperationException("Not used") + + companion object { + // Let's hope this doesn't change + private const val WP_NONCE = "40229f97a5" + + private val httpDate by lazy { + SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.ENGLISH) + } + } +} diff --git a/src/all/hennojin/src/eu/kanade/tachiyomi/extension/all/hennojin/HennojinFactory.kt b/src/all/hennojin/src/eu/kanade/tachiyomi/extension/all/hennojin/HennojinFactory.kt new file mode 100644 index 000000000..7b1da024c --- /dev/null +++ b/src/all/hennojin/src/eu/kanade/tachiyomi/extension/all/hennojin/HennojinFactory.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.all.hennojin + +import eu.kanade.tachiyomi.source.SourceFactory + +class HennojinFactory : SourceFactory { + override fun createSources() = listOf( + Hennojin("en", ""), + Hennojin("ja", "?archive=raw") + ) +}