Skip to content
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
12 changes: 12 additions & 0 deletions electron/main/apis/netease/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Netease API 通用常量(密钥、域名、客户端伪装标识等)
*/

import { release, type as osType } from "node:os";

/** AES-CBC 初始向量 */
export const IV = "0102030405060708";
/** weapi 第一层 AES 预设密钥 */
Expand Down Expand Up @@ -75,6 +77,16 @@ export const OS_MAP = {
},
} as const;

/**
* 宿主平台对应的客户端标识
* 协议层不依赖 electron,直接读 process.platform
*/
export const HOST_OS_KEY: keyof typeof OS_MAP =
process.platform === "darwin" ? "osx" : process.platform === "linux" ? "linux" : "pc";

/** 宿主真实系统版本,作为未显式指定 os 时的 osver */
export const HOST_OS_VERSION = `${osType()} ${release()}`;

/** 不同加密方式 + 设备类型下的 User-Agent */
export const UA_MAP = {
weapi: {
Expand Down
9 changes: 7 additions & 2 deletions electron/main/apis/netease/core/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
DOMAIN,
EAPI_DOMAIN,
ENCRYPT_RESPONSE,
HOST_OS_KEY,
HOST_OS_VERSION,
OS_MAP,
SPECIAL_STATUS_CODES,
UA_MAP,
Expand Down Expand Up @@ -81,7 +83,10 @@ const processCookieObject = (
crypto: CryptoMode | "",
): Record<string, string> => {
const ntesNuid = cookie._ntes_nuid || randomBytes(16).toString("hex");
const os = OS_MAP[(cookie.os as keyof typeof OS_MAP) || "pc"] || OS_MAP.pc;
/** 未显式指定 os 时跟随宿主平台,系统版本取本机真实值 */
const osKey = (cookie.os as keyof typeof OS_MAP) || HOST_OS_KEY;
const os = OS_MAP[osKey] || OS_MAP[HOST_OS_KEY];
const osver = cookie.osver || (cookie.os ? os.osver : HOST_OS_VERSION);

const processed: Record<string, string> = {
...cookie,
Expand All @@ -91,7 +96,7 @@ const processCookieObject = (
_ntes_nnid: cookie._ntes_nnid || `${ntesNuid},${Date.now()}`,
WNMCID: cookie.WNMCID || WNMCID,
WEVNSM: cookie.WEVNSM || "1.0.0",
osver: cookie.osver || os.osver,
osver,
deviceId: cookie.deviceId || getDeviceId(),
os: cookie.os || os.os,
channel: cookie.channel || os.channel,
Expand Down
1 change: 1 addition & 0 deletions electron/main/apis/netease/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const NON_CACHEABLE: ReadonlySet<string> = new Set([
"login_status",
"logout",
"register_anonimous",
"deviceinfo_center_upload",
"song_url",
"song_download_url",
"scrobble",
Expand Down
28 changes: 28 additions & 0 deletions electron/main/apis/netease/modules/deviceinfo_center_upload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* 上报登录设备名称
*
* 官方客户端在登录成功后调用,用于「登录设备管理」展示自定义名称
*
* params:
* - deviceName 设备名,缺省为「用户名 的 计算机名」
*
* 响应:`{ code }`,成功为 200
*/

import { hostname, userInfo } from "node:os";
import { createOption } from "../core/option";
import type { NeteaseModule } from "../core/types";

/** 与官方客户端一致的设备名,取不到用户名时退化为计算机名 */
const defaultDeviceName = (): string => {
const user = userInfo().username.trim();
const host = hostname();
return user ? `${user} 的 ${host}` : host;
};

const deviceinfoCenterUpload: NeteaseModule = async (query, request) => {
const deviceName = String(query.deviceName ?? "").trim() || defaultDeviceName();
return request("/api/deviceinfo/center/upload", { deviceName }, createOption(query, "eapi"));
};

export default deviceinfoCenterUpload;
2 changes: 2 additions & 0 deletions electron/main/apis/netease/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import user_playlist from "./user_playlist";
import user_record from "./user_record";
import record_recent_song from "./record_recent_song";
import user_subcount from "./user_subcount";
import deviceinfo_center_upload from "./deviceinfo_center_upload";

import cloudsearch from "./cloudsearch";
import search from "./search";
Expand Down Expand Up @@ -123,6 +124,7 @@ export const modules: Record<string, NeteaseModule> = {
user_record,
record_recent_song,
user_subcount,
deviceinfo_center_upload,

cloudsearch,
search,
Expand Down
16 changes: 15 additions & 1 deletion src/apis/login/netease.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import type { UserProfile } from "@/types/user";
import { NeteaseApiError, netease as neteaseApi } from "@/apis/netease";
import { NeteaseApiError, netease as neteaseApi, neteaseCall } from "@/apis/netease";
import type { QrLoginAdapter, QrLoginState } from "./platform";

interface LoginStatusBody {
Expand Down Expand Up @@ -37,6 +37,18 @@ export interface QrCheckResult {
avatarUrl?: string;
}

/**
* 登录成功后上报设备名,避免「登录设备管理」里显示为 pc
* 名称由主进程按当前宿主平台生成;纯展示信息,失败不触发登录失效流程
*/
export const syncNeteaseDeviceName = async (): Promise<void> => {
try {
await neteaseCall("deviceinfo_center_upload", {}, { notifyAuthFailure: false });
} catch {
// 上报失败只影响设备名展示,不影响登录态
}
};

/**
* 轮询扫码状态
* - 800 已过期 / 801 待扫码 / 802 待确认 / 803 已确认(含 cookie)
Expand All @@ -46,6 +58,8 @@ export interface QrCheckResult {
export const qrCheck = async (key: string): Promise<QrCheckResult> => {
const body = await neteaseApi.login_qr_check({ key, timestamp: Date.now() });
const code = (body?.code ?? 801) as QrStatusCode;
/** 803 时主进程已写回登录凭据,可以带上新会话上报设备名 */
if (code === 803) void syncNeteaseDeviceName();
return {
code,
cookie: body?.cookie,
Expand Down
3 changes: 2 additions & 1 deletion src/components/modals/LoginDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useUserStore } from "@/stores/user";
import { toast } from "@/composables/useToast";
import { dialog } from "@/composables/useDialog";
import { REPO_NAME } from "@/utils/config";
import { neteaseQrLoginAdapter } from "@/apis/login/netease";
import { neteaseQrLoginAdapter, syncNeteaseDeviceName } from "@/apis/login/netease";

const props = defineProps<{ open: boolean }>();
const emit = defineEmits<{ "update:open": [value: boolean] }>();
Expand Down Expand Up @@ -53,6 +53,7 @@ const startAutoFetch = async (): Promise<void> => {
return;
}
if (!(await finishLogin())) void panelRef.value?.refresh();
else void syncNeteaseDeviceName();
} finally {
loading.value = false;
}
Expand Down
Loading