diff --git a/src/pt/animefire/AndroidManifest.xml b/src/pt/animefire/AndroidManifest.xml new file mode 100644 index 000000000..94339ee7a --- /dev/null +++ b/src/pt/animefire/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/src/pt/animefire/build.gradle b/src/pt/animefire/build.gradle new file mode 100644 index 000000000..1c3d5a31c --- /dev/null +++ b/src/pt/animefire/build.gradle @@ -0,0 +1,14 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = 'Anime Fire' + pkgNameSuffix = 'pt.animefire' + extClass = '.AnimeFire' + extVersionCode = 1 + libVersion = '12' +} + + +apply from: "$rootDir/common.gradle" diff --git a/src/pt/animefire/res/mipmap-hdpi/ic_launcher.png b/src/pt/animefire/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..23d1c683b Binary files /dev/null and b/src/pt/animefire/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/pt/animefire/res/mipmap-mdpi/ic_launcher.png b/src/pt/animefire/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..b2bb7e8fe Binary files /dev/null and b/src/pt/animefire/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/pt/animefire/res/mipmap-xhdpi/ic_launcher.png b/src/pt/animefire/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..99bcbba1d Binary files /dev/null and b/src/pt/animefire/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/pt/animefire/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/animefire/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..aa15db9a3 Binary files /dev/null and b/src/pt/animefire/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/pt/animefire/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/animefire/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..ff5cc2f7e Binary files /dev/null and b/src/pt/animefire/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/pt/animefire/src/eu/kanade/tachiyomi/animeextension/pt/animefire/AFFilters.kt b/src/pt/animefire/src/eu/kanade/tachiyomi/animeextension/pt/animefire/AFFilters.kt new file mode 100644 index 000000000..ad39cecbe --- /dev/null +++ b/src/pt/animefire/src/eu/kanade/tachiyomi/animeextension/pt/animefire/AFFilters.kt @@ -0,0 +1,95 @@ +package eu.kanade.tachiyomi.animeextension.pt.animefire + +import eu.kanade.tachiyomi.animesource.model.AnimeFilter +import eu.kanade.tachiyomi.animesource.model.AnimeFilterList + +object AFFilters { + + open class QueryPartFilter( + displayName: String, + val vals: Array> + ) : AnimeFilter.Select( + displayName, + vals.map { it.first }.toTypedArray() + ) { + fun toQueryPart() = vals[state].second + } + + private inline fun AnimeFilterList.asQueryPart(): String { + return this.filterIsInstance().joinToString("") { + (it as QueryPartFilter).toQueryPart() + } + } + + class GenreFilter : QueryPartFilter("Gênero", AFFiltersData.genres) + class SeasonFilter : QueryPartFilter("Temporada", AFFiltersData.seasons) + + val filterList = AnimeFilterList( + AnimeFilter.Header(AFFiltersData.IGNORE_SEARCH_MSG), + SeasonFilter(), + AnimeFilter.Header(AFFiltersData.IGNORE_SEASON_MSG), + GenreFilter() + ) + + data class FilterSearchParams( + val genre: String = "", + val season: String = "" + ) + + internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams { + return FilterSearchParams( + filters.asQueryPart(), + filters.asQueryPart() + ) + } + + private object AFFiltersData { + + const val IGNORE_SEARCH_MSG = "NOTA: Os filtros abaixos são IGNORADOS durante a pesquisa." + const val IGNORE_SEASON_MSG = "NOTA: O filtro de gêneros IGNORA o de temporadas." + val every = Pair("Qualquer um", "") + + val seasons = arrayOf( + every, + Pair("Outono", "outono"), + Pair("Inverno", "inverno"), + Pair("Primavera", "primavera"), + Pair("Verão", "verao") + ) + + val genres = arrayOf( + Pair("Ação", "acao"), + Pair("Artes Marciais", "artes-marciais"), + Pair("Aventura", "aventura"), + Pair("Comédia", "comedia"), + Pair("Demônios", "demonios"), + Pair("Drama", "drama"), + Pair("Ecchi", "ecchi"), + Pair("Espaço", "espaco"), + Pair("Esporte", "esporte"), + Pair("Fantasia", "fantasia"), + Pair("Ficção Científica", "ficcao-cientifica"), + Pair("Harém", "harem"), + Pair("Horror", "horror"), + Pair("Jogos", "jogos"), + Pair("Josei", "josei"), + Pair("Magia", "magia"), + Pair("Mecha", "mecha"), + Pair("Militar", "militar"), + Pair("Mistério", "misterio"), + Pair("Musical", "musical"), + Pair("Paródia", "parodia"), + Pair("Psicológico", "psicologico"), + Pair("Romance", "romance"), + Pair("Seinen", "seinen"), + Pair("Shoujo-ai", "shoujo-ai"), + Pair("Shounen", "shounen"), + Pair("Slice of Life", "slice-of-life"), + Pair("Sobrenatural", "sobrenatural"), + Pair("Superpoder", "superpoder"), + Pair("Suspense", "suspense"), + Pair("Vampiros", "vampiros"), + Pair("Vida Escolar", "vida-escolar") + ) + } +} diff --git a/src/pt/animefire/src/eu/kanade/tachiyomi/animeextension/pt/animefire/AnimeFire.kt b/src/pt/animefire/src/eu/kanade/tachiyomi/animeextension/pt/animefire/AnimeFire.kt new file mode 100644 index 000000000..51e61e10d --- /dev/null +++ b/src/pt/animefire/src/eu/kanade/tachiyomi/animeextension/pt/animefire/AnimeFire.kt @@ -0,0 +1,220 @@ +package eu.kanade.tachiyomi.animeextension.pt.animefire + +import android.app.Application +import android.content.SharedPreferences +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.animeextension.pt.animefire.extractors.AnimeFireExtractor +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.asObservableSuccess +import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.json.Json +import okhttp3.Headers +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import rx.Observable +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.lang.Exception + +class AnimeFire : ConfigurableAnimeSource, ParsedAnimeHttpSource() { + + override val name = "Anime Fire" + + override val baseUrl = "https://animefire.net" + + override val lang = "pt-BR" + + override val supportsLatest = true + + override val client: OkHttpClient = network.cloudflareClient + + private val json = Json { + ignoreUnknownKeys = true + } + + private val preferences: SharedPreferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + + override fun headersBuilder(): Headers.Builder = Headers.Builder() + .add("Referer", baseUrl) + .add("Accept-Language", ACCEPT_LANGUAGE) + + // ============================== Popular =============================== + override fun popularAnimeSelector() = latestUpdatesSelector() + override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/top-animes/$page") + override fun popularAnimeFromElement(element: Element) = latestUpdatesFromElement(element) + override fun popularAnimeNextPageSelector() = latestUpdatesNextPageSelector() + + // ============================== Episodes ============================== + override fun episodeListSelector(): String = "div.div_video_list > a" + override fun episodeListParse(response: Response) = super.episodeListParse(response).reversed() + + override fun episodeFromElement(element: Element): SEpisode { + val episode = SEpisode.create() + val url = element.attr("href") + episode.setUrlWithoutDomain(url) + episode.name = element.text() + episode.episode_number = try { + url.substringAfterLast("/").toFloat() + } catch (e: NumberFormatException) { 0F } + return episode + } + + // ============================ Video Links ============================= + override fun videoListParse(response: Response): List