Skip to content
This repository has been archived by the owner on Jan 21, 2024. It is now read-only.

Commit

Permalink
manga source fixed
Browse files Browse the repository at this point in the history
  • Loading branch information
tas33n committed Oct 10, 2023
1 parent dc90feb commit 8b34347
Show file tree
Hide file tree
Showing 14 changed files with 1,170 additions and 0 deletions.
165 changes: 165 additions & 0 deletions app/src/main/java/ani/saikou/parsers/manga/sources/AllAnime.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package ani.saikou.parsers.manga.sources

import ani.saikou.connections.anilist.Anilist
import ani.saikou.client
import ani.saikou.parsers.manga.MangaChapter
import ani.saikou.parsers.manga.MangaImage
import ani.saikou.parsers.manga.MangaParser
import ani.saikou.parsers.ShowResponse
import ani.saikou.tryWithSuspend
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import okhttp3.HttpUrl.Companion.toHttpUrl
import java.text.DecimalFormat

class AllAnime : MangaParser() {
override val name = "AllAnime"
override val saveName = "all_anime_manga"
override val hostUrl = "https://allmanga.to"

private val ytAnimeCoversHost = "https://wp.youtube-anime.com/aln.youtube-anime.com"
private val idRegex = Regex("${hostUrl}/manga/(\\w+)")
private val epNumRegex = Regex("/[sd]ub/(\\d+)")

private val idHash = "affde0163d56435e1c5378d50b3ef0b3aa614c83936a6833e6e5f0fd142055b4"
private val episodeInfoHash = "c8f3ac51f598e630a1d09d7f7fb6924cff23277f354a23e473b962a367880f7d"
private val searchHash = "a27e57ef5de5bae714db701fb7b5cf57e13d57938fc6256f7d5c70a975d11f3d"
private val chapterHash = "d877ecac37a54bd0599836d275acbb30a53d6ff50780c5da1f6c76048eebb388"

override suspend fun loadChapters(mangaLink: String, extra: Map<String, String>?): List<MangaChapter> {
val showId = idRegex.find(mangaLink)?.groupValues?.get(1)!!
val episodeInfos = getEpisodeInfos(showId)!!
val format = DecimalFormat("#####.#####")

return episodeInfos.sortedBy { it.episodeIdNum }.map { epInfo ->
val link = """${hostUrl}/manga/$showId/chapters/sub/${epInfo.episodeIdNum}"""
val epNum = format.format(epInfo.episodeIdNum).toString()
MangaChapter(epNum, link, epInfo.notes)
}
}

override suspend fun loadImages(chapterLink: String): List<MangaImage> {
val showId = idRegex.find(chapterLink)?.groupValues?.get(1)!!
val episodeNum = epNumRegex.find(chapterLink)?.groupValues?.get(1)!!
val variables = """{"mangaId":"$showId","translationType":"sub","chapterString":"$episodeNum","limit":1000000}"""
val chapterPages = graphqlQuery(variables, chapterHash)?.data?.chapterPages?.edges
// For future reference: If pictureUrlHead is null then the link provided is a relative link of the "apivtwo" variety, but it doesn't seem to contain useful images
val chapter = chapterPages?.filter { !it.pictureUrlHead.isNullOrEmpty() }?.get(0)!!
return chapter.pictureUrls.sortedBy { it.num }
.map { MangaImage("""${chapter.pictureUrlHead}${it.url}""") }

}

override suspend fun search(query: String): List<ShowResponse> {

val variables =
"""{"search":{"query":"$query","isManga":true},"limit":26,"page":1,"translationType":"sub","countryOrigin":"ALL"}"""
val edges =
graphqlQuery(variables, searchHash)?.data?.mangas?.edges!!

return edges.map { show ->
val link = """${hostUrl}/manga/${show.id}"""
val otherNames = mutableListOf<String>()
show.englishName?.let { otherNames.add(it) }
show.nativeName?.let { otherNames.add(it) }
show.altNames?.forEach { otherNames.add(it) }
var thumbnail = show.thumbnail
if (thumbnail.startsWith("mcovers")) {
thumbnail = "$ytAnimeCoversHost/$thumbnail"
}
ShowResponse(show.name, link, thumbnail, otherNames, show.availableChapters.sub)
}
}

private suspend fun graphqlQuery(variables: String, persistHash: String): Query? {
val extensions = """{"persistedQuery":{"version":1,"sha256Hash":"$persistHash"}}"""
val graphqlUrl = ("https://api.allanime.day/api").toHttpUrl().newBuilder().addQueryParameter("variables", variables)
.addQueryParameter("extensions", extensions).build()
val headers = mutableMapOf<String, String>()
headers["Host"] = "api.allanime.day"
headers["origin"] = "https://allmanga.to"
return tryWithSuspend {
client.get(graphqlUrl.toString(), headers).parsed()
}
}

private suspend fun getEpisodeInfos(showId: String): List<EpisodeInfo>? {
val variables = """{"ids": "$showId"}"""

val manga = graphqlQuery(variables, idHash)?.data?.manga
println("manga info : $manga")
if (manga != null) {
val epCount = manga.availableChapters.sub
val epVariables = """{"showId":"manga@$showId","episodeNumStart":0,"episodeNumEnd":${epCount}}"""
return graphqlQuery(
epVariables,
episodeInfoHash
)?.data?.episodeInfos
}

return null
}

@Serializable
private data class Query(
@SerialName("data") var data: Data?
) {
@Serializable
data class Data(
@SerialName("manga") val manga: Manga?,
@SerialName("mangas") val mangas: MangasConnection?,
@SerialName("episodeInfos") val episodeInfos: List<EpisodeInfo>?,
@SerialName("chapterPages") val chapterPages: ChapterConnection?,
)

@Serializable
data class MangasConnection(
@SerialName("edges") val edges: List<Manga>
)

@Serializable
data class Manga(
@SerialName("_id") val id: String,
@SerialName("name") val name: String,
@SerialName("description") val description: String?,
@SerialName("englishName") val englishName: String?,
@SerialName("nativeName") val nativeName: String?,
@SerialName("thumbnail") val thumbnail: String,
@SerialName("availableChapters") val availableChapters: AvailableChapters,
@SerialName("altNames") val altNames: List<String>?
)

@Serializable
data class AvailableChapters(
@SerialName("sub") val sub: Int,
@SerialName("raw") val raw: Int
)

@Serializable
data class ChapterConnection(
@SerialName("edges") val edges: List<Chapter>
) {
@Serializable
data class Chapter(
@SerialName("pictureUrls") val pictureUrls: List<PictureUrl>,
@SerialName("pictureUrlHead") val pictureUrlHead: String?
)

@Serializable
data class PictureUrl(
@SerialName("num") val num: Int,
@SerialName("url") val url: String

)
}
}

@Serializable
private data class EpisodeInfo(
// Episode "numbers" can have decimal values, hence float
@SerialName("episodeIdNum") val episodeIdNum: Float,
@SerialName("notes") val notes: String?,
@SerialName("thumbnails") val thumbnails: List<String>?,
)

}
83 changes: 83 additions & 0 deletions app/src/main/java/ani/saikou/parsers/manga/sources/ComickFun.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package ani.saikou.parsers.manga.sources

