mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}…`));
|
||||
|
||||
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user