[add] function implementation

This commit is contained in:
acite
2025-08-24 20:07:38 +08:00
parent 996c1ff5cf
commit d0a6497dd6
64 changed files with 2924 additions and 1 deletions

View File

@@ -0,0 +1,25 @@
package com.acitelight.aether.service
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object ApiClient {
const val base: String = "http://192.168.1.213/"
private val json = Json {
ignoreUnknownKeys = true
}
private val retrofit = Retrofit.Builder()
.baseUrl(base)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
val api: ApiInterface by lazy {
retrofit.create(ApiInterface::class.java)
}
}

View File

@@ -0,0 +1,55 @@
package com.acitelight.aether.service
import com.acitelight.aether.model.ChallengeResponse
import com.acitelight.aether.model.Comic
import com.acitelight.aether.model.VideoResponse
import okhttp3.ResponseBody
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
import retrofit2.http.Streaming
interface ApiInterface {
@GET("api/video")
suspend fun getVideoClasses(
@Query("token") token: String
): List<String>
@GET("api/video/{klass}")
suspend fun queryVideoClasses(
@Path("klass") klass: String,
@Query("token") token: String
): List<String>
@GET("api/video/{klass}/{id}")
suspend fun queryVideo(
@Path("klass") klass: String,
@Path("id") id: String,
@Query("token") token: String
): VideoResponse
@GET("api/video/{klass}/{id}/nv")
@Streaming
suspend fun getNailVideo(
@Path("klass") klass: String,
@Path("id") id: String,
@Query("token") token: String
): ResponseBody
@GET("api/image/collections")
suspend fun getComicCollections(): List<String>
@GET("api/image/meta")
suspend fun queryComicInfo(@Query("collection") collection: String): Comic
@GET("api/user/{user}")
suspend fun getChallenge(
@Path("user") user: String
): ResponseBody
@POST("api/user/{user}")
suspend fun verifyChallenge(
@Path("user") user: String,
@Body challengeResponse: ChallengeResponse
): ResponseBody
}

View File

@@ -0,0 +1,46 @@
package com.acitelight.aether.service
import android.util.Base64
import com.acitelight.aether.model.ChallengeResponse
import kotlinx.coroutines.runBlocking
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters
import org.bouncycastle.crypto.signers.Ed25519Signer
import java.security.PrivateKey
import java.security.Signature
object AuthManager {
suspend fun fetchToken(baseUrl: String, username: String, privateKey: String): String? = runBlocking {
val api = ApiClient.api
var challengeBase64 = ""
try{
challengeBase64 = api.getChallenge(username).string()
}catch (e: Exception)
{
print(e.message)
}
val signedBase64 = signChallenge(db64(privateKey), db64(challengeBase64))
return@runBlocking try {
api.verifyChallenge(username, ChallengeResponse(response = signedBase64)).string()
} catch (e: Exception) {
e.printStackTrace()
null
}
}
fun db64(b64: String): ByteArray {
return Base64.decode(b64, Base64.DEFAULT) // 32 bytes
}
fun signChallenge(privateKey: ByteArray, data: ByteArray): String
{
val privateKeyParams = Ed25519PrivateKeyParameters(privateKey, 0)
val signer = Ed25519Signer()
signer.init(true, privateKeyParams)
signer.update(data, 0, data.size)
val signature = signer.generateSignature()
return Base64.encodeToString(signature, Base64.NO_WRAP)
}
}

View File

@@ -0,0 +1,43 @@
package com.acitelight.aether.service
import com.acitelight.aether.model.Comic
import com.acitelight.aether.model.Video
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.IOException
object MediaManager
{
var token: String = "null"
suspend fun listVideoKlasses(): List<String>
{
val j = ApiClient.api.getVideoClasses(token)
return j.toList()
}
suspend fun listVideos(klass: String): List<Video>
{
val j = ApiClient.api.queryVideoClasses(klass, token)
return j.map{
queryVideo(klass, it)
}.toList()
}
suspend fun queryVideo(klass: String, id: String): Video
{
val j = ApiClient.api.queryVideo(klass, id, token)
return Video(klass = klass, id = id, token=token, j)
}
suspend fun listComics() : List<String>
{
return ApiClient.api.getComicCollections()
}
suspend fun queryComicInfo(c: String) : Comic
{
return ApiClient.api.queryComicInfo(c)
}
}