diff --git a/lib-multisrc/galleryadults/build.gradle.kts b/lib-multisrc/galleryadults/build.gradle.kts index 7dd8f08b9e..dc076cc378 100644 --- a/lib-multisrc/galleryadults/build.gradle.kts +++ b/lib-multisrc/galleryadults/build.gradle.kts @@ -2,4 +2,4 @@ plugins { id("lib-multisrc") } -baseVersionCode = 0 +baseVersionCode = 1 diff --git a/lib-multisrc/galleryadults/src/eu/kanade/tachiyomi/multisrc/galleryadults/GalleryAdults.kt b/lib-multisrc/galleryadults/src/eu/kanade/tachiyomi/multisrc/galleryadults/GalleryAdults.kt index 891961ea50..b50dcad801 100644 --- a/lib-multisrc/galleryadults/src/eu/kanade/tachiyomi/multisrc/galleryadults/GalleryAdults.kt +++ b/lib-multisrc/galleryadults/src/eu/kanade/tachiyomi/multisrc/galleryadults/GalleryAdults.kt @@ -82,7 +82,7 @@ abstract class GalleryAdults( protected fun Element.mangaFullTitle(selector: String) = selectFirst(selector)?.text() - private fun String.shortenTitle() = this.replace(shortenTitleRegex, "").trim() + protected fun String.shortenTitle() = this.replace(shortenTitleRegex, "").trim() /* List detail */ protected class SMangaDto( diff --git a/src/all/hentaiera/build.gradle b/src/all/hentaiera/build.gradle new file mode 100644 index 0000000000..9b9ae9c8c3 --- /dev/null +++ b/src/all/hentaiera/build.gradle @@ -0,0 +1,10 @@ +ext { + extName = 'HentaiEra' + extClass = '.HentaiEraFactory' + themePkg = 'galleryadults' + baseUrl = 'https://hentaiera.com' + overrideVersionCode = 0 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/all/hentaiera/res/mipmap-hdpi/ic_launcher.png b/src/all/hentaiera/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..e24518fdfa Binary files /dev/null and b/src/all/hentaiera/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/hentaiera/res/mipmap-mdpi/ic_launcher.png b/src/all/hentaiera/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..3b9fbb2bb3 Binary files /dev/null and b/src/all/hentaiera/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/hentaiera/res/mipmap-xhdpi/ic_launcher.png b/src/all/hentaiera/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..45442b2ce5 Binary files /dev/null and b/src/all/hentaiera/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/hentaiera/res/mipmap-xxhdpi/ic_launcher.png b/src/all/hentaiera/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..4a1c3e4a63 Binary files /dev/null and b/src/all/hentaiera/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/hentaiera/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/hentaiera/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..9c81ff484d Binary files /dev/null and b/src/all/hentaiera/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/hentaiera/src/eu/kanade/tachiyomi/extension/all/hentaiera/HentaiEra.kt b/src/all/hentaiera/src/eu/kanade/tachiyomi/extension/all/hentaiera/HentaiEra.kt new file mode 100644 index 0000000000..0ae0deb6f8 --- /dev/null +++ b/src/all/hentaiera/src/eu/kanade/tachiyomi/extension/all/hentaiera/HentaiEra.kt @@ -0,0 +1,156 @@ +package eu.kanade.tachiyomi.extension.all.hentaiera + +import eu.kanade.tachiyomi.multisrc.galleryadults.GalleryAdults +import eu.kanade.tachiyomi.multisrc.galleryadults.SearchFlagFilter +import eu.kanade.tachiyomi.multisrc.galleryadults.SortOrderFilter +import eu.kanade.tachiyomi.multisrc.galleryadults.cleanTag +import eu.kanade.tachiyomi.multisrc.galleryadults.imgAttr +import eu.kanade.tachiyomi.source.model.FilterList +import okhttp3.FormBody +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element + +class HentaiEra( + lang: String = "all", + override val mangaLang: String = LANGUAGE_MULTI, +) : GalleryAdults( + "HentaiEra", + "https://hentaiera.com", + lang = lang, +) { + override val supportsLatest = true + override val useIntermediateSearch: Boolean = true + + override fun Element.mangaLang() = + select("a:has(.g_flag)").attr("href") + .removeSuffix("/").substringAfterLast("/") + + override fun Element.mangaTitle(selector: String): String? = + mangaFullTitle(if (selector == ".caption") ".gallery_title" else selector).let { + if (preferences.shortTitle) it?.shortenTitle() else it + } + + override val favoritePath = "user/fav_pags.php" + + /* Popular */ + override fun popularMangaRequest(page: Int): Request { + return if (mangaLang.isBlank()) { + val popularFilter = SortOrderFilter(getSortOrderURIs()) + .apply { + state = 0 + } + searchMangaRequest(page, "", FilterList(popularFilter)) + } else { + super.popularMangaRequest(page) + } + } + + /* Details */ + override fun Element.getInfo(tag: String): String { + return select("li:has(.tags_text:contains($tag)) .tag .item_name").map { + it?.run { + listOf( + ownText().cleanTag(), + select(".split_tag").text() + .trim() + .removePrefix("| ") + .cleanTag(), + ) + .filter { s -> s.isNotBlank() } + .joinToString() + } + }.joinToString() + } + + override fun Element.getDescription(): String { + return ( + listOf("Parodies", "Characters", "Languages", "Category") + .mapNotNull { tag -> + getInfo(tag) + .let { if (it.isNotBlank()) "$tag: $it" else null } + } + + listOfNotNull( + selectFirst("#pages_btn")?.ownText(), + selectFirst(".subtitle")?.ownText() + .let { altTitle -> if (!altTitle.isNullOrBlank()) "Alternate Title: $altTitle" else null }, + ) + ) + .joinToString("\n\n") + .plus( + if (preferences.shortTitle) { + "\nFull title: ${mangaFullTitle("h1")}" + } else { + "" + }, + ) + } + + override fun Element.getCover() = + selectFirst(".left_cover img")?.imgAttr() + + override val mangaDetailInfoSelector = ".gallery_first" + + /* Pages */ + override val pageUri = "view" + override val pageSelector = ".gthumb" + private val serverSelector = "load_server" + + private fun serverNumber(document: Document, galleryId: String): String { + return document.inputIdValueOf(serverSelector).takeIf { + it.isNotBlank() + } ?: when (galleryId.toInt()) { + in 1..274825 -> "1" + in 274826..403818 -> "2" + in 403819..527143 -> "3" + in 527144..632481 -> "4" + in 632482..816010 -> "5" + in 816011..970098 -> "6" + in 970099..1121113 -> "7" + else -> "8" + } + } + + override fun getServer(document: Document, galleryId: String): String { + val domain = baseUrl.toHttpUrl().host + return "m${serverNumber(document, galleryId)}.$domain" + } + + override fun pageRequestForm(document: Document, totalPages: String): FormBody { + val galleryId = document.inputIdValueOf(galleryIdSelector) + + return FormBody.Builder() + .add("server", serverNumber(document, galleryId)) + .add("u_id", document.inputIdValueOf(galleryIdSelector)) + .add("g_id", document.inputIdValueOf(loadIdSelector)) + .add("img_dir", document.inputIdValueOf(loadDirSelector)) + .add("visible_pages", "12") + .add("total_pages", totalPages) + .add("type", "2") // 1 would be "more", 2 is "all remaining" + .build() + } + + /* Filters */ + override fun tagsParser(document: Document): List> { + return document.select(".galleries .gallery_title a") + .mapNotNull { + Pair( + it.ownText() ?: "", + it.attr("href") + .removeSuffix("/").substringAfterLast('/'), + ) + } + } + + override fun getCategoryURIs() = listOf( + SearchFlagFilter("Manga", "mg"), + SearchFlagFilter("Doujinshi", "dj"), + SearchFlagFilter("Western", "ws"), + SearchFlagFilter("Image Set", "is"), + SearchFlagFilter("Artist CG", "ac"), + SearchFlagFilter("Game CG", "gc"), + ) + + override val idPrefixUri = "gallery" +} diff --git a/src/all/hentaiera/src/eu/kanade/tachiyomi/extension/all/hentaiera/HentaiEraFactory.kt b/src/all/hentaiera/src/eu/kanade/tachiyomi/extension/all/hentaiera/HentaiEraFactory.kt new file mode 100644 index 0000000000..55f444e576 --- /dev/null +++ b/src/all/hentaiera/src/eu/kanade/tachiyomi/extension/all/hentaiera/HentaiEraFactory.kt @@ -0,0 +1,19 @@ +package eu.kanade.tachiyomi.extension.all.hentaiera + +import eu.kanade.tachiyomi.multisrc.galleryadults.GalleryAdults +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceFactory + +class HentaiEraFactory : SourceFactory { + + override fun createSources(): List = listOf( + HentaiEra("en", GalleryAdults.LANGUAGE_ENGLISH), + HentaiEra("ja", GalleryAdults.LANGUAGE_JAPANESE), + HentaiEra("es", GalleryAdults.LANGUAGE_SPANISH), + HentaiEra("fr", GalleryAdults.LANGUAGE_FRENCH), + HentaiEra("ko", GalleryAdults.LANGUAGE_KOREAN), + HentaiEra("de", GalleryAdults.LANGUAGE_GERMAN), + HentaiEra("ru", GalleryAdults.LANGUAGE_RUSSIAN), + HentaiEra("all", GalleryAdults.LANGUAGE_MULTI), + ) +} diff --git a/src/all/imhentai/AndroidManifest.xml b/src/all/imhentai/AndroidManifest.xml deleted file mode 100644 index a8e83cde1c..0000000000 --- a/src/all/imhentai/AndroidManifest.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/all/imhentai/src/eu/kanade/tachiyomi/extension/all/imhentai/IMHentaiUrlActivity.kt b/src/all/imhentai/src/eu/kanade/tachiyomi/extension/all/imhentai/IMHentaiUrlActivity.kt deleted file mode 100644 index 4d76b0763e..0000000000 --- a/src/all/imhentai/src/eu/kanade/tachiyomi/extension/all/imhentai/IMHentaiUrlActivity.kt +++ /dev/null @@ -1,38 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.imhentai - -import android.app.Activity -import android.content.ActivityNotFoundException -import android.content.Intent -import android.os.Bundle -import android.util.Log -import kotlin.system.exitProcess - -/** - * Springboard that accepts https://imhentai.xxx/gallery/xxxxxx intents and redirects them to - * the main Tachiyomi process. - */ -class IMHentaiUrlActivity : Activity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val pathSegments = intent?.data?.pathSegments - if (pathSegments != null && pathSegments.size > 1) { - val id = pathSegments[1] - val mainIntent = Intent().apply { - action = "eu.kanade.tachiyomi.SEARCH" - putExtra("query", "id:$id") - putExtra("filter", packageName) - } - - try { - startActivity(mainIntent) - } catch (e: ActivityNotFoundException) { - Log.e("IMHentaiUrlActivity", e.toString()) - } - } else { - Log.e("IMHentaiUrlActivity", "could not parse uri from intent $intent") - } - - finish() - exitProcess(0) - } -}