百度扫码登录

This commit is contained in:
lushunming 2025-10-17 10:20:18 +08:00
parent 9afcc6e32a
commit e67b2d874a
7 changed files with 339 additions and 27 deletions

View File

@ -1,37 +1,256 @@
package com.github.catvod.api;
package com.github.catvod.api
import android.app.AlertDialog
import android.content.DialogInterface
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.text.TextUtils
import android.view.Gravity
import android.widget.FrameLayout
import android.widget.ImageView
import com.github.catvod.bean.BD.Cache
import com.github.catvod.bean.yun.User
import com.github.catvod.crawler.SpiderDebug
import com.github.catvod.net.OkHttp
import com.github.catvod.spider.Init
import com.github.catvod.utils.*
import okhttp3.Headers
import okhttp3.Request
import org.apache.commons.lang3.StringEscapeUtils
import java.io.File
import java.io.IOException
import java.io.UnsupportedEncodingException
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import kotlin.concurrent.Volatile
class BaiDuYunHandler private constructor() {
private val cache: Cache
private var service: ScheduledExecutorService? = null
private var dialog: AlertDialog? = null
private var cookies = ""
private val headers = mapOf(
"User-Agent" to "Mozilla/5.0 (Linux; Android 12; SM-X800) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.40 Safari/537.36",
"Accept" to "application/json, text/plain, */*",
"Content-Type" to "application/x-www-form-urlencoded",
"Origin" to "https://pan.baidu.com",
"Referer" to "https://pan.baidu.com/"
)
import com.github.catvod.bean.BD.Cache;
import com.github.catvod.bean.yun.User;
import com.github.catvod.utils.Path;
import java.io.File;
public class BaiDuYunHandler {
private final Cache cache;
public File getCache() {
return Path.tv("bd");
fun getCache(): File {
return Path.tv("bd")
}
private BaiDuYunHandler() {
cache = Cache.objectFrom(Path.read(getCache()));
init {
cache = Cache.objectFrom(Path.read(getCache()))
}
private static class Loader {
static volatile BaiDuYunHandler INSTANCE = new BaiDuYunHandler();
private object Loader {
@Volatile
var INSTANCE: BaiDuYunHandler = BaiDuYunHandler()
}
public static BaiDuYunHandler get() {
return BaiDuYunHandler.Loader.INSTANCE;
}
public String getToken() {
User user = cache.getUser();
return user.getCookie();
val token: String
get() {
val user: User = cache.getUser()
return user.getCookie()
//return "cGM6MTg4OTY3ODE2MDE6eTM1Tjd1dG58MXxSQ1N8MTc1NDQ2OTgwNzEyOXxzMlN0T1VEV3lOVmF5V3pNbGFfM2tJbVp1ZmlqSHBqaEhTSzVyNHZqVXNRLmlhV3loSUxHNDFkMUI5N1BqXzhWN0dtVWtKLnBTclhpNGpZU1EuTGZWMTV3MVFoZmNpcEVoZkxUV2tvYjB0bkFTYV9RTUhhaHhveWx6YkdmcEhQdjNCS1lrbnp1LkxaWDdKOE40YkNNRjkzT3piNmx2Y0d3TWdVUkl5b18ubVUt";
}
companion object {
@JvmStatic
fun get(): BaiDuYunHandler {
return Loader.INSTANCE
}
}
@Throws(java.lang.Exception::class)
fun startScan(): ByteArray {
val result = loginByQRCode()
// Step 2: Get QR Code
val byteStr: ByteArray = downloadQRCode(result["qrCodeImageUrl"]!!);
Init.run(Runnable { showQRCode(byteStr) })
// Step 3: Check login status
Init.execute(Runnable {
startService(
result["sign"]!!
)
})
return byteStr
}
@Throws(IOException::class)
fun downloadQRCode(url: String): ByteArray {
val headers: MutableMap<String?, String?> = HashMap<String?, String?>()
headers.put(
"user-agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36"
)
val request = Request.Builder().url(url).headers(Headers.of(headers)).build()
val response = OkHttp.newCall(request)
if (response.code() == 200) {
return response.body()!!.bytes()
}
return "".toByteArray()
}
fun loginByQRCode(): Map<String, String> {
return try {
// 获取登录二维码
val timestamp = System.currentTimeMillis()
val qrCodeUrl = "https://passport.baidu.com/v2/api/getqrcode?lp=pc&_=$timestamp"
val response = OkHttp.string(qrCodeUrl, emptyMap(), headers)
val json = Json.safeObject(response)
if (json["errno"].asInt != 0) {
return mapOf("error" to "获取登录二维码错误, code: ${json["errno"].asInt}")
}
val sign = json["sign"].asString
val imgurl = json["imgurl"].asString
val qrCodeImageUrl = "https://$imgurl"
val qrLoginUrl =
"https://wappass.baidu.com/wp/?qrlogin&t=$timestamp" + "&error=0&sign=$sign&cmd=login&lp=pc&tpl=netdisk&uaonly=" + "&client_id=&adapter=3&client=&qrloginfrom=pc&wechat=0&traceid="
// 返回二维码信息供前端显示
val result = mapOf(
"qrCodeImageUrl" to qrCodeImageUrl, "qrLoginUrl" to qrLoginUrl, "sign" to sign
)
result
} catch (e: Exception) {
mapOf("error" to "获取二维码失败: ${e.message}")
}
}
fun checkQRLoginStatus(sign: String): Map<String, Any> {
return try {
val timestamp = System.currentTimeMillis()
val checkUrl =
"https://passport.baidu.com/channel/unicast?channel_id=$sign" + "&tpl=netdisk&callback=&apiver=v3&tt=$timestamp&_=$timestamp"
val response = OkHttp.string(checkUrl, emptyMap(), headers)
val cleanResponse = response.trim('(', ' ', '\n', ')')
val json = Json.safeObject(cleanResponse)
if (json["errno"].asInt == 0) {
SpiderDebug.log("百度扫码成功")
val channelV = json["channel_v"].asString
val channelJson = Json.safeObject(channelV)
if (channelJson["status"].asInt == 0) {
val bduss = channelJson["v"].asString
// 执行登录
val loginTimestamp = System.currentTimeMillis()
val loginUrl =
"https://passport.baidu.com/v3/login/main/qrbdusslogin?" + "v=$loginTimestamp&bduss=$bduss&u=&loginVersion=v4&qrcode=1&tpl=netdisk&apiver=v3" + "&tt=$loginTimestamp&traceid=&callback=bd__cbs__cupstt"
val loginResponse = OkHttp.get(loginUrl, emptyMap(), headers)
val cleanLoginResponse = loginResponse.body.substringAfter("(").substringBeforeLast(")")
val loginJson = Json.safeObject(StringEscapeUtils.unescapeHtml4(cleanLoginResponse))
if (loginJson.has("errInfo") && loginJson["errInfo"].asJsonObject["no"].asString == "0") {
// 登录成功设置cookie
SpiderDebug.log("百度登录成功设置cookie${bduss}")
cookies = "BDUSS=$bduss"
cookies = generateCooike(loginResponse.resp["set-cookie"])
cache.setUser(User.objectFrom(this.cookies))
//停止检验线程,关闭弹窗
stopService()
mapOf("success" to true, "bduss" to bduss)
} else {
mapOf("error" to "登录失败: $cleanLoginResponse")
}
} else {
mapOf("status" to channelJson["status"].asInt)
}
} else {
mapOf("errno" to json["errno"].asInt)
}
} catch (e: Exception) {
mapOf("error" to "检查登录状态失败: ${e.message}")
}
}
fun generateCooike(cookies: List<String>?): String {
if (cookies == null || cookies.isEmpty()) {
return ""
}
val cookieList: MutableList<String?> = ArrayList<String?>()
for (cookie in cookies) {
cookieList.add(cookie.split(";".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0])
}
return TextUtils.join(";", cookieList)
}
/**
* 显示qrcode
*
* @param base64Str
*/
fun showQRCode(bytes: ByteArray) {
try {
val size = ResUtil.dp2px(240)
val params = FrameLayout.LayoutParams(size, size)
val image = ImageView(Init.context())
image.setScaleType(ImageView.ScaleType.CENTER_CROP)
image.setImageBitmap(QRCode.Bytes2Bimap(bytes))
val frame = FrameLayout(Init.context())
params.gravity = Gravity.CENTER
frame.addView(image, params)
dialog = AlertDialog.Builder(Init.getActivity()).setView(frame)
.setOnCancelListener(DialogInterface.OnCancelListener { dialog: DialogInterface? -> this.dismiss(dialog) })
.setOnDismissListener(DialogInterface.OnDismissListener { dialog: DialogInterface? ->
this.dismiss(dialog)
}).show()
dialog!!.getWindow()!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
Notify.show("请使用百度网盘App扫描二维码")
} catch (ignored: java.lang.Exception) {
}
}
private fun dismiss() {
try {
dialog?.dismiss()
} catch (ignored: java.lang.Exception) {
}
}
private fun dismiss(dialog: DialogInterface?) {
stopService()
}
private fun stopService() {
service?.shutdownNow()
Init.run(Runnable { this.dismiss() })
}
fun startService(sign: String) {
SpiderDebug.log("----start 百度 token service")
service = Executors.newScheduledThreadPool(1)
service?.scheduleWithFixedDelay(Runnable {
try {
SpiderDebug.log("----check百度tatus中")
checkQRLoginStatus(sign)
} catch (e: UnsupportedEncodingException) {
throw RuntimeException(e)
}
}, 3, 3, TimeUnit.SECONDS)
}
}

View File

@ -22,6 +22,7 @@ object BaiduDrive {
"Referer" to "https://pan.baidu.com/"
)
private val saveDirName = "TVBOX_BD"
private var cookies = BaiDuYunHandler.get().token
@ -37,6 +38,11 @@ object BaiduDrive {
}
fun processShareLinks(urls: List<String>): Pair<List<String>, List<String>> {
//首先确保cookie不为空
if (cookies.isEmpty()) {
BaiDuYunHandler.get().startScan()
cookies = BaiDuYunHandler.get().token
}
if (urls.isEmpty()) return emptyList<String>() to emptyList()
@ -314,6 +320,7 @@ object BaiduDrive {
}
private fun parseQueryParams(url: String): Map<String, List<String>> {
val query = url.substringAfter(
"?"

View File

@ -2,7 +2,6 @@ package com.github.catvod.bean.BD;
import com.github.catvod.api.BaiDuYunHandler;
import com.github.catvod.api.YunTokenHandler;
import com.github.catvod.bean.yun.User;
import com.github.catvod.spider.Init;
import com.github.catvod.utils.Path;

View File

@ -0,0 +1,87 @@
package com.github.catvod.api;
import cn.hutool.core.io.FileUtil;
import com.github.catvod.utils.Json;
import com.google.gson.JsonObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.net.URLEncoder;
@RunWith(RobolectricTestRunner.class)
public class BaiDuYunHandlerTest {
private BaiDuYunHandler baiDuYunHandler;
@Before
public void setUp() {
baiDuYunHandler = BaiDuYunHandler.get();
}
@Test
public void startScan() throws Exception {
// Mock the OkHttp.get method to return a predefined OkResult
// Execute the method under test
FileUtil.writeBytes(baiDuYunHandler.startScan(), "c://qrcode.png");
while (true) {
}
}
/* @Test
public void refreshCookie() throws Exception {
JsonObject obj = Json.safeObject("{\"open.e.189.cn\":{\"OPENINFO\":\"33c28688ef52ce9e3a9ef87388047efbde5e3e2e4c7ef6ef267632468c7dfaf294ff59fa59d34801\",\"pageOp\":\"f73420158c5c010491f1faa4fc91870e\",\"LT\":\"a8900fc0ecae0c59\",\"GUID\":\"b959026ffdf84080ae8567afd9ea4c32\",\"SSON\":\"dc466c8192e3109eaea837c1d136c1fd065253ce1c7d3a66ca1520d7d6d6307b10a1fe65c7becac73b95f24a6e681e654ec4f47c39533ebcc48bb78d6d6e63d1bbf3334e6e97eaa7092d34f87bf1209e256cd4822db68da051a0aeb532d94408c8e50486347fc713813dafc5776a7cfa665ddf96837151232745aa2957fb441d8a79ca7d86f46452060794e6f4b5873ab99ed476629aed2c7b36a44613c92f925dcfd221fce142cd1ecaab667236df697ece293e3ca24030918e5b357bc193118772278748606ade7262bf25ae7527d3c8a059bd48fc08b53b182e61e543a7e9bd1562b50bf80438\"},\"cloud.189.cn\":{\"JSESSIONID\":\"12088774C4B78E632EB944ECA2E6705F\",\"COOKIE_LOGIN_USER\":\"24DA4CBA27A8388982710C2F3D55EFAA84AEE67E9B3EF1B7AC1C565BEEF24C562052CB9B5EAC85E733C10C2704225133CD625407C352ED5D\"}}");
baiDuYunHandler.setCookie(obj);
baiDuYunHandler.refreshCookie();
while (true) {
}
}
@Test
public void download() throws Exception {
// Mock the OkHttp.get method to return a predefined OkResult
// Execute the method under test
}
@Test
public void testgetUUID() throws Exception {
JsonObject uuid = tianYiHandler.getUUID();
System.out.println(uuid);
}
@Test
public void testdownloadQRCode() throws Exception {
*//*
JsonObject uuidInfo = tianYiHandler.getUUID();
String uuid = uuidInfo.get("uuid").getAsString();
byte[] qrCode = tianYiHandler.downloadQRCode(uuid);
FileUtil.writeBytes(qrCode, "c://qrcode.png");
System.out.println(uuid);*//*
String url = "https://cloud.189.cn/api/portal/callbackUnify.action?browserId=dff95dced0b03d9d972d920f03ddd05e&redirectURL=https%3A%2F%2Fcloud.189.cn%2Fweb%2Fredirect.html";
String encode = "https%3A%2F%2Fcloud.189.cn%2Fapi%2Fportal%2FcallbackUnify.action%3FbrowserId%3Ddff95dced0b03d9d972d920f03ddd05e%26redirectURL%3Dhttps%253A%252F%252Fcloud.189.cn%252Fweb%252Fredirect.html";
assert URLEncoder.encode(url, "UTF-8").equals(encode);
}
@Test
public void loginWithPassword() throws Exception {
tianYiHandler.loginWithPassword("18896781601","Lushunming@0526");
System.out.println("1111");
}
*/
}

Binary file not shown.

View File

@ -1 +1 @@
061c059a302046f02caa7543ad2b5ad4
4f3072169892a8d61428ab147dea4766

View File

@ -1,5 +1,5 @@
{
"spider": "https://andoridspidermt.netlify.app/jar/custom_spider.jar;md5;061c059a302046f02caa7543ad2b5ad4",
"spider": "https://andoridspidermt.netlify.app/jar/custom_spider.jar;md5;4f3072169892a8d61428ab147dea4766",
"lives": [
{
"name": "电视直播",