Skip to content

Commit 22bf130

Browse files
authored
Merge pull request #7 from pelikhan/copilot/fix-a5e52bc0-33dc-4256-9143-f7d56da9e69a
Add video URL parameter support with optional instructions and dedicated workflow
2 parents fdd20f0 + 7b9fff1 commit 22bf130

File tree

7 files changed

+430
-259
lines changed

7 files changed

+430
-259
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ on:
66
pull_request:
77
branches:
88
- main
9-
workflow_dispatch:
10-
inputs:
11-
github_issue:
12-
description: "GitHub issue number to comment on"
13-
required: true
149
permissions:
1510
contents: read
1611
issues: write
@@ -55,6 +50,6 @@ jobs:
5550
genaiscript-
5651
- uses: ./
5752
with:
58-
github_issue: 1
53+
github_issue: "1"
5954
github_token: ${{ secrets.GITHUB_TOKEN }}
60-
debug: "script"
55+
debug: "script"

.github/workflows/genai-video-issue-analyzer.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1-
name: genai video issue analyzer
1+
name: Video Analyzer
22
on:
33
issues:
44
types: [opened, edited]
5+
workflow_dispatch:
6+
inputs:
7+
video_url:
8+
description: 'Direct video URL to analyze'
9+
required: true
10+
type: string
11+
instructions:
12+
description: 'Custom prompting instructions for the video'
13+
required: false
14+
default: 'Analyze the video and provide a summary of its content. Extract list of followup subissues if any. The transcript is your primary source of text information, ignore text in images.'
15+
type: string
516
permissions:
617
contents: read
718
issues: write
@@ -31,4 +42,6 @@ jobs:
3142
- uses: pelikhan/action-genai-video-issue-analyzer@main
3243
with:
3344
github_issue: ${{ github.event.issue.number }}
34-
github_token: ${{ secrets.GITHUB_TOKEN }}
45+
github_token: ${{ secrets.GITHUB_TOKEN }}
46+
video_url: ${{ github.event.inputs.video_url }}
47+
instructions: ${{ github.event.inputs.instructions }}

README.md

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# GitHub Action Video Issue Analyzer
22

3-
This GitHub Action runs all video assets in an issue body through a LLM model to analyze the content.
4-
The default behavior is to summarize and extract task items but this can be customized through the `prompt` input.
3+
This GitHub Action runs all video assets in an issue body through a LLM model to analyze the content, or can analyze a direct video URL when triggered via workflow_dispatch.
4+
The default behavior is to summarize and extract task items but this can be customized through the `instructions` input.
5+
6+
The action outputs the analysis results to the GitHub Step Summary for easy viewing in the Actions tab.
57

68
## Inputs
79

