diff --git a/src/pt/animeq/build.gradle b/src/pt/animeq/build.gradle new file mode 100644 index 0000000000..4c1377545c --- /dev/null +++ b/src/pt/animeq/build.gradle @@ -0,0 +1,12 @@ +ext { + extName = 'AnimeQ' + extClass = '.AnimeQ' + extVersionCode = 1 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" + +dependencies { + implementation(project(':lib:blogger-extractor')) +} diff --git a/src/pt/animeq/res/mipmap-hdpi/ic_launcher.png b/src/pt/animeq/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..6d50fcafc4 Binary files /dev/null and b/src/pt/animeq/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/pt/animeq/res/mipmap-mdpi/ic_launcher.png b/src/pt/animeq/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..771d991558 Binary files /dev/null and b/src/pt/animeq/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/pt/animeq/res/mipmap-xhdpi/ic_launcher.png b/src/pt/animeq/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..e0bbb3f5c8 Binary files /dev/null and b/src/pt/animeq/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/pt/animeq/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/animeq/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ce9293eb83 Binary files /dev/null and b/src/pt/animeq/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/pt/animeq/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/animeq/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..2cb4eb5d3d Binary files /dev/null and b/src/pt/animeq/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/pt/animeq/src/eu/kanade/tachiyomi/animeextension/pt/doramogo/AnimeQ.kt b/src/pt/animeq/src/eu/kanade/tachiyomi/animeextension/pt/doramogo/AnimeQ.kt new file mode 100644 index 0000000000..5116cba0ab --- /dev/null +++ b/src/pt/animeq/src/eu/kanade/tachiyomi/animeextension/pt/doramogo/AnimeQ.kt @@ -0,0 +1,254 @@ +package eu.kanade.tachiyomi.animeextension.pt.animeq + +import android.app.Application +import android.util.Base64 +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.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.lib.bloggerextractor.BloggerExtractor +import eu.kanade.tachiyomi.network.GET +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 AnimeQ : ConfigurableAnimeSource, ParsedAnimeHttpSource() { + + override val name = "AnimeQ" + + override val baseUrl = "https://animeq.blog" + + 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/160679", headers) + + override fun popularAnimeSelector() = "div.widget_block:contains(Acessados) a" + + override fun popularAnimeFromElement(element: Element) = SAnime.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + title = element.selectFirst("a img")!!.attr("title") + thumbnail_url = element.selectFirst("a img")!!.attr("src") + } + + override fun popularAnimeNextPageSelector() = null + + // =============================== Latest =============================== + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/page/$page", headers) + + override fun latestUpdatesSelector() = "div.ContainerEps > article.EpsItem > a" + + override fun latestUpdatesFromElement(element: Element) = SAnime.create().apply { + setUrlWithoutDomain(element.attr("href")) + title = element.attr("title") + val img = element.selectFirst("div.EpsItemImg > img") + thumbnail_url = img?.attr("data-src")?.ifEmpty { img?.attr("src") } + } + + override fun latestUpdatesNextPageSelector() = "div.ContainerEps a.next.page-numbers" + + // =============================== Search =============================== + 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() = "div.ContainerEps > article.AniItem > a" + + override fun searchAnimeFromElement(element: Element) = SAnime.create().apply { + setUrlWithoutDomain(element.attr("href")) + title = element.attr("title").substringBefore(" – Todos os Epis") + val img = element.selectFirst("div.AniItemImg > img") + thumbnail_url = img?.attr("data-src")?.ifEmpty { img?.attr("src") } + } + + override fun searchAnimeNextPageSelector() = "div.ContainerEps a.next.page-numbers" + + // =========================== Anime Details ============================ + override fun animeDetailsParse(document: Document): SAnime { + val doc = getRealDoc(document) + + return SAnime.create().apply { + setUrlWithoutDomain(doc.location()) + title = doc.title().substringBefore(" – Todos os Epis") + val img = doc.selectFirst("#capaAnime > img") + thumbnail_url = img?.attr("data-src")?.ifEmpty { img?.attr("src") } + description = doc.selectFirst("#sinopse2")?.text() + + with(doc.selectFirst("div.boxAnimeSobre")!!) { + artist = getInfo("Estúdio") + author = getInfo("Autor") ?: getInfo("Diretor") + genre = getInfo("Tags") + status = parseStatus(getInfo("Episódios")) + } + } + } + + // ============================== Episodes ============================== + override fun episodeListParse(response: Response): List { + return getRealDoc(response.asJsoup()) + .select(episodeListSelector()) + .map(::episodeFromElement) + .reversed() + } + + override fun episodeListSelector() = "#lAnimes a" + + override fun episodeFromElement(element: Element) = SEpisode.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + element.selectFirst("a")!!.attr("title") + .substringBeforeLast(" – Final") + .substringAfterLast(" ").let { + name = it.trim() + episode_number = name.toFloatOrNull() ?: 1F + } + } + + // ============================ Video Links ============================= + override fun videoListParse(response: Response): List