fix(cli): beats given a media file points to hyperframes audio instead of a folder error (#6544)

* fix(cli): beats given a media file points to hyperframes audio instead of a folder error

* fix(cli): beats names the folder for a composition file and syncs the skills manifest
This commit is contained in:
Miguel Ángel
2026-10-27 10:49:26 +00:00
committed by GitHub
parent c2b68fbe07
commit 5a92fe7e5e
5 changed files with 33 additions and 3 deletions
+3
View File
@@ -672,6 +672,9 @@ npx hyperframes beats [dir]
npx hyperframes beats [dir] --json # { ok, file, count, bpm }
```
`beats` takes the project folder, not a media file. For the beats of one audio
or video file, run `npx hyperframes audio <file> --only beats --json`.
`beats` finds the music track — an `<audio>` element with
`data-timeline-role="music"`, or an id like `music`, `bgm`, or `soundtrack` —
decodes it with ffmpeg, runs the same beat tracker Studio uses, and writes
+15
View File
@@ -90,6 +90,21 @@ describe.skipIf(!canDecode)("hyperframes beats", () => {
expect(Math.abs(report.bpm - 120)).toBeLessThanOrEqual(1);
});
it("points a media file argument to hyperframes audio instead of reading it as a project", async () => {
const song = join(project("file-arg", 120), "assets", "song.wav");
const { exitCode, out } = await runBeats(song);
expect(exitCode).toBe(1);
expect(out).toContain(`hyperframes audio ${JSON.stringify(song)} --only beats --json`);
});
it("tells a composition file argument to pass its folder", async () => {
const dir = project("html-arg", 120);
const { exitCode, out } = await runBeats(join(dir, "index.html"));
expect(exitCode).toBe(1);
expect(out).toContain(`hyperframes beats ${JSON.stringify(dir)}`);
expect(out).not.toContain("hyperframes audio");
});
it("writes nothing for a track with no steady beat", async () => {
const dir = project("silent", null);
const { exitCode, out } = await runBeats(dir);
+12 -2
View File
@@ -1,7 +1,7 @@
import { failCommand } from "../utils/commandResult.js";
import { defineCommand } from "citty";
import { existsSync, readFileSync, mkdirSync, writeFileSync } from "node:fs";
import { join, dirname } from "node:path";
import { existsSync, readFileSync, mkdirSync, statSync, writeFileSync } from "node:fs";
import { join, dirname, resolve } from "node:path";
import {
analyzeMusicFromSignal,
audioRelPathForSrc,
@@ -21,6 +21,7 @@ import { c } from "../ui/colors.js";
export const examples: Example[] = [
["Generate the beat file for the current project", "hyperframes beats"],
["Generate for a specific directory", "hyperframes beats ./my-video"],
["Beats of a single audio file", "hyperframes audio music.mp3 --only beats --json"],
];
function fail(message: string): never {
@@ -101,6 +102,15 @@ export default defineCommand({
json: { type: "boolean", description: "Output result as JSON", default: false },
},
async run({ args }) {
if (args.dir && existsSync(resolve(args.dir)) && statSync(resolve(args.dir)).isFile()) {
const file = JSON.stringify(args.dir);
fail(
/\.html?$/i.test(args.dir)
? `${args.dir} is a file; beats takes the project folder: hyperframes beats ${JSON.stringify(dirname(args.dir))}`
: `${args.dir} is a file; beats takes a project folder. ` +
`For one audio or video file run: hyperframes audio ${file} --only beats --json`,
);
}
const project = resolveProject(args.dir);
const { rel, audioPath } = resolveMusicTarget(project);
if (!args.json) console.log(c.dim(`Analyzing ${rel}…`));
+1 -1
View File
@@ -10,7 +10,7 @@
"files": 77
},
"hyperframes-cli": {
"hash": "49fa62fa12913672",
"hash": "433959bc422463f4",
"files": 13
},
"hyperframes-registry": {
@@ -8,6 +8,8 @@ npx hyperframes beats ./my-video
npx hyperframes beats ./my-video --json
```
`beats` takes the project folder, never a media file. For the beats of one audio or video file, run `npx hyperframes audio <file> --only beats --json`.
The project must contain a local music `<audio>` source. Mark it with `data-timeline-role="music"`; an id containing `music`, `bgm`, or `soundtrack` is also recognized. The command decodes that file with ffmpeg, tracks its beats, and writes `beats/<audio-relative-path>.json`.
If the track has no steady beat, the command fails and writes nothing. If ffmpeg is missing, run `npx hyperframes doctor`.