mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(cli): beats --json answers with no track when the composition has no music (#6690)
* fix(cli): beats --json answers with no track when the composition has no music * fix(cli): drop a comment the name already says
This commit is contained in:
@@ -44,7 +44,7 @@ function project(name: string, bpm: number | null, seconds = 12): string {
|
||||
return dir;
|
||||
}
|
||||
|
||||
async function runBeats(dir: string): Promise<{ exitCode: number; out: string }> {
|
||||
async function runBeats(dir: string, json = true): Promise<{ exitCode: number; out: string }> {
|
||||
const command = (await import("./beats.js")).default as unknown as {
|
||||
run: (context: { args: Record<string, unknown> }) => Promise<void>;
|
||||
};
|
||||
@@ -54,7 +54,7 @@ async function runBeats(dir: string): Promise<{ exitCode: number; out: string }>
|
||||
const error = vi.spyOn(console, "error").mockImplementation(capture);
|
||||
let exitCode = 0;
|
||||
try {
|
||||
await command.run({ args: { dir, json: true } });
|
||||
await command.run({ args: { dir, json } });
|
||||
} catch (thrown) {
|
||||
exitCode = (thrown as { result?: { exitCode?: number } }).result?.exitCode ?? -1;
|
||||
} finally {
|
||||
@@ -105,6 +105,23 @@ describe.skipIf(!canDecode)("hyperframes beats", () => {
|
||||
expect(out).not.toContain("hyperframes audio");
|
||||
});
|
||||
|
||||
it("answers --json with no track, not a failure, when the composition has no music", async () => {
|
||||
const dir = project("no-track", 120);
|
||||
writeFileSync(join(dir, "index.html"), `<div data-composition-id="main"></div>`);
|
||||
const { exitCode, out } = await runBeats(dir);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(JSON.parse(out)).toEqual({ ok: true, track: null, file: null, count: 0, bpm: null });
|
||||
expect(existsSync(join(dir, "beats"))).toBe(false);
|
||||
});
|
||||
|
||||
it("still tells a person with no music track how to mark one", async () => {
|
||||
const dir = project("no-track-person", 120);
|
||||
writeFileSync(join(dir, "index.html"), `<div data-composition-id="main"></div>`);
|
||||
const { exitCode, out } = await runBeats(dir, false);
|
||||
expect(exitCode).toBe(1);
|
||||
expect(out).toMatch(/No music track found/);
|
||||
});
|
||||
|
||||
it("writes nothing for a track with no steady beat", async () => {
|
||||
const dir = project("silent", null);
|
||||
const { exitCode, out } = await runBeats(dir);
|
||||
|
||||
@@ -50,16 +50,15 @@ function findMusicSrc(html: string): string | null {
|
||||
return findMusicAudioSrc(document.toString());
|
||||
}
|
||||
|
||||
/** Locate the music track + its on-disk audio, or fail with a clear message. */
|
||||
function resolveMusicTarget(project: ProjectDir): { rel: string; audioPath: string } {
|
||||
const NO_MUSIC_TRACK =
|
||||
'No music track found. Add data-timeline-role="music" to the <audio> element, or to a ' +
|
||||
'<video data-has-audio="true"> whose own sound is the music ' +
|
||||
"(or give it an id like music/bgm/soundtrack).";
|
||||
|
||||
/** Locate the music track + its on-disk audio: null when the composition has none, or fail with a clear message. */
|
||||
function resolveMusicTarget(project: ProjectDir): { rel: string; audioPath: string } | null {
|
||||
const src = findMusicSrc(readFileSync(project.indexPath, "utf-8"));
|
||||
if (!src) {
|
||||
fail(
|
||||
'No music track found. Add data-timeline-role="music" to the <audio> element, or to a ' +
|
||||
'<video data-has-audio="true"> whose own sound is the music ' +
|
||||
"(or give it an id like music/bgm/soundtrack).",
|
||||
);
|
||||
}
|
||||
if (!src) return null;
|
||||
const rel = audioRelPathForSrc(src); // same derivation the Studio uses
|
||||
if (!rel) fail(`Cannot derive a beat-file path for music src: ${src}`);
|
||||
const audioPath = resolveWithinProject(project.dir, rel);
|
||||
@@ -68,6 +67,11 @@ function resolveMusicTarget(project: ProjectDir): { rel: string; audioPath: stri
|
||||
return { rel, audioPath };
|
||||
}
|
||||
|
||||
function answerNoTrack(json: boolean): void {
|
||||
if (!json) fail(NO_MUSIC_TRACK);
|
||||
console.log(JSON.stringify({ ok: true, track: null, file: null, count: 0, bpm: null }, null, 2));
|
||||
}
|
||||
|
||||
async function decode(audioPath: string): Promise<AudioSignal> {
|
||||
try {
|
||||
const decoded = await decodeAudio(audioPath);
|
||||
@@ -112,7 +116,9 @@ export default defineCommand({
|
||||
);
|
||||
}
|
||||
const project = resolveProject(args.dir);
|
||||
const { rel, audioPath } = resolveMusicTarget(project);
|
||||
const target = resolveMusicTarget(project);
|
||||
if (!target) return answerNoTrack(Boolean(args.json));
|
||||
const { rel, audioPath } = target;
|
||||
if (!args.json) console.log(c.dim(`Analyzing ${rel}…`));
|
||||
|
||||
const result = await analyzeMusicFromSignal(await decode(audioPath));
|
||||
|
||||
Reference in New Issue
Block a user