Yeet onepace and netfilm (#1637)

This commit is contained in:
Claudemirovsky 2023-05-22 22:05:11 +00:00 committed by GitHub
parent ffdd988eb1
commit 055c99be5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 0 additions and 631 deletions

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 = 'NetFilm'
pkgNameSuffix = 'all.netfilm'
extClass = '.NetFilm'
extVersionCode = 2
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.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@ -1,85 +0,0 @@
package eu.kanade.tachiyomi.animeextension.all.netfilm
import kotlinx.serialization.Serializable
@Serializable
data class CategoryResponse(
val data: List<CategoryData>,
) {
@Serializable
data class CategoryData(
val coverVerticalUrl: String,
val domainType: Int,
val id: String,
val name: String,
val sort: String,
)
}
@Serializable
data class AnimeInfoResponse(
val data: InfoData,
) {
@Serializable
data class InfoData(
val coverVerticalUrl: String,
val episodeVo: List<EpisodeInfo>,
val id: String,
val introduction: String,
val name: String,
val category: Int,
val tagList: List<IdInfo>,
) {
@Serializable
data class EpisodeInfo(
val id: Int,
val seriesNo: Float,
)
@Serializable
data class IdInfo(
val name: String,
)
}
}
@Serializable
data class SearchResponse(
val data: InfoData,
) {
@Serializable
data class InfoData(
val results: List<CategoryResponse.CategoryData>,
)
}
@Serializable
data class EpisodeResponse(
val data: EpisodeData,
) {
@Serializable
data class EpisodeData(
val qualities: List<Quality>,
val subtitles: List<Subtitle>,
) {
@Serializable
data class Quality(
val quality: Int,
val url: String,
)
@Serializable
data class Subtitle(
val language: String,
val url: String,
)
}
}
@Serializable
data class LinkData(
val category: String,
val id: String,
val url: String,
val episodeId: String? = null,
)

View File

@ -1,279 +0,0 @@
package eu.kanade.tachiyomi.animeextension.all.netfilm
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 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 rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import kotlin.math.ceil
import kotlin.math.floor
class NetFilm : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "NetFilm"
override val baseUrl = "https://net-film.vercel.app/api"
private val hostName = baseUrl.toHttpUrl().host
override val lang = "all"
private var sort = ""
override val supportsLatest = true
private val json: Json by injectLazy()
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override val client: OkHttpClient = network.cloudflareClient
private val newHeaders = headers.newBuilder()
.add("Accept", "application/json, text/plain, */*")
.add("appid", "eyJhbGciOiJIUzI1NiJ9")
.add("Host", hostName)
.add("Sec-Fetch-Dest", "empty")
.add("Sec-Fetch-Mode", "cors")
.add("Sec-Fetch-Site", "same-origin")
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int): Request {
if (page == 1) sort = ""
val popHeaders = newHeaders.add("Referer", "https://$hostName/explore").build()
return GET("$baseUrl/category?area=&category=1&order=count&params=COMIC&size=30&sort=$sort&subtitles=&year=", headers = popHeaders)
}
override fun popularAnimeParse(response: Response): AnimesPage {
val parsed = json.decodeFromString<CategoryResponse>(response.body.string())
if (parsed.data.isEmpty()) {
return AnimesPage(emptyList(), false)
}
val animeList = parsed.data.map { ani ->
SAnime.create().apply {
title = ani.name
thumbnail_url = ani.coverVerticalUrl
setUrlWithoutDomain(
LinkData(
ani.domainType.toString(),
ani.id,
response.request.url.toString(),
).toJsonString(),
)
}
}
sort = parsed.data.last().sort
return AnimesPage(animeList, animeList.size == 30)
}
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request {
if (page == 1) sort = ""
val latestHeaders = newHeaders.add("Referer", "https://$hostName/explore").build()
return GET("$baseUrl/category?area=&category=1&order=up&params=COMIC&size=30&sort=$sort&subtitles=&year=", headers = latestHeaders)
}
override fun latestUpdatesParse(response: Response): AnimesPage = popularAnimeParse(response)
// =============================== Search ===============================
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
if (page == 1) sort = ""
return if (query.isNotEmpty()) {
val searchHeaders = newHeaders.add("Referer", "$baseUrl/search?keyword=$query&size=30").build()
GET("$baseUrl/search?keyword=$query&size=30", headers = searchHeaders)
} else {
val pageList = filters.find { it is SubPageFilter } as SubPageFilter
val pageHeaders = newHeaders.add("Referer", "https://$hostName/explore").build()
GET("$baseUrl${pageList.toUriPart()}&sort=$sort&subtitles=&year=", headers = pageHeaders)
}
}
override fun searchAnimeParse(response: Response): AnimesPage {
val url = response.request.url.encodedPath
return if (url.startsWith("/api/category")) {
popularAnimeParse(response)
} else {
val parsed = json.decodeFromString<SearchResponse>(response.body.string())
if (parsed.data.results.isEmpty()) {
return AnimesPage(emptyList(), false)
}
val animeList = parsed.data.results.map { ani ->
SAnime.create().apply {
title = ani.name
thumbnail_url = ani.coverVerticalUrl
setUrlWithoutDomain(
LinkData(
ani.domainType.toString(),
ani.id,
response.request.url.toString(),
).toJsonString(),
)
}
}
sort = parsed.data.results.last().sort
AnimesPage(animeList, animeList.size == 30)
}
}
// ============================== FILTERS ===============================
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
AnimeFilter.Header("Text search ignores filters"),
SubPageFilter(),
)
private class SubPageFilter : UriPartFilter(
"Sub Page",
arrayOf(
Pair("Popular Movie", "/category?area=&category=1&order=count&params=MOVIE,TVSPECIAL&size=30"),
Pair("Recent Movie", "/category?area=&category=1&order=up&params=MOVIE,TVSPECIAL&size=30"),
Pair("Popular TV Series", "/category?area=&category=1&order=count&params=TV,SETI,MINISERIES,VARIETY,TALK,DOCUMENTARY&size=30"),
Pair("Recent TV Series", "/category?area=&category=1&order=up&params=TV,SETI,MINISERIES,VARIETY,TALK,DOCUMENTARY&size=30"),
Pair("Popular Anime", "/category?area=&category=1&order=count&params=COMIC&size=30"),
Pair("Recent Anime", "/category?area=&category=1&order=up&params=COMIC&size=30"),
),
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
// =========================== Anime Details ============================
override fun fetchAnimeDetails(anime: SAnime): Observable<SAnime> {
val parsed = json.decodeFromString<LinkData>(anime.url)
val detailsHeader = newHeaders.add("Referer", parsed.url).build()
val resp = client.newCall(
GET("$baseUrl/detail?category=${parsed.category}&id=${parsed.id}", headers = detailsHeader),
).execute()
val data = json.decodeFromString<AnimeInfoResponse>(resp.body.string()).data
return Observable.just(
anime.apply {
title = data.name
thumbnail_url = data.coverVerticalUrl
description = data.introduction
genre = data.tagList.joinToString(", ") { it.name }
},
)
}
override fun animeDetailsParse(response: Response): SAnime = throw Exception("Not used")
// ============================== Episodes ==============================
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
val parsed = json.decodeFromString<LinkData>(anime.url)
val episodeHeader = newHeaders.add("Referer", parsed.url).build()
val resp = client.newCall(
GET("$baseUrl/detail?category=${parsed.category}&id=${parsed.id}", headers = episodeHeader),
).execute()
val data = json.decodeFromString<AnimeInfoResponse>(resp.body.string()).data
val episodeList = data.episodeVo.map { ep ->
val formattedEpNum = if (floor(ep.seriesNo) == ceil(ep.seriesNo)) {
ep.seriesNo.toInt()
} else {
ep.seriesNo
}
SEpisode.create().apply {
episode_number = ep.seriesNo
setUrlWithoutDomain(
LinkData(
data.category.toString(),
data.id,
"$baseUrl/detail?category=${parsed.category}&id=${parsed.id}",
ep.id.toString(),
).toJsonString(),
)
name = "Episode $formattedEpNum"
}
}
return Observable.just(episodeList.reversed())
}
override fun episodeListParse(response: Response): List<SEpisode> = throw Exception("Not used")
// ============================ Video Links =============================
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
val parsed = json.decodeFromString<LinkData>(episode.url)
val videoHeaders = newHeaders.add("Referer", parsed.url).build()
val resp = client.newCall(
GET("$baseUrl/episode?category=${parsed.category}&id=${parsed.id}&episode=${parsed.episodeId!!}", headers = videoHeaders),
).execute()
val episodeParsed = json.decodeFromString<EpisodeResponse>(resp.body.string())
val subtitleList = episodeParsed.data.subtitles.map { sub ->
Track(sub.url, sub.language)
}
val videoList = episodeParsed.data.qualities.map { quality ->
Video(quality.url, "${quality.quality}p", quality.url, subtitleTracks = subtitleList)
}
return Observable.just(videoList.sort())
}
// ============================= Utilities ==============================
private fun LinkData.toJsonString(): String {
return json.encodeToString(this)
}
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", "1080")!!
return this.sortedWith(
compareBy { it.quality.contains(quality) },
).reversed()
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p", "240p", "80p")
entryValues = arrayOf("1080", "720", "480", "360", "240", "80")
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,13 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'Onepace'
pkgNameSuffix = 'all.onepace'
extClass = '.OnepaceFactory'
extVersionCode = 8
libVersion = '13'
containsNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View File

