Finish some more advanced mangadex delegation features, more to come later
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package exh.md.utils
|
||||
|
||||
enum class FollowStatus(val int: Int) {
|
||||
UNFOLLOWED(0),
|
||||
READING(1),
|
||||
COMPLETED(2),
|
||||
ON_HOLD(3),
|
||||
PLAN_TO_READ(4),
|
||||
DROPPED(5),
|
||||
RE_READING(6);
|
||||
|
||||
companion object {
|
||||
fun fromInt(value: Int): FollowStatus? = values().find { it.int == value }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package exh.md.utils
|
||||
|
||||
enum class MdLang(val lang: String, val dexLang: String, val langId: Int) {
|
||||
English("en", "gb", 1),
|
||||
Japanese("ja", "jp", 2),
|
||||
Polish("pl", "pl", 3),
|
||||
SerboCroatian("sh", "rs", 4),
|
||||
Dutch("nl", "nl", 5),
|
||||
Italian("it", "it", 6),
|
||||
Russian("ru", "ru", 7),
|
||||
German("de", "de", 8),
|
||||
Hungarian("hu", "hu", 9),
|
||||
French("fr", "fr", 10),
|
||||
Finnish("fi", "fi", 11),
|
||||
Vietnamese("vi", "vn", 12),
|
||||
Greek("el", "gr", 13),
|
||||
Bulgarian("bg", "bg", 14),
|
||||
Spanish("es", "es", 15),
|
||||
PortugeseBrazilian("pt-BR", "br", 16),
|
||||
Portuguese("pt", "pt", 17),
|
||||
Swedish("sv", "se", 18),
|
||||
Arabic("ar", "sa", 19),
|
||||
Danish("da", "dk", 20),
|
||||
ChineseSimplifed("zh-Hans", "cn", 21),
|
||||
Bengali("bn", "bd", 22),
|
||||
Romanian("ro", "ro", 23),
|
||||
Czech("cs", "cz", 24),
|
||||
Mongolian("mn", "mn", 25),
|
||||
Turkish("tr", "tr", 26),
|
||||
Indonesian("id", "id", 27),
|
||||
Korean("ko", "kr", 28),
|
||||
SpanishLTAM("es-419", "mx", 29),
|
||||
Persian("fa", "ir", 30),
|
||||
Malay("ms", "my", 31),
|
||||
Thai("th", "th", 32),
|
||||
Catalan("ca", "ct", 33),
|
||||
Filipino("fil", "ph", 34),
|
||||
ChineseTraditional("zh-Hant", "hk", 35),
|
||||
Ukrainian("uk", "ua", 36),
|
||||
Burmese("my", "mm", 37),
|
||||
Lithuanian("lt", "il", 38),
|
||||
Hebrew("he", "il", 39),
|
||||
Hindi("hi", "in", 40),
|
||||
Norwegian("no", "no", 42)
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
package exh.md.utils
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
import kotlin.math.floor
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.jsoup.parser.Parser
|
||||
|
||||
class MdUtil {
|
||||
|
||||
companion object {
|
||||
const val cdnUrl = "https://mangadex.org" // "https://s0.mangadex.org"
|
||||
const val baseUrl = "https://mangadex.org"
|
||||
const val randMangaPage = "/manga/"
|
||||
const val apiManga = "/api/manga/"
|
||||
const val apiChapter = "/api/chapter/"
|
||||
const val apiChapterSuffix = "?mark_read=0"
|
||||
const val groupSearchUrl = "$baseUrl/groups/0/1/"
|
||||
const val followsAllApi = "/api/?type=manga_follows"
|
||||
const val followsMangaApi = "/api/?type=manga_follows&manga_id="
|
||||
const val coversApi = "/api/index.php?type=covers&id="
|
||||
const val reportUrl = "https://api.mangadex.network/report"
|
||||
const val imageUrl = "$baseUrl/data"
|
||||
|
||||
val jsonParser = Json {
|
||||
isLenient = true
|
||||
ignoreUnknownKeys = true
|
||||
allowSpecialFloatingPointValues = true
|
||||
useArrayPolymorphism = true
|
||||
prettyPrint = true
|
||||
}
|
||||
|
||||
private const
|
||||
val scanlatorSeparator = " & "
|
||||
|
||||
val validOneShotFinalChapters = listOf("0", "1")
|
||||
|
||||
val englishDescriptionTags = listOf(
|
||||
"[b][u]English:",
|
||||
"[b][u]English",
|
||||
"[English]:",
|
||||
"[B][ENG][/B]"
|
||||
)
|
||||
|
||||
val descriptionLanguages = listOf(
|
||||
"Russian / Русский",
|
||||
"[u]Russian",
|
||||
"[b][u]Russian",
|
||||
"[RUS]",
|
||||
"Russian / Русский",
|
||||
"Russian/Русский:",
|
||||
"Russia/Русское",
|
||||
"Русский",
|
||||
"RUS:",
|
||||
"[b][u]German / Deutsch",
|
||||
"German/Deutsch:",
|
||||
"Español / Spanish",
|
||||
"Spanish / Español",
|
||||
"Spanish / Espa & ntilde; ol",
|
||||
"Spanish / Español",
|
||||
"[b][u]Spanish",
|
||||
"[Español]:",
|
||||
"[b] Spanish: [/ b]",
|
||||
"Spanish/Español",
|
||||
"Español / Spanish",
|
||||
"Italian / Italiano",
|
||||
"Pasta-Pizza-Mandolino/Italiano",
|
||||
"Polish / polski",
|
||||
"Polish / Polski",
|
||||
"Polish Summary / Polski Opis",
|
||||
"Polski",
|
||||
"Portuguese (BR) / Português",
|
||||
"Portuguese / Português",
|
||||
"Português / Portuguese",
|
||||
"Portuguese / Portugu",
|
||||
"Portuguese / Português",
|
||||
"Português",
|
||||
"Portuguese (BR) / Portugu & ecirc;",
|
||||
"Portuguese (BR) / Portuguê",
|
||||
"[PTBR]",
|
||||
"Résume Français",
|
||||
"Résumé Français",
|
||||
"[b][u]French",
|
||||
"French / Français",
|
||||
"Français",
|
||||
"[hr]Fr:",
|
||||
"French - Français:",
|
||||
"Turkish / Türkçe",
|
||||
"Turkish/Türkçe",
|
||||
"[b][u]Chinese",
|
||||
"Arabic / العربية",
|
||||
"العربية",
|
||||
"[hr]TH",
|
||||
"[b][u]Vietnamese",
|
||||
"[b]Links:",
|
||||
"[b]Link[/b]",
|
||||
"Links:",
|
||||
"[b]External Links"
|
||||
)
|
||||
|
||||
// guess the thumbnail url is .jpg this has a ~80% success rate
|
||||
fun formThumbUrl(mangaUrl: String, lowQuality: Boolean): String {
|
||||
var ext = ".jpg"
|
||||
if (lowQuality) {
|
||||
ext = ".thumb$ext"
|
||||
}
|
||||
return cdnUrl + "/images/manga/" + getMangaId(mangaUrl) + ext
|
||||
}
|
||||
|
||||
// Get the ID from the manga url
|
||||
fun getMangaId(url: String): String {
|
||||
val lastSection = url.trimEnd('/').substringAfterLast("/")
|
||||
return if (lastSection.toIntOrNull() != null) {
|
||||
lastSection
|
||||
} else {
|
||||
// this occurs if person has manga from before that had the id/name/
|
||||
url.trimEnd('/').substringBeforeLast("/").substringAfterLast("/")
|
||||
}
|
||||
}
|
||||
|
||||
fun getChapterId(url: String) = url.substringBeforeLast(apiChapterSuffix).substringAfterLast("/")
|
||||
|
||||
// creates the manga url from the browse for the api
|
||||
fun modifyMangaUrl(url: String): String =
|
||||
url.replace("/title/", "/manga/").substringBeforeLast("/") + "/"
|
||||
|
||||
// Removes the ?timestamp from image urls
|
||||
fun removeTimeParamUrl(url: String): String = url.substringBeforeLast("?")
|
||||
|
||||
fun cleanString(string: String): String {
|
||||
val bbRegex =
|
||||
"""\[(\w+)[^]]*](.*?)\[/\1]""".toRegex()
|
||||
var intermediate = string
|
||||
.replace("[list]", "", true)
|
||||
.replace("[/list]", "", true)
|
||||
.replace("[*]", "")
|
||||
.replace("[hr]", "", true)
|
||||
.replace("[u]", "", true)
|
||||
.replace("[/u]", "", true)
|
||||
.replace("[b]", "", true)
|
||||
.replace("[/b]", "", true)
|
||||
|
||||
// Recursively remove nested bbcode
|
||||
while (bbRegex.containsMatchIn(intermediate)) {
|
||||
intermediate = intermediate.replace(bbRegex, "$2")
|
||||
}
|
||||
return Parser.unescapeEntities(intermediate, false)
|
||||
}
|
||||
|
||||
fun cleanDescription(string: String): String {
|
||||
var newDescription = string
|
||||
descriptionLanguages.forEach {
|
||||
newDescription = newDescription.substringBefore(it)
|
||||
}
|
||||
|
||||
englishDescriptionTags.forEach {
|
||||
newDescription = newDescription.replace(it, "")
|
||||
}
|
||||
return cleanString(newDescription)
|
||||
}
|
||||
|
||||
fun getImageUrl(attr: String): String {
|
||||
// Some images are hosted elsewhere
|
||||
if (attr.startsWith("http")) {
|
||||
return attr
|
||||
}
|
||||
return baseUrl + attr
|
||||
}
|
||||
|
||||
fun getScanlators(scanlators: String): List<String> {
|
||||
if (scanlators.isBlank()) return emptyList()
|
||||
return scanlators.split(scanlatorSeparator).distinct()
|
||||
}
|
||||
|
||||
fun getScanlatorString(scanlators: Set<String>): String {
|
||||
return scanlators.toList().sorted().joinToString(scanlatorSeparator)
|
||||
}
|
||||
|
||||
fun getMissingChapterCount(chapters: List<SChapter>, mangaStatus: Int): String? {
|
||||
if (mangaStatus == SManga.COMPLETED) return null
|
||||
|
||||
// TODO
|
||||
val remove0ChaptersFromCount = chapters.distinctBy {
|
||||
/*if (it.chapter_txt.isNotEmpty()) {
|
||||
it.vol + it.chapter_txt
|
||||
} else {*/
|
||||
it.name
|
||||
/*}*/
|
||||
}.sortedByDescending { it.chapter_number }
|
||||
|
||||
remove0ChaptersFromCount.firstOrNull()?.let {
|
||||
val chpNumber = floor(it.chapter_number).toInt()
|
||||
val allChapters = (1..chpNumber).toMutableSet()
|
||||
|
||||
remove0ChaptersFromCount.forEach {
|
||||
allChapters.remove(floor(it.chapter_number).toInt())
|
||||
}
|
||||
|
||||
if (allChapters.size <= 0) return null
|
||||
return allChapters.size.toString()
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the url of the chapter without the scheme and domain. It saves some redundancy from
|
||||
* database and the urls could still work after a domain change.
|
||||
*
|
||||
* @param url the full url to the chapter.
|
||||
*/
|
||||
fun SChapter.setMDUrlWithoutDomain(url: String) {
|
||||
this.url = getMDUrlWithoutDomain(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the url of the manga without the scheme and domain. It saves some redundancy from
|
||||
* database and the urls could still work after a domain change.
|
||||
*
|
||||
* @param url the full url to the manga.
|
||||
*/
|
||||
fun SManga.setMDUrlWithoutDomain(url: String) {
|
||||
this.url = getMDUrlWithoutDomain(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the url of the given string without the scheme and domain.
|
||||
*
|
||||
* @param orig the full url.
|
||||
*/
|
||||
private fun getMDUrlWithoutDomain(orig: String): String {
|
||||
return try {
|
||||
val uri = URI(orig)
|
||||
var out = uri.path
|
||||
if (uri.query != null) {
|
||||
out += "?" + uri.query
|
||||
}
|
||||
if (uri.fragment != null) {
|
||||
out += "#" + uri.fragment
|
||||
}
|
||||
out
|
||||
} catch (e: URISyntaxException) {
|
||||
orig
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user