(args: VideoModeArgs)
| 185 | |
| 186 | // fallow-ignore-next-line complexity |
| 187 | export async function runVideoMode(args: VideoModeArgs): Promise<void> { |
| 188 | const projectDir = resolve(args.project); |
| 189 | // standalone capture writes `<dir>/extracted/…`; W2H project nests under `<dir>/capture/extracted/…`. |
| 190 | const directPath = join(projectDir, "extracted", "video-manifest.json"); |
| 191 | const w2hPath = join(projectDir, "capture", "extracted", "video-manifest.json"); |
| 192 | const manifestPath = existsSync(directPath) ? directPath : w2hPath; |
| 193 | const isW2hLayout = manifestPath === w2hPath; |
| 194 | if (!existsSync(manifestPath)) { |
| 195 | console.error( |
| 196 | `${c.error("✗")} no video-manifest.json at ${directPath} or ${w2hPath}\n` + |
| 197 | ` Was this directory produced by \`hyperframes capture\`?`, |
| 198 | ); |
| 199 | process.exitCode = 1; |
| 200 | return; |
| 201 | } |
| 202 | let manifest: ManifestEntry[]; |
| 203 | try { |
| 204 | manifest = JSON.parse(readFileSync(manifestPath, "utf-8")); |
| 205 | } catch (e) { |
| 206 | console.error(`${c.error("✗")} video-manifest.json is malformed: ${(e as Error).message}`); |
| 207 | process.exitCode = 1; |
| 208 | return; |
| 209 | } |
| 210 | |
| 211 | if (args.list) { |
| 212 | if (manifest.length === 0) { |
| 213 | console.log(c.dim("(manifest is empty — no <video> elements on the captured page)")); |
| 214 | return; |
| 215 | } |
| 216 | console.log( |
| 217 | `${manifest.length} video entr${manifest.length === 1 ? "y" : "ies"} in ${manifestPath}:`, |
| 218 | ); |
| 219 | for (const e of manifest) { |
| 220 | console.log( |
| 221 | ` ${c.bold(`[${e.index}]`)} ${e.filename} — ${e.sourceWidth || e.width}×${e.sourceHeight || e.height}` + |
| 222 | (e.heading ? `\n heading: "${e.heading}"` : "") + |
| 223 | `\n url: ${e.url}`, |
| 224 | ); |
| 225 | } |
| 226 | return; |
| 227 | } |
| 228 | |
| 229 | const pick = pickManifestEntry(manifest, args); |
| 230 | if (!pick.ok) { |
| 231 | console.error( |
| 232 | `${c.error("✗")} ${pick.message}` + |
| 233 | (pick.code === "no-match-url" ? `\n Run with --list to see what's available.` : ""), |
| 234 | ); |
| 235 | process.exitCode = 1; |
| 236 | return; |
| 237 | } |
| 238 | const entry = pick.entry; |
| 239 | |
| 240 | const collisions = findFilenameCollision(manifest, entry); |
| 241 | if (collisions.length > 0) { |
| 242 | console.error( |
| 243 | `${c.error("✗")} filename "${safeFilename(entry.filename || basename(entry.url))}" ` + |
| 244 | `collides with manifest entr${collisions.length === 1 ? "y" : "ies"} ` + |
no test coverage detected