-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
913 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}") | ||
} | ||
} | ||
|
Oops, something went wrong.