Fix episodes and added search [Neko-Sama] (#786)
This commit is contained in:
committed by
GitHub
parent
225eaacc02
commit
34fb9ba2bd
@ -1,11 +1,12 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'NekoSama'
|
||||
pkgNameSuffix = 'fr.nekosama'
|
||||
extClass = '.NekoSama'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
libVersion = '13'
|
||||
containsNsfw = false
|
||||
}
|
||||
|
@ -9,12 +9,16 @@ import eu.kanade.tachiyomi.animeextension.fr.nekosama.extractors.StreamTapeExtra
|
||||
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.Video
|
||||
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
@ -23,6 +27,7 @@ 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 java.lang.Exception
|
||||
|
||||
class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
@ -37,6 +42,8 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
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)
|
||||
}
|
||||
@ -61,11 +68,16 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val pageBody = response.asJsoup()
|
||||
return pageBody.select("div.row.no-gutters.js-list-episode-container div.col-lg-4.col-sm-6.col-xs-6").map {
|
||||
val episodesJson = pageBody.selectFirst("script:containsData(var episodes =)").data()
|
||||
.substringAfter("var episodes = ").substringBefore(";")
|
||||
val json = json.decodeFromString<List<EpisodesJson>>(episodesJson)
|
||||
|
||||
return json.map {
|
||||
SEpisode.create().apply {
|
||||
url = it.select("div div.text a").attr("href")
|
||||
episode_number = it.select("div div.text a span").text().substringAfter(". ").toFloat()
|
||||
name = it.select("div div.text a span").text()
|
||||
name = try { it.episode!! } catch (e: Exception) { "episode" }
|
||||
url = it.url!!.replace("\\", "")
|
||||
|
||||
episode_number = try { it.episode!!.substringAfter(". ").toFloat() } catch (e: Exception) { (0..10).random() }.toFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,14 +93,13 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
val script = document.selectFirst("script:containsData(var video = [];)").data()
|
||||
val firstVideo = script.substringBefore("else {").substringAfter("video[0] = '").substringBefore("'").lowercase()
|
||||
val secondVideo = script.substringAfter("else {").substringAfter("video[0] = '").substringBefore("'").lowercase()
|
||||
|
||||
when {
|
||||
firstVideo.contains("streamtape") -> StreamTapeExtractor(client).videoFromUrl(firstVideo, "StreamTape")?.let { videoList.add(it) }
|
||||
firstVideo.contains("pstream") -> videoList.add(PstreamExtractor(firstVideo))
|
||||
firstVideo.contains("pstream") -> videoList.add(pstreamExtractor(firstVideo))
|
||||
}
|
||||
when {
|
||||
secondVideo.contains("streamtape") -> StreamTapeExtractor(client).videoFromUrl(secondVideo, "StreamTape")?.let { videoList.add(it) }
|
||||
secondVideo.contains("pstream") -> videoList.add(PstreamExtractor(secondVideo))
|
||||
secondVideo.contains("pstream") -> videoList.add(pstreamExtractor(secondVideo))
|
||||
}
|
||||
return videoList
|
||||
}
|
||||
@ -120,9 +131,14 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
||||
val typeFilter = filterList.find { it is TypeFilter } as TypeFilter
|
||||
val typeSearch = when (typeFilter.toUriPart()) {
|
||||
"anime" -> "vostfr"
|
||||
"anime-vf" -> "vf"
|
||||
else -> "vostfr"
|
||||
}
|
||||
|
||||
return when {
|
||||
query.isNotBlank() -> throw Exception("Recherche de texte non prise en charge")
|
||||
query.isNotBlank() -> GET("$baseUrl/animes-search-$typeSearch.json?$query")
|
||||
typeFilter.state != 0 || query.isNotBlank() -> when (page) {
|
||||
1 -> GET("$baseUrl/${typeFilter.toUriPart()}")
|
||||
else -> GET("$baseUrl/${typeFilter.toUriPart()}/$page")
|
||||
@ -134,19 +150,51 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element)
|
||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||
val pageUrl = response.request.url.toString()
|
||||
val query = pageUrl.substringAfter("?").lowercase()
|
||||
|
||||
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
|
||||
return when {
|
||||
pageUrl.contains("animes-search") -> {
|
||||
val jsonSearch = json.decodeFromString<List<SearchJson>>(response.asJsoup().body().text())
|
||||
val animes = mutableListOf<SAnime>()
|
||||
jsonSearch.map {
|
||||
if (it.title!!.lowercase().contains(query)) {
|
||||
val animeResult = SAnime.create().apply {
|
||||
url = it.url!!
|
||||
title = it.title!!
|
||||
thumbnail_url = try {
|
||||
it.url_image
|
||||
} catch (e: Exception) {
|
||||
"$baseUrl/images/default_poster.png"
|
||||
}
|
||||
}
|
||||
animes.add(animeResult)
|
||||
}
|
||||
}
|
||||
AnimesPage(
|
||||
animes, false
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
AnimesPage(
|
||||
response.asJsoup().select(popularAnimeSelector()).map { popularAnimeFromElement(it) }, true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchAnimeSelector(): String = popularAnimeSelector()
|
||||
override fun searchAnimeFromElement(element: Element): SAnime = throw Exception("not used")
|
||||
|
||||
override fun searchAnimeNextPageSelector(): String = throw Exception("not used")
|
||||
|
||||
override fun searchAnimeSelector(): String = throw Exception("not used")
|
||||
|
||||
override fun animeDetailsParse(document: Document): SAnime {
|
||||
val anime = SAnime.create()
|
||||
anime.title = document.selectFirst("div.col.offset-lg-3.offset-md-4 h1").text()
|
||||
anime.description = document.select("div.synopsis p").text()
|
||||
anime.thumbnail_url = document.select("div.cover img").attr("src")
|
||||
val a = document.select("div.cover img")
|
||||
|
||||
return anime
|
||||
}
|
||||
|
||||
@ -196,8 +244,8 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
screen.addPreference(videoQualityPref)
|
||||
}
|
||||
|
||||
private fun PstreamExtractor(url: String): Video {
|
||||
val noVideo = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4"
|
||||
private fun pstreamExtractor(url: String): Video {
|
||||
val noVideo = "http://discloud-storage.herokuapp.com/file/cf781d7d4d02a84b85620ed9ddf7066b/amogus.mp4"
|
||||
val document = Jsoup.connect(url).headers(
|
||||
mapOf(
|
||||
"Accept" to "*/*",
|
||||
@ -207,7 +255,6 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
)
|
||||
).get()
|
||||
document.select("script").forEach { Script ->
|
||||
|
||||
if (Script.attr("src").contains("https://www.pstream.net/u/player-script")) {
|
||||
val playerScript = Jsoup.connect(Script.attr("src")).headers(
|
||||
mapOf(
|
||||
@ -237,6 +284,36 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
}
|
||||
return Video(noVideo, "NO VIDEO", noVideo)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class EpisodesJson(
|
||||
var time: String? = null,
|
||||
var episode: String? = null,
|
||||
var title: String? = null,
|
||||
var url: String? = null,
|
||||
var url_image: String? = null
|
||||
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SearchJson(
|
||||
var id: Int? = null,
|
||||
var title: String? = null,
|
||||
var titleEnglish: String? = null,
|
||||
var titleRomanji: String? = null,
|
||||
var titleFrench: String? = null,
|
||||
var others: String? = null,
|
||||
var type: String? = null,
|
||||
var status: String? = null,
|
||||
var popularity: Double? = null,
|
||||
var url: String? = null,
|
||||
var genres: ArrayList<String> = arrayListOf(),
|
||||
var url_image: String? = null,
|
||||
var score: String? = null,
|
||||
var startDateYear: String? = null,
|
||||
var nbEps: String? = null
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user