|
|
|
@ -6,14 +6,11 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
|
|
|
|
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
|
|
|
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
|
|
|
import org.koitharu.kotatsu.parsers.core.LegacyPagedMangaParser
|
|
|
|
import org.koitharu.kotatsu.parsers.core.LegacyPagedMangaParser
|
|
|
|
import org.koitharu.kotatsu.parsers.model.*
|
|
|
|
import org.koitharu.kotatsu.parsers.model.*
|
|
|
|
import org.koitharu.kotatsu.parsers.network.UserAgents
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.parsers.util.*
|
|
|
|
import org.koitharu.kotatsu.parsers.util.*
|
|
|
|
import org.koitharu.kotatsu.parsers.util.json.*
|
|
|
|
import org.koitharu.kotatsu.parsers.util.json.*
|
|
|
|
import java.text.SimpleDateFormat
|
|
|
|
import java.text.SimpleDateFormat
|
|
|
|
import java.util.*
|
|
|
|
import java.util.*
|
|
|
|
import org.koitharu.kotatsu.parsers.Broken
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Broken("TODO: Add author search")
|
|
|
|
|
|
|
|
@MangaSourceParser("SHINIGAMI", "Shinigami", "id")
|
|
|
|
@MangaSourceParser("SHINIGAMI", "Shinigami", "id")
|
|
|
|
internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
LegacyPagedMangaParser(context, MangaParserSource.SHINIGAMI, 24) {
|
|
|
|
LegacyPagedMangaParser(context, MangaParserSource.SHINIGAMI, 24) {
|
|
|
|
@ -47,12 +44,12 @@ internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
availableStates = EnumSet.of(
|
|
|
|
availableStates = EnumSet.of(
|
|
|
|
MangaState.ONGOING,
|
|
|
|
MangaState.ONGOING,
|
|
|
|
MangaState.FINISHED,
|
|
|
|
MangaState.FINISHED,
|
|
|
|
MangaState.PAUSED
|
|
|
|
MangaState.PAUSED,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
availableContentTypes = EnumSet.of(
|
|
|
|
availableContentTypes = EnumSet.of(
|
|
|
|
ContentType.MANGA,
|
|
|
|
ContentType.MANGA,
|
|
|
|
ContentType.MANHWA,
|
|
|
|
ContentType.MANHWA,
|
|
|
|
ContentType.MANHUA
|
|
|
|
ContentType.MANHUA,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -82,14 +79,14 @@ internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
SortOrder.NEWEST, SortOrder.NEWEST_ASC -> "latest"
|
|
|
|
SortOrder.NEWEST, SortOrder.NEWEST_ASC -> "latest"
|
|
|
|
SortOrder.RATING, SortOrder.RATING_ASC -> "rating"
|
|
|
|
SortOrder.RATING, SortOrder.RATING_ASC -> "rating"
|
|
|
|
else -> "latest"
|
|
|
|
else -> "latest"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
)
|
|
|
|
append("&sort_order=")
|
|
|
|
append("&sort_order=")
|
|
|
|
append(
|
|
|
|
append(
|
|
|
|
when (order) {
|
|
|
|
when (order) {
|
|
|
|
SortOrder.POPULARITY_ASC, SortOrder.NEWEST_ASC, SortOrder.RATING_ASC -> "asc"
|
|
|
|
SortOrder.POPULARITY_ASC, SortOrder.NEWEST_ASC, SortOrder.RATING_ASC -> "asc"
|
|
|
|
else -> "desc"
|
|
|
|
else -> "desc"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
filter.states.oneOrThrowIfMany()?.let {
|
|
|
|
filter.states.oneOrThrowIfMany()?.let {
|
|
|
|
@ -100,7 +97,7 @@ internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
MangaState.ONGOING -> "ongoing"
|
|
|
|
MangaState.ONGOING -> "ongoing"
|
|
|
|
MangaState.PAUSED -> "hiatus"
|
|
|
|
MangaState.PAUSED -> "hiatus"
|
|
|
|
else -> ""
|
|
|
|
else -> ""
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -113,7 +110,7 @@ internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
ContentType.MANHUA -> "manhua"
|
|
|
|
ContentType.MANHUA -> "manhua"
|
|
|
|
ContentType.MANHWA -> "manhwa"
|
|
|
|
ContentType.MANHWA -> "manhwa"
|
|
|
|
else -> ""
|
|
|
|
else -> ""
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -130,6 +127,11 @@ internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
append("&genre_exclude_mode=and")
|
|
|
|
append("&genre_exclude_mode=and")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!filter.author.isNullOrEmpty()) {
|
|
|
|
|
|
|
|
append("&author=")
|
|
|
|
|
|
|
|
append(filter.author.lowercase().replace(' ', '-').urlEncoded())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!filter.query.isNullOrEmpty()) {
|
|
|
|
if (!filter.query.isNullOrEmpty()) {
|
|
|
|
append("&q=")
|
|
|
|
append("&q=")
|
|
|
|
val encodedQuery = filter.query.splitByWhitespace()
|
|
|
|
val encodedQuery = filter.query.splitByWhitespace()
|
|
|
|
@ -150,10 +152,9 @@ internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
url = "/manga/detail/$id",
|
|
|
|
url = "/manga/detail/$id",
|
|
|
|
publicUrl = "https://$domain/series/$id",
|
|
|
|
publicUrl = "https://$domain/series/$id",
|
|
|
|
title = jo.getString("title"),
|
|
|
|
title = jo.getString("title"),
|
|
|
|
altTitles = setOf(jo.optString("alternative_title") ?: ""),
|
|
|
|
altTitles = setOfNotNull(jo.getStringOrNull("alternative_title")),
|
|
|
|
coverUrl = jo.getString("cover_image_url"),
|
|
|
|
coverUrl = jo.getStringOrNull("cover_image_url"),
|
|
|
|
largeCoverUrl = jo.optString("cover_portrait_url")
|
|
|
|
largeCoverUrl = jo.getStringOrNull("cover_portrait_url"),
|
|
|
|
.takeIf { it.isNotEmpty() },
|
|
|
|
|
|
|
|
authors = jo.optJSONObject("taxonomy")
|
|
|
|
authors = jo.optJSONObject("taxonomy")
|
|
|
|
?.optJSONArray("Author")
|
|
|
|
?.optJSONArray("Author")
|
|
|
|
?.mapJSONToSet { x ->
|
|
|
|
?.mapJSONToSet { x ->
|
|
|
|
@ -164,26 +165,26 @@ internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
?.mapJSONToSet { x ->
|
|
|
|
?.mapJSONToSet { x ->
|
|
|
|
MangaTag(
|
|
|
|
MangaTag(
|
|
|
|
key = x.getString("slug"),
|
|
|
|
key = x.getString("slug"),
|
|
|
|
title = x.getString("name"),
|
|
|
|
title = x.getString("name").toTitleCase(sourceLocale),
|
|
|
|
source = source
|
|
|
|
source = source,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}.orEmpty(),
|
|
|
|
}.orEmpty(),
|
|
|
|
state = when (jo.getInt("status")) {
|
|
|
|
state = when (jo.getIntOrDefault("status", 0)) {
|
|
|
|
1 -> MangaState.ONGOING
|
|
|
|
1 -> MangaState.ONGOING
|
|
|
|
2 -> MangaState.FINISHED
|
|
|
|
2 -> MangaState.FINISHED
|
|
|
|
3 -> MangaState.PAUSED
|
|
|
|
3 -> MangaState.PAUSED
|
|
|
|
else -> null
|
|
|
|
else -> null
|
|
|
|
},
|
|
|
|
},
|
|
|
|
description = jo.optString("description"),
|
|
|
|
description = jo.getStringOrNull("description"),
|
|
|
|
contentRating = null,
|
|
|
|
contentRating = null,
|
|
|
|
source = source,
|
|
|
|
source = source,
|
|
|
|
rating = RATING_UNKNOWN
|
|
|
|
rating = RATING_UNKNOWN,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override suspend fun getDetails(manga: Manga): Manga {
|
|
|
|
override suspend fun getDetails(manga: Manga): Manga {
|
|
|
|
val json = webClient.httpGet("https://$apiSuffix" + manga.url).parseJson()
|
|
|
|
val json = webClient.httpGet("https://$apiSuffix${manga.url}").parseJson()
|
|
|
|
val jo = json.getJSONObject("data")
|
|
|
|
val jo = json.getJSONObject("data")
|
|
|
|
val id = jo.getString("manga_id")
|
|
|
|
val id = jo.getString("manga_id")
|
|
|
|
|
|
|
|
|
|
|
|
@ -196,22 +197,21 @@ internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
title = x.getString("name"),
|
|
|
|
title = x.getString("name"),
|
|
|
|
source = source,
|
|
|
|
source = source,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}.orEmpty(),
|
|
|
|
} ?: manga.tags,
|
|
|
|
authors = jo.optJSONObject("taxonomy")
|
|
|
|
authors = jo.optJSONObject("taxonomy")
|
|
|
|
?.optJSONArray("Author")
|
|
|
|
?.optJSONArray("Author")
|
|
|
|
?.mapJSONToSet { x ->
|
|
|
|
?.mapJSONToSet { x ->
|
|
|
|
x.getString("name")
|
|
|
|
x.getString("name")
|
|
|
|
}.orEmpty(),
|
|
|
|
} ?: manga.authors,
|
|
|
|
state = when (jo.getInt("status")) {
|
|
|
|
state = when (jo.getIntOrDefault("status", 0)) {
|
|
|
|
1 -> MangaState.ONGOING
|
|
|
|
1 -> MangaState.ONGOING
|
|
|
|
2 -> MangaState.FINISHED
|
|
|
|
2 -> MangaState.FINISHED
|
|
|
|
3 -> MangaState.PAUSED
|
|
|
|
3 -> MangaState.PAUSED
|
|
|
|
else -> null
|
|
|
|
else -> null
|
|
|
|
},
|
|
|
|
},
|
|
|
|
description = jo.optString("description"),
|
|
|
|
description = jo.getStringOrNull("description"),
|
|
|
|
largeCoverUrl = jo.optString("cover_portrait_url")
|
|
|
|
largeCoverUrl = jo.getStringOrNull("cover_portrait_url"),
|
|
|
|
.takeIf { it.isNotEmpty() },
|
|
|
|
chapters = getChapters(id),
|
|
|
|
chapters = getChapters(id)
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -222,21 +222,18 @@ internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
|
|
|
|
|
|
|
|
return data.mapJSON { jo ->
|
|
|
|
return data.mapJSON { jo ->
|
|
|
|
val chapterId = jo.getString("chapter_id")
|
|
|
|
val chapterId = jo.getString("chapter_id")
|
|
|
|
val number = jo.getInt("chapter_number")
|
|
|
|
val title = jo.getStringOrNull("chapter_title")
|
|
|
|
val title = jo.optString("chapter_title")
|
|
|
|
|
|
|
|
.takeIf { it.isNotEmpty() }
|
|
|
|
|
|
|
|
?: "Chapter $number"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MangaChapter(
|
|
|
|
MangaChapter(
|
|
|
|
id = generateUid(chapterId),
|
|
|
|
id = generateUid(chapterId),
|
|
|
|
title = title,
|
|
|
|
title = title,
|
|
|
|
number = number.toFloat(),
|
|
|
|
number = jo.getFloatOrDefault("chapter_number", 0f),
|
|
|
|
url = "chapter/detail/$chapterId",
|
|
|
|
url = "chapter/detail/$chapterId",
|
|
|
|
scanlator = null,
|
|
|
|
scanlator = null,
|
|
|
|
uploadDate = jo.getString("release_date").parseDate(),
|
|
|
|
uploadDate = jo.getString("release_date").parseDate(),
|
|
|
|
branch = null,
|
|
|
|
branch = null,
|
|
|
|
source = source,
|
|
|
|
source = source,
|
|
|
|
volume = 0
|
|
|
|
volume = 0,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -250,7 +247,7 @@ internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
|
|
|
|
|
|
|
|
return List(datas.length()) { i ->
|
|
|
|
return List(datas.length()) { i ->
|
|
|
|
val imgExt = datas.getString(i)
|
|
|
|
val imgExt = datas.getString(i)
|
|
|
|
val imageUrl = "https://$cdnSuffix" + basePath + imgExt
|
|
|
|
val imageUrl = "https://$cdnSuffix$basePath$imgExt"
|
|
|
|
|
|
|
|
|
|
|
|
MangaPage(
|
|
|
|
MangaPage(
|
|
|
|
id = generateUid(imageUrl),
|
|
|
|
id = generateUid(imageUrl),
|
|
|
|
@ -267,7 +264,7 @@ internal class Shinigami(context: MangaLoaderContext) :
|
|
|
|
return json.getJSONArray("data").mapJSONToSet { x ->
|
|
|
|
return json.getJSONArray("data").mapJSONToSet { x ->
|
|
|
|
MangaTag(
|
|
|
|
MangaTag(
|
|
|
|
key = x.getString("slug"),
|
|
|
|
key = x.getString("slug"),
|
|
|
|
title = x.getString("name"),
|
|
|
|
title = x.getString("name").toTitleCase(sourceLocale),
|
|
|
|
source = source,
|
|
|
|
source = source,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|