-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add Remotion video composition for lyrics and integrate AI-driv… #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 |
| 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 |
| 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 |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||
| name: Phase 2 Test Render | ||||||
|
|
||||||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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/workflowsRepository: 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 ( 🤖 Prompt for AI Agents |
||||||
| name: phase-2-test-video | ||||||
|
||||||
| name: phase-2-test-video | |
| name: render-test-video |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| node_modules/ | ||
| output/*.mp4 | ||
| .env |
There was a problem hiding this comment.
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.