lots of background work for manga extensions

This commit is contained in:
Finnley Somdahl 2023-10-18 23:52:03 -05:00
parent dbe573131e
commit 57a584a820
123 changed files with 2676 additions and 553 deletions

View file

@ -1,34 +1,11 @@
package ani.dantotsu.parsers
import ani.dantotsu.Lazier
import ani.dantotsu.aniyomi.anime.model.AnimeExtension
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import ani.dantotsu.lazyList
//import ani.dantotsu.parsers.anime.AllAnime
//import ani.dantotsu.parsers.anime.AnimeDao
//import ani.dantotsu.parsers.anime.AnimePahe
//import ani.dantotsu.parsers.anime.Gogo
//import ani.dantotsu.parsers.anime.Haho
//import ani.dantotsu.parsers.anime.HentaiFF
//import ani.dantotsu.parsers.anime.HentaiMama
//import ani.dantotsu.parsers.anime.HentaiStream
//import ani.dantotsu.parsers.anime.Marin
//import ani.dantotsu.parsers.anime.AniWave
//import ani.dantotsu.parsers.anime.Kaido
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
/*
object AnimeSources_old : WatchSources() {
override val list: List<Lazier<BaseParser>> = lazyList(
"AllAnime" to ::AllAnime,
"Gogo" to ::Gogo,
"Kaido" to ::Kaido,
"Marin" to ::Marin,
"AnimePahe" to ::AnimePahe,
"AniWave" to ::AniWave,
"AnimeDao" to ::AnimeDao,
)
}
*/
object AnimeSources : WatchSources() {
override var list: List<Lazier<BaseParser>> = emptyList()
@ -52,13 +29,8 @@ object AnimeSources : WatchSources() {
}
object HAnimeSources : WatchSources() {
private val aList: List<Lazier<BaseParser>> = lazyList(
//"HentaiMama" to ::HentaiMama,
//"Haho" to ::Haho,
//"HentaiStream" to ::HentaiStream,
//"HentaiFF" to ::HentaiFF,
)
override val list = listOf(aList,AnimeSources.list).flatten()

View file

@ -1,10 +1,18 @@
package ani.dantotsu.parsers
import android.content.ContentResolver
import android.content.ContentValues
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.widget.Toast
import ani.dantotsu.FileUrl
import ani.dantotsu.aniyomi.anime.model.AnimeExtension
import ani.dantotsu.aniyomi.animesource.AnimeCatalogueSource
import ani.dantotsu.aniyomi.util.network.interceptor.CloudflareBypassException
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
import eu.kanade.tachiyomi.network.interceptor.CloudflareBypassException
import ani.dantotsu.currContext
import ani.dantotsu.logger
import eu.kanade.tachiyomi.animesource.model.SEpisode
@ -13,6 +21,22 @@ import eu.kanade.tachiyomi.animesource.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.lang.awaitSingle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStream
import java.net.URL
import java.net.URLDecoder
@ -91,7 +115,7 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
return emptyList() // Return an empty list if source is not an AnimeCatalogueSource
}
fun convertAnimesPageToShowResponse(animesPage: AnimesPage): List<ShowResponse> {
private fun convertAnimesPageToShowResponse(animesPage: AnimesPage): List<ShowResponse> {
return animesPage.animes.map { sAnime ->
// Extract required fields from sAnime
val name = sAnime.title
@ -106,34 +130,160 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
}
}
fun SEpisodeToEpisode(sEpisode: SEpisode): Episode {
val episode = Episode(
sEpisode.episode_number.toString(),
private fun SEpisodeToEpisode(sEpisode: SEpisode): Episode {
//if the float episode number is a whole number, convert it to an int
val episodeNumberInt =
if (sEpisode.episode_number % 1 == 0f) {
sEpisode.episode_number.toInt()
} else {
sEpisode.episode_number
}
return Episode(
episodeNumberInt.toString(),
sEpisode.url,
sEpisode.name,
null,
null,
false,
null,
sEpisode)
return episode
sEpisode
)
}
fun VideoToVideoServer(video: Video): VideoServer {
val videoServer = VideoServer(
private fun VideoToVideoServer(video: Video): VideoServer {
return VideoServer(
video.quality,
video.url,
null,
video)
return videoServer
video
)
}
}
class VideoServerPassthrough : VideoExtractor{
val videoServer: VideoServer
constructor(videoServer: VideoServer) {
this.videoServer = videoServer
class DynamicMangaParser(extension: MangaExtension.Installed) : MangaParser() {
val extension: MangaExtension.Installed
init {
this.extension = extension
}
override val name = extension.name
override val saveName = extension.name
override val hostUrl = extension.sources.first().name
override suspend fun loadChapters(mangaLink: String, extra: Map<String, String>?, sManga: SManga): List<MangaChapter> {
val source = extension.sources.first()
if (source is CatalogueSource) {
try {
val res = source.getChapterList(sManga)
var chapterList: List<MangaChapter> = emptyList()
for (chapter in res) {
chapterList += SChapterToMangaChapter(chapter)
}
logger("chapterList size: ${chapterList.size}")
return chapterList
}
catch (e: Exception) {
logger("loadChapters Exception: $e")
}
return emptyList()
}
return emptyList() // Return an empty list if source is not a catalogueSource
}
override suspend fun loadImages(chapterLink: String, sChapter: SChapter): List<MangaImage> {
val source = extension.sources.first()
if (source is HttpSource) {
//try {
val res = source.getPageList(sChapter)
var chapterList: List<MangaImage> = emptyList()
for (page in res) {
println("page: $page")
currContext()?.let { fetchAndProcessImage(page, source, it.contentResolver) }
logger("new image url: ${page.imageUrl}")
chapterList += PageToMangaImage(page)
}
logger("image url: chapterList size: ${chapterList.size}")
return chapterList
//}
//catch (e: Exception) {
// logger("loadImages Exception: $e")
//}
return emptyList()
}
return emptyList() // Return an empty list if source is not a CatalogueSource
}
override suspend fun search(query: String): List<ShowResponse> {
val source = extension.sources.first()
if (source is HttpSource) {
var res: MangasPage? = null
try {
res = source.fetchSearchManga(1, query, FilterList()).toBlocking().first()
logger("res observable: $res")
}
catch (e: CloudflareBypassException) {
logger("Exception in search: $e")
Toast.makeText(currContext(), "Failed to bypass Cloudflare", Toast.LENGTH_SHORT).show()
}
val conv = convertMangasPageToShowResponse(res!!)
return conv
}
return emptyList() // Return an empty list if source is not a CatalogueSource
}
private fun convertMangasPageToShowResponse(mangasPage: MangasPage): List<ShowResponse> {
return mangasPage.mangas.map { sManga ->
// Extract required fields from sManga
val name = sManga.title
val link = sManga.url
val coverUrl = sManga.thumbnail_url ?: ""
val otherNames = emptyList<String>() // Populate as needed
val total = 20
val extra: Map<String, String>? = null // Populate as needed
// Create a new ShowResponse
ShowResponse(name, link, coverUrl, sManga)
}
}
private fun PageToMangaImage(page: Page): MangaImage {
//find and move any headers from page.imageUrl to headersMap
val headersMap: Map<String, String> = page.imageUrl?.split("&")?.mapNotNull {
val idx = it.indexOf("=")
if (idx != -1) {
val key = URLDecoder.decode(it.substring(0, idx), "UTF-8")
val value = URLDecoder.decode(it.substring(idx + 1), "UTF-8")
Pair(key, value)
} else {
null // Or some other default value
}
}?.toMap() ?: mapOf()
val urlWithoutHeaders = page.imageUrl?.split("&")?.get(0) ?: ""
val url = page.imageUrl ?: ""
logger("Pageurl: $url")
logger("regularurl: ${page.url}")
logger("regularurl: ${page.status}")
return MangaImage(
FileUrl(url, headersMap),
false,
page
)
}
private fun SChapterToMangaChapter(sChapter: SChapter): MangaChapter {
return MangaChapter(
sChapter.name,
sChapter.url,
sChapter.name,
null,
sChapter
)
}
}
class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
override val server: VideoServer
get() {
return videoServer

View file

@ -3,6 +3,7 @@ package ani.dantotsu.parsers
import ani.dantotsu.*
import ani.dantotsu.media.Media
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.source.model.SManga
import java.io.Serializable
import java.net.URLDecoder
import java.net.URLEncoder
@ -141,7 +142,10 @@ data class ShowResponse(
val extra : Map<String,String>?=null,
//SAnime object from Aniyomi
val sAnime: SAnime?=null
val sAnime: SAnime? = null,
//SManga object from Aniyomi
val sManga: SManga? = null
) : Serializable {
constructor(name: String, link: String, coverUrl: String, otherNames: List<String> = listOf(), total: Int? = null, extra: Map<String, String>?=null)
: this(name, link, FileUrl(coverUrl), otherNames, total, extra)
@ -157,6 +161,9 @@ data class ShowResponse(
constructor(name: String, link: String, coverUrl: String, sAnime: SAnime)
: this(name, link, FileUrl(coverUrl), sAnime = sAnime)
constructor(name: String, link: String, coverUrl: String, sManga: SManga)
: this(name, link, FileUrl(coverUrl), sManga = sManga)
}

View file

@ -1,6 +1,7 @@
package ani.dantotsu.parsers
import ani.dantotsu.Lazier
import ani.dantotsu.logger
import ani.dantotsu.media.anime.Episode
import ani.dantotsu.media.manga.MangaChapter
import ani.dantotsu.media.Media
@ -52,11 +53,17 @@ abstract class MangaReadSources : BaseSources() {
suspend fun loadChapters(i: Int, show: ShowResponse): MutableMap<String, MangaChapter> {
val map = mutableMapOf<String, MangaChapter>()
val parser = get(i)
tryWithSuspend(true) {
parser.loadChapters(show.link, show.extra).forEach {
map[it.number] = MangaChapter(it)
show.sManga?.let { sManga ->
tryWithSuspend(true) {
parser.loadChapters(show.link, show.extra, sManga).forEach {
map[it.number] = MangaChapter(it)
}
}
}
if(show.sManga == null) {
logger("sManga is null")
}
logger("map size ${map.size}")
return map
}
}

View file

@ -3,6 +3,9 @@ package ani.dantotsu.parsers
import ani.dantotsu.FileUrl
import ani.dantotsu.media.Media
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import java.io.Serializable
abstract class MangaParser : BaseParser() {
@ -10,7 +13,7 @@ abstract class MangaParser : BaseParser() {
/**
* Takes ShowResponse.link and ShowResponse.extra (if any) as arguments & gives a list of total chapters present on the site.
* **/
abstract suspend fun loadChapters(mangaLink: String, extra: Map<String, String>?): List<MangaChapter>
abstract suspend fun loadChapters(mangaLink: String, extra: Map<String, String>?, sManga: SManga): List<MangaChapter>
/**
* Takes ShowResponse.link, ShowResponse.extra & the Last Largest Chapter Number known by app as arguments
@ -18,8 +21,8 @@ abstract class MangaParser : BaseParser() {
* Returns the latest chapter (If overriding, Make sure the chapter is actually the latest chapter)
* Returns null, if no latest chapter is found.
* **/
open suspend fun getLatestChapter(mangaLink: String, extra: Map<String, String>?, latest: Float): MangaChapter? {
return loadChapters(mangaLink, extra)
open suspend fun getLatestChapter(mangaLink: String, extra: Map<String, String>?, sManga: SManga, latest: Float): MangaChapter? {
return loadChapters(mangaLink, extra, sManga)
.maxByOrNull { it.number.toFloatOrNull() ?: 0f }
?.takeIf { latest < (it.number.toFloatOrNull() ?: 0.001f) }
}
@ -27,7 +30,7 @@ abstract class MangaParser : BaseParser() {
/**
* Takes MangaChapter.link as an argument & returns a list of MangaImages with their Url (with headers & transformations, if needed)
* **/
abstract suspend fun loadImages(chapterLink: String): List<MangaImage>
abstract suspend fun loadImages(chapterLink: String, sChapter: SChapter): List<MangaImage>
override suspend fun autoSearch(mediaObj: Media): ShowResponse? {
var response = loadSavedShowResponse(mediaObj.id)
@ -65,6 +68,8 @@ data class MangaChapter(
//Self-Descriptive
val title: String? = null,
val description: String? = null,
val sChapter: SChapter,
)
data class MangaImage(
@ -75,8 +80,10 @@ data class MangaImage(
* **/
val url: FileUrl,
val useTransformation: Boolean = false
val useTransformation: Boolean = false,
val page: Page
) : Serializable{
constructor(url: String,useTransformation: Boolean=false)
: this(FileUrl(url),useTransformation)
constructor(url: String,useTransformation: Boolean=false, page: Page)
: this(FileUrl(url),useTransformation, page)
}

View file

@ -2,10 +2,30 @@ package ani.dantotsu.parsers
import ani.dantotsu.Lazier
import ani.dantotsu.lazyList
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
object MangaSources : MangaReadSources() {
override val list: List<Lazier<BaseParser>> = lazyList(
)
override var list: List<Lazier<BaseParser>> = emptyList()
suspend fun init(fromExtensions: StateFlow<List<MangaExtension.Installed>>) {
// Initialize with the first value from StateFlow
val initialExtensions = fromExtensions.first()
list = createParsersFromExtensions(initialExtensions)
// Update as StateFlow emits new values
fromExtensions.collect { extensions ->
list = createParsersFromExtensions(extensions)
}
}
private fun createParsersFromExtensions(extensions: List<MangaExtension.Installed>): List<Lazier<BaseParser>> {
return extensions.map { extension ->
val name = extension.name
Lazier({ DynamicMangaParser(extension) }, name)
}
}
}
object HMangaSources : MangaReadSources() {