分享码功能的实现,并修复了几个 bug
This commit is contained in:
+16
-7
@@ -7,6 +7,7 @@ import {
|
||||
LuxsinAPI,
|
||||
PeqFilter,
|
||||
FILTER_TYPE_LABELS,
|
||||
buildPeqPresetBody,
|
||||
dbToVolume,
|
||||
volumeToDb,
|
||||
} from "./luxsinApi";
|
||||
@@ -34,6 +35,8 @@ export interface OptimizePeqPayload {
|
||||
name?: string;
|
||||
brand?: string;
|
||||
model?: string;
|
||||
target?: string;
|
||||
form?: string;
|
||||
preamp?: number;
|
||||
canDel?: number;
|
||||
autoPre?: number;
|
||||
@@ -327,13 +330,19 @@ export async function executeFrontendTool(
|
||||
case "set_peq": {
|
||||
const p = block.input ?? {};
|
||||
await api.upgradePeqChange({
|
||||
peqChange: {
|
||||
name: p.name,
|
||||
filters: p.filters ?? [],
|
||||
autoPre: p.autoPre,
|
||||
preamp: p.preamp,
|
||||
canDel: p.canDel,
|
||||
},
|
||||
peqChange: buildPeqPresetBody(
|
||||
{
|
||||
name: p.name,
|
||||
brand: p.brand,
|
||||
model: p.model,
|
||||
target: p.target,
|
||||
form: p.form,
|
||||
autoPre: p.autoPre,
|
||||
preamp: p.preamp,
|
||||
canDel: p.canDel,
|
||||
},
|
||||
(p.filters ?? []) as PeqFilter[],
|
||||
),
|
||||
});
|
||||
await api.refreshState();
|
||||
return { ok: true, content: { ok: true, name: p.name } };
|
||||
|
||||
@@ -145,6 +145,7 @@ export interface PeqState {
|
||||
canDel?: number;
|
||||
brand?: string;
|
||||
model?: string;
|
||||
target?: string;
|
||||
form?: string;
|
||||
}>;
|
||||
}
|
||||
@@ -162,6 +163,38 @@ export interface PeqPresetBody {
|
||||
form?: string;
|
||||
}
|
||||
|
||||
/** PEQ preset metadata used when building peqChange / peqApply bodies. */
|
||||
export type PeqPresetMeta = {
|
||||
name: string;
|
||||
autoPre?: number;
|
||||
preamp?: number;
|
||||
canDel?: number;
|
||||
brand?: string;
|
||||
model?: string;
|
||||
target?: string;
|
||||
form?: string;
|
||||
};
|
||||
|
||||
/** Build device PEQ POST body; optional brand/model/target/form included only when non-empty. */
|
||||
export function buildPeqPresetBody(peq: PeqPresetMeta, filters: PeqFilter[]): PeqPresetBody {
|
||||
const body: PeqPresetBody = {
|
||||
name: peq.name,
|
||||
filters,
|
||||
autoPre: peq.autoPre ?? 0,
|
||||
preamp: peq.preamp ?? 0,
|
||||
canDel: peq.canDel ?? 1,
|
||||
};
|
||||
const brand = peq.brand?.trim();
|
||||
const model = peq.model?.trim();
|
||||
const target = peq.target?.trim();
|
||||
const form = peq.form?.trim();
|
||||
if (brand) body.brand = brand;
|
||||
if (model) body.model = model;
|
||||
if (target) body.target = target;
|
||||
if (form) body.form = form;
|
||||
return body;
|
||||
}
|
||||
|
||||
/** POST body for `/dev/info.cgi` — full peq preset update (custom base64 `json` field). */
|
||||
export interface PeqChangePayload {
|
||||
peqChange: PeqPresetBody;
|
||||
@@ -635,3 +668,110 @@ export async function fetchLuxsinAudioCurve(
|
||||
const body = (await res.text()).trim();
|
||||
return decodeCustomBase64(body);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Share Code API — shareCreate / shareList / shareQuery / shareAccept
|
||||
// ============================================================
|
||||
|
||||
export interface ShareCodeCreateResponse {
|
||||
code: number;
|
||||
msg: string;
|
||||
share_code?: string;
|
||||
expire_at?: string;
|
||||
eq_data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface ShareCodeListItem {
|
||||
share_code: string;
|
||||
expire_at: string;
|
||||
eq_data: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface ShareCodeListResponse {
|
||||
code: number;
|
||||
msg: string;
|
||||
share_codes: ShareCodeListItem[];
|
||||
}
|
||||
|
||||
export interface ShareCodeAcceptResponse {
|
||||
code: number;
|
||||
msg: string;
|
||||
eq_data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface ShareCodeQueryResponse {
|
||||
code: number;
|
||||
msg: string;
|
||||
eq_data?: Record<string, unknown>;
|
||||
expire_at?: string;
|
||||
}
|
||||
|
||||
/** Unwrap stringified PEQ filters (remove JSON escape layers) for API submit. */
|
||||
export function normalizePeqFiltersForSubmit(filters: unknown): PeqFilter[] {
|
||||
if (Array.isArray(filters)) return filters as PeqFilter[];
|
||||
if (typeof filters !== "string" || !filters.trim()) return [];
|
||||
|
||||
let current: unknown = filters.trim();
|
||||
for (let depth = 0; depth < 3; depth++) {
|
||||
if (Array.isArray(current)) return current as PeqFilter[];
|
||||
if (typeof current !== "string") return [];
|
||||
try {
|
||||
current = JSON.parse(current);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return Array.isArray(current) ? (current as PeqFilter[]) : [];
|
||||
}
|
||||
|
||||
/** POST `/audio/shareCreate` — 创建 EQ 分享码,返回 share_code + expire_at + eq_data */
|
||||
export async function createShareCode(
|
||||
mac: string,
|
||||
eqData: Record<string, unknown>,
|
||||
): Promise<ShareCodeCreateResponse> {
|
||||
const payload = {
|
||||
...eqData,
|
||||
...(eqData.filters !== undefined
|
||||
? { filters: normalizePeqFiltersForSubmit(eqData.filters) }
|
||||
: {}),
|
||||
};
|
||||
const res = await fetch(buildLuxsinAudioUrl("shareCreate"), {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ mac, eq_data: payload }),
|
||||
});
|
||||
if (!res.ok) throw new Error(`shareCreate HTTP ${res.status}`);
|
||||
return (await res.json()) as ShareCodeCreateResponse;
|
||||
}
|
||||
|
||||
/** GET `/audio/shareList?mac=...` — 查询某 MAC 尚未过期的分享码列表 */
|
||||
export async function listShareCodes(mac: string): Promise<ShareCodeListResponse> {
|
||||
const res = await fetch(
|
||||
buildLuxsinAudioUrl(`shareList?mac=${encodeURIComponent(mac)}`),
|
||||
);
|
||||
if (!res.ok) throw new Error(`shareList HTTP ${res.status}`);
|
||||
return (await res.json()) as ShareCodeListResponse;
|
||||
}
|
||||
|
||||
/** GET `/audio/shareQuery?shareCode=...` — 预览分享码 EQ 数据(不记录导入) */
|
||||
export async function queryShareCode(shareCode: string): Promise<ShareCodeQueryResponse> {
|
||||
const res = await fetch(
|
||||
buildLuxsinAudioUrl(`shareQuery?shareCode=${encodeURIComponent(shareCode)}`),
|
||||
);
|
||||
if (!res.ok) throw new Error(`shareQuery HTTP ${res.status}`);
|
||||
return (await res.json()) as ShareCodeQueryResponse;
|
||||
}
|
||||
|
||||
/** GET `/audio/shareAccept?mac=...&shareCode=...` — 确认导入分享码并记录流水 */
|
||||
export async function acceptShareCode(
|
||||
mac: string,
|
||||
shareCode: string,
|
||||
): Promise<ShareCodeAcceptResponse> {
|
||||
const res = await fetch(
|
||||
buildLuxsinAudioUrl(
|
||||
`shareAccept?mac=${encodeURIComponent(mac)}&shareCode=${encodeURIComponent(shareCode)}`,
|
||||
),
|
||||
);
|
||||
if (!res.ok) throw new Error(`shareAccept HTTP ${res.status}`);
|
||||
return (await res.json()) as ShareCodeAcceptResponse;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user