@ -1,193 +0,0 @@
package eu.kanade.tachiyomi.animeextension.all.onepace
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.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.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
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 uy.kohesive.injekt.injectLazy
import kotlin.Exception
open class Onepace(override val lang: String, override val name: String) : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val baseUrl = "https://www.zippyshare.com/rest/public/getTree?user=onepace&ident=kbvatgfc&id=%23"
override val supportsLatest = false
override val client: OkHttpClient = network.cloudflareClient
private val json: Json by injectLazy()
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
private val thumPref by lazy { preferences.getString("thumpreference", "false")!! }
override fun popularAnimeParse(response: Response): AnimesPage {
val document = client.newCall(GET(baseUrl)).execute().asJsoup()
val responseJson = json.decodeFromString<JsonObject>(document.select("body").text().dropLast(1).drop(1))
val childrenJson = responseJson["children"]?.jsonArray
// 0 = eng, 1 = sp, 2 = fr
val langId = when (lang) {
"es" -> 1
"en" -> 0
"fr" -> 2
else -> 0
}
val langAnJson = childrenJson!![langId].jsonObject["children"]!!.jsonArray
val thumJson by lazy { client.newCall(GET("https://onepace.net/_next/data/RQ8jGOTF74G85UWtiDofs/es/watch.json")).execute().asJsoup() }
return AnimesPage(
langAnJson.map {
val anName = it.jsonObject["text"].toString().replace("\"", "")
val anId = it.jsonObject["li_attr"]!!.jsonObject["ident"].toString().replace("\"", "")
val anStatus = if (anName.contains("Completo")) SAnime.COMPLETED else SAnime.ONGOING
SAnime.create().apply {
title = anName
status = anStatus
url = "https://www.zippyshare.com/onepace/$anId/dir.html"
thumbnail_url = when (thumPref) {
"true" -> thumAnimeParser(anName, thumJson)
else -> ""
}
}
},
false,
)
}
private fun thumAnimeParser(animeName: String, document: Document): String {
val jsonResponse = json.decodeFromString<JsonObject>(document.body().text())["pageProps"]!!
val arcsJson = jsonResponse.jsonObject["arcs"]!!.jsonArray
arcsJson.forEach {
val thumId = it.jsonObject["images"]!!.jsonArray[0].jsonObject["src"].toString().replace("\"", "")
val langTitle = it.jsonObject["translations"]!!.jsonArray
langTitle.forEach { j ->
val langCode = j.jsonObject["language"]!!.jsonObject["code"]
if (langCode.toString().replace("\"", "") == lang) {
val title = j.jsonObject["title"].toString().replace("\"", "")
if (animeName.lowercase().contains(title.lowercase())) return "https://onepace.net/_next/image?url=%2Fimages%2Farcs%2F$thumId&w=828&q=75"
}
}
}
return ""
}
override fun popularAnimeRequest(page: Int): Request = GET(baseUrl)
override fun popularAnimeNextPageSelector() = throw Exception("not used")
override fun popularAnimeSelector() = throw Exception("not used")
override fun popularAnimeFromElement(element: Element) = throw Exception("not used")
override fun episodeListParse(response: Response): List<SEpisode> {
val realUrl = response.request.url.toString().substringAfter("%23")
val jsoup = client.newCall(GET(realUrl)).execute().asJsoup()
return jsoup.select("table.listingplikow tbody tr.filerow.even").map {
val epName = it.select("td.cien a.name").text().replace(".mp4", "")
val epNum = try {
epName.substringAfter("][").substringBefore("]")
.replace("-", ".")
.replace(",", ".")
.replace("F", ".").replace("B", "0").toFloat()
} catch (e: Exception) {
// bruh
(Math.random() * 100).toFloat()
}
val epUrl = it.select("td.cien a.name").attr("href")
SEpisode.create().apply {
name = epName
url = epUrl
episode_number = epNum
}
}
}
override fun episodeListSelector() = throw Exception("not used")
override fun episodeFromElement(element: Element) = throw Exception("not used")
override fun videoListParse(response: Response): List<Video> {
val realUrl = "https:" + response.request.url.toString().substringAfter("%23")
val hostUrl = realUrl.substringBefore("/v/")
val videoUrlD = ZippyExtractor().getVideoUrl(realUrl, json)
val videoUrl = hostUrl + videoUrlD
return listOf(Video(videoUrl, "ZippyShare", videoUrl))
}
override fun videoListSelector() = throw Exception("not used")
override fun videoUrlParse(document: Document) = throw Exception("not used")
override fun videoFromElement(element: Element) = throw Exception("not used")
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = throw Exception("not used")
override fun searchAnimeFromElement(element: Element) = throw Exception("not used")
override fun searchAnimeNextPageSelector() = throw Exception("not used")
override fun searchAnimeSelector() = throw Exception("not used")
override fun latestUpdatesNextPageSelector() = throw Exception("not used")
override fun latestUpdatesFromElement(element: Element) = throw Exception("not used")
override fun latestUpdatesRequest(page: Int) = throw Exception("not used")
override fun latestUpdatesSelector() = throw Exception("not used")
override fun animeDetailsParse(document: Document): SAnime {
return SAnime.create().apply { title = "OnePace" }
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val thumPreference = ListPreference(screen.context).apply {
key = "thumpreference"
title = when (lang) {
"es" -> "Habilita la carga de miniaturas (requiere reiniciar)"
"en" -> "Enable load thumbnails (requires app restart)"
"fr" -> "Activer le chargement des vignettes (nécessite un redémarrage)"
else -> "Enable load thumbnails (requires app restart)"
}
entries = when (lang) {
"es" -> arrayOf("Habilitar", "Deshabilitar")
"en" -> arrayOf("Enable", "Unable")
"fr" -> arrayOf("Activer", "désactiver")
else -> arrayOf("Enable", "Unable")
}
entryValues = arrayOf("true", "false")
setDefaultValue("false")
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(thumPreference)
}
}

