Add Remanga source

pull/26/head
Koitharu 5 years ago
parent 0d0e3acd04
commit 85c424580a

@ -27,8 +27,9 @@ enum class MangaSource(
MANGATOWN("MangaTown", "en", MangaTownRepository::class.java), MANGATOWN("MangaTown", "en", MangaTownRepository::class.java),
MANGALIB("MangaLib", "ru", MangaLibRepository::class.java), MANGALIB("MangaLib", "ru", MangaLibRepository::class.java),
NUDEMOON("Nude-Moon", "ru", NudeMoonRepository::class.java), NUDEMOON("Nude-Moon", "ru", NudeMoonRepository::class.java),
MANGAREAD("MangaRead", "en", MangareadRepository::class.java); MANGAREAD("MangaRead", "en", MangareadRepository::class.java),
// HENTAILIB("HentaiLib", "ru", HentaiLibRepository::class.java) REMANGA("Remanga", "ru", RemangaRepository::class.java),
HENTAILIB("HentaiLib", "ru", HentaiLibRepository::class.java);
@get:Throws(NoBeanDefFoundException::class) @get:Throws(NoBeanDefFoundException::class)
@Deprecated("") @Deprecated("")

@ -19,7 +19,9 @@ val parserModule
factory<MangaRepository>(named(MangaSource.HENCHAN)) { HenChanRepository(get()) } factory<MangaRepository>(named(MangaSource.HENCHAN)) { HenChanRepository(get()) }
factory<MangaRepository>(named(MangaSource.YAOICHAN)) { YaoiChanRepository(get()) } factory<MangaRepository>(named(MangaSource.YAOICHAN)) { YaoiChanRepository(get()) }
factory<MangaRepository>(named(MangaSource.MANGATOWN)) { MangaTownRepository(get()) } factory<MangaRepository>(named(MangaSource.MANGATOWN)) { MangaTownRepository(get()) }
single<MangaRepository>(named(MangaSource.MANGALIB)) { MangaLibRepository(get()) } factory<MangaRepository>(named(MangaSource.MANGALIB)) { MangaLibRepository(get()) }
factory<MangaRepository>(named(MangaSource.NUDEMOON)) { NudeMoonRepository(get()) } factory<MangaRepository>(named(MangaSource.NUDEMOON)) { NudeMoonRepository(get()) }
factory<MangaRepository>(named(MangaSource.MANGAREAD)) { MangareadRepository(get()) } factory<MangaRepository>(named(MangaSource.MANGAREAD)) { MangareadRepository(get()) }
factory<MangaRepository>(named(MangaSource.REMANGA)) { RemangaRepository(get()) }
factory<MangaRepository>(named(MangaSource.HENTAILIB)) { HentaiLibRepository(get()) }
} }

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.core.parser package org.koitharu.kotatsu.core.parser
import android.net.Uri
import org.koitharu.kotatsu.base.domain.MangaLoaderContext import org.koitharu.kotatsu.base.domain.MangaLoaderContext
import org.koitharu.kotatsu.core.model.MangaPage import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSource
@ -23,4 +24,17 @@ abstract class RemoteMangaRepository(
override suspend fun getTags(): Set<MangaTag> = emptySet() override suspend fun getTags(): Set<MangaTag> = emptySet()
abstract fun onCreatePreferences(): Set<String> abstract fun onCreatePreferences(): Set<String>
protected fun generateUid(url: String): Long {
val uri = Uri.parse(url)
val path = uri.path ?: error("Cannot generate uid: bad uri \"$url\"")
val x = source.name.hashCode()
val y = path.hashCode()
return (x.toLong() shl 32) or (y.toLong() and 0xffffffffL)
}
protected fun generateUid(id: Int): Long {
val x = source.name.hashCode()
return (x.toLong() shl 32) or (id.toLong() and 0xffffffffL)
}
} }

@ -1,10 +1,12 @@
package org.koitharu.kotatsu.core.parser.site package org.koitharu.kotatsu.core.parser.site
/* import org.koitharu.kotatsu.base.domain.MangaLoaderContext
import org.koitharu.kotatsu.core.model.MangaSource
class HentaiLibRepository(loaderContext: MangaLoaderContext) : MangaLibRepository(loaderContext) { class HentaiLibRepository(loaderContext: MangaLoaderContext) : MangaLibRepository(loaderContext) {
protected override val defaultDomain = "hentailib.me" override val defaultDomain = "hentailib.me"
override val source = MangaSource.HENTAILIB override val source = MangaSource.HENTAILIB
}*/ }

@ -216,6 +216,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
.parseJsonArray() .parseJsonArray()
return json.map { jo -> return json.map { jo ->
val url = "https://$domain/${jo.getString("slug")}" val url = "https://$domain/${jo.getString("slug")}"
val covers = jo.getJSONObject("covers")
Manga( Manga(
id = url.longHashCode(), id = url.longHashCode(),
url = url, url = url,
@ -227,7 +228,8 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
.toFloatOrNull()?.div(5f) ?: Manga.NO_RATING, .toFloatOrNull()?.div(5f) ?: Manga.NO_RATING,
state = null, state = null,
source = source, source = source,
coverUrl = "https://$domain${jo.getJSONObject("covers").getString("thumbnail")}" coverUrl = "https://$domain${covers.getString("thumbnail")}",
largeCoverUrl = "https://$domain${covers.getString("default")}"
) )
} }
} }

