chore: Remove even more dead sources (#1674)

This commit is contained in:
Claudemirovsky
2023-06-03 10:58:11 +00:00
committed by GitHub
parent 740b7ceda7
commit fc6fe13857
49 changed files with 0 additions and 1840 deletions

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,12 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'CARTOONS4U'
pkgNameSuffix = 'ar.cartons4u'
extClass = '.Cartoons4U'
extVersionCode = 3
libVersion = '13'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

View File

@ -1,188 +0,0 @@
package eu.kanade.tachiyomi.animeextension.ar.cartons4u
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.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.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
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
import java.lang.Exception
class Cartoons4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "CARTOONS4U"
override val baseUrl = "https://cartoons4u.net"
override val lang = "ar"
override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// Popular
override fun popularAnimeSelector(): String = "ul.MovieList.Rows.Alt li.TPostMv article.TPost a:has(div.Image)"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/category/movies/page/$page/")
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
// anime.thumbnail_url = "https:" + element.select("div.Image figure img").attr("data-src") // .replace("//", "")
anime.title = element.select("div.Title").text().replace("فيلم", "")
return anime
}
override fun popularAnimeNextPageSelector(): String = "div.wp-pagenavi a.next"
// episodes
override fun episodeListSelector() = "link[rel=canonical]"
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(element.attr("href"))
episode.name = element.ownerDocument()!!.select("header.Top h1").text()
return episode
}
// Video links
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val iframe = document.select("span.Button.TrLnk").attr("data-id")
val referer = response.request.url.toString()
val refererHeaders = Headers.headersOf("referer", referer)
val iframeResponse = client.newCall(GET(iframe, refererHeaders))
.execute().asJsoup()
return videosFromElement(iframeResponse.selectFirst(videoListSelector())!!)
}
override fun videoListSelector() = "a.button.is-success"
private fun videosFromElement(element: Element): List<Video> {
val vidURL = element.attr("abs:href")
val apiCall = client.newCall(POST(vidURL.replace("/v/", "/api/source/"))).execute().body.string()
val data = apiCall.substringAfter("\"data\":[").substringBefore("],")
val sources = data.split("\"file\":\"").drop(1)
val videoList = mutableListOf<Video>()
for (source in sources) {
val src = source.substringAfter("\"file\":\"").substringBefore("\"").replace("\\/", "/")
val quality = source.substringAfter("\"label\":\"").substringBefore("\"")
val video = Video(vidURL, quality, src)
videoList.add(video)
}
return videoList
}
override fun videoFromElement(element: Element) = throw Exception("not used")
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", null)
if (quality != null) {
val newList = mutableListOf<Video>()
var preferred = 0
for (video in this) {
if (video.quality.contains(quality)) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
}
return this
}
override fun videoUrlParse(document: Document) = throw Exception("not used")
// Search
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
// anime.thumbnail_url = element.select("div.Image figure img").attr("data-src").replace("//", "")
anime.title = element.select("div.Title").text().replace("فيلم", "")
return anime
}
override fun searchAnimeNextPageSelector(): String = "div.wp-pagenavi a.next"
override fun searchAnimeSelector(): String = "ul.MovieList.Rows.Alt li.TPostMv article.TPost a:has(div.Image)"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val url = "$baseUrl/page/$page/?s=$query".toHttpUrlOrNull()!!.newBuilder()
return GET(url.build().toString(), headers)
}
// Anime Details
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
val getThumbnail = document.select("figure.Objf img.lazy").attr("data-src")
val getThumbnail1 = document.select("figure.Image img").attr("data-lazy-src")
if (getThumbnail.startsWith("//")) {
anime.thumbnail_url = "https:" + getThumbnail
} else {
anime.thumbnail_url = getThumbnail1
}
anime.title = document.select("header.Top h1.Title").text()
anime.genre = document.select("p.Genre a").joinToString(", ") { it.text() }
anime.description = document.select("div.Description p:first-child").text()
anime.status = SAnime.COMPLETED
return anime
}
// Latest
override fun latestUpdatesNextPageSelector(): String? = throw Exception("Not used")
override fun latestUpdatesFromElement(element: Element): SAnime = throw Exception("Not used")
override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used")
override fun latestUpdatesSelector(): String = throw Exception("Not used")
// preferred quality settings
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p", "240p")
entryValues = arrayOf("1080", "720", "480", "360", "240")
setDefaultValue("1080")
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}
screen.addPreference(videoQualityPref)
}
}

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,17 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'شاهد فور يو'
pkgNameSuffix = 'ar.shahid4u'
extClass = '.Shahid4U'
extVersionCode = 9
libVersion = '13'
}
dependencies {
implementation(project(':lib-okru-extractor'))
implementation(project(':lib-dood-extractor'))
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

View File

@ -1,380 +0,0 @@
package eu.kanade.tachiyomi.animeextension.ar.shahid4u
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors.UQLoadExtractor
import eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors.VidBomExtractor
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.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.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.OkHttpClient
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
import java.lang.Exception
class Shahid4U : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "شاهد فور يو"
override val baseUrl = "https://shahed4u.agency"
override val lang = "ar"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// Popular
override fun popularAnimeSelector(): String = "div.glide-slides div.media-block"
override fun popularAnimeRequest(page: Int): Request = GET(if (page == 1)" $baseUrl/home2/" else baseUrl)
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.title = titleEdit(element.select("h3").text()).trim()
anime.thumbnail_url = element.select("a.image img").attr("data-src")
anime.setUrlWithoutDomain(element.select("a.fullClick").attr("href") + "watch/")
return anime
}
private fun titleEdit(title: String, details: Boolean = false): String {
return if (Regex("فيلم (.*?) مترجم").containsMatchIn(title)) {
Regex("فيلم (.*?) مترجم").find(title)!!.groupValues[1] + " (فيلم)" // افلام اجنبيه مترجمه
} else if (Regex("فيلم (.*?) مدبلج").containsMatchIn(title)) {
Regex("فيلم (.*?) مدبلج").find(title)!!.groupValues[1] + " (مدبلج)(فيلم)" // افلام اجنبيه مدبلجه
} else if (Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").containsMatchIn(title)) {
// افلام عربى
Regex("فيلم ([^a-zA-Z]+) ([0-9]+)").find(title)!!.groupValues[1] + " (فيلم)"
} else if (title.contains("مسلسل")) {
if (title.contains("الموسم") and details) {
val newTitle = Regex("مسلسل (.*?) الموسم (.*?) الحلقة ([0-9]+)").find(title)
return "${newTitle!!.groupValues[1]} (م.${newTitle.groupValues[2]})(${newTitle.groupValues[3]}ح)"
} else if (title.contains("الحلقة") and details) {
val newTitle = Regex("مسلسل (.*?) الحلقة ([0-9]+)").find(title)
return "${newTitle!!.groupValues[1]} (${newTitle.groupValues[2]}ح)"
} else {
Regex(if (title.contains("الموسم")) "مسلسل (.*?) الموسم" else "مسلسل (.*?) الحلقة").find(title)!!.groupValues[1] + " (مسلسل)"
}
} else if (title.contains("انمي")) {
return Regex(if (title.contains("الموسم"))"انمي (.*?) الموسم" else "انمي (.*?) الحلقة").find(title)!!.groupValues[1] + " (انمى)"
} else if (title.contains("برنامج")) {
Regex(if (title.contains("الموسم"))"برنامج (.*?) الموسم" else "برنامج (.*?) الحلقة").find(title)!!.groupValues[1] + " (برنامج)"
} else {
title
}
}
override fun popularAnimeNextPageSelector(): String = "div.paginate ul.page-numbers li.active + li a"
// episodes
private fun seasonsNextPageSelector() = "div.allseasonstab ul li"
override fun episodeListParse(response: Response): List<SEpisode> {
val episodes = mutableListOf<SEpisode>()
fun addEpisodeNew(url: String, type: String, title: String = "") {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(url)
if (type == "assembly") {
episode.name = title.replace("فيلم", "").trim()
} else if (type == "movie") {
episode.name = "مشاهدة"
} else {
episode.name = title
}
episodes.add(episode)
}
fun addEpisodes(response: Response) {
val document = response.asJsoup()
val url = response.request.url.toString()
if (url.contains("assemblies")) {
for (movie in document.select(searchAnimeSelector())) {
addEpisodeNew(
movie.select("a.fullClick").attr("href") + "watch/",
"assembly",
movie.select("h3").text(),
)
}
return
}
if (document.select("div.seasons--episodes").isNullOrEmpty()) {
// Movies
addEpisodeNew(url, "movie")
} else {
// Series
// look for what is wrong
for (season in document.select(seasonsNextPageSelector())) {
val seasonNum = season.text()
if (season.hasClass("active")) {
// get episodes from page
for (episode in document.select("ul.episodes-list li a")) {
addEpisodeNew(
episode.attr("href"),
"series",
seasonNum + " " + episode.text(),
)
}
} else {
// send request to get episodes
val seasonData = season.attr("data-id")
val refererHeaders = Headers.headersOf("referer", response.request.url.toString(), "x-requested-with", "XMLHttpRequest")
val requestBody = FormBody.Builder().add("season", seasonData).build()
val getEpisodes = client.newCall(POST("$baseUrl/wp-content/themes/Shahid4u-WP_HOME/Ajaxat/Single/Episodes.php", refererHeaders, requestBody)).execute().asJsoup()
for (episode in getEpisodes.select("li a")) {
addEpisodeNew(
episode.attr("href"),
"series",
seasonNum + " " + episode.text(),
)
}
}
}
}
}
addEpisodes(response)
return episodes
}
override fun episodeListSelector() = "link[rel=canonical]"
override fun episodeFromElement(element: Element): SEpisode = throw Exception("not used")
// Video links
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val videos = mutableListOf<Video>()
val servers = document.select(videoListSelector())
fun getUrl(v_id: String, i: String): String {
val refererHeaders = Headers.headersOf(
"referer",
response.request.url.toString(),
"x-requested-with",
"XMLHttpRequest",
"Content-Type",
"application/x-www-form-urlencoded; charset=UTF-8",
"user-agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.42",
)
val requestBody = FormBody.Builder().add("id", v_id).add("i", i).build()
val iframe = client.newCall(
POST(
"$baseUrl/wp-content/themes/Shahid4u-WP_HOME/Ajaxat/Single/Server.php",
refererHeaders,
requestBody,
),
).execute().asJsoup()
return iframe.select("iframe").attr("src")
}
for (server in servers) {
if (server.hasClass("active")) {
// special server
val videosFromURL = videosFromElement(client.newCall(GET(document.select("input[name=fserver]").`val`(), headers)).execute().asJsoup())
videos.addAll(videosFromURL)
} else if (server.text().contains("ok")) {
val videosFromURL = OkruExtractor(client).videosFromUrl(getUrl(server.attr("data-id"), server.attr("data-i")))
videos.addAll(videosFromURL)
} else if (server.text().contains("vidbom", ignoreCase = true) or server.text().contains("Vidshare", ignoreCase = true)) {
val videosFromURL = VidBomExtractor(client).videosFromUrl(getUrl(server.attr("data-id"), server.attr("data-i")))
videos.addAll(videosFromURL)
} else if (server.text().contains("dood", ignoreCase = true)) {
val videosFromURL = DoodExtractor(client).videoFromUrl(getUrl(server.attr("data-id"), server.attr("data-i")))
if (videosFromURL != null) videos.add(videosFromURL)
} else if (server.text().contains("uqload", ignoreCase = true)) {
val videosFromURL = UQLoadExtractor(client).videoFromUrl(getUrl(server.attr("data-id"), server.attr("data-i")), "Uqload: 720p")
if (videosFromURL != null) videos.add(videosFromURL)
}
}
return videos
}
override fun videoListSelector() = "ul.servers-list li.server--item"
private fun videosFromElement(document: Document): List<Video> {
val videoList = mutableListOf<Video>()
return try {
val scriptSelect = document.selectFirst("script:containsData(eval)")!!.data()
val serverPrefix =
scriptSelect.substringAfter("|net|cdn|amzn|").substringBefore("|rewind|icon|")
val sourceServer = "https://$serverPrefix.e-amzn-cdn.net"
val qualities = scriptSelect.substringAfter("|image|").substringBefore("|sources|")
.replace("||", "|").split("|")
qualities.forEachIndexed { i, q ->
if (i % 2 == 0) {
val id = qualities[i + 1]
val src = "$sourceServer/$id/v.mp4"
val video = Video(src, "Main: $q", src)
videoList.add(video)
}
}
videoList
} catch (e: Exception) {
videoList
}
}
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", null)
if (quality != null) {
val newList = mutableListOf<Video>()
var preferred = 0
for (video in this) {
if (video.quality.contains(quality)) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
}
return this
}
override fun videoUrlParse(document: Document) = throw Exception("not used")
override fun videoFromElement(element: Element) = throw Exception("not used")
// Search
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
var link = element.select("a.fullClick").attr("href")
anime.title = titleEdit(element.select("h3").text(), details = true).trim()
if (link.contains("assemblies")) {
anime.thumbnail_url = element.select("a.image img").attr("data-src")
} else {
anime.thumbnail_url = element.select("a.image img.imgInit").attr("data-image")
}
if (!link.contains("assemblies")) link += "watch/"
anime.setUrlWithoutDomain(link)
return anime
}
override fun searchAnimeNextPageSelector(): String = "div.paginate ul.page-numbers li.active + li a"
override fun searchAnimeSelector(): String = "div.MediaGrid div.media-block"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val url = if (query.isNotBlank()) {
"$baseUrl/page/$page/?s=$query"
} else {
val url = "$baseUrl/home2/page/$page"
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
is CategoryList -> {
if (filter.state > 0) {
val catQ = getCategoryList()[filter.state].query
val catUrl = "$baseUrl/$catQ/page/$page/"
return GET(catUrl, headers)
}
}
else -> {}
}
}
return GET(url, headers)
}
return GET(url, headers)
}
// Anime Details
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
// div.CoverSingle div.CoverSingleContent
anime.genre = document.select("div.SingleDetails li:contains(النوع) a").joinToString(", ") { it.text() }
anime.title = titleEdit(document.select("meta[property=og:title]").attr("content")).trim()
anime.author = document.select("div.SingleDetails li:contains(دولة) a").text()
anime.description = document.select("div.ServersEmbeds section.story").text().replace(document.select("meta[property=og:title]").attr("content"), "").replace(":", "").trim()
anime.status = SAnime.COMPLETED
if (anime.title.contains("سلسلة")) anime.thumbnail_url = document.selectFirst("img.imgInit")!!.attr("data-image")
return anime
}
// Latest
override fun latestUpdatesNextPageSelector(): String = "div.paginate ul.page-numbers li.active + li a"
override fun latestUpdatesFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.title = titleEdit(element.select("h3").text()).trim()
anime.thumbnail_url = element.select("a.image img").attr("data-image")
anime.setUrlWithoutDomain(element.select("a.fullClick").attr("href") + "watch/")
return anime
}
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/home2/page/$page/")
override fun latestUpdatesSelector(): String = "div.MediaGrid div.media-block"
// Filters
override fun getFilterList() = AnimeFilterList(
CategoryList(categoriesName),
)
private class CategoryList(categories: Array<String>) : AnimeFilter.Select<String>("الأقسام", categories)
private data class CatUnit(val name: String, val query: String)
private val categoriesName = getCategoryList().map {
it.name
}.toTypedArray()
private fun getCategoryList() = listOf(
CatUnit("اختر", ""),
CatUnit("افلام اجنبى", "category/%d8%a7%d9%81%d9%84%d8%a7%d9%85-%d8%a7%d8%ac%d9%86%d8%a8%d9%8a-3/"),
CatUnit("افلام انمى", "category/افلام-انمي/"),
CatUnit("افلام تركيه", "category/افلام-تركية/"),
CatUnit("افلام اسيويه", "category/افلام-اسيوية/"),
CatUnit("افلام هنديه", "category/افلام-هندي-1/"),
CatUnit("سلاسل افلام", "assemblies/"),
CatUnit("مسلسلات اجنبى", "category/مسلسلات-اجنبي-1/"),
CatUnit("مسلسلات انمى", "category/مسلسلات-انمي-4/"),
CatUnit("مسلسلات تركى", "category/مسلسلات-تركي-3/"),
CatUnit("مسلسلات اسيوى", "category/مسلسلات-اسيوي/"),
CatUnit("مسلسلات هندى", "category/مسلسلات-هندية/"),
)
// preferred quality settings
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p", "240p")
entryValues = arrayOf("1080", "720", "480", "360", "240")
setDefaultValue("1080")
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}
screen.addPreference(videoQualityPref)
}
}

