Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Phase 4 AI provider configuration
POLLINATIONS_OPENAI_URL=https://text.pollinations.ai/openai
POLLINATIONS_API_KEY=
# Alias supported for compatibility with existing secrets naming
POLLINATIONS_API=
POLLINATIONS_GITHUB_MODEL=github:gpt-4o-mini
POLLINATIONS_FALLBACK_MODEL=openai-fast

# Set true to force strict behavior: if Pollinations cannot route a GitHub model,
# Phase 4 fails unless GLM fallback is configured.
PHASE4_STRICT_GLM_FALLBACK=false

# GLM-5 fallback (preferred when Pollinations GitHub model is unavailable)
# Example endpoint style: https://your-glm-endpoint/v1/chat/completions
GLM5_API_BASE_URL=
GLM5_API_KEY=
GLM5_MODEL=glm-5

# Phase 5 render configuration
PHASE5_LYRICS_JSON=data/phase3-lyrics.json
PHASE5_DIRECTION_JSON=data/phase4-direction.json
PHASE5_OUTPUT_VIDEO=output/phase5-video.mp4
# Optional: cap render duration for CI speed
PHASE5_MAX_SECONDS=0
# Optional: tune renderer speed/quality balance
PHASE5_RENDER_CONCURRENCY=2
PHASE5_RENDER_CRF=22

# Phase 7 YouTube upload configuration
YOUTUBE_CLIENT_ID=
YOUTUBE_CLIENT_SECRET=
YOUTUBE_REFRESH_TOKEN=
YOUTUBE_TITLE=
YOUTUBE_DESCRIPTION=
YOUTUBE_TAGS=
YOUTUBE_PRIVACY_STATUS=unlisted
YOUTUBE_CATEGORY_ID=10
PHASE7_VIDEO_FILE=output/phase6-video.mp4
PHASE7_PHASE3_JSON=data/phase3-lyrics.json
PHASE7_OUTPUT_JSON=data/phase7-youtube-upload.json
PHASE7_DRY_RUN=false
103 changes: 103 additions & 0 deletions .github/workflows/phase6-full-pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Phase 6 Full Pipeline Test

on:
workflow_dispatch:
inputs:
song_query:
description: Song query used when song_id is empty
required: false
default: Aaj Ki Raat
type: string
song_id:
description: Optional direct JioSaavn song id
required: false
default: ''
type: string
max_seconds:
description: Clip duration cap for CI runtime stability
required: false
default: '30'
type: string
strict_glm_fallback:
description: Fail Phase 4 if Pollinations GitHub model is unavailable and GLM is not configured
required: false
default: 'false'
type: choice
options:
- 'false'
- 'true'

jobs:
full-pipeline-test:
runs-on: ubuntu-latest
timeout-minutes: 45
permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Prepare folders
run: mkdir -p data output

- name: Phase 3 - Fetch song metadata and lyrics timeline
env:
SONG_QUERY: ${{ inputs.song_query }}
SONG_ID: ${{ inputs.song_id }}
run: |
set -e
echo "[phase-6] Starting Phase 3"
if [ -n "$SONG_ID" ]; then
npm run phase3:build -- --song-id="$SONG_ID" --out=data/phase3-lyrics.json
else
npm run phase3:build -- --query="$SONG_QUERY" --out=data/phase3-lyrics.json
fi

- name: Phase 4 - Generate animation direction JSON
env:
POLLINATIONS_API: ${{ secrets.POLLINATIONS_API }}
POLLINATIONS_API_KEY: ${{ secrets.POLLINATIONS_API_KEY }}
GLM5_API_BASE_URL: ${{ secrets.GLM5_API_BASE_URL }}
GLM5_API_KEY: ${{ secrets.GLM5_API_KEY }}
GLM5_MODEL: ${{ secrets.GLM5_MODEL }}
PHASE4_STRICT_GLM_FALLBACK: ${{ inputs.strict_glm_fallback }}
run: |
set -e
echo "[phase-6] Starting Phase 4"
npm run phase4:direction -- --in=data/phase3-lyrics.json --out=data/phase4-direction.json

- name: Phase 5 - Render dynamic lyrics video
env:
PHASE5_MAX_SECONDS: ${{ inputs.max_seconds }}
PHASE5_RENDER_CONCURRENCY: 2
PHASE5_RENDER_CRF: 22
run: |
set -e
echo "[phase-6] Starting Phase 5"
npm run phase5:render -- --lyrics=data/phase3-lyrics.json --direction=data/phase4-direction.json --out=output/phase6-video.mp4 --max-seconds="$PHASE5_MAX_SECONDS"

- name: Print output summary
run: |
set -e
ls -lh output/phase6-video.mp4 data/phase3-lyrics.json data/phase4-direction.json
node -e "const {getVideoMetadata}=require('@remotion/renderer'); getVideoMetadata('output/phase6-video.mp4').then((m)=>{console.log(JSON.stringify({width:m.width,height:m.height,fps:m.fps,durationInSeconds:m.durationInSeconds},null,2));}).catch((e)=>{console.error(e);process.exit(1);});"

- name: Upload Phase 6 artifacts
uses: actions/upload-artifact@v4
with:
name: phase6-full-pipeline
if-no-files-found: error
path: |
output/phase6-video.mp4
data/phase3-lyrics.json
data/phase4-direction.json
139 changes: 139 additions & 0 deletions .github/workflows/phase7-youtube-upload.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
name: Phase 7 YouTube Upload

