jirallm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +214 -0
- package/dist/cli/commands/auth.d.ts +7 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +69 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +5 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +191 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +198 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/orgs.d.ts +8 -0
- package/dist/cli/commands/orgs.d.ts.map +1 -0
- package/dist/cli/commands/orgs.js +81 -0
- package/dist/cli/commands/orgs.js.map +1 -0
- package/dist/cli/commands/profiles.d.ts +2 -0
- package/dist/cli/commands/profiles.d.ts.map +1 -0
- package/dist/cli/commands/profiles.js +20 -0
- package/dist/cli/commands/profiles.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +7 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +130 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +267 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/issueKey.d.ts +13 -0
- package/dist/cli/issueKey.d.ts.map +1 -0
- package/dist/cli/issueKey.js +50 -0
- package/dist/cli/issueKey.js.map +1 -0
- package/dist/cli/issueKey.test.d.ts +2 -0
- package/dist/cli/issueKey.test.d.ts.map +1 -0
- package/dist/cli/issueKey.test.js +48 -0
- package/dist/cli/issueKey.test.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +49 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +173 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/config.test.d.ts +2 -0
- package/dist/lib/config.test.d.ts.map +1 -0
- package/dist/lib/config.test.js +130 -0
- package/dist/lib/config.test.js.map +1 -0
- package/dist/lib/configSchema.d.ts +42 -0
- package/dist/lib/configSchema.d.ts.map +1 -0
- package/dist/lib/configSchema.js +41 -0
- package/dist/lib/configSchema.js.map +1 -0
- package/dist/lib/credentials.d.ts +8 -0
- package/dist/lib/credentials.d.ts.map +1 -0
- package/dist/lib/credentials.js +62 -0
- package/dist/lib/credentials.js.map +1 -0
- package/dist/lib/credentials.test.d.ts +2 -0
- package/dist/lib/credentials.test.d.ts.map +1 -0
- package/dist/lib/credentials.test.js +35 -0
- package/dist/lib/credentials.test.js.map +1 -0
- package/dist/lib/exporter.d.ts +28 -0
- package/dist/lib/exporter.d.ts.map +1 -0
- package/dist/lib/exporter.js +149 -0
- package/dist/lib/exporter.js.map +1 -0
- package/dist/lib/jiraClient.d.ts +129 -0
- package/dist/lib/jiraClient.d.ts.map +1 -0
- package/dist/lib/jiraClient.js +308 -0
- package/dist/lib/jiraClient.js.map +1 -0
- package/dist/lib/platform.d.ts +9 -0
- package/dist/lib/platform.d.ts.map +1 -0
- package/dist/lib/platform.js +82 -0
- package/dist/lib/platform.js.map +1 -0
- package/dist/lib/runCommand.d.ts +7 -0
- package/dist/lib/runCommand.d.ts.map +1 -0
- package/dist/lib/runCommand.js +26 -0
- package/dist/lib/runCommand.js.map +1 -0
- package/dist/lib/videoFrameExtractor.d.ts +22 -0
- package/dist/lib/videoFrameExtractor.d.ts.map +1 -0
- package/dist/lib/videoFrameExtractor.js +234 -0
- package/dist/lib/videoFrameExtractor.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
const execAsync = promisify(exec);
|
|
4
|
+
export function detectOS() {
|
|
5
|
+
switch (process.platform) {
|
|
6
|
+
case 'darwin':
|
|
7
|
+
return 'macos';
|
|
8
|
+
case 'linux':
|
|
9
|
+
return 'linux';
|
|
10
|
+
case 'win32':
|
|
11
|
+
return 'windows';
|
|
12
|
+
default:
|
|
13
|
+
return 'unknown';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function commandExists(cmd) {
|
|
17
|
+
try {
|
|
18
|
+
await execAsync(`command -v ${cmd}`);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function hasHomebrew() {
|
|
26
|
+
return commandExists('brew');
|
|
27
|
+
}
|
|
28
|
+
export async function detectPackageManager() {
|
|
29
|
+
const os = detectOS();
|
|
30
|
+
if (os === 'macos')
|
|
31
|
+
return (await hasHomebrew()) ? 'brew' : 'none';
|
|
32
|
+
if (os === 'linux') {
|
|
33
|
+
if (await commandExists('apt'))
|
|
34
|
+
return 'apt';
|
|
35
|
+
if (await commandExists('dnf'))
|
|
36
|
+
return 'dnf';
|
|
37
|
+
if (await commandExists('pacman'))
|
|
38
|
+
return 'pacman';
|
|
39
|
+
return 'none';
|
|
40
|
+
}
|
|
41
|
+
return 'none';
|
|
42
|
+
}
|
|
43
|
+
export function ffmpegInstallCommand(pm) {
|
|
44
|
+
switch (pm) {
|
|
45
|
+
case 'brew':
|
|
46
|
+
return 'brew install ffmpeg';
|
|
47
|
+
case 'apt':
|
|
48
|
+
return 'sudo apt install -y ffmpeg';
|
|
49
|
+
case 'dnf':
|
|
50
|
+
return 'sudo dnf install -y ffmpeg';
|
|
51
|
+
case 'pacman':
|
|
52
|
+
return 'sudo pacman -S --noconfirm ffmpeg';
|
|
53
|
+
default:
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function getFfmpegInstallHint() {
|
|
58
|
+
const os = detectOS();
|
|
59
|
+
const pm = await detectPackageManager();
|
|
60
|
+
const cmd = ffmpegInstallCommand(pm);
|
|
61
|
+
const lines = [];
|
|
62
|
+
if (cmd) {
|
|
63
|
+
lines.push(`Install with: ${cmd}`);
|
|
64
|
+
}
|
|
65
|
+
else if (os === 'macos') {
|
|
66
|
+
lines.push('Homebrew not detected. Install Homebrew (https://brew.sh), then: brew install ffmpeg');
|
|
67
|
+
}
|
|
68
|
+
else if (os === 'linux') {
|
|
69
|
+
lines.push('No supported package manager detected. Install ffmpeg via your distro (https://ffmpeg.org/download.html).');
|
|
70
|
+
}
|
|
71
|
+
else if (os === 'windows') {
|
|
72
|
+
lines.push('Install ffmpeg from https://ffmpeg.org/download.html or via Chocolatey: choco install ffmpeg');
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
lines.push('Install ffmpeg from https://ffmpeg.org/download.html');
|
|
76
|
+
}
|
|
77
|
+
lines.push('Or run: jirallm setup');
|
|
78
|
+
lines.push('Or use bundled fallback: pnpm add -g ffmpeg-static (then re-run jirallm)');
|
|
79
|
+
return lines.join('\n');
|
|
80
|
+
}
|
|
81
|
+
export const HOMEBREW_INSTALL_CMD = '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/main/install.sh)"';
|
|
82
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/lib/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEjC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAKlC,MAAM,UAAU,QAAQ;IACtB,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,OAAO,CAAC;QACjB,KAAK,OAAO;YACV,OAAO,OAAO,CAAC;QACjB,KAAK,OAAO;YACV,OAAO,SAAS,CAAC;QACnB;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,GAAW;IACtC,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,cAAc,GAAG,EAAE,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAC;IACtB,IAAI,EAAE,KAAK,OAAO;QAAE,OAAO,CAAC,MAAM,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IACnE,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;QACnB,IAAI,MAAM,aAAa,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC7C,IAAI,MAAM,aAAa,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC7C,IAAI,MAAM,aAAa,CAAC,QAAQ,CAAC;YAAE,OAAO,QAAQ,CAAC;QACnD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,EAAkB;IACrD,QAAQ,EAAE,EAAE,CAAC;QACX,KAAK,MAAM;YACT,OAAO,qBAAqB,CAAC;QAC/B,KAAK,KAAK;YACR,OAAO,4BAA4B,CAAC;QACtC,KAAK,KAAK;YACR,OAAO,4BAA4B,CAAC;QACtC,KAAK,QAAQ;YACX,OAAO,mCAAmC,CAAC;QAC7C;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAC;IACtB,MAAM,EAAE,GAAG,MAAM,oBAAoB,EAAE,CAAC;IACxC,MAAM,GAAG,GAAG,oBAAoB,CAAC,EAAE,CAAC,CAAC;IAErC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,GAAG,EAAE,CAAC;QACR,KAAK,CAAC,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC;IACrC,CAAC;SAAM,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,sFAAsF,CAAC,CAAC;IACrG,CAAC;SAAM,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,2GAA2G,CAAC,CAAC;IAC1H,CAAC;SAAM,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,8FAA8F,CAAC,CAAC;IAC7G,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;IACrE,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACpC,KAAK,CAAC,IAAI,CAAC,0EAA0E,CAAC,CAAC;IACvF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,MAAM,oBAAoB,GAC/B,iGAAiG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runCommand.d.ts","sourceRoot":"","sources":["../../src/lib/runCommand.ts"],"names":[],"mappings":"AAKA,KAAK,iBAAiB,GAAG;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
const execAsync = promisify(exec);
|
|
4
|
+
export async function runCommand(command, description, options = {}) {
|
|
5
|
+
const { shouldThrow = true, silent = false } = options;
|
|
6
|
+
if (!silent)
|
|
7
|
+
console.info(`\n${description}...`);
|
|
8
|
+
try {
|
|
9
|
+
const { stdout, stderr } = await execAsync(command);
|
|
10
|
+
if (!silent) {
|
|
11
|
+
if (stderr)
|
|
12
|
+
console.error(stderr);
|
|
13
|
+
console.info(stdout);
|
|
14
|
+
}
|
|
15
|
+
return stdout.trim();
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (!silent) {
|
|
19
|
+
console.error(`${description} failed:`, error instanceof Error ? error.message : String(error));
|
|
20
|
+
}
|
|
21
|
+
if (shouldThrow)
|
|
22
|
+
throw error;
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=runCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runCommand.js","sourceRoot":"","sources":["../../src/lib/runCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEjC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAOlC,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,WAAmB,EACnB,UAA6B,EAAE;IAE/B,MAAM,EAAE,WAAW,GAAG,IAAI,EAAE,MAAM,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAEvD,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,KAAK,WAAW,KAAK,CAAC,CAAC;IAEjD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,MAAM;gBAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAClC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,CACX,GAAG,WAAW,UAAU,EACxB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;QACJ,CAAC;QACD,IAAI,WAAW;YAAE,MAAM,KAAK,CAAC;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type ExtractionOptions = {
|
|
2
|
+
fps: number;
|
|
3
|
+
format: 'jpeg' | 'png' | 'webp';
|
|
4
|
+
quality: number;
|
|
5
|
+
similarityThreshold: number;
|
|
6
|
+
maxFrames?: number;
|
|
7
|
+
};
|
|
8
|
+
export type ExtractionResult = {
|
|
9
|
+
success: boolean;
|
|
10
|
+
frameCount: number;
|
|
11
|
+
dedupedCount: number;
|
|
12
|
+
error?: string;
|
|
13
|
+
skipped?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare function checkFFmpegInstalled(): Promise<boolean>;
|
|
16
|
+
export declare function resolveFfmpegBinary(): Promise<string | null>;
|
|
17
|
+
export declare const __test: {
|
|
18
|
+
resetFfmpegCache: () => void;
|
|
19
|
+
};
|
|
20
|
+
export declare function isVideoFile(filename: string): boolean;
|
|
21
|
+
export declare function extractAndDeduplicateFrames(videoPath: string, outputDir: string, options: ExtractionOptions): Promise<ExtractionResult>;
|
|
22
|
+
//# sourceMappingURL=videoFrameExtractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"videoFrameExtractor.d.ts","sourceRoot":"","sources":["../../src/lib/videoFrameExtractor.ts"],"names":[],"mappings":"AAwBA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO7D;AAID,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuBlE;AAED,eAAO,MAAM,MAAM;4BACK,IAAI;CAG3B,CAAC;AAMF,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGrD;AA6KD,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CAqD3B"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { existsSync, readdirSync, renameSync, unlinkSync } from 'fs';
|
|
3
|
+
import { mkdir } from 'fs/promises';
|
|
4
|
+
import { dirname, extname, join } from 'path';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import sharp from 'sharp';
|
|
7
|
+
import pixelmatch from 'pixelmatch';
|
|
8
|
+
import { runCommand } from './runCommand.js';
|
|
9
|
+
const execAsync = promisify(exec);
|
|
10
|
+
const VIDEO_EXTENSIONS = [
|
|
11
|
+
'.mp4',
|
|
12
|
+
'.mov',
|
|
13
|
+
'.avi',
|
|
14
|
+
'.webm',
|
|
15
|
+
'.mkv',
|
|
16
|
+
'.m4v',
|
|
17
|
+
'.flv',
|
|
18
|
+
'.wmv',
|
|
19
|
+
'.mpg',
|
|
20
|
+
'.mpeg',
|
|
21
|
+
];
|
|
22
|
+
export async function checkFFmpegInstalled() {
|
|
23
|
+
try {
|
|
24
|
+
await execAsync('ffmpeg -version');
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
let cachedFfmpegPath;
|
|
32
|
+
export async function resolveFfmpegBinary() {
|
|
33
|
+
if (cachedFfmpegPath !== undefined)
|
|
34
|
+
return cachedFfmpegPath;
|
|
35
|
+
if (await checkFFmpegInstalled()) {
|
|
36
|
+
cachedFfmpegPath = 'ffmpeg';
|
|
37
|
+
return cachedFfmpegPath;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
// Use a runtime-only module name so TS doesn't require the optional dep at build time.
|
|
41
|
+
const moduleName = 'ffmpeg-static';
|
|
42
|
+
const mod = (await import(moduleName));
|
|
43
|
+
const path = typeof mod === 'string' ? mod : mod.default;
|
|
44
|
+
if (path) {
|
|
45
|
+
cachedFfmpegPath = path;
|
|
46
|
+
return cachedFfmpegPath;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// ffmpeg-static not installed; fall through
|
|
51
|
+
}
|
|
52
|
+
cachedFfmpegPath = null;
|
|
53
|
+
return cachedFfmpegPath;
|
|
54
|
+
}
|
|
55
|
+
export const __test = {
|
|
56
|
+
resetFfmpegCache: () => {
|
|
57
|
+
cachedFfmpegPath = undefined;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
function quote(p) {
|
|
61
|
+
return `"${p.replace(/"/g, '\\"')}"`;
|
|
62
|
+
}
|
|
63
|
+
export function isVideoFile(filename) {
|
|
64
|
+
const lower = filename.toLowerCase();
|
|
65
|
+
return VIDEO_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
66
|
+
}
|
|
67
|
+
async function extractFramesWithFFmpeg(videoPath, outputDir, fps, format, quality) {
|
|
68
|
+
await mkdir(outputDir, { recursive: true });
|
|
69
|
+
const ffmpegBin = await resolveFfmpegBinary();
|
|
70
|
+
if (!ffmpegBin) {
|
|
71
|
+
throw new Error('ffmpeg not found. Run `jirallm setup` or `jirallm doctor` for install instructions.');
|
|
72
|
+
}
|
|
73
|
+
const ff = quote(ffmpegBin);
|
|
74
|
+
const qScale = Math.round(2 + ((100 - quality) / 100) * 29);
|
|
75
|
+
const ext = format === 'jpeg' ? 'jpg' : format;
|
|
76
|
+
const firstFramePath = `${outputDir}/frame-first.${ext}`;
|
|
77
|
+
await runCommand(`${ff} -i "${videoPath}" -vf "select=eq(n\\,0)" -update 1 -vframes 1 -q:v ${qScale} "${firstFramePath}"`, 'Extracting first frame', { silent: true });
|
|
78
|
+
const lastFramePath = `${outputDir}/frame-last.${ext}`;
|
|
79
|
+
await runCommand(`${ff} -sseof -1 -i "${videoPath}" -update 1 -q:v ${qScale} "${lastFramePath}"`, 'Extracting last frame', { silent: true });
|
|
80
|
+
const outputPattern = `${outputDir}/frame-%04d.${ext}`;
|
|
81
|
+
await runCommand(`${ff} -i "${videoPath}" -vf "fps=${fps}" -q:v ${qScale} "${outputPattern}"`, `Extracting frames at ${fps} fps`, { silent: true });
|
|
82
|
+
const regularFrames = readdirSync(outputDir)
|
|
83
|
+
.filter((f) => f.startsWith('frame-') && f.endsWith(`.${ext}`) && /frame-\d+/.test(f))
|
|
84
|
+
.sort()
|
|
85
|
+
.map((f) => `${outputDir}/${f}`);
|
|
86
|
+
const allFrames = [firstFramePath, ...regularFrames, lastFramePath].filter((p) => existsSync(p));
|
|
87
|
+
const tempFrames = [];
|
|
88
|
+
for (let i = 0; i < allFrames.length; i++) {
|
|
89
|
+
const tempFilename = `frame-${String(i + 1).padStart(4, '0')}.tmp.${ext}`;
|
|
90
|
+
const tempPath = join(outputDir, tempFilename);
|
|
91
|
+
renameSync(allFrames[i], tempPath);
|
|
92
|
+
tempFrames.push(tempPath);
|
|
93
|
+
}
|
|
94
|
+
const renumberedFrames = [];
|
|
95
|
+
for (let i = 0; i < tempFrames.length; i++) {
|
|
96
|
+
const newFilename = `frame-${String(i + 1).padStart(4, '0')}.${ext}`;
|
|
97
|
+
const newPath = join(outputDir, newFilename);
|
|
98
|
+
renameSync(tempFrames[i], newPath);
|
|
99
|
+
renumberedFrames.push(newPath);
|
|
100
|
+
}
|
|
101
|
+
return renumberedFrames;
|
|
102
|
+
}
|
|
103
|
+
async function calculateImageSimilarity(p1, p2) {
|
|
104
|
+
const w = 640;
|
|
105
|
+
const h = 360;
|
|
106
|
+
const [img1, img2] = await Promise.all([
|
|
107
|
+
sharp(p1).resize(w, h, { fit: 'fill' }).ensureAlpha().raw().toBuffer({ resolveWithObject: true }),
|
|
108
|
+
sharp(p2).resize(w, h, { fit: 'fill' }).ensureAlpha().raw().toBuffer({ resolveWithObject: true }),
|
|
109
|
+
]);
|
|
110
|
+
const { width, height, channels } = img1.info;
|
|
111
|
+
if (width !== img2.info.width || height !== img2.info.height || channels !== img2.info.channels) {
|
|
112
|
+
throw new Error('Image dimensions or channels do not match');
|
|
113
|
+
}
|
|
114
|
+
const diffBuffer = Buffer.alloc(width * height * 4);
|
|
115
|
+
const mismatched = pixelmatch(img1.data, img2.data, diffBuffer, width, height, {
|
|
116
|
+
threshold: 0.1,
|
|
117
|
+
});
|
|
118
|
+
return mismatched / (width * height);
|
|
119
|
+
}
|
|
120
|
+
async function deduplicateFrames(framePaths, similarityThreshold, maxFrames) {
|
|
121
|
+
if (framePaths.length === 0)
|
|
122
|
+
return { keptFrames: [], removedFrames: [] };
|
|
123
|
+
const keptFrames = [framePaths[0]];
|
|
124
|
+
const removedFrames = [];
|
|
125
|
+
for (let i = 1; i < framePaths.length; i++) {
|
|
126
|
+
const current = framePaths[i];
|
|
127
|
+
const lastKept = keptFrames[keptFrames.length - 1];
|
|
128
|
+
try {
|
|
129
|
+
const similarity = await calculateImageSimilarity(lastKept, current);
|
|
130
|
+
if (similarity >= similarityThreshold)
|
|
131
|
+
keptFrames.push(current);
|
|
132
|
+
else
|
|
133
|
+
removedFrames.push(current);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
137
|
+
console.warn(`Failed to compare frames ${lastKept} and ${current}: ${msg}`);
|
|
138
|
+
keptFrames.push(current);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (maxFrames && keptFrames.length > maxFrames) {
|
|
142
|
+
const selected = [keptFrames[0]];
|
|
143
|
+
if (keptFrames.length > 1)
|
|
144
|
+
selected.push(keptFrames[keptFrames.length - 1]);
|
|
145
|
+
const middleCount = Math.max(0, maxFrames - 2);
|
|
146
|
+
if (middleCount > 0 && keptFrames.length > 2) {
|
|
147
|
+
const middle = keptFrames.slice(1, -1);
|
|
148
|
+
const step = middle.length / (middleCount + 1);
|
|
149
|
+
for (let i = 1; i <= middleCount; i++)
|
|
150
|
+
selected.push(middle[Math.floor(step * i)]);
|
|
151
|
+
}
|
|
152
|
+
selected.sort((a, b) => keptFrames.indexOf(a) - keptFrames.indexOf(b));
|
|
153
|
+
for (const frame of keptFrames) {
|
|
154
|
+
if (!selected.includes(frame))
|
|
155
|
+
removedFrames.push(frame);
|
|
156
|
+
}
|
|
157
|
+
keptFrames.length = 0;
|
|
158
|
+
keptFrames.push(...selected);
|
|
159
|
+
}
|
|
160
|
+
for (const framePath of removedFrames) {
|
|
161
|
+
try {
|
|
162
|
+
unlinkSync(framePath);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
166
|
+
console.warn(`Failed to delete frame ${framePath}: ${msg}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (keptFrames.length > 0) {
|
|
170
|
+
const outputDir = dirname(keptFrames[0]);
|
|
171
|
+
const ext = extname(keptFrames[0]);
|
|
172
|
+
const renumbered = [];
|
|
173
|
+
for (let i = 0; i < keptFrames.length; i++) {
|
|
174
|
+
const oldPath = keptFrames[i];
|
|
175
|
+
const newFilename = `frame-${String(i + 1).padStart(4, '0')}${ext}`;
|
|
176
|
+
const newPath = join(outputDir, newFilename);
|
|
177
|
+
if (oldPath !== newPath) {
|
|
178
|
+
try {
|
|
179
|
+
renameSync(oldPath, newPath);
|
|
180
|
+
renumbered.push(newPath);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
renumbered.push(oldPath);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
renumbered.push(oldPath);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return { keptFrames: renumbered, removedFrames };
|
|
191
|
+
}
|
|
192
|
+
return { keptFrames, removedFrames };
|
|
193
|
+
}
|
|
194
|
+
export async function extractAndDeduplicateFrames(videoPath, outputDir, options) {
|
|
195
|
+
const ffmpegInstalled = await checkFFmpegInstalled();
|
|
196
|
+
if (!ffmpegInstalled) {
|
|
197
|
+
return {
|
|
198
|
+
success: false,
|
|
199
|
+
frameCount: 0,
|
|
200
|
+
dedupedCount: 0,
|
|
201
|
+
error: 'FFmpeg not found. Install ffmpeg to enable video frame extraction.',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (!existsSync(videoPath)) {
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
frameCount: 0,
|
|
208
|
+
dedupedCount: 0,
|
|
209
|
+
error: `Video file not found: ${videoPath}`,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
const extractedFrames = await extractFramesWithFFmpeg(videoPath, outputDir, options.fps, options.format, options.quality);
|
|
214
|
+
if (extractedFrames.length === 0) {
|
|
215
|
+
return {
|
|
216
|
+
success: false,
|
|
217
|
+
frameCount: 0,
|
|
218
|
+
dedupedCount: 0,
|
|
219
|
+
error: 'No frames extracted from video',
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
const { keptFrames } = await deduplicateFrames(extractedFrames, options.similarityThreshold, options.maxFrames);
|
|
223
|
+
return {
|
|
224
|
+
success: true,
|
|
225
|
+
frameCount: extractedFrames.length,
|
|
226
|
+
dedupedCount: keptFrames.length,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
231
|
+
return { success: false, frameCount: 0, dedupedCount: 0, error: msg };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=videoFrameExtractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"videoFrameExtractor.js","sourceRoot":"","sources":["../../src/lib/videoFrameExtractor.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jirallm",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Export Jira issues as LLM-ready context bundles — markdown, attachments, video frames",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"jirallm": "dist/cli/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"keywords": [
|
|
17
|
+
"jira",
|
|
18
|
+
"llm",
|
|
19
|
+
"ai",
|
|
20
|
+
"claude",
|
|
21
|
+
"context",
|
|
22
|
+
"export",
|
|
23
|
+
"markdown",
|
|
24
|
+
"adf",
|
|
25
|
+
"video-frames",
|
|
26
|
+
"ffmpeg"
|
|
27
|
+
],
|
|
28
|
+
"author": "Dominik Rycharski (https://github.com/doryski)",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=20"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@clack/prompts": "^1.3.0",
|
|
35
|
+
"@iarna/toml": "^2.2.5",
|
|
36
|
+
"commander": "^14.0.3",
|
|
37
|
+
"keytar": "^7.9.0",
|
|
38
|
+
"pixelmatch": "^6.0.0",
|
|
39
|
+
"sharp": "^0.33.5",
|
|
40
|
+
"zod": "^4.4.2"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/iarna__toml": "^2.0.5",
|
|
44
|
+
"@types/node": "^22.0.0",
|
|
45
|
+
"@types/pixelmatch": "^5.2.6",
|
|
46
|
+
"typescript": "^5.6.0",
|
|
47
|
+
"vitest": "^4.1.5"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc",
|
|
51
|
+
"dev": "tsc --watch",
|
|
52
|
+
"start": "node dist/cli/index.js",
|
|
53
|
+
"test": "vitest run --bail=5",
|
|
54
|
+
"test:watch": "vitest",
|
|
55
|
+
"release": "git tag v$npm_package_version && git push --follow-tags"
|
|
56
|
+
}
|
|
57
|
+
}
|