Files
teamspeak-music-bot/test-ts6-version.cjs
saopig1andClaude Opus 5 af1dac848d fix(local): remux aac into .m4a so the extracted audio is bit-exact (#149)
Extraction always used Matroska (.mka) because it takes essentially any
audio codec. That is right for most codecs but wrong for AAC: MP4 records
the AAC encoder priming (the ~1000 warm-up samples every AAC encoder emits)
in an edit list, and the edit list does not survive into Matroska. The
remuxed track then decodes ~23 ms longer than the source, with the priming
samples played at the head instead of discarded.

Measured on a 5s 640x480 fixture: source audio decodes to 962980 bytes of
PCM, the .mka to 967440 — 4460 bytes / ~23 ms extra, peaking at -66 dBFS.
Inaudible in practice, but it also puts the track fractionally out of step
with its own reported duration, for no reason.

Pick the container by codec instead: aac -> .m4a (keeps the edit list),
everything else -> .mka as before. If the preferred container refuses the
codec, retry into .mka before falling back to keeping the whole video. AAC
is worth the special case because mp4 / mov / m4v — what people actually
upload — almost always carry it.

Adds the strongest available test of the "lossless" claim: decode the audio
straight out of the source mp4, decode the stored extract, assert the PCM is
byte-for-byte equal. Forcing .mka fails it with exactly the 4460-byte delta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 01:50:08 +08:00

93 lines
2.8 KiB
JavaScript

const { Client, generateIdentity } = require('@honeybbq/teamspeak-client');
function escapeTS3(value) {
return value
.replace(/\\/g, "\\\\")
.replace(/\//g, "\\/")
.replace(/ /g, "\\s")
.replace(/\|/g, "\\p")
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r")
.replace(/\t/g, "\\t");
}
function replaceField(cmd, key, value) {
const escaped = escapeTS3(value);
const regex = new RegExp(key + "=\\S*");
if (regex.test(cmd)) return cmd.replace(regex, key + "=" + escaped);
return cmd;
}
// Version to test - passed via env
const VERSIONS = [
{
name: "6.0.0-beta2",
version: "6.0.0-beta2 [Build: 1737468425]",
platform: "Windows",
sign: "b5mySiqYAd4Lv5TZNflW+G5Gk8y7Woo9YnJfHRjmzhUyMdGfF1O7fSGJzmx2Hpe/PKaY2aDuKKD6lSxbLYlKCw==",
},
{
name: "3.?.? wildcard",
version: "3.?.? [Build: 5680278000]",
platform: "Windows",
sign: "DX5NIYLvfJEUjuIbCidnoeozxIDRRkpq3I9vVMBmE9L2qnekOoBzSenkzsg2lC9CMv8K5hkEzhr2TYUYSwUXCg==",
},
{
name: "5.0.0-beta77",
version: "5.0.0-beta77 [Build: 1702382332]",
platform: "Windows",
sign: "Ee6DzP16MUXpdKWjiSY0NGb4thN22/Ks0hwNcaMrWoaadgkM6c5477X0IbGFWVjzTWfjFTEad5noYLUPDWSgCQ==",
},
{
name: "3.6.2 (corrected sign)",
version: "3.6.2 [Build: 1695203293]",
platform: "Windows",
sign: "4BdaZpdgUSMCuIs8qcloJPNxNlJ4o7QKnxMCRO60mSOTtJZyKjOrGLAmeAEtLIJjcjmdSpycMbQOIV92K2vXAw==",
},
];
const idx = parseInt(process.env.VERSION_IDX || "0");
const V = VERSIONS[idx];
const identity = generateIdentity(8);
const client = new Client(identity, "localhost:9987", "MusicBot", {
logger: {
debug: () => {},
info: (m) => console.log("[INFO]", m),
warn: (m) => console.log("[WARN]", m),
error: (m) => console.log("[ERROR]", m),
},
});
console.log("Testing version: " + V.name + " -> " + V.version);
client.connect().then(() => {
const handler = client.handler;
const origSendPacket = handler.sendPacket.bind(handler);
handler.sendPacket = (pType, data, flags) => {
if (pType === 2) {
let str = Buffer.from(data).toString("utf-8");
if (str.startsWith("clientinit ")) {
str = replaceField(str, "client_version", V.version);
str = replaceField(str, "client_platform", V.platform);
str = replaceField(str, "client_version_sign", V.sign);
console.log("[PATCHED] version=" + V.version);
origSendPacket(pType, Buffer.from(str), flags);
return;
}
}
origSendPacket(pType, data, flags);
};
return client.waitConnected();
}).then(() => {
console.log("SUCCESS! Connected with clientId = " + client.clientID());
client.disconnect();
process.exit(0);
}).catch((err) => {
console.log("ERROR:", err && err.message || err);
process.exit(1);
});
setTimeout(() => {
console.log("TIMEOUT - version rejected");
process.exit(2);
}, 10000);