View File

@ -1,16 +0,0 @@
package eu.kanade.tachiyomi.animeextension.all.onepace
import eu.kanade.tachiyomi.animesource.AnimeSource
import eu.kanade.tachiyomi.animesource.AnimeSourceFactory
class OnepaceFactory : AnimeSourceFactory {
override fun createSources(): List<AnimeSource> = listOf(
OnepaceEspa(),
OnepaceFr(),
OnepaceEn(),
)
}
class OnepaceEspa : Onepace("es", "OnePaceESP")
class OnepaceFr : Onepace("fr", "OnePaceFR")
class OnepaceEn : Onepace("en", "OnePaceEN")

View File

@ -1,28 +0,0 @@
package eu.kanade.tachiyomi.animeextension.all.onepace
import app.cash.quickjs.QuickJs
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.jsoup.Jsoup
class ZippyExtractor {
fun getVideoUrl(url: String, json: Json): String {
val document = Jsoup.connect(url).get()
val jscript = document.selectFirst("script:containsData(dlbutton)")!!.data()
.replace("document.getElementById('dlbutton').href", "a")
.replace("document.getElementById('fimage').href", "b")
.replace("document.getElementById('fimage')", "false")
val quickjs = QuickJs.create()
val objectA = quickjs.evaluate(objectScript(jscript)).toString()
quickjs.close()
return json.decodeFromString<JsonObject>(objectA)["a"]!!.jsonPrimitive.content
}
private fun objectScript(script: String) = """
$script;
let return_object = {a:a};
JSON.stringify(return_object);
"""
}