优化 peq 流程

This commit is contained in:
eafonyang
2026-09-20 13:58:54 +08:00
parent 1dd747065f
commit f4c9579e4f
6 changed files with 560 additions and 406 deletions
+78 -55
View File
@@ -295,6 +295,14 @@ function normalizeDeviceHost(input: string): string {
// ============================================================
export class LuxsinAPI {
private baseUrl: string;
private peqRequestQueue: Promise<void> = Promise.resolve();
/** EQ 写入、试听、选择和目录读取按调用顺序执行;单次失败不阻塞后续重试。 */
private enqueuePeqRequest<T>(request: () => Promise<T>): Promise<T> {
const result = this.peqRequestQueue.then(request);
this.peqRequestQueue = result.then(() => undefined, () => undefined);
return result;
}
constructor(ip: string) {
const host = normalizeDeviceHost(ip);
@@ -339,17 +347,25 @@ export class LuxsinAPI {
}
async setSetting(params: Record<string, string | number>): Promise<void> {
const query = Object.entries(params)
.map(([k, v]) => `${k}=${v}`)
.join("&");
await fetch(`${this.baseUrl}/dev/info.cgi?action=setting&${query}`);
const request = async () => {
const query = Object.entries(params)
.map(([k, v]) => `${k}=${v}`)
.join("&");
const response = await fetch(`${this.baseUrl}/dev/info.cgi?action=setting&${query}`);
if (!response.ok) throw new Error(`PEQ request failed: ${response.status}`);
};
if (params.peqSelect !== undefined) return this.enqueuePeqRequest(request);
return request();
}
async getPeqState(): Promise<PeqState> {
const response = await fetch(`${this.baseUrl}/dev/info.cgi?action=syncPeq`);
const text = await response.text();
const decoded = decodeCustomBase64(text.trim());
return JSON.parse(decoded) as PeqState;
return this.enqueuePeqRequest(async () => {
const response = await fetch(`${this.baseUrl}/dev/info.cgi?action=syncPeq`);
if (!response.ok) throw new Error(`PEQ read failed: ${response.status}`);
const text = await response.text();
const decoded = decodeCustomBase64(text.trim());
return JSON.parse(decoded) as PeqState;
});
}
async setPeqFilters(filters: PeqFilter[]): Promise<void> {
@@ -368,69 +384,76 @@ export class LuxsinAPI {
/** POST `json=<custom-base64>` — matches legacy axios `upgradePeq`. */
private async postPeqJson(body: PeqChangePayload | PeqApplyPayload): Promise<void> {
if (import.meta.env.DEV) {
const action =
"peqChange" in body ? "peqChange" : "peqApply" in body ? "peqApply" : "peq";
console.log(`[dev/info.cgi] POST ${action}`, body);
}
const encoded = encodeCustomBase64(JSON.stringify(body));
const form = new URLSearchParams();
form.set("json", encoded);
const response = await fetch(`${this.baseUrl}/dev/info.cgi`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
body: form.toString(),
return this.enqueuePeqRequest(async () => {
if (import.meta.env.DEV) {
const action = "peqChange" in body ? "peqChange" : "peqApply" in body ? "peqApply" : "peq";
console.log(`[dev/info.cgi] POST ${action}`, body);
}
const encoded = encodeCustomBase64(JSON.stringify(body));
const form = new URLSearchParams();
form.set("json", encoded);
const response = await fetch(`${this.baseUrl}/dev/info.cgi`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
body: form.toString(),
});
if (!response.ok) {
throw new Error(`PEQ request failed: ${response.status}`);
}
});
if (!response.ok) {
throw new Error(`PEQ request failed: ${response.status}`);
}
}
/** Remove one or more headphone PEQ profiles. */
async removePeq(names: string[]): Promise<void> {
const encoded = encodeCustomBase64(JSON.stringify({ peqRemove: names }));
const form = new URLSearchParams();
form.set("json", encoded);
const response = await fetch(`${this.baseUrl}/dev/info.cgi`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
body: form.toString(),
return this.enqueuePeqRequest(async () => {
const encoded = encodeCustomBase64(JSON.stringify({ peqRemove: names }));
const form = new URLSearchParams();
form.set("json", encoded);
const response = await fetch(`${this.baseUrl}/dev/info.cgi`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
body: form.toString(),
});
if (!response.ok) {
throw new Error(`PEQ remove failed: ${response.status}`);
}
});
if (!response.ok) {
throw new Error(`PEQ remove failed: ${response.status}`);
}
}
/** Rename a headphone PEQ profile. */
async renamePeq(oldName: string, newName: string): Promise<void> {
const payload: PeqRenamePayload = { peqRename: { oldName, newName } };
const encoded = encodeCustomBase64(JSON.stringify(payload));
const form = new URLSearchParams();
form.set("json", encoded);
const response = await fetch(`${this.baseUrl}/dev/info.cgi`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
body: form.toString(),
return this.enqueuePeqRequest(async () => {
const payload: PeqRenamePayload = { peqRename: { oldName, newName } };
const encoded = encodeCustomBase64(JSON.stringify(payload));
const form = new URLSearchParams();
form.set("json", encoded);
const response = await fetch(`${this.baseUrl}/dev/info.cgi`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
body: form.toString(),
});
if (!response.ok) {
throw new Error(`PEQ rename failed: ${response.status}`);
}
});
if (!response.ok) {
throw new Error(`PEQ rename failed: ${response.status}`);
}
}
/** Reorder headphone PEQ profiles. */
async sortPeq(names: string[], peqSelect: number): Promise<void> {
const payload: PeqSoftPayload = { peqSoft: names, peqSelect };
const encoded = encodeCustomBase64(JSON.stringify(payload));
const form = new URLSearchParams();
form.set("json", encoded);
const response = await fetch(`${this.baseUrl}/dev/info.cgi`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
body: form.toString(),
return this.enqueuePeqRequest(async () => {
const payload: PeqSoftPayload = { peqSoft: names, peqSelect };
const encoded = encodeCustomBase64(JSON.stringify(payload));
const form = new URLSearchParams();
form.set("json", encoded);
const response = await fetch(`${this.baseUrl}/dev/info.cgi`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
body: form.toString(),
});
if (!response.ok) {
throw new Error(`PEQ sort failed: ${response.status}`);
}
});
if (!response.ok) {
throw new Error(`PEQ sort failed: ${response.status}`);
}
}
setVolume(volume: number) { return this.setSetting({ volume }); }