diff --git a/src/en/hstream/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/animestream/hstream/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from src/en/hstream/res/mipmap-hdpi/ic_launcher.png rename to multisrc/overrides/animestream/hstream/res/mipmap-hdpi/ic_launcher.png diff --git a/src/en/hstream/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/animestream/hstream/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from src/en/hstream/res/mipmap-mdpi/ic_launcher.png rename to multisrc/overrides/animestream/hstream/res/mipmap-mdpi/ic_launcher.png diff --git a/src/en/hstream/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/animestream/hstream/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from src/en/hstream/res/mipmap-xhdpi/ic_launcher.png rename to multisrc/overrides/animestream/hstream/res/mipmap-xhdpi/ic_launcher.png diff --git a/src/en/hstream/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/animestream/hstream/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from src/en/hstream/res/mipmap-xxhdpi/ic_launcher.png rename to multisrc/overrides/animestream/hstream/res/mipmap-xxhdpi/ic_launcher.png diff --git a/src/en/hstream/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/animestream/hstream/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from src/en/hstream/res/mipmap-xxxhdpi/ic_launcher.png rename to multisrc/overrides/animestream/hstream/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/multisrc/overrides/animestream/hstream/src/Hstream.kt b/multisrc/overrides/animestream/hstream/src/Hstream.kt new file mode 100644 index 000000000..e112fd5eb --- /dev/null +++ b/multisrc/overrides/animestream/hstream/src/Hstream.kt @@ -0,0 +1,113 @@ +package eu.kanade.tachiyomi.animeextension.en.hstream + +import eu.kanade.tachiyomi.animesource.model.AnimeFilterList +import eu.kanade.tachiyomi.animesource.model.AnimesPage +import eu.kanade.tachiyomi.animesource.model.SAnime +import eu.kanade.tachiyomi.animesource.model.Track +import eu.kanade.tachiyomi.animesource.model.Video +import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.util.asJsoup +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 Hstream : AnimeStream( + "en", + "Hstream", + "https://hstream.moe", +) { + override val animeListUrl = "$baseUrl/hentai" + + override val dateFormatter by lazy { + SimpleDateFormat("yyyy-mm-dd", Locale.ENGLISH) + } + + override val client = network.cloudflareClient + + override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl) + + // ============================== Popular =============================== + override fun popularAnimeRequest(page: Int) = GET("$animeListUrl/list") + + override fun popularAnimeSelector() = "div.soralist ul > li > a.tip" + + override fun popularAnimeFromElement(element: Element) = SAnime.create().apply { + val href = element.attr("href") + setUrlWithoutDomain(href) + title = element.ownText() + thumbnail_url = "$baseUrl/images$href/cover.webp" + } + + // =============================== Latest =============================== + override fun latestUpdatesRequest(page: Int) = GET("$animeListUrl/search?page=$page&order=latest") + override fun latestUpdatesParse(response: Response) = searchAnimeParse(response) + + // =============================== Search =============================== + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val params = HstreamFilters.getSearchParameters(filters) + val multiString = buildString { + if (params.tags.isNotEmpty()) append(params.tags + "&") + if (params.studios.isNotEmpty()) append(params.studios + "&") + if (query.isNotEmpty()) append("s=$query") + } + + return GET("$animeListUrl/search?page=$page&order=${params.order}&$multiString", headers) + } + + override fun searchAnimeParse(response: Response): AnimesPage { + val original = super.searchAnimeParse(response) + val animes = original.animes.distinctBy { it.url } + return AnimesPage(animes, original.hasNextPage) + } + + override fun searchAnimeFromElement(element: Element) = SAnime.create().apply { + val href = element.attr("href").substringBeforeLast("/") + setUrlWithoutDomain(href) + title = element.selectFirst("div.tt, div.ttl")!! + .ownText() + .substringBeforeLast(" -") + thumbnail_url = "$baseUrl/images$href/cover.webp" + } + + override fun searchAnimeNextPageSelector() = "ul.pagination button[rel=next]" + + // ============================== Filters =============================== + override val fetchFilters = false + + override fun getFilterList() = HstreamFilters.FILTER_LIST + + // =========================== Anime Details ============================ + override fun getAnimeDescription(document: Document) = + super.getAnimeDescription(document) + ?.substringAfter("720p 1080p and (if available) 2160p (4k).") + ?.trim() + + // ============================== Episodes ============================== + override fun episodeListParse(response: Response) = super.episodeListParse(response).reversed() + + // ============================ Video Links ============================= + private val urlRegex by lazy { Regex("https?:\\/\\/[^\\s][^']+") } + + override fun videoListParse(response: Response): List