if playlist available treat as series

This commit is contained in:
wasu-code 2025-03-29 10:20:56 +01:00
parent d70fe1af45
commit 8809a69f50

View file

@ -3,14 +3,15 @@ package eu.kanade.tachiyomi.animeextension.all.newgrounds
import android.app.Application import android.app.Application
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.preference.CheckBoxPreference
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
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.AnimeUpdateStrategy
import eu.kanade.tachiyomi.animesource.model.AnimesPage 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
@ -18,6 +19,7 @@ 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 okhttp3.FormBody
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request import okhttp3.Request
@ -25,6 +27,7 @@ import okhttp3.Response
import org.json.JSONObject import org.json.JSONObject
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable
import tryParse import tryParse
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -233,8 +236,21 @@ class NewGrounds : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
return description.toString() return description.toString()
} }
val shouldCheckPlaylist = preferences.getBoolean("CHECK_COLLECTION", false)
val relatedPlaylistElement = document.selectFirst("div[id^=\"related_playlists\"] ")
val relatedPlaylistUrl = relatedPlaylistElement?.selectFirst("a:not([id^=\"related_playlists\"])")?.absUrl("href")
val relatedPlaylistName = relatedPlaylistElement?.selectFirst(".detail-title h4")?.text()
val animeTitle: String = relatedPlaylistName.takeIf { shouldCheckPlaylist }
?: document.selectFirst("h2[itemprop=\"name\"]")!!.text()
val animeUrl: String = relatedPlaylistUrl.takeIf { shouldCheckPlaylist }
?: document.selectFirst("meta[itemprop=\"url\"]")!!.absUrl("content")
Log.d("Tst", "anime/playlist utL: $animeUrl")
return SAnime.create().apply { return SAnime.create().apply {
title = document.selectFirst("h2[itemprop=\"name\"]")!!.text() title = animeTitle
description = prepareDescription() description = prepareDescription()
author = document.selectFirst(".authorlinks > div:first-of-type .item-details-main")?.text() author = document.selectFirst(".authorlinks > div:first-of-type .item-details-main")?.text()
artist = document.select(".authorlinks > div:not(:first-of-type) .item-details-main").joinToString { artist = document.select(".authorlinks > div:not(:first-of-type) .item-details-main").joinToString {
@ -243,8 +259,8 @@ class NewGrounds : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
thumbnail_url = document.selectFirst("meta[itemprop=\"thumbnailUrl\"]")?.absUrl("content") thumbnail_url = document.selectFirst("meta[itemprop=\"thumbnailUrl\"]")?.absUrl("content")
genre = document.select(".tags li a").joinToString { it.text() } genre = document.select(".tags li a").joinToString { it.text() }
status = SAnime.COMPLETED status = SAnime.COMPLETED
setUrlWithoutDomain(document.selectFirst("meta[itemprop=\"url\"]")!!.absUrl("content")) setUrlWithoutDomain(animeUrl)
update_strategy = AnimeUpdateStrategy.ONLY_FETCH_ONCE // update_strategy = AnimeUpdateStrategy.ONLY_FETCH_ONCE
} }
} }
@ -262,8 +278,45 @@ class NewGrounds : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
} }
} }
override fun episodeListRequest(anime: SAnime): Request {
// episodeListParse and animeDetailsParse are started at the same time? so episodeListRequest uses old SAnime.url value
// get animeDetails page again
val response = client.newCall(GET("${baseUrl}${anime.url}")).execute()
val document = response.asJsoup()
// check and return playlist url
val shouldCheckPlaylist = preferences.getBoolean("CHECK_COLLECTION", false)
val relatedPlaylistElement = document.selectFirst("div[id^=\"related_playlists\"] ")
val relatedPlaylistUrl = relatedPlaylistElement?.selectFirst("a:not([id^=\"related_playlists\"])")?.absUrl("href")
val relatedPlaylistName = relatedPlaylistElement?.selectFirst(".detail-title h4")?.text()
val animeUrl: String = relatedPlaylistUrl.takeIf { shouldCheckPlaylist }
?: document.selectFirst("meta[itemprop=\"url\"]")!!.absUrl("content")
val animeTitle: String = relatedPlaylistName.takeIf { shouldCheckPlaylist }
?: document.selectFirst("h2[itemprop=\"name\"]")!!.text()
// set new anime url
anime.title = animeTitle
anime.setUrlWithoutDomain(animeUrl)
Log.d("Tst", "new url: ${anime.url}")
return super.episodeListRequest(anime)
}
override fun episodeListParse(response: Response): List<SEpisode> { override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup() val document = response.asJsoup()
val hasMultipleEpisodes = response.request.url.toString().startsWith("$baseUrl/series/")
Log.d("Tst", "multi? $hasMultipleEpisodes : ${response.request.url}")
return if (!hasMultipleEpisodes) {
parseSingleEpisodeList(document)
} else {
parseMultiEpisodeList(document)
}
}
private fun parseSingleEpisodeList(document: Document): List<SEpisode> {
val episodeIdScript = document.selectFirst("#ng-global-video-player script") val episodeIdScript = document.selectFirst("#ng-global-video-player script")
val episodeId = extractEpisodeIdFromScript(episodeIdScript) val episodeId = extractEpisodeIdFromScript(episodeIdScript)
val dateString = document.selectFirst("#sidestats > dl:nth-of-type(2) > dd:first-of-type")?.text() val dateString = document.selectFirst("#sidestats > dl:nth-of-type(2) > dd:first-of-type")?.text()
@ -278,6 +331,59 @@ class NewGrounds : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
) )
} }
private fun parseMultiEpisodeList(document: Document): List<SEpisode> {
val ids = document.select("li.visual-link-container").map { it.attr("data-visual-link") }
val formBody = FormBody.Builder()
.add("ids", ids.toString())
.add("component_params[include_author]", "1")
.add("include_all_suitabilities", "0")
.add("isAjaxRequest", "1")
.build()
val request = Request.Builder()
.url("$baseUrl/visual-links-fetch")
.post(formBody)
.apply {
headers.forEach { (key, value) ->
addHeader(key, value)
}
}
.build()
// val client = OkHttpClient()
val response = client.newCall(request).execute()
val jsonObject = JSONObject(response.body.string())
val episodes = mutableListOf<SEpisode>()
val simples = jsonObject.getJSONObject("simples")
var index = 1
for (key in simples.keys()) {
val subObject = simples.getJSONObject(key)
for (episodeKey in subObject.keys()) {
val episodeData = subObject.getJSONObject(episodeKey)
val title = episodeData.getString("title")
val url = episodeData.getString("url")
val user = episodeData.getJSONObject("user")
val userName = user.getString("user_name")
val episode = SEpisode.create().apply {
episode_number = index.toFloat()
name = title
scanlator = userName
setUrlWithoutDomain(url)
}
episodes.add(episode)
index++
}
}
return episodes.reversed()
}
override fun videoListRequest(episode: SEpisode): Request = GET("$baseUrl${episode.url}", videoListHeaders) override fun videoListRequest(episode: SEpisode): Request = GET("$baseUrl${episode.url}", videoListHeaders)
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
@ -384,6 +490,13 @@ class NewGrounds : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
true true
} }
}.also(screen::addPreference) }.also(screen::addPreference)
CheckBoxPreference(screen.context).apply {
key = "CHECK_COLLECTION"
title = "Try to find associated collections"
setDefaultValue(false)
summary = "If video is associated with playlist from the same author it will fetch episodes from this playlist"
}.also(screen::addPreference)
} }
// ========================== Helper Functions ========================== // ========================== Helper Functions ==========================