Fix episodes and added search [Neko-Sama] (#786)

This commit is contained in:
Diego Peña Y Lillo
2022-08-20 06:15:55 -04:00
committed by GitHub
parent 225eaacc02
commit 34fb9ba2bd
2 changed files with 95 additions and 17 deletions

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'NekoSama' extName = 'NekoSama'
pkgNameSuffix = 'fr.nekosama' pkgNameSuffix = 'fr.nekosama'
extClass = '.NekoSama' extClass = '.NekoSama'
extVersionCode = 1 extVersionCode = 2
libVersion = '13' libVersion = '13'
containsNsfw = false containsNsfw = false
} }

View File

@ -9,12 +9,16 @@ import eu.kanade.tachiyomi.animeextension.fr.nekosama.extractors.StreamTapeExtra
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList 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.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
@ -23,6 +27,7 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.lang.Exception import java.lang.Exception
class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() { class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
@ -37,6 +42,8 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val client: OkHttpClient = network.cloudflareClient override val client: OkHttpClient = network.cloudflareClient
private val json: Json by injectLazy()
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -61,11 +68,16 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun episodeListParse(response: Response): List<SEpisode> { override fun episodeListParse(response: Response): List<SEpisode> {
val pageBody = response.asJsoup() 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 { SEpisode.create().apply {
url = it.select("div div.text a").attr("href") name = try { it.episode!! } catch (e: Exception) { "episode" }
episode_number = it.select("div div.text a span").text().substringAfter(". ").toFloat() url = it.url!!.replace("\\", "")
name = it.select("div div.text a span").text()
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 script = document.selectFirst("script:containsData(var video = [];)").data()
val firstVideo = script.substringBefore("else {").substringAfter("video[0] = '").substringBefore("'").lowercase() val firstVideo = script.substringBefore("else {").substringAfter("video[0] = '").substringBefore("'").lowercase()
val secondVideo = script.substringAfter("else {").substringAfter("video[0] = '").substringBefore("'").lowercase() val secondVideo = script.substringAfter("else {").substringAfter("video[0] = '").substringBefore("'").lowercase()
when { when {
firstVideo.contains("streamtape") -> StreamTapeExtractor(client).videoFromUrl(firstVideo, "StreamTape")?.let { videoList.add(it) } 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 { when {
secondVideo.contains("streamtape") -> StreamTapeExtractor(client).videoFromUrl(secondVideo, "StreamTape")?.let { videoList.add(it) } 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 return videoList
} }
@ -120,9 +131,14 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val filterList = if (filters.isEmpty()) getFilterList() else filters val filterList = if (filters.isEmpty()) getFilterList() else filters
val typeFilter = filterList.find { it is TypeFilter } as TypeFilter val typeFilter = filterList.find { it is TypeFilter } as TypeFilter
val typeSearch = when (typeFilter.toUriPart()) {
"anime" -> "vostfr"
"anime-vf" -> "vf"
else -> "vostfr"
}
return when { 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) { typeFilter.state != 0 || query.isNotBlank() -> when (page) {
1 -> GET("$baseUrl/${typeFilter.toUriPart()}") 1 -> GET("$baseUrl/${typeFilter.toUriPart()}")
else -> GET("$baseUrl/${typeFilter.toUriPart()}/$page") 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 { override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create() val anime = SAnime.create()
anime.title = document.selectFirst("div.col.offset-lg-3.offset-md-4 h1").text() anime.title = document.selectFirst("div.col.offset-lg-3.offset-md-4 h1").text()
anime.description = document.select("div.synopsis p").text() anime.description = document.select("div.synopsis p").text()
anime.thumbnail_url = document.select("div.cover img").attr("src") anime.thumbnail_url = document.select("div.cover img").attr("src")
val a = document.select("div.cover img")
return anime return anime
} }
@ -196,8 +244,8 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
screen.addPreference(videoQualityPref) screen.addPreference(videoQualityPref)
} }
private fun PstreamExtractor(url: String): Video { private fun pstreamExtractor(url: String): Video {
val noVideo = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4" val noVideo = "http://discloud-storage.herokuapp.com/file/cf781d7d4d02a84b85620ed9ddf7066b/amogus.mp4"
val document = Jsoup.connect(url).headers( val document = Jsoup.connect(url).headers(
mapOf( mapOf(
"Accept" to "*/*", "Accept" to "*/*",
@ -207,7 +255,6 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
) )
).get() ).get()
document.select("script").forEach { Script -> document.select("script").forEach { Script ->
if (Script.attr("src").contains("https://www.pstream.net/u/player-script")) { if (Script.attr("src").contains("https://www.pstream.net/u/player-script")) {
val playerScript = Jsoup.connect(Script.attr("src")).headers( val playerScript = Jsoup.connect(Script.attr("src")).headers(
mapOf( mapOf(
@ -237,6 +284,36 @@ class NekoSama : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
return Video(noVideo, "NO VIDEO", noVideo) 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
)
} }
/* /*