View File

@ -1,19 +0,0 @@
package eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
class UQLoadExtractor(private val client: OkHttpClient) {
fun videoFromUrl(url: String, quality: String): Video? {
val document = client.newCall(GET(url)).execute().asJsoup()
val check = document.selectFirst("script:containsData(sources)")!!.data()
val videoUrl = check.substringAfter("sources: [\"").substringBefore("\"")
return if (check.contains("sources")) {
Video(url, quality, videoUrl)
} else {
Video(url, "no 1video", "https")
}
}
}

View File

@ -1,57 +0,0 @@
package eu.kanade.tachiyomi.animeextension.ar.shahid4u.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
class VidBomExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val doc = client.newCall(GET(url)).execute().asJsoup()
val script = doc.selectFirst("script:containsData(sources)")!!
val data = script.data().substringAfter("sources: [").substringBefore("],")
val sources = data.split("file:\"").drop(1)
val videoList = mutableListOf<Video>()
for (source in sources) {
val src = source.substringBefore("\"")
var quality = "Vidbom: " + source.substringAfter("label:\"").substringBefore("\"") // .substringAfter("format: '")
if (quality.length > 15) {
quality = "Vidshare: 480p"
}
val video = Video(src, quality, src)
videoList.add(video)
}
return videoList
/*Log.i("looool", "$js")
val json = JSONObject(js)
Log.i("looool", "$json")
val videoList = mutableListOf<Video>()
val jsonArray = json.getJSONArray("sources")
for (i in 0 until jsonArray.length()) {
val `object` = jsonArray.getJSONObject(i)
val videoUrl = `object`.getString("file")
Log.i("looool", videoUrl)
val quality = "Vidbom:" + `object`.getString("label")
videoList.add(Video(videoUrl, quality, videoUrl))
}
return videoList*/
/*if (jas.contains("sources")) {
val js = script.data()
val json = JSONObject(js)
val videoList = mutableListOf<Video>()
val jsonArray = json.getJSONArray("sources")
for (i in 0 until jsonArray.length()) {
val `object` = jsonArray.getJSONObject(i)
val videoUrl = `object`.getString("file")
Log.i("lol", videoUrl)
val quality = "Vidbom:" + `object`.getString("label")
videoList.add(Video(videoUrl, quality, videoUrl))
}
return videoList
} else {
val videoList = mutableListOf<Video>()
videoList.add(Video(url, "no 2video", null))
return videoList
}*/
}
}

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,18 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'FireAnime'
pkgNameSuffix = 'de.fireanime'
extClass = '.FireAnime'
extVersionCode = 7
libVersion = '13'
containsNsfw = true
}
dependencies {
implementation(project(':lib-dood-extractor'))
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,21 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.fireanime
object FAConstants {
const val LANG_SUB = "Sub"
const val LANG_DUB = "Dub"
val LANGS = arrayOf(LANG_SUB, LANG_DUB)
const val PREFERRED_SOURCE = "preferred_source"
const val PREFERRED_LANG = "preferred_sub"
const val SOURCE_SELECTION = "source_selection"
const val NAME_FIRECDN = "FireCDN"
const val NAME_DOOD = "Doodstream"
const val URL_FIRECDN = "https://firecdn"
const val URL_DOOD = "https://dood"
val SOURCE_NAMES = arrayOf(NAME_FIRECDN, NAME_DOOD)
val SOURCE_URLS = arrayOf(URL_FIRECDN, URL_DOOD)
}

View File

@ -1,314 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.fireanime
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.de.fireanime.dto.AbsSourceBaseDto
import eu.kanade.tachiyomi.animeextension.de.fireanime.dto.AnimeBaseDto
import eu.kanade.tachiyomi.animeextension.de.fireanime.dto.AnimeDetailsDto
import eu.kanade.tachiyomi.animeextension.de.fireanime.dto.AnimeDetailsWrapperDto
import eu.kanade.tachiyomi.animeextension.de.fireanime.dto.CdnSourceDto
import eu.kanade.tachiyomi.animeextension.de.fireanime.dto.EpisodeListingWrapperDto
import eu.kanade.tachiyomi.animeextension.de.fireanime.dto.EpisodeSourcesDto
import eu.kanade.tachiyomi.animeextension.de.fireanime.dto.HosterSourceDto
import eu.kanade.tachiyomi.animeextension.de.fireanime.dto.VideoLinkDto
import eu.kanade.tachiyomi.animeextension.de.fireanime.extractors.FireCdnExtractor
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.AnimeHttpSource
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
class FireAnime : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "FireAnime"
override val baseUrl = "https://api.fireani.me"
override val lang = "de"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.rateLimit(120, 1, TimeUnit.MINUTES)
.build()
private val json = Json {
isLenient = true
ignoreUnknownKeys = true
}
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ===== POPULAR ANIME =====
override fun popularAnimeRequest(page: Int): Request = POST(
"$baseUrl/api/public/airing",
body = FormBody.Builder()
.add("langs[0]", "de-DE")
.build(),
)
override fun popularAnimeParse(response: Response): AnimesPage = parseAnimeListJson(response, true)
// ===== LATEST ANIME =====
override fun latestUpdatesRequest(page: Int): Request = POST(
"$baseUrl/api/public/new",
body = FormBody.Builder()
.add("langs[0]", "de-DE")
.add("limit", "30")
.add("offset", (page - 1).toString())
.build(),
)
override fun latestUpdatesParse(response: Response): AnimesPage = parseAnimeListJson(response)
// ===== ANIME SEARCH =====
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = POST(
"$baseUrl/api/public/search",
body = FormBody.Builder()
.add("q", query)
.build(),
)
override fun searchAnimeParse(response: Response): AnimesPage = parseAnimeListJson(response, true)
// ===== ANIME LIST PARSING =====
private fun parseAnimeListJson(response: Response, singlePage: Boolean = false): AnimesPage {
val animes = json.decodeFromString(ListSerializer(AnimeBaseDto.serializer()), response.body.string())
.distinctBy { it.url }
return AnimesPage(animes.map { createAnime(it) }, animes.count() > 0 && !singlePage)
}
// ===== ANIME DETAILS =====
override fun animeDetailsRequest(anime: SAnime): Request = POST(
"$baseUrl/api/public/anime",
body = FormBody.Builder()
.add("url", anime.url)
.build(),
)
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
return client.newCall(animeDetailsRequest(anime))
.asObservableSuccess()
.map { response ->
animeDetailsParse(response, anime).apply { initialized = true }
}
}
override fun animeDetailsParse(response: Response): SAnime = throw UnsupportedOperationException("Not used")
private fun animeDetailsParse(response: Response, baseAnime: SAnime): SAnime {
val anime = json.decodeFromString(AnimeDetailsWrapperDto.serializer(), response.body.string()).response
return createAnime(baseAnime, anime)
}
// ===== CREATE ANIME =====
private fun createAnime(anime: AnimeBaseDto): SAnime {
return SAnime.create().apply {
title = anime.title
url = anime.url
thumbnail_url = "$baseUrl/api/get/img/" + anime.imgPoster + "-normal-poster.webp"
status = SAnime.UNKNOWN
}
}
private fun createAnime(baseAnime: SAnime, details: AnimeDetailsDto): SAnime {
return baseAnime.apply {
description = details.description
genre = "FSK ${details.fsk}, " + (if (details.votingDouble != null) "%.1f/5 ⭐, ".format(details.votingDouble) else "") + details.genres.joinToString(", ") { it.genre }
}
}
// ===== EPISODE =====
override fun episodeListRequest(anime: SAnime): Request = POST(
"$baseUrl/api/public/episodes",
body = FormBody.Builder()
.add("url", anime.url)
.build(),
)
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
return if (anime.status != SAnime.LICENSED) {
client.newCall(episodeListRequest(anime))
.asObservableSuccess()
.map { response ->
episodeListParse(response, anime.url)
}
} else {
Observable.error(Exception("Licensed - No episodes to show"))
}
}
override fun episodeListParse(response: Response): List<SEpisode> = throw UnsupportedOperationException("Not used")
private fun episodeListParse(response: Response, animeUrl: String): List<SEpisode> {
val episodes = json.decodeFromString(EpisodeListingWrapperDto.serializer(), response.body.string()).response
return episodes.mapIndexed { i, ep ->
SEpisode.create().apply {
episode_number = ep.episode.toFloat()
name = if (ep.title.startsWith("Episode")) ep.title else "Episode ${i + 1}: ${ep.title}"
url = animeUrl + (-1..i).joinToString("") { " " } // Add some spaces so that all episodes are shown
}
}.reversed()
}
// ===== VIDEO SOURCES =====
override fun videoListRequest(episode: SEpisode): Request = POST(
"$baseUrl/api/public/episode",
body = FormBody.Builder()
.add("url", episode.url.trim())
.add("ep", "%.0f".format(episode.episode_number))
.build(),
)
override fun videoListParse(response: Response): List<Video> {
val episode = json.decodeFromString(EpisodeSourcesDto.serializer(), response.body.string().substringAfter("\"ep\":").substringBefore(",\"next\""))
val videos = mutableListOf<Video>()
videos.addAll(getVideos(episode.cdns, "$baseUrl/api/public/cdn"))
videos.addAll(getVideos(episode.hosters, "$baseUrl/api/public/link"))
return videos
}
private fun getVideos(sources: List<AbsSourceBaseDto>, apiUrl: String): List<Video> {
return sources.mapNotNull { source ->
val linkRequest = POST(
apiUrl,
body = FormBody.Builder()
.add("id", source.id.toString())
.build(),
)
val link = json.decodeFromString(VideoLinkDto.serializer(), client.newCall(linkRequest).execute().body.string()).url
val sourceName = if (source is CdnSourceDto) FAConstants.NAME_FIRECDN else (source as HosterSourceDto).hoster
val lang = if (source.isSub == 1) FAConstants.LANG_SUB else FAConstants.LANG_DUB
val quality = "$sourceName, $lang"
val sourceSelection = preferences.getStringSet(FAConstants.SOURCE_SELECTION, FAConstants.SOURCE_NAMES.toSet())
fun isSource(source: String): Boolean = sourceName == source && sourceSelection?.contains(source) == true
when {
isSource(FAConstants.NAME_FIRECDN) -> {
FireCdnExtractor(client, json).videoFromUrl(link, quality)
}
isSource(FAConstants.NAME_DOOD) -> {
val video = try {
DoodExtractor(client).videoFromUrl(link, quality, false)
} catch (e: Exception) {
null
}
video
}
else -> null
}
}
}
override fun List<Video>.sort(): List<Video> {
val hoster = preferences.getString(FAConstants.PREFERRED_SOURCE, null)
val subPreference = preferences.getString(FAConstants.PREFERRED_LANG, FAConstants.LANG_SUB)!!
val hosterList = mutableListOf<Video>()
val otherList = mutableListOf<Video>()
if (hoster != null) {
for (video in this) {
if (video.url.contains(hoster)) {
hosterList.add(video)
} else {
otherList.add(video)
}
}
} else {
otherList += this
}
val newList = mutableListOf<Video>()
var preferred = 0
for (video in hosterList) {
if (video.quality.contains(subPreference)) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
for (video in otherList) {
if (video.quality.contains(subPreference)) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
}
// ===== PREFERENCES ======
@Suppress("UNCHECKED_CAST")
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val hosterPref = ListPreference(screen.context).apply {
key = FAConstants.PREFERRED_SOURCE
title = "Standard-Quelle"
entries = FAConstants.SOURCE_NAMES
entryValues = FAConstants.SOURCE_URLS
setDefaultValue(FAConstants.URL_FIRECDN)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}
val subPref = ListPreference(screen.context).apply {
key = FAConstants.PREFERRED_LANG
title = "Standardmäßig Sub oder Dub?"
entries = FAConstants.LANGS
entryValues = FAConstants.LANGS
setDefaultValue(FAConstants.LANG_SUB)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}
val subSelection = MultiSelectListPreference(screen.context).apply {
key = FAConstants.SOURCE_SELECTION
title = "Quellen auswählen"
entries = FAConstants.SOURCE_NAMES
entryValues = FAConstants.SOURCE_NAMES
setDefaultValue(FAConstants.SOURCE_NAMES.toSet())
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}
screen.addPreference(subPref)
screen.addPreference(hosterPref)
screen.addPreference(subSelection)
}
}

