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
RcQ^n5002ovPDHLkV1klbr1Jm(
literal 0
HcmV?d00001
diff --git a/src/all/mangahosted/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/mangahosted/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..fe00773e6864096f5054794d779f41bfa0cafcd3
GIT binary patch
literal 12421
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
+ val mangas = dto.data.map(::mangaParse)
+ return MangasPage(
+ mangas = mangas,
+ hasNextPage = dto.hasNextPage(),
+ )
+ }
+
+ // ================================= Latest ===========================================
+
+ override fun latestUpdatesRequest(page: Int): Request {
+ val maxResult = 24
+ val url = "$apiUrl/${langOption.infix}/HomeLastUpdate".toHttpUrl().newBuilder()
+ .addPathSegment("$maxResult")
+ .addPathSegment("${page - 1}")
+ .build()
+ return GET(url, headers)
+ }
+
+ override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
+
+ // ================================= Search ===========================================
+
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+ val maxResult = 20
+ val url = "$apiUrl/${langOption.infix}/SeachPage/$maxResult/${page - 1}".toHttpUrl().newBuilder()
+ .addPathSegment(query)
+ .build()
+ return GET(url, headers)
+ }
+
+ override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
+ if (query.startsWith(SEARCH_PREFIX)) {
+ val url = "$baseUrl/${langOption.infix}/${query.substringAfter(SEARCH_PREFIX)}"
+ return client.newCall(GET(url, headers))
+ .asObservableSuccess().map { response ->
+ val mangas = try { listOf(mangaDetailsParse(response)) } catch (_: Exception) { emptyList() }
+ MangasPage(mangas, false)
+ }
+ }
+ return super.fetchSearchManga(page, query, filters)
+ }
+
+ override fun searchMangaParse(response: Response): MangasPage {
+ val dto = response.parseAs()
+ return MangasPage(
+ dto.mangas.map(::mangaParse),
+ false,
+ )
+ }
+
+ // ================================= Details ==========================================
+
+ override fun mangaDetailsRequest(manga: SManga): Request {
+ val url = "$apiUrl/${langOption.infix}/getInfoManga".toHttpUrl().newBuilder()
+ .addPathSegment(manga.slug())
+ .build()
+ return GET(url, headers)
+ }
+
+ override fun mangaDetailsParse(response: Response): SManga {
+ val dto = response.parseAs()
+ return mangaParse(dto.details)
+ }
+
+ override fun getMangaUrl(manga: SManga): String {
+ return baseUrl + manga.url.replace(langOption.infix, langOption.mangaSubstring)
+ }
+
+ // ================================= Chapter ==========================================
+
+ override fun fetchChapterList(manga: SManga): Observable> {
+ val chapters = mutableListOf()
+ var currentPage = 0
+ do {
+ val chaptersDto = fetchChapterListPageable(manga, currentPage++)
+ chapters += chaptersDto.data.map { chapter ->
+ SChapter.create().apply {
+ name = chapter.name
+ date_upload = chapter.date.toDate()
+ url = chapter.toChapterUrl(langOption.infix)
+ }
+ }
+ } while (chaptersDto.hasNextPage())
+ return Observable.just(chapters)
+ }
+
+ private fun fetchChapterListPageable(manga: SManga, page: Int): Pageable {
+ val maxResult = 100
+ val url = "$apiUrl/${langOption.infix}/GetChapterListFilter/${manga.slug()}/$maxResult/$page/all/${langOption.orderBy}"
+ return client.newCall(GET(url, headers)).execute()
+ .parseAs>()
+ }
+
+ override fun chapterListParse(response: Response) = throw UnsupportedOperationException()
+
+ // ================================= Pages ============================================
+
+ override fun pageListRequest(chapter: SChapter): Request {
+ val chapterSlug = chapter.url.substringAfter(langOption.infix)
+ val url = "$apiUrl/${langOption.infix}/GetImageChapter$chapterSlug"
+ return GET(url, headers)
+ }
+
+ override fun imageRequest(page: Page): Request {
+ val imageHeaders = headers.newBuilder()
+ .set("Accept", "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8")
+ .removeAll("Referer")
+ .build()
+ return super.imageRequest(page).newBuilder()
+ .headers(imageHeaders)
+ .build()
+ }
+
+ override fun pageListParse(response: Response): List {
+ val location = response.request.url.toString()
+ val dto = response.parseAs()
+ return dto.pages.mapIndexed { index, url ->
+ Page(index, location, imageUrl = url)
+ }
+ }
+
+ override fun imageUrlParse(response: Response): String = ""
+
+ // ================================= Utilities =======================================
+
+ private inline fun Response.parseAs(): T {
+ return json.decodeFromString(body.string())
+ }
+
+ private fun SManga.slug() = this.url.split("/").last()
+
+ private fun mangaParse(dto: MangaDto): SManga {
+ return SManga.create().apply {
+ title = dto.title
+ thumbnail_url = dto.thumbnailUrl
+ status = dto.status
+ url = "/${langOption.infix}/${dto.slug}"
+ genre = dto.genres
+ initialized = true
+ }
+ }
+
+ private fun String.toDate(): Long =
+ try { dateFormat.parse(trim())!!.time } catch (_: Exception) { 0L }
+
+ companion object {
+ const val SEARCH_PREFIX = "slug:"
+ val baseApiUrl = "https://api.novelfull.us"
+ val apiUrl = "$baseApiUrl/api"
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS", Locale.ENGLISH)
+ }
+}
diff --git a/src/all/mangahosted/src/eu/kanade/tachiyomi/extension/all/mangahosted/MangaHostedDto.kt b/src/all/mangahosted/src/eu/kanade/tachiyomi/extension/all/mangahosted/MangaHostedDto.kt
new file mode 100644
index 000000000..4ef2d8c42
--- /dev/null
+++ b/src/all/mangahosted/src/eu/kanade/tachiyomi/extension/all/mangahosted/MangaHostedDto.kt
@@ -0,0 +1,68 @@
+package eu.kanade.tachiyomi.extension.all.mangahosted
+
+import eu.kanade.tachiyomi.source.model.SManga
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+class MangaDetailsDto(private val data: Props) {
+ val details: MangaDto get() = data.details
+
+ @Serializable
+ class Props(
+ @SerialName("infoDoc") val details: MangaDto,
+ )
+}
+
+@Serializable
+open class Pageable(
+ var currentPage: Int,
+ var totalPage: Int,
+ val data: List,
+) {
+ fun hasNextPage() = (currentPage + 1) <= totalPage
+}
+
+@Serializable
+class ChapterDto(
+ val date: String,
+ @SerialName("idDoc") val slugManga: String,
+ @SerialName("idDetail") val id: String,
+ @SerialName("nameChapter") val name: String,
+) {
+ fun toChapterUrl(lang: String) = "/$lang/${this.slugManga}/$id"
+}
+
+@Serializable
+class MangaDto(
+ @SerialName("name") val title: String,
+ @SerialName("image") private val _thumbnailUrl: String,
+ @SerialName("idDoc") val slug: String,
+ @SerialName("genresName") val genres: String,
+ @SerialName("status") val _status: String,
+) {
+ val thumbnailUrl get() = "${MangaHosted.baseApiUrl}$_thumbnailUrl"
+
+ val status get() = when (_status) {
+ "ongoing" -> SManga.ONGOING
+ "completed" -> SManga.COMPLETED
+ else -> SManga.UNKNOWN
+ }
+}
+
+@Serializable
+class SearchDto(
+ @SerialName("data")
+ val mangas: List,
+)
+
+@Serializable
+class PageDto(val `data`: Data) {
+ val pages: List get() = `data`.detailDocuments.source.split("#")
+
+ @Serializable
+ class Data(@SerialName("detail_documents") val detailDocuments: DetailDocuments)
+
+ @Serializable
+ class DetailDocuments(val source: String)
+}
diff --git a/src/all/mangahosted/src/eu/kanade/tachiyomi/extension/all/mangahosted/MangaHostedFactory.kt b/src/all/mangahosted/src/eu/kanade/tachiyomi/extension/all/mangahosted/MangaHostedFactory.kt
new file mode 100644
index 000000000..5cd1d05b2
--- /dev/null
+++ b/src/all/mangahosted/src/eu/kanade/tachiyomi/extension/all/mangahosted/MangaHostedFactory.kt
@@ -0,0 +1,30 @@
+package eu.kanade.tachiyomi.extension.all.mangahosted
+
+import eu.kanade.tachiyomi.source.Source
+import eu.kanade.tachiyomi.source.SourceFactory
+
+class MangaHostedFactory : SourceFactory {
+ override fun createSources(): List = languages.map { MangaHosted(it) }
+}
+
+class LanguageOption(
+ val lang: String,
+ val infix: String = lang,
+ val mangaSubstring: String = infix,
+ val nameSuffix: String = "",
+ val orderBy: String = "DESC",
+)
+
+val languages = listOf(
+ LanguageOption("en", "manga", "scan"),
+ LanguageOption("en", "manga-v2", "kaka", " v2"),
+ LanguageOption("en", "comic", "comic-dc", " Comics"),
+ LanguageOption("es", "manga-spanish", "manga-es"),
+ LanguageOption("id", "manga-indo", "id"),
+ LanguageOption("it", "manga-italia", "manga-it"),
+ LanguageOption("ja", "mangaraw", "raw"),
+ LanguageOption("pt-BR", "manga-br", orderBy = "ASC"),
+ LanguageOption("ru", "manga-ru", "mangaru"),
+ LanguageOption("ru", "manga-ru-hentai", "hentai", " +18"),
+ LanguageOption("ru", "manga-ru-yaoi", "yaoi", " +18 Yaoi"),
+)
diff --git a/src/all/mangahosted/src/eu/kanade/tachiyomi/extension/all/mangahosted/MangaHostedUrlActivity.kt b/src/all/mangahosted/src/eu/kanade/tachiyomi/extension/all/mangahosted/MangaHostedUrlActivity.kt
new file mode 100644
index 000000000..98c94933e
--- /dev/null
+++ b/src/all/mangahosted/src/eu/kanade/tachiyomi/extension/all/mangahosted/MangaHostedUrlActivity.kt
@@ -0,0 +1,36 @@
+package eu.kanade.tachiyomi.extension.all.mangahosted
+
+import android.app.Activity
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import kotlin.system.exitProcess
+
+class MangaHostedUrlActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val pathSegments = intent?.data?.pathSegments
+
+ if (pathSegments != null && pathSegments.size > 1) {
+ val intent = Intent().apply {
+ action = "eu.kanade.tachiyomi.SEARCH"
+ putExtra("query", slug(pathSegments))
+ putExtra("filter", packageName)
+ }
+
+ try {
+ startActivity(intent)
+ } catch (e: ActivityNotFoundException) {
+ Log.e("UnionMangasUrlActivity", e.toString())
+ }
+ }
+
+ finish()
+ exitProcess(0)
+ }
+
+ private fun slug(pathSegments: List) =
+ "${MangaHosted.SEARCH_PREFIX}${pathSegments[1]}"
+}