@@ -22,6 +24,7 @@ The default behavior is to summarize and extract task items but this can be cust
2224
| `azure_ai_inference_api_version` | Azure Serverless OpenAI API version | false | |
2325
| `azure_ai_inference_api_credentials` | Azure Serverless OpenAI API credentials type | false | |
2426
| `github_token` | GitHub token with `models: read` permission at least (https://microsoft.github.io/genaiscript/reference/github-actions/#github-models-permissions). | false | |
27+
| `video_url` | Direct video URL to analyze (alternative to extracting from issue body). Used when triggered via workflow_dispatch. | false | |
2528

2629
## Outputs
2730

@@ -30,6 +33,8 @@ The default behavior is to summarize and extract task items but this can be cust
3033

3134
| `text` | The generated text output. |
3235

36+
**Note**: The action also outputs the analysis results to the GitHub Step Summary (`$GITHUB_STEP_SUMMARY`) for easy viewing in the Actions tab.
37+
3338
## Usage
3439

3540
Add the following to your step in your workflow file.
@@ -62,6 +67,8 @@ It will launch a whisper service in a container that can be used by genaiscript.
6267
6368
Save the following in `.github/workflows/genai-video-issue-analyzer.yml` file:
6469

70+
### For Issue-based Analysis (automatic trigger)
71+
6572
```yaml
6673
name: genai video issue analyzer
6774
on:
@@ -104,6 +111,114 @@ jobs:
104111
github_token: ${{ secrets.GITHUB_TOKEN }}
105112
```
106113

114+
### For Direct Video URL Analysis (manual trigger)
115+
116+
```yaml
117+
name: genai video issue analyzer
118+
on:
119+
workflow_dispatch:
120+
inputs:
121+
video_url:
122+
description: 'Direct video URL to analyze'
123+
required: true
124+
type: string
125+
instructions:
126+
description: 'Custom prompting instructions for the video'
127+
required: false
128+
default: 'Analyze the video and provide a summary of its content. Extract list of followup subissues if any. The transcript is your primary source of text information, ignore text in images.'
129+
type: string
130+
permissions:
131+
contents: read
132+
models: read
133+
jobs:
134+
genai-video-analyze:
135+
runs-on: ubuntu-latest
136+
services:
137+
whisper:
138+
image: onerahmet/openai-whisper-asr-webservice:latest
139+
env:
140+
ASR_MODEL: base
141+
ASR_ENGINE: openai_whisper
142+
ports:
143+
- 9000:9000
144+
options: >-
145+
--health-cmd "curl -f http://localhost:9000/docs || exit 1"
146+
--health-interval 10s
147+
--health-timeout 5s
148+
--health-retries 5
149+
--health-start-period 20s
150+
steps:
151+
- uses: actions/checkout@v4
152+
- uses: actions/cache@v4
153+
with:
154+
path: .genaiscript/cache/**
155+
key: genaiscript-${{ github.run_id }}
156+
restore-keys: |
157+
genaiscript-
158+
- uses: pelikhan/action-genai-video-issue-analyzer@v0 # update to the major version you want to use
159+
with:
160+
github_token: ${{ secrets.GITHUB_TOKEN }}
161+
video_url: ${{ github.event.inputs.video_url }}
162+
instructions: ${{ github.event.inputs.instructions }}
163+
```
164+
165+
### Combined Workflow (both triggers)
166+
167+
```yaml
168+
name: genai video issue analyzer
169+
on:
170+
issues:
171+
types: [opened, edited]
172+
workflow_dispatch:
173+
inputs:
174+
video_url:
175+
description: 'Direct video URL to analyze'
176+
required: true
177+
type: string
178+
instructions:
179+
description: 'Custom prompting instructions for the video'
180+
required: false
181+
default: 'Analyze the video and provide a summary of its content. Extract list of followup subissues if any. The transcript is your primary source of text information, ignore text in images.'
182+
type: string
183+
permissions:
184+
contents: read
185+
issues: write
186+
models: read
187+
concurrency:
188+
group: ${{ github.workflow }}-${{ github.ref }}
189+
cancel-in-progress: true
190+
jobs:
191+
genai-video-analyze:
192+
runs-on: ubuntu-latest
193+
services:
194+
whisper:
195+
image: onerahmet/openai-whisper-asr-webservice:latest
196+
env:
197+
ASR_MODEL: base
198+
ASR_ENGINE: openai_whisper
199+
ports:
200+
- 9000:9000
201+
options: >-
202+
--health-cmd "curl -f http://localhost:9000/docs || exit 1"
203+
--health-interval 10s
204+
--health-timeout 5s
205+
--health-retries 5
206+
--health-start-period 20s
207+
steps:
208+
- uses: actions/checkout@v4
209+
- uses: actions/cache@v4
210+
with:
211+
path: .genaiscript/cache/**
212+
key: genaiscript-${{ github.run_id }}
213+
restore-keys: |
214+
genaiscript-
215+
- uses: pelikhan/action-genai-video-issue-analyzer@v0 # update to the major version you want to use
216+
with:
217+
github_token: ${{ secrets.GITHUB_TOKEN }}
218+
video_url: ${{ github.event.inputs.video_url }}
219+
instructions: ${{ github.event.inputs.instructions }}
220+
```
221+
107222
## Development
108223

109224
This action was automatically generated by [GenAIScript](https://microsoft.github.io/genaiscript/reference/github-actions) from the script metadata.

action.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ inputs:
5757
(https://microsoft.github.io/genaiscript/reference/github-actions/#github\
5858
-models-permissions)."
5959
required: false
60+
github_issue:
61+
description: "GitHub issue number to analyze. If not provided, the action will\
62+
\ analyze the current pull request."
63+
required: false
64+
video_url:
65+
description: "Direct video URL to analyze (alternative to extracting from issue body)"
66+
required: false
6067
outputs:
6168
text:
6269
description: The generated text output.

genaisrc/action-video-issue-analyzer.genai.mts

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,46 @@ script({
88
default:
99
"Analyze the video and provide a summary of its content. Extract list of followup subissues if any. The transcript is your primary source of text information, ignore text in images.",
1010
},
11+
videoUrl: {
12+
type: "string",
13+
description: "Direct video URL to analyze (alternative to extracting from issue body)",
14+
},
1115
},
1216
});
1317