on:
workflow_dispatch:
inputs:
song_query:
description: Song query used when song_id is empty
required: false
default: Aaj Ki Raat
type: string
song_id:
description: Optional direct JioSaavn song id
required: false
default: ''
type: string
max_seconds:
description: Clip duration cap (0 means full song)
required: false
default: '30'
type: string
youtube_title:
description: Optional YouTube title override
required: false
default: ''
type: string
youtube_description:
description: Optional YouTube description override
required: false
default: ''
type: string
youtube_tags:
description: Optional comma-separated tags
required: false
default: ''
type: string
privacy_status:
description: YouTube privacy status
required: false
default: unlisted
type: choice
options:
- private
- unlisted
- public
strict_glm_fallback:
description: Fail if Pollinations GitHub model is unavailable and GLM fallback is not configured
required: false
default: 'false'
type: choice
options:
- 'false'
- 'true'

jobs:
render-and-upload:
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Prepare folders
run: mkdir -p data output

- name: Phase 3 - Fetch song and lyrics
env:
SONG_QUERY: ${{ inputs.song_query }}
SONG_ID: ${{ inputs.song_id }}
run: |
set -e
echo "[phase-7] Starting Phase 3"
if [ -n "$SONG_ID" ]; then
npm run phase3:build -- --song-id="$SONG_ID" --out=data/phase3-lyrics.json
else
npm run phase3:build -- --query="$SONG_QUERY" --out=data/phase3-lyrics.json
fi

- name: Phase 4 - Generate AI direction
env:
POLLINATIONS_API: ${{ secrets.POLLINATIONS_API }}
POLLINATIONS_API_KEY: ${{ secrets.POLLINATIONS_API_KEY }}
GLM5_API_BASE_URL: ${{ secrets.GLM5_API_BASE_URL }}
GLM5_API_KEY: ${{ secrets.GLM5_API_KEY }}
GLM5_MODEL: ${{ secrets.GLM5_MODEL }}
PHASE4_STRICT_GLM_FALLBACK: ${{ inputs.strict_glm_fallback }}
run: |
set -e
echo "[phase-7] Starting Phase 4"
npm run phase4:direction -- --in=data/phase3-lyrics.json --out=data/phase4-direction.json

- name: Phase 5 - Render video
env:
PHASE5_MAX_SECONDS: ${{ inputs.max_seconds }}
PHASE5_RENDER_CONCURRENCY: 2
PHASE5_RENDER_CRF: 22
run: |
set -e
echo "[phase-7] Starting Phase 5"
npm run phase5:render -- --lyrics=data/phase3-lyrics.json --direction=data/phase4-direction.json --out=output/phase7-video.mp4 --max-seconds="$PHASE5_MAX_SECONDS"

- name: Phase 7 - Upload to YouTube
env:
YOUTUBE_CLIENT_ID: ${{ secrets.YOUTUBE_CLIENT_ID }}
YOUTUBE_CLIENT_SECRET: ${{ secrets.YOUTUBE_CLIENT_SECRET }}
YOUTUBE_REFRESH_TOKEN: ${{ secrets.YOUTUBE_REFRESH_TOKEN }}
YOUTUBE_TITLE: ${{ inputs.youtube_title }}
YOUTUBE_DESCRIPTION: ${{ inputs.youtube_description }}
YOUTUBE_TAGS: ${{ inputs.youtube_tags }}
YOUTUBE_PRIVACY_STATUS: ${{ inputs.privacy_status }}
PHASE7_VIDEO_FILE: output/phase7-video.mp4
PHASE7_PHASE3_JSON: data/phase3-lyrics.json
PHASE7_OUTPUT_JSON: data/phase7-youtube-upload.json
run: |
set -e
echo "[phase-7] Starting YouTube upload"
npm run phase7:upload

- name: Upload Phase 7 artifacts
uses: actions/upload-artifact@v4
with:
name: phase7-youtube-upload
if-no-files-found: error
path: |
output/phase7-video.mp4
data/phase3-lyrics.json
data/phase4-direction.json
data/phase7-youtube-upload.json
32 changes: 32 additions & 0 deletions .github/workflows/test-render.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Phase 2 Test Render

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Workflow name refers to "Phase 2", but the pipeline scripts added in this PR are phase-3/phase-4. Consider aligning the workflow name with the actual phase/purpose of the job to avoid confusion.

Copilot uses AI. Check for mistakes.

on:
workflow_dispatch:

jobs:
render-test-video:
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Render test video
run: npm run render:test

- name: Upload rendered video artifact
uses: actions/upload-artifact@v4
with:
Comment on lines +13 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify workflow action refs that are NOT pinned to a 40-char commit SHA.
rg -nP '^\s*uses:\s*[^@]+@(?![0-9a-f]{40}\s*$).+' .github/workflows

Repository: Dcode9/MPLayer

Length of output: 279


Pin GitHub Actions to immutable commit SHAs instead of mutable version tags.

Lines 13, 16, and 28 use mutable tags (@v4) for actions/checkout, actions/setup-node, and actions/upload-artifact. This exposes the workflow to supply chain attacks if upstream actions are compromised. Replace with full commit SHAs (40-character format).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/test-render.yml around lines 13 - 29, Replace mutable
action tags with pinned commit SHAs: update the uses entries for
actions/checkout@v4, actions/setup-node@v4, and actions/upload-artifact@v4 to
their corresponding full 40-character commit SHAs; for example replace the
string in the uses field for actions/checkout, actions/setup-node (the step that
sets node-version: 20), and actions/upload-artifact (the upload artifact step)
with the specific commit SHA for each repository so the workflow references
immutable commits.

name: phase-2-test-video

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The uploaded artifact is named phase-2-test-video, but the job runs npm run render:test (and the rest of the pipeline uses phase-3/4 naming). Consider renaming the artifact to match the job/script so it’s easier to find and relate to logs.

Suggested change
name: phase-2-test-video
name: render-test-video

Copilot uses AI. Check for mistakes.
path: output/test-video.mp4
if-no-files-found: error
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
output/*.mp4
.env
Loading