Skip to content

Commit

Permalink
优化弹幕匹配规则, 修复一些匹配错误的情况 close #257
Browse files Browse the repository at this point in the history
  • Loading branch information
Him188 committed May 19, 2024
1 parent 8fb89fd commit 70a1121
Show file tree
Hide file tree
Showing 12 changed files with 913 additions and 66 deletions.
78 changes: 78 additions & 0 deletions app/shared/data/common/data/subject/PackedDate.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
@file:Suppress("NOTHING_TO_INLINE")

package me.him188.ani.app.data.subject

import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import kotlinx.serialization.Serializable

@Immutable
@JvmInline
@Serializable
value class PackedDate @PublishedApi internal constructor(
@JvmField
@PublishedApi
internal val packed: Int
) {
inline val isValid: Boolean get() = packed != Int.MAX_VALUE

inline val year: Int get() = if (isValid) DatePacker.unpack1(packed) else 0
inline val rawMonth: Int get() = if (isValid) DatePacker.unpack2(packed) else 0
inline val day: Int get() = if (isValid) DatePacker.unpack3(packed) else 0

@Stable
val coercedMonth: Int
get() = when (rawMonth) {
12, in 1..2 -> 1
in 3..5 -> 4
in 6..8 -> 7
in 9..11 -> 10
else -> 0
}

companion object {
@JvmStatic
val Invalid = PackedDate(Int.MAX_VALUE)


/**
* @param date `2024-05-18`
*/
fun parseFromDate(date: String): PackedDate {
val split = date.split("-")
if (split.size != 3) return Invalid
return PackedDate(
split[0].toIntOrNull() ?: return Invalid,
split[1].toIntOrNull() ?: return Invalid,
split[2].toIntOrNull() ?: return Invalid
)
}
}
}

@Stable
inline fun PackedDate(
year: Int,
month: Int,
day: Int,
): PackedDate = if (year in 0..9999 && month in 1..12 && day in 1..31) {
PackedDate(DatePacker.pack(year, month, day))
} else {
PackedDate.Invalid // invalid
}

@Suppress("NOTHING_TO_INLINE")
@PublishedApi
internal object DatePacker {
inline fun pack(
val1: Int, // short
val2: Int, // byte
val3: Int, // byte
): Int {
return val1.shl(16) or val2.shl(8) or val3
}

inline fun unpack1(value: Int): Int = value.shr(16).and(0xFFFF)
inline fun unpack2(value: Int): Int = value.shr(8).and(0xFF)
inline fun unpack3(value: Int): Int = value.and(0xFF)
}
120 changes: 120 additions & 0 deletions app/shared/data/common/data/subject/SubjectInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package me.him188.ani.app.data.subject

import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.contentOrNull
import org.openapitools.client.models.Subject

/**
* 详细信息.
*/
@Immutable
@Serializable
class SubjectInfo(
// 可搜索 "吹响!悠风号 第三季.json" 看示例
val name: String = "",
val nameCn: String = "",
val summary: String = "",
val nsfw: Boolean = false,
val locked: Boolean = false,
/* TV, Web, 欧美剧, PS4... */
val platform: String = "",
// val images: Images,
/* 书籍条目的册数,由旧服务端从wiki中解析 */
val volumes: Int = 0,
/* 由旧服务端从wiki中解析,对于书籍条目为`话数` */
val eps: Int = 0,
/* 数据库中的章节数量 */
val totalEpisodes: Int = 0,
// val rating: Rating,
val tags: List<Tag> = emptyList(),
/* air date in `YYYY-MM-DD` format */
val date: String? = null,
val infobox: List<InfoboxItem> = emptyList(),
) {
val publishDate: PackedDate = if (date == null) PackedDate.Invalid else PackedDate.parseFromDate(date)

/**
* 主要显示名称
*/
val displayName: String get() = nameCn.takeIf { it.isNotBlank() } ?: name

/**
* 主中文名, 主日文名, 以及所有别名
*/
val allNames by lazy(LazyThreadSafetyMode.PUBLICATION) {
buildList {
add(name)
add(nameCn)
(infobox.firstOrNull { it.name == "别名" }?.value as? JsonArray)
?.forEach { element -> // interesting fact, 如果 `element` 改名成 `name`, 编译器就会编译错 (runtime class cast exception)
when (element) {
is JsonPrimitive -> add(element.content)
is JsonObject -> (element["v"] as? JsonPrimitive)?.contentOrNull?.let { add(it) }
else -> {}
}
}
}
}

companion object {
@Stable
@JvmStatic
val Empty = SubjectInfo()
}
}


@Serializable
@Immutable
class Tag(
val name: String,
val count: Int,
)

@Serializable
@Immutable
class InfoboxItem(
val name: String,
val value: JsonElement,
)

fun Subject.createSubjectInfo(): SubjectInfo {
return SubjectInfo(
name = name,
nameCn = nameCn,
summary = this.summary,
nsfw = this.nsfw,
locked = this.locked,
platform = this.platform,
volumes = this.volumes,
eps = this.eps,
totalEpisodes = this.totalEpisodes,
date = this.date,
tags = this.tags.map { Tag(it.name, it.count) },
infobox = this.infobox?.map { InfoboxItem(it.key, convertToJsonElement(it.value)) }.orEmpty(),
)
}

private fun convertToJsonElement(value: Any?): JsonElement {
return when (value) {
null -> JsonNull
is String -> JsonPrimitive(value)
is Number -> JsonPrimitive(value)
is Boolean -> JsonPrimitive(value)
is Array<*> -> JsonArray(value.map { it?.let { convertToJsonElement(it) } ?: JsonNull })
is List<*> -> JsonArray(value.map { it?.let { convertToJsonElement(it) } ?: JsonNull })
is Map<*, *> -> JsonObject(
value.map { (k, v) -> k.toString() to convertToJsonElement(v) }.toMap()
)

else -> throw IllegalArgumentException("Unsupported type: ${value::class.java}")
}
}

Loading

0 comments on commit 70a1121

Please sign in to comment.