View File

@ -1,161 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.fireanime.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
abstract class AbsAnimeBaseDto {
abstract val url: String
abstract val title: String
abstract val season: Int?
abstract val part: Int?
abstract val imgPoster: String
abstract val imgWallpaper: String?
}
@Serializable
class AnimeBaseDto(
override val url: String,
override val title: String,
override val season: Int?,
override val part: Int?,
override val imgPoster: String,
override val imgWallpaper: String?,
) : AbsAnimeBaseDto()
@Serializable
data class AnimeDto(
val id: Int,
override val url: String,
override val title: String,
override val season: Int?,
override val part: Int?,
val episodes: Int,
override val imgPoster: String,
override val imgWallpaper: String?,
) : AbsAnimeBaseDto()
@Serializable
data class AnimeDetailsWrapperDto(
val response: AnimeDetailsDto,
)
@Serializable
data class AnimeDetailsDto(
val title: String,
val description: String,
val season: Int?,
val part: Int?,
val imgPoster: String,
val imgWallpaper: String?,
@SerialName("generes")
val genres: List<GenreDto>,
val tags: List<TagDto>,
val fsk: Int,
@SerialName("voting")
val votingStr: String?,
val votingDouble: Double? = votingStr?.toDoubleOrNull(),
)
@Serializable
data class AiringEpisodeDto(
override val url: String,
override val title: String,
val info: String,
val status: String,
override val season: Int?,
override val part: Int?,
val episode: Int,
val langId: Int,
val isSub: Int,
@SerialName("time")
val timeSeconds: Long,
val timeMillis: Long = timeSeconds * 1000,
override val imgPoster: String,
override val imgWallpaper: String?,
val imgThumb: String,
) : AbsAnimeBaseDto()
@Serializable
data class EpisodeListingWrapperDto(
val response: List<EpisodeListingDto>,
)
@Serializable
data class EpisodeListingDto(
val title: String,
val episode: Int,
val img: String,
val version: String,
)
@Serializable
data class EpisodeSourcesDto(
val id: Int,
val title: String,
val episode: Int,
val img: String,
@SerialName("links")
val hosters: List<HosterSourceDto>,
val cdns: List<CdnSourceDto>,
)
@Serializable
data class VideoLinkDto(
@SerialName("response")
val url: String,
)
abstract class AbsSourceBaseDto {
abstract val id: Int
abstract val isSub: Int
}
@Serializable
data class HosterSourceDto(
override val id: Int,
override val isSub: Int,
val hoster: String,
) : AbsSourceBaseDto()
@Serializable
data class CdnSourceDto(
override val id: Int,
override val isSub: Int,
) : AbsSourceBaseDto()
@Serializable
data class GenreDto(
val id: Int,
@SerialName("genere")
val genre: String,
)
@Serializable
data class TagDto(
val id: Int,
val name: String,
val description: String,
val category: String,
val isAdult: Int,
)
@Serializable
data class FireCdnFileDto(
val proxy: String,
val file: String,
)