import ani.saikou.Mapper
import ani.saikou.client
import ani.saikou.parsers.manga.MangaChapter
import ani.saikou.parsers.manga.MangaImage
import ani.saikou.parsers.manga.MangaParser
import ani.saikou.parsers.ShowResponse
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

class ComickFun : MangaParser() {

override val name = "ComickFun"
override val saveName = "comick_fun"
override val hostUrl = "https://api.comick.app"

override suspend fun search(query: String): List<ShowResponse> {
val resp = Mapper.parse<List<SearchData>>(client.get("https://api.comick.app/v1.0/search?q=${encode(query)}").text)

return resp.map { manga ->
val coverimg = "https://meo.comick.pictures/llxWY.jpg"

val mangaLink = "$hostUrl/comic/${manga.hid}/chapters"
ShowResponse(manga.title,mangaLink,coverimg ,manga.md_titles.map { it.title })
}
}

override suspend fun loadChapters(mangaLink: String, extra: Map<String, String>?): List<MangaChapter> {
val resp = client.get(mangaLink).parsed<MangaChapterData>()
return resp.chapters.reversed().map {
val chapterLink = "$hostUrl/chapter/${it.hid}?tachiyomi=true"
MangaChapter(number = it.chap.toString(), link = chapterLink, title = it.title)
}
}

override suspend fun loadImages(chapterLink: String): List<MangaImage> {
val resp = client.get(chapterLink).parsed<MangaImageData>()
return resp.chapter.images.map { MangaImage(url = it.url) }
}

@Serializable
private data class SearchData(
@SerialName("title") val title: String,
@SerialName("id") val id: Int,
@SerialName("hid") val hid: String,
@SerialName("slug") val slug: String,
@SerialName("md_titles") val md_titles: List<MdTitles>, // other titles
@SerialName("md_covers") val md_covers: String,
@SerialName("b2key") val b2key: String,
) {
@Serializable
data class MdTitles(
@SerialName("title") val title: String,
)
}

@Serializable
private data class MangaChapterData(
@SerialName("chapters") val chapters: List<Chap>
)

@Serializable
private data class Chap(
val chap: String? = null,
val title: String? = null,
val vol: String? = null,
val lang: String? = null,
val hid: String? = null,
)

@Serializable
private data class MangaImageData(@SerialName("chapter") val chapter: Chapter) {

@Serializable
data class Chapter(@SerialName("images") val images: List<Image>) {

@Serializable
data class Image(@SerialName("url") val url: String)
}
}

}
128 changes: 128 additions & 0 deletions app/src/main/java/ani/saikou/parsers/manga/sources/Manga4Life.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package ani.saikou.parsers.manga.sources