1418
const { dbg, output, vars } = env;
15-
const issue = await github.getIssue();
16-
if (!issue)
17-
throw new Error(
18-
"No issue found in the context. This action requires an issue to be present.",
19-
);
20-
const { instructions } = vars as { instructions: string };
21-
if (!instructions)
22-
throw new Error(
23-
"No instructions provided. Please provide instructions to process the video.",
24-
);
19+
const { instructions, videoUrl } = vars as { instructions?: string; videoUrl?: string };
20+
21+
// Use default instructions if not provided
22+
const finalInstructions = instructions ||
23+
"Analyze the video and provide a summary of its content. Extract list of followup subissues if any. The transcript is your primary source of text information, ignore text in images.";
24+
25+
// Process direct video URL if provided
26+
if (videoUrl) {
27+
dbg(`Processing direct video URL: ${videoUrl}`);
28+
await processDirectVideoUrl(videoUrl);
29+
} else {
30+
// Fallback to extracting from issue body
31+
const issue = await github.getIssue();
32+
if (!issue)
33+
throw new Error(
34+
"No issue found in the context and no videoUrl provided. This action requires either an issue to be present or a videoUrl parameter.",
35+
);
2536

26-
const RX = /^https:\/\/github.com\/user-attachments\/assets\/.+$/gim;
27-
const assetLinks = Array.from(
28-
new Set(Array.from(issue.body.matchAll(RX), (m) => m[0])),
29-
);
30-
if (assetLinks.length === 0)
31-
cancel("No video assets found in the issue body, nothing to do.");
37+
const RX = /^https:\/\/github.com\/user-attachments\/assets\/.+$/gim;
38+
const assetLinks = Array.from(
39+
new Set(Array.from(issue.body.matchAll(RX), (m) => m[0])),
40+
);
41+
if (assetLinks.length === 0)
42+
cancel("No video assets found in the issue body, nothing to do.");
3243

33-
dbg(`issue: %s`, issue.title);
44+
dbg(`issue: %s`, issue.title);
3445

35-
for (const assetLink of assetLinks) await processAssetLink(assetLink);
46+
for (const assetLink of assetLinks) await processAssetLink(assetLink);
47+
}
3648

3749
async function processAssetLink(assetLink: string) {
38-
output.heading(3, assetLink);
50+
output.heading(4, assetLink);
3951
dbg(assetLink);
4052
const downloadUrl = await github.resolveAssetUrl(assetLink);
4153
const res = await fetch(downloadUrl, { method: "GET" });
@@ -75,7 +87,7 @@ async function processVideo(filename: string) {
7587
(ctx) => {
7688
ctx.def("TRANSCRIPT", transcript?.srt, { ignoreEmpty: true }); // ignore silent videos
7789
ctx.defImages(frames, { detail: "low", sliceSample: 40 }); // low detail for better performance
78-
ctx.$`${instructions}
90+
ctx.$`${finalInstructions}
7991
## Output format
8092
- Use GitHub Flavored Markdown (GFM) for markdown syntax formatting.
8193
- If you need to list tasks, use the format \`- [ ] task description\`.
@@ -99,3 +111,32 @@ async function processVideo(filename: string) {
99111
output.appendContent(text);
100112
}
101113
}
114+
115+
async function processDirectVideoUrl(videoUrl: string) {
116+
output.heading(4, videoUrl);
117+
dbg(`Processing direct video URL: ${videoUrl}`);
118+
119+
// Download video from direct URL
120+
const res = await fetch(videoUrl, { method: "GET" });
121+
const contentType = res.headers.get("content-type") || "";
122+
dbg(`download url: %s`, videoUrl);
123+
dbg(`headers: %O`, res.headers);
124+
125+
if (!res.ok)
126+
throw new Error(
127+
`Failed to download video from ${videoUrl}: ${res.status} ${res.statusText}`,
128+
);
129+
130+
if (!/^video\//.test(contentType)) {
131+
output.p(`URL does not point to a video file, skipping`);
132+
return;
133+
}
134+
135+
// save and cache
136+
const buffer = await res.arrayBuffer();
137+
dbg(`size`, `${(buffer.byteLength / 1e6) | 0}Mb`);
138+
const filename = await workspace.writeCached(buffer, { scope: "run" });
139+
dbg(`filename`, filename);
140+
141+
await processVideo(filename);
142+
}

0 commit comments

Comments
 (0)