Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/ja/kisslove/assets/i18n/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ all=All
ongoing=Ongoing
completed=Completed
genreTip=Press 'Reset' to refresh filters after loading
useRomajiTitle=use romaji
useRomajiSummary=This feature requires "Update library manga titles to match source" to be enabled.
2 changes: 2 additions & 0 deletions src/ja/kisslove/assets/i18n/messages_ja.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ all=すべて
ongoing=連載中
completed=完結
genreTip=読み込み後、「リセット」を押してフィルターを更新してください
useRomajiTitle=ローマ字タイトルを使用
useRomajiSummary=この機能を使用するには、「ライブラリーのマンガのタイトルをソースに合わせて更新」が有効になっている必要があります。
2 changes: 2 additions & 0 deletions src/ja/kisslove/assets/i18n/messages_zh.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ all=全部
ongoing=连载中
completed=已完结
genreTip=加载后请点击“重置”以刷新筛选条件。
useRomajiTitle=使用罗马音
useRomajiSummary=该功能需要“更新书架中的漫画标题以匹配图源”开启
2 changes: 1 addition & 1 deletion src/ja/kisslove/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {

keiyoushi {
name = "KissLove"
versionCode = 19
versionCode = 20
contentWarning = ContentWarning.MIXED
libVersion = "1.4"

Expand Down
108 changes: 66 additions & 42 deletions src/ja/kisslove/src/eu/kanade/tachiyomi/extension/ja/kisslove/Dto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import eu.kanade.tachiyomi.source.model.SManga
import keiyoushi.utils.tryParse
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonPrimitive
import org.jsoup.Jsoup

@Serializable
Expand All @@ -24,7 +28,7 @@ data class Manga(
val cover: String,
@SerialName("daily_views") val dailyViews: Long?,
val description: String = "",
val genres: String?,
@SerialName("genres") val genresRaw: JsonElement? = null,
@SerialName("group_uploader") val groupUploader: Long?,
val hidden: Int?,
val id: Long,
Expand All @@ -42,50 +46,70 @@ data class Manga(
val views: Long,
@SerialName("vote_count") val voteCount: Long?,
) {
fun toSManga() = SManga.create().apply {
url = this@Manga.slug
title = this@Manga.name
artist = this@Manga.artists
author = this@Manga.authors
description = "${Jsoup.parse(this@Manga.description).text()}\n\n$otherName"
thumbnail_url = this@Manga.cover
genre = this@Manga.genres?.split(",")?.joinToString { it.trim() }
status = if (mStatus == 1) SManga.COMPLETED else SManga.ONGOING
fun toSManga(useRomajiTitle: Boolean = true): SManga {
val (mainName, extraName) = if (useRomajiTitle) {
name to otherName
} else {
val parts = otherName.split(Regex(",\\s*"))
.map { it.trim() }
.filter { it.isNotEmpty() }

val (jpParts, nonJpParts) = parts.partition { hasJapaneseKana(it) }

val mainTitle = if (jpParts.isNotEmpty()) {
jpParts.maxByOrNull { it.length } ?: jpParts.first()
} else {
parts.firstOrNull() ?: name
}

val extra = buildExtraName(mainTitle, name, jpParts, nonJpParts)

mainTitle to extra
}

val genres = when (val raw = genresRaw) {
is JsonArray -> raw.map { it.jsonPrimitive.content }
is JsonPrimitive -> raw.content.split(",")
else -> emptyList()
}

return SManga.create().apply {
url = this@Manga.slug
title = mainName
artist = this@Manga.artists
author = this@Manga.authors
description = "${Jsoup.parse(this@Manga.description).text()}\n\n$extraName"
thumbnail_url = this@Manga.cover
genre = genres.joinToString { it.trim() }
status = if (mStatus == 1) SManga.COMPLETED else SManga.ONGOING
}
}
}

@Serializable
data class ListPagedManga(
val currentPage: Long,
val items: List<MangaEntry>,
val totalItems: Long,
val totalPages: Long,
)
private fun hasJapaneseKana(text: String): Boolean {
val kanaRegex = Regex("[\\p{Script=Hiragana}\\p{Script=Katakana}]")
return kanaRegex.containsMatchIn(text)
}

@Serializable
data class MangaEntry(
val artists: String?,
val authors: String,
val cover: String,
val description: String = "",
val genres: List<String>?,
val hidden: Int?,
val id: Long,
@SerialName("m_status") val mStatus: Int,
val name: String,
@SerialName("other_name") val otherName: String,
val slug: String,
@SerialName("trans_group") val transGroup: String,
) {
fun toSManga() = SManga.create().apply {
url = this@MangaEntry.slug
title = this@MangaEntry.name
artist = this@MangaEntry.artists
author = this@MangaEntry.authors
description = "${Jsoup.parse(this@MangaEntry.description).text()}\n\n$otherName"
thumbnail_url = this@MangaEntry.cover
genre = this@MangaEntry.genres?.joinToString { it.trim() }
status = if (mStatus == 1) SManga.COMPLETED else SManga.ONGOING
private fun buildExtraName(
mainTitle: String,
originalName: String,
jpParts: List<String>,
nonJpParts: List<String>,
): String {
val extraParts = mutableListOf<String>()

extraParts.add(originalName)

extraParts.addAll(nonJpParts)

jpParts
.filter { it != mainTitle }
.forEach { extraParts.add(it) }

return extraParts
.filter { it.isNotEmpty() }
.distinct()
.joinToString(", ")
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package eu.kanade.tachiyomi.extension.ja.kisslove

import android.content.SharedPreferences
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
Expand All @@ -11,6 +15,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import keiyoushi.annotation.Source
import keiyoushi.lib.i18n.Intl
import keiyoushi.utils.getPreferences
import keiyoushi.utils.parseAs
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -24,15 +29,22 @@ import java.text.SimpleDateFormat
import java.util.*

@Source
abstract class KissLove : HttpSource() {
override val supportsLatest = true
class KissLove(
override val id: Long,
override val name: String,
override val baseUrl: String,
override val lang: String,
override val supportsLatest: Boolean = true,
) : HttpSource(),
ConfigurableSource {

private val intl = Intl(
Locale.getDefault().language,
setOf("en", "ja", "zh"),
lang,
this::class.java.classLoader!!,
)
private val preferences: SharedPreferences = getPreferences()
private var cachedGenres: List<CheckBoxFilter>? = null
private val scope = CoroutineScope(Dispatchers.IO)

Expand All @@ -45,7 +57,7 @@ abstract class KissLove : HttpSource() {

override fun popularMangaParse(response: Response): MangasPage {
val result = response.parseAs<List<Manga>>()
val mangas = result.map { it.toSManga() }
val mangas = result.map { it.toSManga(preferences.getBoolean(USE_ROMAJI_PREF, true)) }
return MangasPage(mangas, false)
}

Expand All @@ -62,7 +74,7 @@ abstract class KissLove : HttpSource() {

override fun latestUpdatesParse(response: Response): MangasPage {
val result = response.parseAs<PagedManga>()
val mangas = result.items.map { it.toSManga() }
val mangas = result.items.map { it.toSManga(preferences.getBoolean(USE_ROMAJI_PREF, true)) }
val hasNextPage = result.currentPage < result.totalPages
return MangasPage(mangas, hasNextPage)
}
Expand All @@ -82,20 +94,14 @@ abstract class KissLove : HttpSource() {
}

is StatusFilter -> addQueryParameter("status", filter.toUriPart())

else -> {}
}
}
}.build()
return GET(url, sigAppend())
}

override fun searchMangaParse(response: Response): MangasPage {
val result = response.parseAs<ListPagedManga>()
val mangas = result.items.map { it.toSManga() }
val hasNextPage = result.currentPage < result.totalPages
return MangasPage(mangas, hasNextPage)
}
override fun searchMangaParse(response: Response): MangasPage = latestUpdatesParse(response)

override fun mangaDetailsRequest(manga: SManga): Request {
val url = baseUrl.toHttpUrl().newBuilder()
Expand All @@ -107,7 +113,7 @@ abstract class KissLove : HttpSource() {

override fun mangaDetailsParse(response: Response): SManga {
val result = response.parseAs<Manga>()
return result.toSManga()
return result.toSManga(preferences.getBoolean(USE_ROMAJI_PREF, true))
}

override fun chapterListRequest(manga: SManga): Request = mangaDetailsRequest(manga)
Expand Down Expand Up @@ -239,5 +245,15 @@ abstract class KissLove : HttpSource() {
"h2.klimv1.xyz" to "j2.jfimv2.xyz",
"h4.klimv1.xyz" to "j4.jfimv2.xyz",
)
const val USE_ROMAJI_PREF = "USE_ROMAJI_PREF"
}

override fun setupPreferenceScreen(screen: PreferenceScreen) {
SwitchPreferenceCompat(screen.context).apply {
key = USE_ROMAJI_PREF
title = intl["useRomajiTitle"]
summary = intl["useRomajiSummary"]
setDefaultValue(true)
}.let(screen::addPreference)
}
}