import ani.saikou.Mapper
import ani.saikou.client
import ani.saikou.findBetween
import ani.saikou.parsers.manga.MangaChapter
import ani.saikou.parsers.manga.MangaImage
import ani.saikou.parsers.manga.MangaParser
import ani.saikou.parsers.ShowResponse
import ani.saikou.sortByTitle
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

open class Manga4Life : MangaParser() {

override val name = "Manga4Life"
override val saveName = "manga_see"
override val hostUrl = host

override suspend fun loadChapters(mangaLink: String, extra: Map<String, String>?): List<MangaChapter> {

val json = client.get("$hostUrl/manga/$mangaLink").text.findBetween("vm.Chapters = ", ";")!!

return Mapper.parse<List<MangaResponse>>(json).reversed().map {
val chap = it.chapter
val num = "${
if (chap.startsWith("0") || chap.startsWith("1")) "" else "S" + chap[0] + " : "
}${
chap.drop(1).dropLast(1).toInt()
}${
if (chap.endsWith("0")) "" else (".${chap[chap.length - 1]}")
}"
val link = hostUrl + "/read-online/$mangaLink" + chapterURLEncode(chap)
MangaChapter(num, link, it.chapterName)
}
}

override suspend fun loadImages(chapterLink: String): List<MangaImage> {
val str = client.get(chapterLink).text
val server = str.findBetween("vm.CurPathName = ", ";")?.trim('"') ?: return listOf()
var slug = str.findBetween("vm.IndexName = ", ";")?.trim('"') ?: return listOf()
val json = Mapper.parse<ChapterResponse>(
str.findBetween("vm.CurChapter = ", ";") ?: return listOf()
)
slug += json.directory.let { if (it.isEmpty()) "" else "/$it" }
val chap = chapterImage(json.chapter)

return (1..json.page.toInt()).map {
val link = "https://$server/manga/$slug/$chap-${"000$it".takeLast(3)}.png"
MangaImage(link)
}
}

override suspend fun search(query: String): List<ShowResponse> {
val list = getSearchData().toMutableList()
list.sortByTitle(query)
return list
}

companion object {
private const val host = "https://manga4life.com"
private var response: List<ShowResponse>? = null
suspend fun getSearchData(): List<ShowResponse> {
response = if (response != null) response ?: listOf()
else {
val json = client.get("$host/search/").text.findBetween("vm.Directory = ", "\n")!!.replace(";", "")
Mapper.parse<List<SearchResponse>>(json).map {
ShowResponse(
it.s, it.i, "https://temp.compsci88.com/cover/${it.i}.jpg"
)
}
}
return response ?: listOf()
}
}

private fun chapterURLEncode(e: String): String {
var index = ""
val t = e.substring(0, 1).toInt()
if (1 != t) {
index = "-index-$t"
}
val dgt = when {
e.toInt() < 100100 -> 4
e.toInt() < 101000 -> 3
e.toInt() < 110000 -> 2
else -> 1
}
val n = e.substring(dgt, e.length - 1)
var suffix = ""
val path = e.substring(e.length - 1).toInt()
if (0 != path) {
suffix = ".$path"
}
return "-chapter-$n$suffix$index.html"
}

private val chapterImageRegex = Regex("""^0+""")

private fun chapterImage(e: String, cleanString: Boolean = false): String {
val a = e.substring(1, e.length - 1).let { if (cleanString) it.replace(chapterImageRegex, "") else it }
val b = e.substring(e.length - 1).toInt()
return when {
(b == 0 && a.isNotEmpty()) -> a
(b == 0 && a.isEmpty()) -> "0"
else -> "$a.$b"
}
}

@Serializable
private data class MangaResponse(
@SerialName("Chapter") val chapter: String,
@SerialName("ChapterName") val chapterName: String?
)

@Serializable
private data class ChapterResponse(
@SerialName("Chapter") val chapter: String,
@SerialName("Page") val page: String,
@SerialName("Directory") val directory: String
)

@Serializable
private data class SearchResponse(
val s: String,
val i: String
)
}
Loading

0 comments on commit 8b34347

Please sign in to comment.