Skip to content

Commit

Permalink
support chunithm upload
Browse files Browse the repository at this point in the history
  • Loading branch information
SkyDynamic committed Nov 20, 2024
1 parent d26c5a7 commit 0b6fc90
Show file tree
Hide file tree
Showing 32 changed files with 574 additions and 159 deletions.
Binary file added app/src/main/ic_launcher-playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package io.github.skydynamic.maiproberplus.core.data.chuni

import android.content.Context
import io.github.skydynamic.maiproberplus.Application
import io.github.skydynamic.maiproberplus.core.prober.client
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import java.io.File

val JSON = Json {
ignoreUnknownKeys = true
encodeDefaults = true
}

class ChuniData {
@Serializable
data class SongDifficulty(
val difficulty: Int,
val level: String,
@SerialName("level_value") val levelValue: Float,
@SerialName("note_designer") val noteDesigner: String,
val version: Int,
val kanji: String = "",
val star: Int = 0,
)

@Serializable
data class SongInfo(
val id: Int, val title: String, val artist: String, val genre: String,
val bpm: Int, val version: Int, val difficulties: List<SongDifficulty>
)

@Serializable
data class MusicDetail(
val name: String, val level: Float,
val score: Int, val rating: Float,
val version: Int, val rankType: ChuniEnums.RankType,
val diff: ChuniEnums.Difficulty, val fullComboType: ChuniEnums.FullComboType,
val clearType: ChuniEnums.ClearType, val fullChainType: ChuniEnums.FullChainType
)

@Serializable
data class LxnsSongListResponse(val songs: List<SongInfo>)

companion object {
var CHUNI_SONG_LIST = readChuniSongList()

@OptIn(DelicateCoroutinesApi::class)
fun syncMaimaiSongList() {
val context = Application.application
var listFile = File(context.filesDir, "chuni_song_list.json")

GlobalScope.launch(Dispatchers.IO) {
val result =
client.get("https://maimai.lxns.net/api/v0/chunithm/song/list?notes=true")
listFile.deleteOnExit()
listFile.createNewFile()
val bufferedWriter =
context.openFileOutput("chuni_song_list.json", Context.MODE_PRIVATE)
.bufferedWriter()
bufferedWriter.write(result.bodyAsText())
bufferedWriter.close()
}

CHUNI_SONG_LIST = readChuniSongList()
}

private fun readChuniSongList(): List<SongInfo> {
return JSON.decodeFromString<LxnsSongListResponse>(
Application.application.getFilesDirInputStream("chuni_song_list.json")
.bufferedReader().use { it.readText() }
).songs
}

fun getSongIdFromTitle(title: String): Int {
return CHUNI_SONG_LIST.find { it.title == title }?.id ?: -1
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package io.github.skydynamic.maiproberplus.core.data.chuni

import kotlinx.serialization.Serializable

class ChuniEnums {
@Serializable
enum class Difficulty(val diffName: String, val diffIndex: Int) {
BASIC("Basic", 0),
ADVANCED("Advanced", 1),
EXPERT("Expert", 2),
MASTER("Master", 3),
ULTIMA("Ultima", 4),
WORLDSEND("World's End", 5),
RECENT("Recent", 6);

companion object {
@JvmStatic
fun getDifficultyWithName(diffName: String): Difficulty {
for (difficulty in entries) {
if (difficulty.diffName.lowercase() == diffName.lowercase()) {
return difficulty
}
}
throw IllegalArgumentException("No such difficulty")
}
}
}

@Serializable
enum class ClearType(val type: String) {
CATASTROPHY("catastrophy"),
ABSOLUTEP("absolutep"),
ABSOLUTE("absolute"),
HARD("hard"),
CLEAR("clear"),
FAILED("failed");
}

@Serializable
enum class FullComboType(val type: String) {
NULL(""),
AJC("alljusticecritical"),
AJ("alljustice"),
FC("fullcombo");
}

@Serializable
enum class FullChainType(val type: String) {
NULL(""),
FC("fullchain"),
GFC("fullchain2")
}

@Serializable
enum class RankType(
val rank: String,
private val intRange: IntRange,
) {
D("d", 0..499999),
C("c", 500000..599999),
B("b", 600000..699999),
BB("bb", 700000..799999),
BBB("bbb", 800000..899999),
A("a", 900000..924999),
AA("aa", 925000..949999),
AAA("aaa", 950000..974999),
S("s", 975000..989999),
SP("sp", 990000..999999),
SS("ss", 1000000..1004999),
SSP("ssp", 1005000..1007499),
SSS("sss", 1007500..1008999),
SSSP("sssp", 1009000..1010000);

companion object {
@JvmStatic
fun getRankTypeByScore(score: Int): RankType {
var returnValue = D
for (rank in entries) {
if (score in rank.intRange) {
returnValue = rank
}
}
return returnValue
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package io.github.skydynamic.maiproberplus.core.data.maimai

import android.content.Context
import io.github.skydynamic.maiproberplus.Application
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.HttpTimeout
import io.github.skydynamic.maiproberplus.core.prober.client
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import kotlinx.coroutines.DelicateCoroutinesApi
Expand All @@ -21,13 +19,6 @@ val JSON = Json {
encodeDefaults = true
}

val client = HttpClient(CIO) {
install(HttpTimeout) {
requestTimeoutMillis = 30000
connectTimeoutMillis = 30000
}
}

class MaimaiData {
@Serializable
data class Notes(
Expand All @@ -41,10 +32,13 @@ class MaimaiData {

@Serializable
data class SongDiffculty(
val type: MaimaiEnums.SongType, val difficulty: Int, val level: String,
val type: MaimaiEnums.SongType,
val difficulty: Int,
val level: String,
@SerialName("level_value") val levelValue: Float,
@SerialName("note_designer") val noteDesigner: String,
val version: Int, val notes: Notes
val version: Int,
val notes: Notes
)

@Serializable
Expand All @@ -62,8 +56,8 @@ class MaimaiData {
val score: Float, val dxScore: Int,
val rating: Int, val version: Int,
val type: MaimaiEnums.SongType, val diff: MaimaiEnums.Difficulty,
val clearType: MaimaiEnums.ClearType, val syncType: MaimaiEnums.SyncType,
val specialClearType: MaimaiEnums.SpecialClearType
val rankType: MaimaiEnums.RankType, val syncType: MaimaiEnums.SyncType,
val fullComboType: MaimaiEnums.FullComboType
)

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ class MaimaiEnums {
}

@Serializable
enum class ClearType(
val clearName: String,
private val closedFloatingPointRange: ClosedFloatingPointRange<Double>,
enum class RankType(
val rank: String,
private val scoreRange: ClosedFloatingPointRange<Double>,
) {
D("d", 0.0000..49.9999),
C("c", 50.0000..59.9999),
Expand All @@ -54,11 +54,11 @@ class MaimaiEnums {

companion object {
@JvmStatic
fun getClearTypeByScore(score: Float): ClearType {
fun getRankTypeByScore(score: Float): RankType {
var returnValue = D
for (clearType in entries) {
if (score in clearType.closedFloatingPointRange) {
returnValue = clearType
for (rank in entries) {
if (score in rank.scoreRange) {
returnValue = rank
}
}
return returnValue
Expand All @@ -67,25 +67,13 @@ class MaimaiEnums {
}

@Serializable
enum class SpecialClearType(val sepcialClearName: String) {
enum class FullComboType(val typeName: String) {
@SerialName("")
NULL(""),
FC("fc"),
FCP("fcp"),
AP("ap"),
APP("app");

companion object {
@JvmStatic
fun getSpecialClearType(specialClearName: String): SpecialClearType {
for (specialClearType in entries) {
if (specialClearType.sepcialClearName == specialClearName.lowercase()) {
return specialClearType
}
}
throw IllegalArgumentException("No such special clear type")
}
}
}

@Serializable
Expand All @@ -97,17 +85,5 @@ class MaimaiEnums {
FSP("fsp"),
FDX("fsd"),
FDXP("fsdp");

companion object {
@JvmStatic
fun getSyncType(syncName: String): SyncType {
for (syncType in entries) {
if (syncType.syncName == syncName.lowercase()) {
return syncType
}
}
return NULL
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import io.github.skydynamic.maiproberplus.GlobalViewModel
import io.ktor.client.request.headers
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.contentType
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
Expand All @@ -32,11 +34,15 @@ class DivingFishProberUtil : IProberUtil {
sendMessageToUi("开始获取Maimai数据并上传到水鱼查分器")
val scores = getMaimaiPageData(authUrl)

if (scores.isEmpty()) {
return
}

val postScores = scores.map {
DivingFishScoreUploadBody(
achievements = it.score,
dxScore = it.dxScore,
fc = it.specialClearType.sepcialClearName,
fc = it.fullComboType.typeName,
fs = it.syncType.syncName,
levelIndex = it.diff.diffIndex,
title = it.name,
Expand All @@ -61,4 +67,25 @@ class DivingFishProberUtil : IProberUtil {
}
GlobalViewModel.maimaiHooking = false
}

override suspend fun uploadChunithmProberData(
importToken: String,
authUrl: String
) {
sendMessageToUi("开始获取中二节奏数据并上传到水鱼查分器")
fetchChuniScores(authUrl) { diff, body ->
val recentParam = if (diff.diffName.lowercase().contains("recent")) "?recent=1" else ""
client.post("https://www.diving-fish.com/api/chunithmprober/player/update_records_html$recentParam") {
headers {
append("Import-Token", importToken)
append(HttpHeaders.ContentType, "text/plain")
}
contentType(ContentType.Text.Plain)
setBody(body)
}
}
sendMessageToUi("上传中二节奏成绩到水鱼查分器完成")
Log.d("DivingFishProberUtil", "上传完毕")
GlobalViewModel.chuniHooking = false
}
}
Loading

0 comments on commit 0b6fc90

Please sign in to comment.