diff --git a/src/en/animeparadise/AndroidManifest.xml b/src/en/animeparadise/AndroidManifest.xml new file mode 100644 index 000000000..568741e54 --- /dev/null +++ b/src/en/animeparadise/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/en/animeparadise/build.gradle b/src/en/animeparadise/build.gradle new file mode 100644 index 000000000..19dade8da --- /dev/null +++ b/src/en/animeparadise/build.gradle @@ -0,0 +1,15 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.serialization) +} + +ext { + extName = 'AnimeParadise' + pkgNameSuffix = 'en.animeparadise' + extClass = '.AnimeParadise' + extVersionCode = 1 + libVersion = '13' +} + +apply from: "$rootDir/common.gradle" diff --git a/src/en/animeparadise/res/mipmap-hdpi/ic_launcher.png b/src/en/animeparadise/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..605692827 Binary files /dev/null and b/src/en/animeparadise/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/en/animeparadise/res/mipmap-mdpi/ic_launcher.png b/src/en/animeparadise/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..03b6a2f5c Binary files /dev/null and b/src/en/animeparadise/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/en/animeparadise/res/mipmap-xhdpi/ic_launcher.png b/src/en/animeparadise/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..84ab73968 Binary files /dev/null and b/src/en/animeparadise/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/en/animeparadise/res/mipmap-xxhdpi/ic_launcher.png b/src/en/animeparadise/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..e67f32630 Binary files /dev/null and b/src/en/animeparadise/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/en/animeparadise/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/animeparadise/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..f1fcace08 Binary files /dev/null and b/src/en/animeparadise/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/en/animeparadise/res/web_hi_res_512.png b/src/en/animeparadise/res/web_hi_res_512.png new file mode 100644 index 000000000..1927b8de0 Binary files /dev/null and b/src/en/animeparadise/res/web_hi_res_512.png differ diff --git a/src/en/animeparadise/src/eu/kanade/tachiyomi/animeextension/en/animeparadise/AnimeParadise.kt b/src/en/animeparadise/src/eu/kanade/tachiyomi/animeextension/en/animeparadise/AnimeParadise.kt new file mode 100644 index 000000000..40d8e895f --- /dev/null +++ b/src/en/animeparadise/src/eu/kanade/tachiyomi/animeextension/en/animeparadise/AnimeParadise.kt @@ -0,0 +1,227 @@ +package eu.kanade.tachiyomi.animeextension.en.animeparadise + +import android.app.Application +import android.content.SharedPreferences +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource +import eu.kanade.tachiyomi.animesource.model.AnimeFilter +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.Track +import eu.kanade.tachiyomi.animesource.model.Video +import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.json.Json +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import uy.kohesive.injekt.injectLazy + +class AnimeParadise : ConfigurableAnimeSource, AnimeHttpSource() { + + override val name = "AnimeParadise" + + override val baseUrl = "https://www.animeparadise.moe" + + private val apiUrl = "https://api.animeparadise.moe" + + override val lang = "en" + + override val supportsLatest = true + + override val client: OkHttpClient = network.cloudflareClient + + private val json: Json by injectLazy() + + private val preferences: SharedPreferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + + private val apiHeaders = headers.newBuilder().apply { + add("Accept", "application/json, text/plain, */*") + add("Host", apiUrl.toHttpUrl().host) + add("Origin", baseUrl) + add("Referer", "$baseUrl/") + }.build() + + private val docHeaders = headers.newBuilder().apply { + add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8") + add("Host", baseUrl.toHttpUrl().host) + }.build() + + // ============================== Popular =============================== + + override fun popularAnimeRequest(page: Int): Request = GET("$apiUrl/?sort={\"rate\": -1}", apiHeaders) + + override fun popularAnimeParse(response: Response): AnimesPage { + val animeList = response.parseAs().data.map { it.toSAnime(json) } + return AnimesPage(animeList, false) + } + + // =============================== Latest =============================== + + override fun latestUpdatesRequest(page: Int): Request = GET("$apiUrl/?sort={\"startDate\": -1 }&type=TV", apiHeaders) + + override fun latestUpdatesParse(response: Response): AnimesPage = popularAnimeParse(response) + + // =============================== Search =============================== + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val filterList = if (filters.isEmpty()) getFilterList() else filters + val genreFilter = filterList.find { it is GenreFilter } as GenreFilter + + val url = when { + genreFilter.state != 0 -> apiUrl + genreFilter.toUriPart() + else -> "$apiUrl/?title=$query" + } + + return GET(url, headers = apiHeaders) + } + + override fun searchAnimeParse(response: Response): AnimesPage = popularAnimeParse(response) + + // ============================== Filters =============================== + + override fun getFilterList(): AnimeFilterList = AnimeFilterList( + AnimeFilter.Header("NOTE: Filters are going to be ignored if using search text"), + GenreFilter(), + ) + + private class GenreFilter : UriPartFilter( + "Genre", + arrayOf( + Pair("