View File

@ -1,30 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.fireanime.extractors
import eu.kanade.tachiyomi.animeextension.de.fireanime.dto.FireCdnFileDto
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import kotlinx.serialization.json.Json
import okhttp3.FormBody
import okhttp3.OkHttpClient
class FireCdnExtractor(private val client: OkHttpClient, private val json: Json) {
fun videoFromUrl(url: String, quality: String): Video? {
// Check if video is available. It is faster than waiting for the api response status
if (client.newCall(GET(url)).execute().body.string().contains("still converting")) {
return null
}
val fileName = url.split("/").last()
val fileRequest = POST(
"https://firecdn.cc/api/stream/deploy",
body = FormBody.Builder()
.add("file", fileName)
.build(),
)
val file = json.decodeFromString(FireCdnFileDto.serializer(), client.newCall(fileRequest).execute().body.string())
val videoUrl = "${file.proxy.replace("\\", "")}/share/${file.file}/apple.mp4"
return Video(url, quality, videoUrl)
}
}

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,13 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Index1763686211'
pkgNameSuffix = 'en.index1763686211'
extClass = '.Index1763686211'
extVersionCode = 1
libVersion = '13'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,277 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.index1763686211
import android.app.Application
import android.content.SharedPreferences
import android.util.Base64
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.asObservableSuccess
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.HttpUrl.Companion.toHttpUrl
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 uy.kohesive.injekt.injectLazy
class Index1763686211 : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Index - 176.36.86.211"
override val baseUrl = Base64.decode("aHR0cDovLzE3Ni4zNi44Ni4yMTEvdmlkZW8vYW5pbWU=", Base64.DEFAULT).toString(Charsets.UTF_8)
private val videoFormats = arrayOf(".mkv", ".mp4", ".avi")
override val lang = "en"
override val supportsLatest = false
private val json: Json by injectLazy()
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int): Request = GET(baseUrl)
override fun popularAnimeParse(response: Response): AnimesPage {
val document = response.asJsoup()
val badNames = arrayOf("../", "gifs/")
val animeList = mutableListOf<SAnime>()
document.select(popularAnimeSelector()).forEach {
val name = it.text()
if (name in badNames) return@forEach
if (videoFormats.any { t -> name.endsWith(t) }) {
val anime = SAnime.create()
anime.title = videoFormats.fold(name) { acc, suffix -> acc.removeSuffix(suffix) }
anime.setUrlWithoutDomain(
LinkData(
"single",
"$baseUrl/${it.attr("href")}",
it.nextSibling()?.toString()?.substringAfterLast(" ")?.trim(),
).toJsonString(),
)
animeList.add(anime)
} else if (name.endsWith("/")) {
val anime = SAnime.create()
anime.title = name.removeSuffix("/")
anime.setUrlWithoutDomain(
LinkData(
"multi",
"/${it.attr("href")}",
).toJsonString(),
)
animeList.add(anime)
} else { }
}
return AnimesPage(animeList, false)
}
override fun popularAnimeSelector(): String = "pre > a"
override fun popularAnimeNextPageSelector(): String? = null
override fun popularAnimeFromElement(element: Element): SAnime = throw Exception("Not used")
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used")
override fun latestUpdatesSelector(): String = throw Exception("Not used")
override fun latestUpdatesNextPageSelector(): String = throw Exception("Not used")
override fun latestUpdatesFromElement(element: Element): SAnime = throw Exception("Not used")
// =============================== Search ===============================
override fun fetchSearchAnime(
page: Int,
query: String,
filters: AnimeFilterList,
): Observable<AnimesPage> {
return Observable.defer {
try {
client.newCall(searchAnimeRequest(page, query, filters)).asObservableSuccess()
} catch (e: NoClassDefFoundError) {
// RxJava doesn't handle Errors, which tends to happen during global searches
// if an old extension using non-existent classes is still around
throw RuntimeException(e)
}
}
.map { response ->
searchAnimeParse(response, query)
}
}
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = popularAnimeRequest(page)
private fun searchAnimeParse(response: Response, query: String): AnimesPage {
val document = response.asJsoup()
val badNames = arrayOf("../", "gifs/")
val animeList = mutableListOf<SAnime>()
document.select(popularAnimeSelector()).forEach {
val name = it.text()
if (name in badNames || !name.contains(query, ignoreCase = true)) return@forEach
if (videoFormats.any { t -> name.endsWith(t) }) {
val anime = SAnime.create()
anime.title = videoFormats.fold(name) { acc, suffix -> acc.removeSuffix(suffix) }
anime.setUrlWithoutDomain(
LinkData(
"single",
"$baseUrl/${it.attr("href")}",
it.nextSibling()?.toString()?.substringAfterLast(" ")?.trim(),
).toJsonString(),
)
animeList.add(anime)
} else if (name.endsWith("/")) {
val anime = SAnime.create()
anime.title = name.removeSuffix("/")
anime.setUrlWithoutDomain(
LinkData(
"multi",
"/${it.attr("href")}",
).toJsonString(),
)
animeList.add(anime)
} else { }
}
return AnimesPage(animeList, false)
}
override fun searchAnimeSelector(): String = throw Exception("Not used")
override fun searchAnimeNextPageSelector(): String = throw Exception("Not used")
override fun searchAnimeFromElement(element: Element): SAnime = throw Exception("Not used")
// =========================== Anime Details ============================
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
return Observable.just(anime)
}
override fun animeDetailsParse(document: Document): SAnime = throw Exception("Not used")
// ============================== Episodes ==============================
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
val episodeList = mutableListOf<SEpisode>()
val parsed = json.decodeFromString<LinkData>(anime.url)
var counter = 1
if (parsed.type == "single") {
val episode = SEpisode.create()
val size = if (parsed.info == null) {
""
} else {
" - ${parsed.info}"
}
episode.name = "${parsed.url.toHttpUrl().pathSegments.last()}$size"
episode.url = parsed.url
episode.episode_number = 1F
episodeList.add(episode)
} else if (parsed.type == "multi") {
fun traverseDirectory(url: String) {
val doc = client.newCall(GET(url)).execute().asJsoup()
doc.select("a").forEach { link ->
val href = link.attr("href")
val text = link.text()
if (href.isNotBlank() && text != "../") {
val fullUrl = url + href
if (fullUrl.endsWith("/")) {
traverseDirectory(fullUrl)
}
if (videoFormats.any { t -> fullUrl.endsWith(t) }) {
val episode = SEpisode.create()
val paths = fullUrl.toHttpUrl().pathSegments
val season = if (paths.size == 4) {
""
} else {
"[${paths[3]}] "
}
val extraInfo = if (paths.size > 5) {
paths.subList(4, paths.size - 1).joinToString("/")
} else {
""
}
val size = link.nextSibling()?.toString()?.substringAfterLast(" ")?.trim()
episode.name = "${season}Episode ${videoFormats.fold(paths.last()) { acc, suffix -> acc.removeSuffix(suffix) }}${if (size == null) "" else " - $size"}"
episode.url = fullUrl
episode.scanlator = extraInfo
episode.episode_number = counter.toFloat()
counter++
episodeList.add(episode)
}
}
}
}
traverseDirectory(baseUrl + parsed.url)
}
return Observable.just(episodeList.reversed())
}
override fun episodeListParse(response: Response): List<SEpisode> = throw Exception("Not used")
override fun episodeListSelector(): String = throw Exception("Not Used")
override fun episodeFromElement(element: Element): SEpisode = throw Exception("Not used")
// ============================ Video Links =============================
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
return Observable.just(listOf(Video(episode.url, "Video", episode.url)))
}
override fun videoFromElement(element: Element): Video = throw Exception("Not Used")
override fun videoListSelector(): String = throw Exception("Not Used")
override fun videoUrlParse(document: Document): String = throw Exception("Not Used")
// ============================= Utilities ==============================
@Serializable
data class LinkData(
val type: String,
val url: String,
val info: String? = null,
)
private fun LinkData.toJsonString(): String {
return json.encodeToString(this)
}
override fun setupPreferenceScreen(screen: PreferenceScreen) { }
}

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,14 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'AnimeVibe'
pkgNameSuffix = 'pt.animevibe'
extClass = '.AnimeVibe'
extVersionCode = 1
libVersion = '13'
containsNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

