Skip to content

Commit

Permalink
feat: 投稿视频页允许收藏并添加关注功能
Browse files Browse the repository at this point in the history
  • Loading branch information
muedsa committed Dec 23, 2024
1 parent c1656c0 commit 5fc6b1b
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,17 @@ object BiliApiHelper {
return params
}

fun buildRelationParams(
fid: Long,
wWebId: String,
mixinKey: String,
): MutableMap<String, String> {
val params = mutableMapOf<String, String>(
"fid" to "$fid",
"web_location" to "333.999",
"w_webid" to wWebId,
)
WBIHelper.fillWbiParams(params = params, mixinKey = mixinKey)
return params
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.muedsa.tvbox.bilibili.model

import kotlinx.serialization.Serializable

@Serializable
data class UserRelationModifyParams(
val fid: Long,
val act: Int,
val referer: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.muedsa.tvbox.bilibili.model.bilibili

import kotlinx.serialization.Serializable

@Serializable
data class FriendRelation(
val mid: Long,
val attribute: Int,
val mtime: Long = 0,
val tag: List<Long>? = null,
val special: Int = 0,
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import com.muedsa.tvbox.api.data.MediaCard
import com.muedsa.tvbox.api.data.MediaCardRow
import com.muedsa.tvbox.api.data.MediaCardType
import com.muedsa.tvbox.api.data.MediaDetail
import com.muedsa.tvbox.api.data.MediaHttpSource
import com.muedsa.tvbox.api.store.IPluginPerfStore
import com.muedsa.tvbox.bilibili.BILI_REFRESH_TOKEN_KEY
import com.muedsa.tvbox.bilibili.BILI_VIDEO_HEARTBEAT
import com.muedsa.tvbox.bilibili.BilibiliConst
import com.muedsa.tvbox.bilibili.helper.BiliCookieHelper
import com.muedsa.tvbox.bilibili.model.LoginState
import com.muedsa.tvbox.bilibili.model.QRCodeLoginCache
import com.muedsa.tvbox.bilibili.model.UserRelationModifyParams
import com.muedsa.tvbox.tool.LenientJson
import com.muedsa.tvbox.tool.SharedCookieSaver
import com.muedsa.tvbox.tool.feignChrome
Expand All @@ -23,8 +25,9 @@ import okhttp3.OkHttpClient
import timber.log.Timber

class ActionDelegate(
private val passportService: BilibiliPassportService,
private val okHttpClient: OkHttpClient,
private val passportService: BilibiliPassportService,
private val apiService: BilibiliApiService,
private val store: IPluginPerfStore,
private val cookieSaver: SharedCookieSaver,
) {
Expand Down Expand Up @@ -58,6 +61,24 @@ class ActionDelegate(
return mediaDetail
}

suspend fun execAsGetEpisodePlayInfo(
action: String,
data: String?
): MediaHttpSource {
val source = when (action) {
ACTION_USER_FOLLOW -> {
userRelationModify(
params = LenientJson.decodeFromString<UserRelationModifyParams>(
data ?: throw RuntimeException("参数错误")
)
)
}

else -> throw RuntimeException("未知动作")
}
return source
}

private suspend fun qrCodeLogin(): MediaDetail {
val resp = passportService.qrcodeGenerate()
if (resp.code != 0L || resp.data == null) {
Expand Down Expand Up @@ -138,13 +159,29 @@ class ActionDelegate(
return createVideoHeartbeatMediaDetail(config)
}

private suspend fun userRelationModify(params: UserRelationModifyParams): MediaHttpSource {
val resp = apiService.relationModify(
fid = params.fid, act = params.act, csrf = BiliCookieHelper.getCookeValue(
cookieSaver = cookieSaver,
cookieName = BiliCookieHelper.COOKIE_B_JCT,
defaultValue = null
) ?: throw RuntimeException("请登录后重试")
)
if (resp.code == 0L) {
throw RuntimeException("关注成功")
} else {
throw RuntimeException(resp.message)
}
}

companion object {
const val ACTION_PREFIX = "action_"
const val ACTION_INVALID = "${ACTION_PREFIX}invalid"
const val ACTION_QRCODE_LOGIN = "${ACTION_PREFIX}qrcode_login"
const val ACTION_QRCODE_LOGIN_POLL = "${ACTION_PREFIX}qrcode_login_poll"
const val ACTION_LOGOUT = "${ACTION_PREFIX}logout"
const val ACTION_VIDEO_HEARTBEAT = "${ACTION_PREFIX}_VIDEO_HEARTBEAT"
const val ACTION_USER_FOLLOW = "${ACTION_PREFIX}_USER_FOLLOW"

val LOGIN_ACTION_CARD = MediaCard(
id = ACTION_QRCODE_LOGIN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.muedsa.tvbox.bilibili.model.bilibili.CoinAdd
import com.muedsa.tvbox.bilibili.model.bilibili.DataFlow
import com.muedsa.tvbox.bilibili.model.bilibili.DynamicCard
import com.muedsa.tvbox.bilibili.model.bilibili.FingerSpi
import com.muedsa.tvbox.bilibili.model.bilibili.FriendRelation
import com.muedsa.tvbox.bilibili.model.bilibili.HistoryCursorFlow
import com.muedsa.tvbox.bilibili.model.bilibili.HistoryToView
import com.muedsa.tvbox.bilibili.model.bilibili.Nav
Expand Down Expand Up @@ -194,4 +195,23 @@ interface BilibiliApiService {
@Header("Referer") referer: String,
@Header("User-Agent") userAgent: String = ChromeUserAgent,
): BiliResp<CoinAdd>

@GET("/x/relation")
suspend fun relation(
@QueryMap params: Map<String, String>,
@Header("Referer") referer: String,
@Header("User-Agent") userAgent: String = ChromeUserAgent,
): BiliResp<FriendRelation>

@POST("x/relation/modify")
@FormUrlEncoded
suspend fun relationModify(
@Field("fid") fid: Long,
@Field("act") act: Int = 1,
@Field("re_src") reSrc: Int = 11,
@Field("gaia_source") gaiaSource: String = "web_main",
@Field("spmid") spmId: String = "333.999.0.0",
@Field("extend_content") extendContent: String? = null,
@Field("csrf") csrf: String,
): BiliResp<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.muedsa.tvbox.bilibili.helper.BiliApiHelper
import com.muedsa.tvbox.bilibili.helper.BiliCookieHelper
import com.muedsa.tvbox.bilibili.helper.WBIHelper.decodeURIComponent
import com.muedsa.tvbox.bilibili.model.BiliVideoDetailUrlAttrs
import com.muedsa.tvbox.bilibili.model.UserRelationModifyParams
import com.muedsa.tvbox.bilibili.model.VideoHeartbeatInfo
import com.muedsa.tvbox.bilibili.model.bilibili.BiliResp
import com.muedsa.tvbox.bilibili.model.bilibili.LiveUserRoomInfo
Expand Down Expand Up @@ -60,15 +61,15 @@ class MediaDetailService(
private val actionDelegate = ActionDelegate(
okHttpClient = okHttpClient,
passportService = passportService,
apiService = apiService,
store = store,
cookieSaver = cookieSaver,
)

override suspend fun getDetailData(mediaId: String, detailUrl: String): MediaDetail {
if (mediaId.startsWith(ActionDelegate.ACTION_PREFIX)) {
return actionDelegate.exec(action = mediaId, data = detailUrl)
}
return if (mediaId.startsWith(MEDIA_ID_BV_PREFIX)) {
return if (mediaId.startsWith(ActionDelegate.ACTION_PREFIX)) {
actionDelegate.exec(action = mediaId, data = detailUrl)
} else if (mediaId.startsWith(MEDIA_ID_BV_PREFIX)) {
videoDetail(attrs = LenientJson.decodeFromString<BiliVideoDetailUrlAttrs>(detailUrl))
} else if (mediaId.startsWith(MEDIA_ID_LIVE_ROOM_PREFIX)) {
liveRoomDetail(roomId = mediaId.removePrefix(MEDIA_ID_LIVE_ROOM_PREFIX).toLong())
Expand Down Expand Up @@ -466,28 +467,66 @@ class MediaDetailService(
?: throw RuntimeException("获取access_id失败")
val renderData =
LenientJson.decodeFromString<UserSpaceRenderData>(html.decodeURIComponent())
val resp = apiService.spaceWbiAccInfo(
val userInfoResp = apiService.spaceWbiAccInfo(
params = BiliApiHelper.buildWbiAccInfoParams(
mid = mid,
wWebId = renderData.accessId,
mixinKey = mixinKey,
),
referer = url
)
if (resp.code != 0L) throw RuntimeException(resp.message)
if (resp.data == null) throw RuntimeException("获取UP信息失败")
if (userInfoResp.code != 0L) throw RuntimeException(userInfoResp.message)
if (userInfoResp.data == null) throw RuntimeException("获取UP信息失败")
val relationResp = apiService.relation(
params = BiliApiHelper.buildRelationParams(
fid = mid,
wWebId = renderData.accessId,
mixinKey = mixinKey,
),
referer = url,
)
val savedId = "$MEDIA_ID_USER_SPACE_PREFIX$mid"
return MediaDetail(
id = savedId,
title = resp.data.name,
title = userInfoResp.data.name,
subTitle = null,
description = resp.data.sign,
description = userInfoResp.data.sign,
detailUrl = savedId,
backgroundImageUrl = resp.data.face,
playSourceList = emptyList(),
favoritedMediaCard = ActionDelegate.INVALID_ACTION_SAVED_MEDIA_CARD,
backgroundImageUrl = userInfoResp.data.face,
playSourceList = buildList {
if (relationResp.data?.attribute == 0) {
add(
MediaPlaySource(
id = "action",
name = "操作",
episodeList = listOf(
MediaEpisode(
id = ActionDelegate.ACTION_USER_FOLLOW,
name = "关注",
flag5 = LenientJson.encodeToString(
UserRelationModifyParams(
fid = userInfoResp.data.mid,
act = 1,
referer = url,
)
),
)
)
)
)
}
},
favoritedMediaCard = SavedMediaCard(
id = savedId,
title = "[投稿]${userInfoResp.data.name}",
detailUrl = savedId,
subTitle = "投稿视频",
coverImageUrl = userInfoResp.data.face,
cardWidth = BilibiliConst.AV_CARD_WIDTH,
cardHeight = BilibiliConst.AV_CARD_HEIGHT,
),
rows = getUserSpaceVideoRow(
mid = resp.data.mid,
mid = userInfoResp.data.mid,
wWebId = renderData.accessId,
mixinKey = mixinKey,
referer = url
Expand Down Expand Up @@ -545,7 +584,9 @@ class MediaDetailService(
playSource: MediaPlaySource,
episode: MediaEpisode
): MediaHttpSource {
return if (episode.id.startsWith(MEDIA_ID_BV_PREFIX)) {
return if (episode.id.startsWith(ActionDelegate.ACTION_PREFIX)) {
return actionDelegate.execAsGetEpisodePlayInfo(action = episode.id, data = episode.flag5)
} else if (episode.id.startsWith(MEDIA_ID_BV_PREFIX)) {
return getVideoEpisodePlayInfo(episode = episode)
} else if (episode.id.startsWith(MEDIA_ID_LIVE_ROOM_PREFIX)) {
return getLiveEpisodePlayInfo(episode = episode)
Expand Down

0 comments on commit 5fc6b1b

Please sign in to comment.