@ -0,0 +1,183 @@
package org.koitharu.kotatsu.core.parser.site
import androidx.collection.arraySetOf
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
import org.koitharu.kotatsu.core.exceptions.ParseException
import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.core.prefs.SourceSettings
import org.koitharu.kotatsu.utils.ext.*
import java.util.*
import kotlin.collections.ArrayList
class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) {
override val source = MangaSource.REMANGA
override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.POPULARITY,
SortOrder.RATING,
SortOrder.ALPHABETICAL,
SortOrder.UPDATED,
SortOrder.NEWEST
)
override suspend fun getList(
offset: Int,
query: String?,
sortOrder: SortOrder?,
tag: MangaTag?
): List<Manga> {
val domain = conf.getDomain(DEFAULT_DOMAIN)
val urlBuilder = StringBuilder()
.append("https://api.")
.append(domain)
if (query != null) {
urlBuilder.append("/api/search/?query=")
.append(query.urlEncoded())
} else {
urlBuilder.append("/api/search/catalog/?page=")
.append("&ordering=")
.append(getSortKey(sortOrder))
if (tag != null) {
urlBuilder.append("&genres=" + tag.key)
}
}
urlBuilder.append((offset / PAGE_SIZE) + 1)
.append("&count=")
.append(PAGE_SIZE)
val content = loaderContext.httpGet(urlBuilder.toString()).parseJson()
.getJSONArray("content")
return content.map { jo ->
val url = "https://$domain/manga/${jo.getString("dir")}"
val img = jo.getJSONObject("img")
Manga(
id = generateUid(url),
url = url,
title = jo.getString("rus_name"),
altTitle = jo.getString("en_name"),
rating = jo.getString("avg_rating").toFloatOrNull()?.div(10f) ?: Manga.NO_RATING,
coverUrl = "https://api.$domain${img.getString("mid")}",
largeCoverUrl = "https://api.$domain${img.getString("high")}",
author = null,
tags = jo.optJSONArray("genres")?.mapToSet { g ->
MangaTag(
title = g.getString("name"),
key = g.getInt("id").toString(),
source = MangaSource.REMANGA
)
}.orEmpty(),
source = MangaSource.REMANGA
)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val domain = conf.getDomain(DEFAULT_DOMAIN)
val slug = manga.url.toHttpUrl().pathSegments.last()
val data = loaderContext.httpGet(
url = "https://api.$domain/api/titles/$slug/"
).parseJson()
val content = try {
data.getJSONObject("content")
} catch (e: JSONException) {
throw ParseException(data.optString("msg"), e)
}
val branchId = content.getJSONArray("branches").optJSONObject(0)
?.getLong("id") ?: throw ParseException("No branches found")
val chapters = loaderContext.httpGet(
url = "https://api.$domain/api/titles/chapters/?branch_id=$branchId"
).parseJson().getJSONArray("content")
return manga.copy(
description = content.getString("description"),
state = when (content.optJSONObject("status")?.getInt("id")) {
STATUS_ONGOING -> MangaState.ONGOING
STATUS_FINISHED -> MangaState.FINISHED
else -> null
},
tags = content.getJSONArray("genres").mapToSet { g ->
MangaTag(
title = g.getString("name"),
key = g.getInt("id").toString(),
source = MangaSource.REMANGA
)
},
chapters = chapters.mapIndexed { i, jo ->
val id = jo.getLong("id")
val name = jo.getString("name")
MangaChapter(
id = generateUid(id.toInt()),
url = "https://api.$domain/api/titles/chapters/$id/",
number = chapters.length() - i,
name = buildString {
append("Глава ")
append(jo.getString("chapter"))
if (name.isNotEmpty()) {
append(" - ")
append(name)
}
},
source = MangaSource.REMANGA
)
}.asReversed()
)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val referer = "https://${conf.getDomain(DEFAULT_DOMAIN)}/"
val content = loaderContext.httpGet(chapter.url).parseJson()
.getJSONObject("content").getJSONArray("pages")
val pages = ArrayList<MangaPage>(content.length())
for (i in 0 until content.length()) {
when (val item = content.get(i)) {
is JSONObject -> pages += parsePage(item, referer)
is JSONArray -> item.mapTo(pages) { parsePage(it, referer) }
else -> throw ParseException("Unknown json item $item")
}
}
return pages
}
override suspend fun getTags(): Set<MangaTag> {
val domain = conf.getDomain(DEFAULT_DOMAIN)
val content = loaderContext.httpGet("https://api.$domain/api/forms/titles/?get=genres")
.parseJson().getJSONObject("content").getJSONArray("genres")
return content.mapToSet { jo ->
MangaTag(
title = jo.getString("name"),
key = jo.getInt("id").toString(),
source = source
)
}
}
override fun onCreatePreferences() = arraySetOf(SourceSettings.KEY_DOMAIN)
private fun getSortKey(order: SortOrder?) = when (order) {
SortOrder.UPDATED -> "-chapter_date"
SortOrder.POPULARITY -> "-rating"
SortOrder.RATING -> "-votes"
SortOrder.NEWEST -> "-id"
else -> "-rating"
}
private fun parsePage(jo: JSONObject, referer: String) = MangaPage(
id = generateUid(jo.getLong("id").toInt()),
url = jo.getString("link"),
referer = referer,
source = source
)
private companion object {
const val PAGE_SIZE = 30
const val DEFAULT_DOMAIN = "remanga.org"
const val STATUS_ONGOING = 1
const val STATUS_FINISHED = 0
}
}

@ -111,4 +111,17 @@ fun String.md5(): String {
return BigInteger(1, md.digest(toByteArray())) return BigInteger(1, md.digest(toByteArray()))
.toString(16) .toString(16)
.padStart(32, '0') .padStart(32, '0')
}
fun String.substringBetween(from: String, to: String, fallbackValue: String): String {
val fromIndex = indexOf(from)
if (fromIndex == -1) {
return fallbackValue
}
val toIndex = lastIndexOf(to)
return if (toIndex == -1) {
fallbackValue
} else {
substring(fromIndex + from.length, toIndex)
}
} }
Loading…
Cancel
Save