diff --git a/src/pt/animesonlinevip/AndroidManifest.xml b/src/pt/animesonlinevip/AndroidManifest.xml new file mode 100644 index 0000000000..0fc6bf722e --- /dev/null +++ b/src/pt/animesonlinevip/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/src/pt/animesonlinevip/build.gradle b/src/pt/animesonlinevip/build.gradle new file mode 100644 index 0000000000..9aa5fe1df5 --- /dev/null +++ b/src/pt/animesonlinevip/build.gradle @@ -0,0 +1,8 @@ +ext { + extName = 'AnimesOnlineVip' + extClass = '.AnimesOnlineVip' + extVersionCode = 1 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/pt/animesonlinevip/res/mipmap-hdpi/ic_launcher.png b/src/pt/animesonlinevip/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..09f062c34c Binary files /dev/null and b/src/pt/animesonlinevip/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/pt/animesonlinevip/res/mipmap-mdpi/ic_launcher.png b/src/pt/animesonlinevip/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..3d71a71b46 Binary files /dev/null and b/src/pt/animesonlinevip/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/pt/animesonlinevip/res/mipmap-xhdpi/ic_launcher.png b/src/pt/animesonlinevip/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..a50e0ca23f Binary files /dev/null and b/src/pt/animesonlinevip/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/pt/animesonlinevip/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/animesonlinevip/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..fa04442160 Binary files /dev/null and b/src/pt/animesonlinevip/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/pt/animesonlinevip/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/animesonlinevip/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..537f9c97a2 Binary files /dev/null and b/src/pt/animesonlinevip/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/pt/animesonlinevip/src/eu/kanade/tachiyomi/animeextension/pt/animesonlinevip/AnimesOnlineVip.kt b/src/pt/animesonlinevip/src/eu/kanade/tachiyomi/animeextension/pt/animesonlinevip/AnimesOnlineVip.kt new file mode 100644 index 0000000000..0e03f0e8a5 --- /dev/null +++ b/src/pt/animesonlinevip/src/eu/kanade/tachiyomi/animeextension/pt/animesonlinevip/AnimesOnlineVip.kt @@ -0,0 +1,215 @@ +package eu.kanade.tachiyomi.animeextension.pt.animesonlinevip + +import android.app.Application +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource +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.SEpisode +import eu.kanade.tachiyomi.animesource.model.Video +import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.awaitSuccess +import eu.kanade.tachiyomi.util.asJsoup +import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class AnimesOnlineVip : ConfigurableAnimeSource, ParsedAnimeHttpSource() { + + override val name = "Animes Online Vip" + + override val baseUrl = "https://animesonlinehd.vip" + + override val lang = "pt-BR" + + override val supportsLatest = true + + private val preferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", baseUrl) + .add("Origin", baseUrl) + + // ============================== Popular =============================== + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/top-100", headers) + + override fun popularAnimeSelector() = "a.top100Item" + + override fun popularAnimeFromElement(element: Element) = SAnime.create().apply { + setUrlWithoutDomain(element.attr("href")) + title = element.attr("title") + thumbnail_url = element.selectFirst("img")?.attr("src") + } + + override fun popularAnimeNextPageSelector() = null + + // =============================== Latest =============================== + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/page/$page", headers) + + override fun latestUpdatesSelector() = "div.videos div.video div.video-thumb a" + + override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element) + + override fun latestUpdatesNextPageSelector() = "ul.paginacao li.next" + + // =============================== Search =============================== + override suspend fun getSearchAnime( + page: Int, + query: String, + filters: AnimeFilterList, + ): AnimesPage { + return if (query.startsWith(PREFIX_SEARCH)) { + val path = query.removePrefix(PREFIX_SEARCH) + client.newCall(GET("$baseUrl/$path")) + .awaitSuccess() + .use(::searchAnimeByIdParse) + } else { + super.getSearchAnime(page, query, filters) + } + } + + private fun searchAnimeByIdParse(response: Response): AnimesPage { + val details = animeDetailsParse(response).apply { + setUrlWithoutDomain(response.request.url.toString()) + initialized = true + } + + return AnimesPage(listOf(details), false) + } + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val url = "$baseUrl/page".toHttpUrl().newBuilder() + .addPathSegment(page.toString()) + .addQueryParameter("s", query) + .build() + + return GET(url, headers = headers) + } + + override fun searchAnimeSelector() = latestUpdatesSelector() + + override fun searchAnimeFromElement(element: Element) = latestUpdatesFromElement(element) + + override fun searchAnimeNextPageSelector() = latestUpdatesNextPageSelector() + + // =========================== Anime Details ============================ + override fun animeDetailsParse(document: Document): SAnime { + val doc = getRealDoc(document) + + return SAnime.create().apply { + setUrlWithoutDomain(doc.location()) + title = doc.selectFirst("div.pagina-titulo h1")!!.text().trim() + thumbnail_url = doc.selectFirst("div.post-capa img")?.attr("src") + description = doc.selectFirst("ul.post-infos p")?.text() + genre = doc.select("ul.post-infos li a").eachText().joinToString(", ") + } + } + + // ============================== Episodes ============================== + override fun episodeListParse(response: Response): List { + return getRealDoc(response.asJsoup()) + .select(episodeListSelector()) + .map(::episodeFromElement) + .reversed() + } + + override fun episodeListSelector() = "ul.episodios li a" + + override fun episodeFromElement(element: Element) = SEpisode.create().apply { + setUrlWithoutDomain(element.attr("href")) + name = element.attr("title") + .substringAfterLast("–").trim() + episode_number = element.selectFirst("div.listaEpInfosEp span:eq(0)") + ?.text() + ?.substringAfter(":") + ?.toFloatOrNull() ?: 1F + } + + // ============================ Video Links ============================= + override fun videoListParse(response: Response): List