From 3ebfccdfcc112b70ec802db613931f9819f8d761 Mon Sep 17 00:00:00 2001 From: imsyy Date: Thu, 20 Nov 2025 14:47:51 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=E6=96=B0=E5=A2=9E=E9=9F=B3?= =?UTF-8?q?=E6=BA=90-gequbao?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/server/unblock/gequbao.ts | 107 +++++++++++++++++++++++++++ electron/server/unblock/index.ts | 15 +++- package.json | 2 +- src/components/Player/MainPlayer.vue | 1 + 4 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 electron/server/unblock/gequbao.ts diff --git a/electron/server/unblock/gequbao.ts b/electron/server/unblock/gequbao.ts new file mode 100644 index 0000000..558ee91 --- /dev/null +++ b/electron/server/unblock/gequbao.ts @@ -0,0 +1,107 @@ +import { SongUrlResult } from "./unblock"; +import { serverLog } from "../../main/logger"; +import axios from "axios"; +import { randomBytes } from "crypto"; + +/** + * 搜索歌曲获取 ID + * @param keyword 搜索关键词 + * @returns 歌曲 ID 或 null + */ +const search = async (keyword: string): Promise => { + try { + const searchUrl = `https://www.gequbao.com/s/${encodeURIComponent(keyword)}`; + const { data } = await axios.get(searchUrl); + + // 匹配第一个歌曲链接 /music/12345 + // + const match = data.match( + //, + ); + if (match && match[1]) { + return match[1]; + } + return null; + } catch (error) { + serverLog.error("❌ Get GequbaoSongId Error:", error); + return null; + } +}; + +/** + * 获取播放 ID + * @param id 歌曲 ID + * @returns 播放 ID 或 null + */ +const getPlayId = async (id: string): Promise => { + try { + const url = `https://www.gequbao.com/music/${id}`; + const { data } = await axios.get(url); + + // 匹配 window.appData 中的 play_id + // "play_id":"EFwMVSQDBgsBQV5WBCUDAVkCSQ9WX3kFXV9XEl0KBSEaVldTR19NVndQVlhXRl5cUA==" + const match = data.match(/"play_id":"(.*?)"/); + if (match && match[1]) { + return match[1]; + } + return null; + } catch (error) { + serverLog.error("❌ Get GequbaoPlayId Error:", error); + return null; + } +}; + +/** + * 获取歌曲 URL + * @param keyword 搜索关键词 + * @returns 包含歌曲 URL 的结果对象 + */ +const getGequbaoSongUrl = async (keyword: string): Promise => { + try { + if (!keyword) return { code: 404, url: null }; + + // 1. 获取 ID + const id = await search(keyword); + if (!id) return { code: 404, url: null }; + + // 2. 获取 play_id + const playId = await getPlayId(id); + if (!playId) return { code: 404, url: null }; + + // 3. 获取播放链接 + const url = "https://www.gequbao.com/api/play-url"; + const headers = { + accept: "application/json, text/javascript, */*; q=0.01", + "accept-language": "zh-CN,zh;q=0.9", + "cache-control": "no-cache", + "content-type": "application/x-www-form-urlencoded; charset=UTF-8", + pragma: "no-cache", + priority: "u=1, i", + "sec-ch-ua": '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"Windows"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + "x-requested-with": "XMLHttpRequest", + cookie: `server_name_session=${randomBytes(16).toString("hex")}`, + Referer: `https://www.gequbao.com/music/${id}`, + }; + + const body = `id=${encodeURIComponent(playId)}`; + + const { data } = await axios.post(url, body, { headers }); + + if (data.code === 1 && data.data && data.data.url) { + serverLog.log("🔗 GequbaoSong URL:", data.data.url); + return { code: 200, url: data.data.url }; + } + + return { code: 404, url: null }; + } catch (error) { + serverLog.error("❌ Get GequbaoSong URL Error:", error); + return { code: 404, url: null }; + } +}; + +export default getGequbaoSongUrl; diff --git a/electron/server/unblock/index.ts b/electron/server/unblock/index.ts index fd5a72e..dbb9643 100644 --- a/electron/server/unblock/index.ts +++ b/electron/server/unblock/index.ts @@ -1,9 +1,10 @@ import { FastifyInstance, FastifyRequest, FastifyReply } from "fastify"; import { SongUrlResult } from "./unblock"; import { serverLog } from "../../main/logger"; -import getKuwoSongUrl from "./kuwo"; import axios from "axios"; +import getKuwoSongUrl from "./kuwo"; import getBodianSongUrl from "./bodian"; +import getGequbaoSongUrl from "./gequbao"; /** * 直接获取 网易云云盘 链接 @@ -74,6 +75,18 @@ export const initUnblockAPI = async (fastify: FastifyInstance) => { return reply.send(result); }, ); + // gequbao + fastify.get( + "/unblock/gequbao", + async ( + req: FastifyRequest<{ Querystring: { [key: string]: string } }>, + reply: FastifyReply, + ) => { + const { keyword } = req.query; + const result = await getGequbaoSongUrl(keyword); + return reply.send(result); + }, + ); serverLog.info("🌐 Register UnblockAPI successfully"); }; diff --git a/package.json b/package.json index 2b61408..eced8d9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "splayer", "productName": "SPlayer", - "version": "3.0.0-beta.5", + "version": "3.0.0-beta.6", "description": "A minimalist music player", "main": "./out/main/index.js", "author": "imsyy", diff --git a/src/components/Player/MainPlayer.vue b/src/components/Player/MainPlayer.vue index 4a3f61c..ee42fb1 100644 --- a/src/components/Player/MainPlayer.vue +++ b/src/components/Player/MainPlayer.vue @@ -494,6 +494,7 @@ const instantLyrics = computed(() => { } } .play-menu { + margin-left: auto; max-width: 640px; .time-container { margin-right: 8px;