Skip to content

Commit ca99e18

Browse files
committed
feat(login): Implement login with FN Connect
1 parent dfb1d17 commit ca99e18

File tree

9 files changed

+401
-6
lines changed

9 files changed

+401
-6
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.jankinwu.fntv.client.data.model.request
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
data class AuthRequest(
8+
@get:JsonProperty("source")
9+
@param:JsonProperty("source")
10+
val source: String,
11+
12+
@get:JsonProperty("code")
13+
@param:JsonProperty("code")
14+
val code: String
15+
)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.jankinwu.fntv.client.data.model.response
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
data class AuthResponse(
8+
9+
@get:JsonProperty("token")
10+
@param:JsonProperty("token")
11+
val token: String
12+
)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.jankinwu.fntv.client.data.model.response
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
data class SysConfigResponse(
8+
9+
@get:JsonProperty("initialized")
10+
@param:JsonProperty("initialized")
11+
val initialized: Boolean,
12+
13+
@get:JsonProperty("region")
14+
@param:JsonProperty("region")
15+
val region: String? = null,
16+
17+
@get:JsonProperty("server_name")
18+
@param:JsonProperty("server_name")
19+
val serverName: String,
20+
21+
@get:JsonProperty("server_guid")
22+
@param:JsonProperty("server_guid")
23+
val serverGuid: String,
24+
25+
@get:JsonProperty("nas_oauth")
26+
@param:JsonProperty("nas_oauth")
27+
val nasOauth: NasOauthConfig
28+
)
29+
30+
@Serializable
31+
data class NasOauthConfig(
32+
33+
@get:JsonProperty("app_id")
34+
@param:JsonProperty("app_id")
35+
val appId: String,
36+
37+
@get:JsonProperty("url")
38+
@param:JsonProperty("url")
39+
val url: String
40+
)

composeApp/src/commonMain/kotlin/com/jankinwu/fntv/client/data/network/FnOfficialApi.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.jankinwu.fntv.client.data.network
22

3+
import com.jankinwu.fntv.client.data.model.request.AuthRequest
34
import com.jankinwu.fntv.client.data.model.request.ItemListQueryRequest
45
import com.jankinwu.fntv.client.data.model.request.LoginRequest
56
import com.jankinwu.fntv.client.data.model.request.MediaPRequest
@@ -12,6 +13,7 @@ import com.jankinwu.fntv.client.data.model.request.SubtitleDownloadRequest
1213
import com.jankinwu.fntv.client.data.model.request.SubtitleMarkRequest
1314
import com.jankinwu.fntv.client.data.model.request.SubtitleSearchRequest
1415
import com.jankinwu.fntv.client.data.model.response.AuthDirResponse
16+
import com.jankinwu.fntv.client.data.model.response.AuthResponse
1517
import com.jankinwu.fntv.client.data.model.response.EpisodeListResponse
1618
import com.jankinwu.fntv.client.data.model.response.GenresResponse
1719
import com.jankinwu.fntv.client.data.model.response.ItemListQueryResponse
@@ -35,11 +37,18 @@ import com.jankinwu.fntv.client.data.model.response.SubtitleDownloadResponse
3537
import com.jankinwu.fntv.client.data.model.response.SubtitleMarkResponse
3638
import com.jankinwu.fntv.client.data.model.response.SubtitleSearchResponse
3739
import com.jankinwu.fntv.client.data.model.response.SubtitleUploadResponse
40+
import com.jankinwu.fntv.client.data.model.response.SysConfigResponse
3841
import com.jankinwu.fntv.client.data.model.response.TagListResponse
3942
import com.jankinwu.fntv.client.data.model.response.UserInfoResponse
4043

