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: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'NekoSama'
pkgNameSuffix = 'fr.nekosama'
extClass = '.NekoSama'
extVersionCode = 1
extVersionCode = 2
libVersion = '13'
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.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
)
}
/*