View File

@ -1,240 +0,0 @@
package eu.kanade.tachiyomi.animeextension.pt.animevibe
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.AnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Protocol
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class AnimeVibe : AnimeHttpSource() {
override val name = "AnimeVibe"
override val baseUrl = "https://animevibe.cc"
override val lang = "pt-BR"
override val supportsLatest = false
private val format = Json { ignoreUnknownKeys = true }
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("Referer", "$baseUrl/")
.add("Accept", "application/json")
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.rateLimit(5, 1, TimeUnit.SECONDS)
.addInterceptor(::requestIntercept)
.build()
private val requestCache: MutableMap<String, String> = mutableMapOf()
private fun requestIntercept(chain: Interceptor.Chain): Response {
val url = chain.request().url.toString()
if (!url.contains("data?page=medias")) return chain.proceed(chain.request())
if (requestCache.containsKey(url)) {
val body = requestCache[url]!!.toResponseBody("application/json; charset=UTF-8".toMediaTypeOrNull())
return Response.Builder()
.code(200)
.protocol(Protocol.HTTP_1_1)
.request(chain.request())
.message("OK")
.body(body)
.build()
}
val response = chain.proceed(chain.request())
val contentType = response.body.contentType()
val body = response.body.string()
requestCache[url] = body
return response.newBuilder()
.body(body.toResponseBody(contentType))
.build()
}
private fun parseDate(dateStr: String): Long {
return runCatching { DATE_FORMATTER.parse(dateStr)?.time }
.getOrNull() ?: 0L
}
private fun getAnimeTitle(anime: AnimeVibeAnimeDto): String = (anime.title["romaji"] ?: anime.title["english"] ?: anime.title["native"]!!) + (if (anime.audio == "Dublado") " (DUBLADO)" else "")
private fun getAnimeFromObject(anime: AnimeVibeAnimeDto): SAnime = SAnime.create().apply {
initialized = true
thumbnail_url = "$CDN_URL/img/animes/${anime.slug}-large.webp"
url = "$baseUrl/anime/${anime.id}"
title = getAnimeTitle(anime)
description = anime.description
genre = anime.genres?.joinToString(", ")
status = when (anime.status) {
"Completo" -> SAnime.COMPLETED
"Em lançamento" -> SAnime.ONGOING
else -> SAnime.UNKNOWN
}
}
override fun popularAnimeRequest(page: Int): Request {
val headers = headersBuilder().build()
return GET("$baseUrl/$API_PATH/data?page=medias", headers)
}
override fun popularAnimeParse(response: Response): AnimesPage {
val animes = format.decodeFromString<AnimeVibePopularDto>(response.body.string())
if (animes.data.isNullOrEmpty()) {
return AnimesPage(emptyList(), hasNextPage = false)
}
val animeList = animes.data
.sortedByDescending { it.views }
.take(50)
.map(::getAnimeFromObject)
return AnimesPage(animeList, hasNextPage = false)
}
override fun animeDetailsRequest(anime: SAnime): Request {
val id = anime.url.substringAfter("/anime/")
.substringBefore("/")
val headers = headersBuilder()
.add("X-id", id)
.build()
return GET("$baseUrl/$API_PATH/data?page=medias", headers)
}
override fun animeDetailsParse(response: Response): SAnime {
val animes = format.decodeFromString<AnimeVibePopularDto>(response.body.string())
if (animes.data.isNullOrEmpty()) throw Exception(COULD_NOT_PARSE_ANIME)
val id = response.request.header("X-id")!!.toInt()
val anime = animes.data.find { it.id == id } ?: throw Exception(COULD_NOT_PARSE_ANIME)
return getAnimeFromObject(anime)
}
override fun episodeListRequest(anime: SAnime): Request {
val id = anime.url.substringAfter("/anime/")
.substringBefore("/")
return GET("$baseUrl/$API_PATH/data?page=episode&mediaID=$id")
}
override fun episodeListParse(response: Response): List<SEpisode> {
val episodes = format.decodeFromString<AnimeVibeEpisodeListDto>(response.body.string())
if (episodes.data.isNullOrEmpty()) return emptyList()
return episodes.data
.map {
SEpisode.create().apply {
url = "$baseUrl/anime/${it.mediaID}?episode=${it.number}"
name = "Episódio ${it.number.toInt()}"
date_upload = parseDate(it.datePublished)
episode_number = it.number
}
}
.reversed()
}
override fun videoListRequest(episode: SEpisode): Request {
val id = episode.url.substringAfter("/anime/")
.substringBefore("?")
val headers = headersBuilder()
.add("X-mediaid", id)
.add("X-number", episode.episode_number.toString())
.add("X-url", episode.url)
.build()
return GET("$baseUrl/$API_PATH/data?page=episode&mediaID=$id", headers)
}
private fun getVideoFromSource(source: String): List<Video> {
return if (source.startsWith('/')) {
val url = "$VIDEO_URL$source"
listOf(Video(url, source.substringBeforeLast('/').substringAfterLast('/').uppercase(), url))
} else if (source.startsWith("https://www.blogger.com/")) {
val headers = headersBuilder()
.add("User-Agent", USER_AGENT)
.build()
val response = client.newCall(GET(source, headers)).execute()
val streams = response.body.string().substringAfter("\"streams\":[").substringBefore("]")
return streams.split("},")
.map {
val url = it.substringAfter("{\"play_url\":\"").substringBefore('"')
val quality = when (it.substringAfter("\"format_id\":").substringBefore("}")) {
"18" -> "360p"
"22" -> "720p"
else -> "Unknown Resolution"
}
Video(url, quality, url, null, headers)
}
} else {
throw Exception("UNKOWN VIDEO SOURCE")
}
}
override fun videoListParse(response: Response): List<Video> {
val number = response.request.header("X-number")!!.toFloat()
val episodes = format.decodeFromString<AnimeVibeEpisodeListDto>(response.body.string())
if (episodes.data.isNullOrEmpty()) throw Exception("NO DATA ${response.request.header("X-mediaid")} ${response.request.header("X-url")}")
val episode = episodes.data.find { it.number == number } ?: throw Exception("NO EPISODE $number")
return episode.videoSource
.flatMap(::getVideoFromSource)
.reversed()
}
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val headers = headersBuilder()
.add("X-page", page.toString())
.add("X-query", query)
.build()
return GET("$baseUrl/$API_PATH/data?page=medias", headers)
}
override fun searchAnimeParse(response: Response): AnimesPage {
val query = response.request.header("X-query")!!
val animes = format.decodeFromString<AnimeVibePopularDto>(response.body.string())
if (animes.data.isNullOrEmpty()) {
return AnimesPage(emptyList(), hasNextPage = false)
}
val animeList = animes.data
.filter { getAnimeTitle(it).contains(query, ignoreCase = true) }
.sortedByDescending { it.views }
.map(::getAnimeFromObject)
return AnimesPage(animeList, hasNextPage = false)
}
override fun latestUpdatesRequest(page: Int): Request {
throw UnsupportedOperationException("Not used.")
}
override fun latestUpdatesParse(response: Response): AnimesPage {
throw UnsupportedOperationException("Not used.")
}
companion object {
private const val API_PATH = "animevibe/api/v1"
private const val CDN_URL = "https://animefire.net"
private const val VIDEO_URL = "https://akumaharu.org"
// blogger.com videos needs an user agent to work
private const val USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
private const val COULD_NOT_PARSE_ANIME = "Ocorreu um erro ao obter as informações do anime."
private val DATE_FORMATTER by lazy {
SimpleDateFormat("yyyy-MM-ddTHH:mm:ss", Locale.ENGLISH)
}
}
}

