Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] master from yujincheng08:master #200

Merged
merged 4 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions app/src/main/java/me/iacn/biliroaming/hook/BangumiPlayUrlHook.kt
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,79 @@ class BangumiPlayUrlHook(classLoader: ClassLoader) : BaseHook(classLoader) {
param.result = purifyViewInfo(response)
}
}
// v8.17.0+
hookBeforeMethod(
"executePlayView",
"com.bapis.bilibili.pgc.gateway.player.v2.PlayViewReq"
) { param ->
val request = param.args[0]
// if getDownload == 1 -> flv download
// if getDownload == 2 -> dash download
// if qn == 0, we are querying available quality
// else we are downloading
// if fnval == 0 -> flv download
// thus fix download will set qn = 0 and set fnval to max
isDownload = sPrefs.getBoolean("allow_download", false)
&& request.callMethodAs<Int>("getDownload") >= 1
if (isDownload) {
if (!sPrefs.getBoolean("fix_download", false)
|| request.callMethodAs<Int>("getFnval") <= 1
) {
request.callMethod("setFnval", MAX_FNVAL)
request.callMethod("setFourk", true)
}
request.callMethod("setDownload", 0)
} else if (halfScreenQuality == 1 || fullScreenQuality != 0) {
request.callMethod("setFnval", MAX_FNVAL)
request.callMethod("setFourk", true)
if (halfScreenQuality == 1 && qnApplied.compareAndSet(false, true)) {
defaultQn?.let { request.callMethod("setQn", it) }
}
}
}
hookAfterMethod(
"executePlayView",
"com.bapis.bilibili.pgc.gateway.player.v2.PlayViewReq"
) { param ->
// th:
// com.bilibili.lib.moss.api.BusinessException: 抱歉您所使用的平台不可观看!
// com.bilibili.lib.moss.api.BusinessException: 啥都木有
// connection err <- should skip because of cache:
// throwable: com.bilibili.lib.moss.api.NetworkException
if (instance.networkExceptionClass?.isInstance(param.throwable) == true)
return@hookAfterMethod
val request = param.args[0]
val response =
param.result ?: "com.bapis.bilibili.pgc.gateway.player.v2.PlayViewReply"
.on(mClassLoader).new()
if (needProxy(response)) {
try {
val serializedRequest = request.callMethodAs<ByteArray>("toByteArray")
val req = PlayViewReq.parseFrom(serializedRequest)
val seasonId = req.seasonId.toString().takeIf { it != "0" }
?: lastSeasonInfo["season_id"] ?: "0"
val (thaiSeason, thaiEp) = getSeasonLazy(seasonId, req.epId)
val content = getPlayUrl(reconstructQuery(req, response, thaiEp))
content?.let {
Log.toast("已从代理服务器获取播放地址\n如加载缓慢或黑屏,可去漫游设置中测速并设置 UPOS")
param.result = reconstructResponse(
req, response, it, isDownload, thaiSeason, thaiEp
)
}
?: throw CustomServerException(mapOf("未知错误" to "请检查哔哩漫游设置中解析服务器设置。"))
} catch (e: CustomServerException) {
param.result = showPlayerError(
response,
"请求解析服务器发生错误(点此查看更多)\n${e.message}"
)
Log.toast("请求解析服务器发生错误: ${e.message}", alsoLog = true)
}
} else if (isDownload) {
param.result = fixDownloadProto(response)
} else if (blockBangumiPageAds) {
param.result = purifyViewInfo(response)
}
}
}
instance.playerMossClass?.run {
var isDownload = false
Expand Down
40 changes: 40 additions & 0 deletions app/src/main/java/me/iacn/biliroaming/hook/BangumiSeasonHook.kt
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,16 @@ class BangumiSeasonHook(classLoader: ClassLoader) : BaseHook(classLoader) {
param.result =
(param.method as Method).returnType.callStaticMethod("parseFrom", serializedReply)
}
// v8.17.0+
instance.viewMossClass?.hookAfterMethod("execView", instance.viewReqClass) { param ->
param.result?.let { return@hookAfterMethod }
val serializedRequest = param.args[0].callMethodAs<ByteArray>("toByteArray")
val req = ViewReq.parseFrom(serializedRequest)
val reply = fixViewProto(req)
val serializedReply = reply?.toByteArray() ?: return@hookAfterMethod
param.result =
(param.method as Method).returnType.callStaticMethod("parseFrom", serializedReply)
}

instance.viewUniteMossClass?.hookAfterMethod(
"view", "com.bapis.bilibili.app.viewunite.v1.ViewReq"
Expand Down Expand Up @@ -360,6 +370,36 @@ class BangumiSeasonHook(classLoader: ClassLoader) : BaseHook(classLoader) {

fixViewProto(response, supplement)
}
// v8.17.0+
instance.viewUniteMossClass?.hookAfterMethod(
"executeView", "com.bapis.bilibili.app.viewunite.v1.ViewReq"
) { param ->
if (instance.networkExceptionClass?.isInstance(param.throwable) == true) return@hookAfterMethod
val response = param.result
if (response == null) {
val req = param.args[0].callMethodAs<ByteArray>("toByteArray").let {
ViewUniteReq.parseFrom(it)
}
val av = (if (req.hasAid()) req.aid.takeIf { it != 0L } else if (req.hasBvid()) bv2av(req.bvid) else null)?.toString()
fixViewProto(req, av)?.toByteArray()?.let {
param.result =
"com.bapis.bilibili.app.viewunite.v1.ViewReply".from(mClassLoader)
?.callStaticMethod("parseFrom", it)
} ?: Log.toast("解锁失败!", force = true)
return@hookAfterMethod
}
val supplementAny = response.callMethod("getSupplement")
val typeUrl = supplementAny?.callMethodAs<String>("getTypeUrl")
// Only handle pgc video
if (param.result != null && typeUrl != PGC_ANY_MODEL_TYPE_URL) {
return@hookAfterMethod
}
val supplement =
supplementAny?.callMethod("getValue")?.callMethodAs<ByteArray>("toByteArray")
?.let { ViewPgcAny.parseFrom(it) } ?: viewPgcAny {}

fixViewProto(response, supplement)
}

val urlHook: Hooker = fun(param) {
val redirectUrl = param.thisObject.getObjectFieldAs<String?>("redirectUrl")
Expand Down
163 changes: 161 additions & 2 deletions app/src/main/java/me/iacn/biliroaming/hook/PegasusHook.kt
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ class PegasusHook(classLoader: ClassLoader) : BaseHook(classLoader) {
else it < hideLowPlayCountLimit
}
}
private fun isLowCountVideoUnite(count: Long): Boolean {
if (hideLowPlayCountLimit == 0L) return false
return count < hideLowPlayCountLimit
}

// 屏蔽指定播放时长
private fun durationVideo(obj: Any): Boolean {
Expand All @@ -126,6 +130,13 @@ class PegasusHook(classLoader: ClassLoader) : BaseHook(classLoader) {
return true
return hideShortDurationLimit != 0 && duration < hideShortDurationLimit
}
private fun durationVideoUnite(duration: Long): Boolean {
if (hideLongDurationLimit == 0 && hideShortDurationLimit == 0)
return false
if (hideLongDurationLimit != 0 && duration > hideLongDurationLimit)
return true
return hideShortDurationLimit != 0 && duration < hideShortDurationLimit
}

private fun isContainsBlockKwd(obj: Any): Boolean {
// 屏蔽标题关键词
Expand Down Expand Up @@ -196,6 +207,46 @@ class PegasusHook(classLoader: ClassLoader) : BaseHook(classLoader) {
return false
}

private fun isContainsBlockKwdUnite(card: Any): Boolean {
if (card.callMethodAs("hasBasicInfo")) {
card.callMethodAs<Any>("getBasicInfo").let { basicInfo ->
// 屏蔽标题关键词
if (kwdFilterTitleList.isNotEmpty()) {
val title = basicInfo.callMethodAs<String>("getTitle")
if (kwdFilterTitleRegexMode && title.isNotEmpty()) {
if (kwdFilterTitleRegexes.any { title.contains(it) })
return true
} else if (title.isNotEmpty()) {
if (kwdFilterTitleList.any { title.contains(it) })
return true
}
}

basicInfo.callMethodAs<Any>("getAuthor").let { author ->
// 屏蔽UID
if (kwdFilterUidList.isNotEmpty()) {
val uid = author.callMethodAs<Long>("getMid")
if (uid != 0L && kwdFilterUidList.any { it == uid })
return true
}

// 屏蔽UP主
if (kwdFilterUpnameList.isNotEmpty()) {
val upName = author.callMethodAs<String>("getTitle")
if (kwdFilterUpnameRegexMode && upName.isNotEmpty()) {
if (kwdFilterUpnameRegexes.any { upName.contains(it) })
return true
} else if (upName.isNotEmpty()) {
if (kwdFilterUpnameList.any { upName.contains(it) })
return true
}
}
}
}
}
return false
}

private fun ArrayList<Any>.appendReasons() = forEach { item ->
val title = item.getObjectFieldAs<String?>("title").orEmpty()
val rcmdReason = item.runCatchingOrNull {
Expand Down Expand Up @@ -486,6 +537,7 @@ class PegasusHook(classLoader: ClassLoader) : BaseHook(classLoader) {

fun MutableList<Any>.filterUnite() = removeAll {
val allowTypeList = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
var shouldFiltered = false
allowTypeList.removeAll { digit ->
(removeRelateOnlyAv && digit != 1) || (removeRelatePromote && digit in listOf(
3, // Resource, like mall
Expand All @@ -494,7 +546,35 @@ class PegasusHook(classLoader: ClassLoader) : BaseHook(classLoader) {
10 // SPECIAL
))
}
removeRelateNothing || it.callMethodAs("getRelateCardTypeValue") !in allowTypeList
// av filter
if (applyToRelate) {
if (it.callMethodAs("hasAv")) {
it.callMethodAs<Any>("getAv").let { av ->
val duration = av.callMethodAs<Long>("getDuration")
if (durationVideoUnite(duration)) {
shouldFiltered = true
return@let
}
if (av.callMethodAs("hasStat")) {
av.callMethodAs<Any>("getStat").let { stat ->
if (stat.callMethodAs("hasVt")) {
stat.callMethodAs<Any>("getVt").let { vt ->
if (isLowCountVideoUnite(vt.callMethodAs<Long>("getValue"))) {
shouldFiltered = true
return@let
}
}
}
}
}
}
if (isContainsBlockKwdUnite(it)) {
shouldFiltered = true
}
}
// todo: support rcmd
}
removeRelateNothing || it.callMethodAs("getRelateCardTypeValue") !in allowTypeList || shouldFiltered
}
instance.viewMossClass?.hookAfterMethod("view", instance.viewReqClass) { param ->
param.result ?: return@hookAfterMethod
Expand All @@ -514,6 +594,28 @@ class PegasusHook(classLoader: ClassLoader) : BaseHook(classLoader) {
param.result.callMethod("ensureListIsMutable")
param.result.callMethodAs<MutableList<Any>>("getListList").filter()
}
// v8.17.0+
instance.viewMossClass?.hookAfterMethod("executeView", instance.viewReqClass) { param ->
param.result ?: return@hookAfterMethod
if (removeRelatePromote && removeRelateOnlyAv && removeRelateNothing) {
param.result.callMethod("clearRelates")
param.result.callMethod("clearPagination")
return@hookAfterMethod
}
param.result.callMethod("ensureRelatesIsMutable")
param.result.callMethodAs<MutableList<Any>>("getRelatesList").filter()
}
instance.viewMossClass?.hookAfterMethod(
"executeRelatesFeed",
"com.bapis.bilibili.app.view.v1.RelatesFeedReq"
) { param ->
param.result ?: return@hookAfterMethod
param.result.callMethod("ensureListIsMutable")
param.result.callMethodAs<MutableList<Any>>("getListList").filter()
}



instance.viewUniteMossClass?.run {
hookAfterMethod("view", instance.viewUniteReqClass) { param ->
param.result ?: return@hookAfterMethod
Expand Down Expand Up @@ -543,6 +645,35 @@ class PegasusHook(classLoader: ClassLoader) : BaseHook(classLoader) {
callMethodAs<MutableList<Any>>("getRelatesList").filterUnite()
}
}
// v8.17.0+
hookAfterMethod("executeView", instance.viewUniteReqClass) { param ->
param.result ?: return@hookAfterMethod
param.result.callMethod("getTab")?.run {
callMethod("ensureTabModuleIsMutable")
callMethodAs<MutableList<Any>>("getTabModuleList").map { originalTabModules ->
if (!originalTabModules.callMethodAs<Boolean>("hasIntroduction")) return@map
originalTabModules.callMethodAs<Any>("getIntroduction").run {
callMethod("ensureModulesIsMutable")
callMethodAs<MutableList<Any>>("getModulesList").map { module ->
if (!module.callMethodAs<Boolean>("hasRelates")) return@map
module.callMethodAs<Any>("getRelates").run {
callMethod("ensureCardsIsMutable")
callMethodAs<MutableList<Any>>("getCardsList").filterUnite()
}
}
}
}
}
}
hookAfterMethod(
"executeRelatesFeed",
"com.bapis.bilibili.app.viewunite.v1.RelatesFeedReq"
) { param ->
param.result?.run {
callMethod("ensureRelatesIsMutable")
callMethodAs<MutableList<Any>>("getRelatesList").filterUnite()
}
}
}

instance.cardClickProcessorClass?.declaredMethods
Expand Down Expand Up @@ -588,6 +719,8 @@ class PegasusHook(classLoader: ClassLoader) : BaseHook(classLoader) {
param.result = null
}



fun MutableList<Any>.filterPopular() = removeIf {
when (it.callMethod("getItemCase")?.toString()) {
"SMALL_COVER_V5" -> {
Expand Down Expand Up @@ -625,7 +758,6 @@ class PegasusHook(classLoader: ClassLoader) : BaseHook(classLoader) {
param.args[0].setObjectField("lastParam_", popularDataVersion)
param.args[0].setLongField("idx_", popularDataCount)
}

instance.popularClass?.hookAfterMethod(
"index",
"com.bapis.bilibili.app.show.popular.v1.PopularResultReq"
Expand All @@ -635,5 +767,32 @@ class PegasusHook(classLoader: ClassLoader) : BaseHook(classLoader) {
param.result.callMethod("ensureItemsIsMutable")
param.result.callMethodAs<MutableList<Any>>("getItemsList").filterPopular()
}

// v8.17.0+
instance.popularClass?.hookBeforeMethod(
"executeIndex",
"com.bapis.bilibili.app.show.popular.v1.PopularResultReq"
) { param ->
param.args ?: return@hookBeforeMethod

val idx = param.args[0].getLongFieldOrNull("idx_")
if (idx == null || idx == 0L) {
popularDataCount = 0
popularDataVersion = ""
return@hookBeforeMethod
}

param.args[0].setObjectField("lastParam_", popularDataVersion)
param.args[0].setLongField("idx_", popularDataCount)
}
instance.popularClass?.hookAfterMethod(
"executeIndex",
"com.bapis.bilibili.app.show.popular.v1.PopularResultReq"
) { param ->
param.result ?: return@hookAfterMethod

param.result.callMethod("ensureItemsIsMutable")
param.result.callMethodAs<MutableList<Any>>("getItemsList").filterPopular()
}
}
}
Loading
Loading