Initial commit

This commit is contained in:
Finnley Somdahl 2023-10-17 18:42:43 -05:00
commit 21bfbfb139
520 changed files with 47819 additions and 0 deletions

View file

@ -0,0 +1,224 @@
package ani.dantotsu.parsers
import android.net.Uri
import ani.dantotsu.R
import ani.dantotsu.FileUrl
import eu.kanade.tachiyomi.animesource.model.SEpisode
import ani.dantotsu.asyncMap
import ani.dantotsu.currContext
import ani.dantotsu.loadData
import ani.dantotsu.others.MalSyncBackup
import ani.dantotsu.saveData
import ani.dantotsu.tryWithSuspend
import eu.kanade.tachiyomi.animesource.model.SAnime
import kotlin.properties.Delegates
/**
* An abstract class for creating a new Source
*
* Most of the functions & variables that need to be overridden are abstract
* **/
abstract class AnimeParser : BaseParser() {
/**
* Takes ShowResponse.link & ShowResponse.extra (if you added any) as arguments & gives a list of total episodes present on the site.
* **/
abstract suspend fun loadEpisodes(animeLink: String, extra: Map<String, String>?, sAnime: SAnime): List<Episode>
/**
* Takes ShowResponse.link, ShowResponse.extra & the Last Largest Episode Number known by app as arguments
*
* Returns the latest episode (If overriding, Make sure the episode is actually the latest episode)
* Returns null, if no latest episode is found.
* **/
open suspend fun getLatestEpisode(animeLink: String, extra: Map<String, String>?, sAnime: SAnime, latest: Float): Episode?{
return loadEpisodes(animeLink, extra, sAnime)
.maxByOrNull { it.number.toFloatOrNull()?:0f }
?.takeIf { latest < (it.number.toFloatOrNull() ?: 0.001f) }
}
/**
* Takes Episode.link as a parameter
*
* This returns a Map of "Video Server's Name" & "Link/Data" of all the Video Servers present on the site, which can be further used by loadVideoServers() & loadSingleVideoServer()
* **/
abstract suspend fun loadVideoServers(episodeLink: String, extra: Map<String,String>?, sEpisode: SEpisode): List<VideoServer>
/**
* This function will receive **url of the embed** & **name** of a Video Server present on the site to host the episode.
*
*
* Create a new VideoExtractor for the video server you are trying to scrape, if there's not one already.
*
*
* (Some sites might not have separate video hosts. In that case, just create a new VideoExtractor for that particular site)
*
*
* returns a **VideoExtractor** containing **`server`**, the app will further load the videos using `extract()` function inside it
*
* **Example for Site with multiple Video Servers**
* ```
val domain = Uri.parse(server.embed.url).host ?: ""
val extractor: VideoExtractor? = when {
"fembed" in domain -> FPlayer(server)
"sb" in domain -> StreamSB(server)
"streamta" in domain -> StreamTape(server)
else -> null
}
return extractor
```
* You can use your own way to get the Extractor for reliability.
* if there's only extractor, you can directly return it.
* **/
open suspend fun getVideoExtractor(server: VideoServer): VideoExtractor? {
var domain = Uri.parse(server.embed.url).host ?: return null
if (domain.startsWith("www.")) {domain = domain.substring(4)}
val extractor: VideoExtractor? = when (domain) {
else -> {
println("$name : No extractor found for: $domain | ${server.embed.url}")
null
}
}
return extractor
}
/**
* If the Video Servers support preloading links for the videos
* typically depends on what Video Extractor is being used
* **/
open val allowsPreloading = true
/**
* This Function used when there "isn't" a default Server set by the user, or when user wants to switch the Server
*
* Doesn't need to be overridden, if the parser is following the norm.
* **/
open suspend fun loadByVideoServers(episodeUrl: String, extra: Map<String,String>?, sEpisode: SEpisode, callback: (VideoExtractor) -> Unit) {
tryWithSuspend(true) {
loadVideoServers(episodeUrl, extra, sEpisode).asyncMap {
getVideoExtractor(it)?.apply {
tryWithSuspend(true) {
load()
}
callback.invoke(this)
}
}
}
}
/**
* This Function used when there "is" a default Server set by the user, only loads a Single Server for faster response.
*
* Doesn't need to be overridden, if the parser is following the norm.
* **/
open suspend fun loadSingleVideoServer(serverName: String, episodeUrl: String, extra: Map<String,String>?, sEpisode: SEpisode, post: Boolean): VideoExtractor? {
return tryWithSuspend(post) {
loadVideoServers(episodeUrl, extra, sEpisode).apply {
find { it.name == serverName }?.also {
return@tryWithSuspend getVideoExtractor(it)?.apply {
load()
}
}
}
null
}
}
/**
* Many sites have Dub & Sub anime as separate Shows
*
* make this `true`, if they are separated else `false`
*
* **NOTE : do not forget to override `search` if the site does not support only dub search**
* **/
open val isDubAvailableSeparately by Delegates.notNull<Boolean>()
/**
* The app changes this, depending on user's choice.
* **/
open var selectDub = false
/**
* Name used to get Shows Directly from MALSyncBackup's github dump
*
* Do not override if the site is not present on it.
* **/
open val malSyncBackupName = ""
/**
* Overridden to add MalSyncBackup support for Anime Sites
* **/
override suspend fun loadSavedShowResponse(mediaId: Int): ShowResponse? {
checkIfVariablesAreEmpty()
val dub = if (isDubAvailableSeparately) "_${if (selectDub) "dub" else "sub"}" else ""
var loaded = loadData<ShowResponse>("${saveName}${dub}_$mediaId")
if (loaded == null && malSyncBackupName.isNotEmpty())
loaded = MalSyncBackup.get(mediaId, malSyncBackupName, selectDub)?.also { saveShowResponse(mediaId, it, true) }
return loaded
}
override fun saveShowResponse(mediaId: Int, response: ShowResponse?, selected: Boolean) {
if (response != null) {
checkIfVariablesAreEmpty()
setUserText("${if (selected) currContext()!!.getString(R.string.selected) else currContext()!!.getString(R.string.found)} : ${response.name}")
val dub = if (isDubAvailableSeparately) "_${if (selectDub) "dub" else "sub"}" else ""
saveData("${saveName}${dub}_$mediaId", response)
}
}
}
/**
* A class for containing Episode data of a particular parser
* **/
data class Episode(
/**
* Number of the Episode in "String",
*
* useful in cases where episode is not a number
* **/
val number: String,
/**
* Link that links to the episode page containing videos
* **/
val link: String,
//Self-Descriptive
val title: String? = null,
val thumbnail: FileUrl? = null,
val description: String? = null,
val isFiller: Boolean = false,
/**
* In case, you want to pass extra data
* **/
val extra: Map<String,String>? = null,
//SEpisode from Aniyomi
val sEpisode: SEpisode? = null
) {
constructor(
number: String,
link: String,
title: String? = null,
thumbnail: String,
description: String? = null,
isFiller: Boolean = false,
extra: Map<String,String>? = null
) : this(number, link, title, FileUrl(thumbnail), description, isFiller, extra)
constructor(
number: String,
link: String,
title: String? = null,
thumbnail: String,
description: String? = null,
isFiller: Boolean = false,
extra: Map<String,String>? = null,
sEpisode: SEpisode? = null
) : this(number, link, title, FileUrl(thumbnail), description, isFiller, extra, sEpisode)
}