View File

@ -1,69 +0,0 @@
package eu.kanade.tachiyomi.animeextension.pt.animevibe
import kotlinx.serialization.Serializable
typealias AnimeVibePopularDto = AnimeVibeResultDto<List<AnimeVibeAnimeDto>>
typealias AnimeVibeEpisodeListDto = AnimeVibeResultDto<List<AnimeVibeEpisodeDto>>
@Serializable
data class AnimeVibeResultDto<T>(
val data: T? = null,
)
/*
id: 26673,
slug: 'kyou-no-asuka-show',
type: 'Anime',
audio: 'Legendado',
score: 6.3,
title: { native: '今日のあすかショー', romaji: 'Kyou no Asuka Show', english: null },
views: 0,
format: 'Ona',
genres: [ 'Comédia', 'Ecchi', 'Seinen' ],
season: 'Verão',
source: 'Manga',
status: 'Completo',
studios: [ 'Lantis', 'Half H.P Studio' ],
duration: '3 min',
episodes: 20,
synonyms: [ "Today's Asuka show" ],
producers: [ 'SILVER LINK.' ],
startDate: { day: 3, year: 2012, month: 8 },
description: `Asuka é uma garota bonita e sem noção do ensino médio que costuma fazer coisas, com toda inocência, que parecem sexualmente sugestivas para os homens ao seu redor. Cada capítulo de "Today's Asuka Show" apresenta uma situação diferente e embaraçosa. \n` +
'\n',
countryOfOrigin: 'JP'
*/
@Serializable
data class AnimeVibeAnimeDto(
val id: Int = -1,
val slug: String = "",
val audio: String = "",
val title: Map<String, String?> = emptyMap(),
val views: Int = -1,
val genres: List<String>? = emptyList(),
val status: String? = "",
val description: String? = "",
)
/*
{
audio: 'Legendado',
title: 'Koharu Biyori',
number: 3,
mediaID: 27476,
videoSource: [
'https://www.blogger.com/video.g?token=AD6v5dyxFaNmvMsasiRlDr8PiwoMlF7j4ykPewpKr_uRyAPZifCqlt0nj5D1oOgI9bjhbvKBO0CRwDUTXg-U81pRt6FBuj_GN6ynwPY5bwtYyAeURxT1HrB4MWxwr70mIP1m-8NHGrNs'
],
datePublished: '2022-01-28T21:41:18.366Z'
}
*/
@Serializable
data class AnimeVibeEpisodeDto(
val title: String = "",
val number: Float = -1f,
val mediaID: Int = -1,
val videoSource: List<String> = emptyList(),
val datePublished: String = "",
)