4144
interface FnOfficialApi {
4245

46+
suspend fun getSysConfig(): SysConfigResponse
47+
48+
suspend fun oauthResult(code: String, state: String?): Boolean
49+
50+
suspend fun auth(request: AuthRequest): AuthResponse
51+
4352
suspend fun getMediaDbList(): List<MediaDbListResponse>
4453

4554
suspend fun getItemList(request: ItemListQueryRequest): ItemListQueryResponse

composeApp/src/commonMain/kotlin/com/jankinwu/fntv/client/data/network/impl/FnOfficialApiImpl.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature
55
import com.fasterxml.jackson.databind.SerializationFeature
66
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
77
import com.fasterxml.jackson.module.kotlin.readValue
8+
import com.jankinwu.fntv.client.data.model.request.AuthRequest
89
import com.jankinwu.fntv.client.data.model.request.FavoriteRequest
910
import com.jankinwu.fntv.client.data.model.request.ItemListQueryRequest
1011
import com.jankinwu.fntv.client.data.model.request.LoginRequest
@@ -20,6 +21,7 @@ import com.jankinwu.fntv.client.data.model.request.SubtitleMarkRequest
2021
import com.jankinwu.fntv.client.data.model.request.SubtitleSearchRequest
2122
import com.jankinwu.fntv.client.data.model.request.WatchedRequest
2223
import com.jankinwu.fntv.client.data.model.response.AuthDirResponse
24+
import com.jankinwu.fntv.client.data.model.response.AuthResponse
2325
import com.jankinwu.fntv.client.data.model.response.EpisodeListResponse
2426
import com.jankinwu.fntv.client.data.model.response.FnBaseResponse
2527
import com.jankinwu.fntv.client.data.model.response.GenresResponse
@@ -44,6 +46,7 @@ import com.jankinwu.fntv.client.data.model.response.SubtitleDownloadResponse
4446
import com.jankinwu.fntv.client.data.model.response.SubtitleMarkResponse
4547
import com.jankinwu.fntv.client.data.model.response.SubtitleSearchResponse
4648
import com.jankinwu.fntv.client.data.model.response.SubtitleUploadResponse
49+
import com.jankinwu.fntv.client.data.model.response.SysConfigResponse
4750
import com.jankinwu.fntv.client.data.model.response.TagListResponse
4851
import com.jankinwu.fntv.client.data.model.response.UserInfoResponse
4952
import com.jankinwu.fntv.client.data.network.FnOfficialApi
@@ -87,6 +90,18 @@ class FnOfficialApiImpl() : FnOfficialApi {
8790
}
8891
}
8992

93+
override suspend fun getSysConfig(): SysConfigResponse {
94+
return get("/v/api/v1/sys/config")
95+
}
96+
97+
override suspend fun oauthResult(code: String, state: String?): Boolean {
98+
return post("/v/oauth/result?code=$code&state=${state ?: "undefined"}")
99+
}
100+
101+
override suspend fun auth(request: AuthRequest): AuthResponse {
102+
return post("/v/api/v1/auth", request)
103+
}
104+
90105
override suspend fun getMediaDbList(): List<MediaDbListResponse> {
91106
return get("/v/api/v1/mediadb/list")
92107
}

composeApp/src/commonMain/kotlin/com/jankinwu/fntv/client/data/store/AccountDataCache.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,35 @@ object AccountDataCache {
5151
_cookieState.value = getCookie()
5252
}
5353

54+
fun insertCookies(cookies: Map<String, String>) {
55+
cookieMap.putAll(cookies)
56+
_cookieState.value = getCookie()
57+
}
58+
59+
fun updateFnOfficialBaseUrlFromUrl(url: String) {
60+
try {
61+
val protocolSplit = url.split("://")
62+
if (protocolSplit.size < 2) return
63+
64+
val protocol = protocolSplit[0]
65+
isHttps = protocol.equals("https", ignoreCase = true)
66+
67+
val afterProtocol = protocolSplit[1]
68+
val authority = afterProtocol.substringBefore("/")
69+
70+
if (authority.contains(":")) {
71+
val hostPort = authority.split(":")
72+
host = hostPort[0]
73+
port = hostPort[1].toIntOrNull() ?: 0
74+
} else {
75+
host = authority
76+
port = 0
77+
}
78+
} catch (e: Exception) {
79+
e.printStackTrace()
80+
}
81+
}
82+
5483
fun clearCookie() {
5584
cookieMap.clear()
5685
_cookieState.value = ""
@@ -66,4 +95,21 @@ object AccountDataCache {
6695
key to value
6796
} as MutableMap<String, String>
6897
}
98+
99+
fun mergeCookieString(cookie: String) {
100+
if (cookie.isBlank()) return
101+
try {
102+
val map = cookie.split(";").associate {
103+
val parts = it.trim().split("=", limit = 2)
104+
if (parts.size == 2) {
105+
parts[0] to parts[1]
106+
} else {
107+
parts[0] to ""
108+
}
109+
}
110+
insertCookies(map)
111+
} catch (e: Exception) {
112+
e.printStackTrace()
113+
}
114+
}
69115
}

0 commit comments

Comments
 (0)