lots of background work for manga extensions
This commit is contained in:
parent
dbe573131e
commit
57a584a820
123 changed files with 2676 additions and 553 deletions
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue