@thierrynakoa/fire-flow 12.2.1 → 13.0.1
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/CREDITS.md +25 -0
- package/DOMINION-FLOW-OVERVIEW.md +182 -38
- package/README.md +399 -455
- package/TROUBLESHOOTING.md +264 -264
- package/agents/fire-debugger.md +54 -0
- package/agents/fire-executor.md +1610 -1033
- package/agents/fire-fact-checker.md +1 -1
- package/agents/fire-planner.md +85 -17
- package/agents/fire-project-researcher.md +1 -1
- package/agents/fire-researcher.md +4 -22
- package/agents/{fire-phoenix-analyst.md → fire-resurrection-analyst.md} +394 -394
- package/agents/fire-reviewer.md +552 -499
- package/agents/fire-verifier.md +114 -19
- package/bin/cli.js +18 -101
- package/commands/fire-0-orient.md +2 -2
- package/commands/fire-1a-new.md +50 -15
- package/commands/fire-1c-setup.md +33 -5
- package/commands/fire-1d-discuss.md +87 -1
- package/commands/fire-2-plan.md +556 -527
- package/commands/fire-3-execute.md +2046 -1356
- package/commands/fire-4-verify.md +975 -906
- package/commands/fire-5-handoff.md +46 -5
- package/commands/fire-6-resume.md +2 -31
- package/commands/fire-add-new-skill.md +138 -19
- package/commands/fire-autonomous.md +14 -2
- package/commands/fire-complete-milestone.md +1 -1
- package/commands/fire-cost.md +179 -183
- package/commands/fire-debug.md +1 -6
- package/commands/fire-loop-resume.md +2 -2
- package/commands/fire-loop-stop.md +1 -1
- package/commands/fire-loop.md +2 -15
- package/commands/fire-map-codebase.md +1 -1
- package/commands/fire-migrate-database.md +548 -0
- package/commands/fire-new-milestone.md +1 -1
- package/commands/fire-reflect.md +1 -2
- package/commands/fire-research.md +142 -21
- package/commands/{fire-phoenix.md → fire-resurrect.md} +859 -603
- package/commands/fire-scaffold.md +297 -0
- package/commands/fire-search.md +1 -2
- package/commands/fire-security-scan.md +483 -484
- package/commands/fire-setup.md +359 -0
- package/commands/fire-skill.md +770 -0
- package/commands/fire-skills-diff.md +506 -506
- package/commands/fire-skills-history.md +388 -388
- package/commands/fire-skills-rollback.md +7 -7
- package/commands/fire-skills-sync.md +470 -470
- package/commands/fire-test.md +5 -5
- package/commands/fire-todos.md +1 -1
- package/commands/fire-update.md +5 -5
- package/commands/fire-validate-skills.md +282 -0
- package/commands/fire-vuln-scan.md +492 -493
- package/hooks/run-hook.sh +8 -8
- package/hooks/run-session-end.sh +7 -7
- package/hooks/session-end.sh +90 -90
- package/hooks/session-start.sh +1 -1
- package/package.json +4 -24
- package/plugin.json +7 -7
- package/references/autonomy-levels.md +235 -0
- package/references/behavioral-directives.md +95 -3
- package/references/blocker-tracking.md +1 -1
- package/references/circuit-breaker.md +93 -2
- package/references/context-engineering.md +227 -9
- package/references/honesty-protocols.md +70 -1
- package/references/issue-to-pr-pipeline.md +149 -150
- package/references/metrics-and-trends.md +1 -2
- package/references/research-improvements.md +4 -108
- package/references/sdlc-mapping.md +73 -0
- package/references/state-machine.md +151 -0
- package/skills-library/AVAILABLE_TOOLS_REFERENCE.md +333 -0
- package/skills-library/SKILLS-INDEX.md +57 -558
- package/skills-library/SKILLS_LIBRARY_INDEX.md +532 -0
- package/skills-library/_general/api-patterns/api-field-name-mismatch.md +107 -0
- package/skills-library/_general/api-patterns/streaming-command-timeout.md +122 -0
- package/skills-library/_general/api-patterns/streaming-proxy-cors-bypass.md +102 -0
- package/skills-library/_general/automation/settings-gui-generator.md +172 -0
- package/skills-library/_general/database-solutions/data-type-mapping-reference.md +181 -0
- package/skills-library/_general/database-solutions/mysql-limit-offset-string-coercion.md +102 -0
- package/skills-library/_general/database-solutions/mysql-to-pg-migration.md +195 -0
- package/skills-library/_general/database-solutions/orm-schema-portability.md +193 -0
- package/skills-library/_general/database-solutions/persistent-analysis-storage.md +207 -0
- package/skills-library/_general/database-solutions/pg-to-mysql-schema-migration-methodology.md +190 -0
- package/skills-library/_general/database-solutions/sql-dialect-compatibility-matrix.md +306 -0
- package/skills-library/_general/database-solutions/sqlite-to-pg-migration.md +219 -0
- package/skills-library/_general/frontend/canvas-bubble-animation-grouping.md +270 -0
- package/skills-library/_general/frontend/color-token-migration.md +112 -0
- package/skills-library/_general/frontend/framer-motion-layoutid-grouping.md +150 -0
- package/skills-library/_general/frontend/pyqt6-settings-dialog.md +191 -0
- package/skills-library/_general/frontend/react-flow-animated-layout-switching.md +101 -0
- package/skills-library/_general/frontend/react-hooks-order-debugging.md +141 -0
- package/skills-library/_general/frontend/redux-localstorage-auth-desync.md +126 -0
- package/skills-library/_general/frontend/safari-csp-theme-color-debugging.md +124 -0
- package/skills-library/_general/frontend/safari-sw-cache-poisoning.md +138 -0
- package/skills-library/_general/frontend/svg-sparkline-no-charting-library.md +131 -0
- package/skills-library/_general/growth-marketing/oss-daily-growth-intelligence.md +224 -0
- package/skills-library/_general/integrations/claude-code-local-mcp-integration.md +250 -0
- package/skills-library/_general/integrations/mcp-composite-tool-orchestration.md +200 -0
- package/skills-library/_general/methodology/AGENT_SDK_STANDALONE_TOOLING.md +181 -0
- package/skills-library/_general/methodology/AGENT_TEAMS_GUIDE.md +169 -0
- package/skills-library/_general/methodology/ALAS_STATEFUL_EXECUTION.md +207 -0
- package/skills-library/_general/methodology/AUTO_REVIEWER_SUBAGENT.md +211 -0
- package/skills-library/_general/methodology/CONSISTENCY_CHECK_AMBIGUITY_GATE.md +96 -0
- package/skills-library/_general/methodology/DEAD_ENDS_SHELF.md +4 -4
- package/skills-library/_general/methodology/DISTILL_NOT_DUMP.md +108 -0
- package/skills-library/_general/methodology/EXECUTION_PROGRESS_MONITOR.md +157 -0
- package/skills-library/_general/methodology/HIERARCHICAL_REVIEW_MARS.md +122 -0
- package/skills-library/_general/methodology/MCP_INTER_AGENT_BRIDGE.md +207 -0
- package/skills-library/_general/methodology/MERMAID_WIZARD_DIAGRAMS.md +77 -0
- package/skills-library/_general/methodology/MISSING_DIMENSION_DETECTOR.md +89 -0
- package/skills-library/_general/methodology/MULTI_AGENT_COORDINATION.md +397 -0
- package/skills-library/_general/methodology/OBSERVATION_MASKING.md +100 -0
- package/skills-library/_general/methodology/PHOENIX_REBUILD_METHODOLOGY.md +82 -11
- package/skills-library/_general/methodology/REVIEW_BACKTRACK_PANEL.md +140 -0
- package/skills-library/_general/methodology/REVIEW_FIX_LOOP.md +117 -0
- package/skills-library/_general/methodology/VOTING_VERDICT_ARBITRATION.md +155 -0
- package/skills-library/_general/methodology/ZERO_FRICTION_CLI_SETUP.md +2 -2
- package/skills-library/_general/methodology/dead-code-activation.md +123 -0
- package/skills-library/_general/methodology/debug-swarm-researcher-escape-hatch.md +240 -240
- package/skills-library/_general/methodology/shell-autonomous-loop-fixplan.md +1 -1
- package/skills-library/_general/patterns-standards/GOF_DESIGN_PATTERNS_FOR_AI_AGENTS.md +5 -5
- package/skills-library/_general/patterns-standards/cascading-failure-diagnosis.md +119 -0
- package/skills-library/_general/patterns-standards/domain-specific-layout-algorithms.md +209 -0
- package/skills-library/_general/patterns-standards/python-desktop-app-architecture.md +399 -0
- package/skills-library/_general/patterns-standards/realtime-monitoring-dashboard.md +457 -0
- package/skills-library/_general/patterns-standards/togglable-processing-pipeline.md +169 -0
- package/skills-library/_general/performance/liveclock-extraction.md +112 -0
- package/skills-library/_general/performance/ref-based-canvas-animation.md +117 -0
- package/skills-library/_general/performance/use-visible-interval.md +131 -0
- package/skills-library/_general/testing/playwright-firefox-withcredentials-auth-issue.md +104 -0
- package/skills-library/_quarantine/README.md +30 -0
- package/skills-library/api-patterns/BROADCAST_SCHEDULER_SHARED_EXECUTE_FUNCTION.md +150 -0
- package/skills-library/api-patterns/ERROR_RESPONSE_STANDARDS.md +145 -0
- package/skills-library/api-patterns/EXPRESS_ROUTE_ORDERING_MIDDLEWARE_INTERCEPTION.md +326 -0
- package/skills-library/api-patterns/PAGINATION_PATTERNS.md +137 -0
- package/skills-library/api-patterns/PODCAST_PROGRESS_TRACKING_THREE_ROOT_CAUSES.md +277 -0
- package/skills-library/api-patterns/RATE_LIMITING_TOGGLE.md +155 -0
- package/skills-library/api-patterns/graphql-content-queries.md +708 -0
- package/skills-library/appointment-scheduler-design.md +423 -0
- package/skills-library/automation/AUTO_POPULATE_COMPLETE_GUIDE.md +631 -0
- package/skills-library/automation/CC_WORKFLOW_STUDIO.md +83 -0
- package/skills-library/automation/CLAUDE_CODE_SWARM_MODE.md +95 -0
- package/skills-library/automation/DAEMON_TRIGGER_FILE_IPC.md +195 -0
- package/skills-library/automation/scheduled-content-publishing.md +608 -0
- package/skills-library/awesome-workflows/Blogging-Platform-Instructions/view_commands.md +25 -0
- package/skills-library/awesome-workflows/CREDENTIAL-SECURITY-WORKFLOW.md +109 -0
- package/skills-library/awesome-workflows/DEBUGGING-WORKFLOW.md +124 -0
- package/skills-library/awesome-workflows/Design-Review-Workflow/README.md +31 -0
- package/skills-library/awesome-workflows/Design-Review-Workflow/design-principles-example.md +129 -0
- package/skills-library/awesome-workflows/Design-Review-Workflow/design-review-agent.md +107 -0
- package/skills-library/awesome-workflows/Design-Review-Workflow/design-review-claude-md-snippet.md +24 -0
- package/skills-library/awesome-workflows/Design-Review-Workflow/design-review-slash-command.md +38 -0
- package/skills-library/awesome-workflows/PARALLEL-RESEARCH-WORKFLOW.md +89 -0
- package/skills-library/awesome-workflows/PHASE-EXECUTION-WORKFLOW.md +97 -0
- package/skills-library/awesome-workflows/SESSION-HANDOFF-WORKFLOW.md +116 -0
- package/skills-library/cms-patterns/content-branch-preview.md +515 -0
- package/skills-library/cms-patterns/inline-visual-editing.md +666 -0
- package/skills-library/cms-patterns/mdx-component-content.md +649 -0
- package/skills-library/cms-patterns/media-manager-abstraction.md +827 -0
- package/skills-library/cms-patterns/schema-driven-form-generator.md +838 -0
- package/skills-library/complexity-metrics/complexity-divider.md +707 -0
- package/skills-library/complexity-metrics/work-with-complexity.md +193 -0
- package/skills-library/creative-multimedia/animation-stack-guide.md +577 -0
- package/skills-library/creative-multimedia/audio-enhancement-pipeline.md +625 -0
- package/skills-library/creative-multimedia/content-repurposing-pipeline.md +1146 -0
- package/skills-library/creative-multimedia/data-visualization-generator.md +862 -0
- package/skills-library/creative-multimedia/doc-to-podcast-pipeline.md +2184 -0
- package/skills-library/creative-multimedia/ffmpeg-command-generator.md +405 -0
- package/skills-library/creative-multimedia/image-optimization-pipeline.md +605 -0
- package/skills-library/creative-multimedia/multi-format-content-generator.md +1759 -0
- package/skills-library/creative-multimedia/og-image-generator.md +635 -0
- package/skills-library/creative-multimedia/podcast-audio-composition.md +1355 -0
- package/skills-library/creative-multimedia/podcast-quality-evaluation.md +1452 -0
- package/skills-library/creative-multimedia/podcast-script-generation.md +1841 -0
- package/skills-library/creative-multimedia/svg-generation.md +750 -0
- package/skills-library/creative-multimedia/text-to-speech-provider-selector.md +1414 -0
- package/skills-library/creative-multimedia/transcription-pipeline-selector.md +677 -0
- package/skills-library/creative-multimedia/video-streaming-setup.md +559 -0
- package/skills-library/database-solutions/AI_RESPONSE_DATABASE_CACHING.md +520 -0
- package/skills-library/database-solutions/CONDITIONAL_SQL_MIGRATION_PATTERN.md +119 -0
- package/skills-library/database-solutions/DATABASE_COLUMN_NAME_MISMATCH.md +393 -0
- package/skills-library/database-solutions/DATABASE_SCHEMA.md +394 -0
- package/skills-library/database-solutions/DATABASE_SCHEMA_VERIFICATION_GUIDE.md +348 -0
- package/skills-library/database-solutions/DATABASE_STRATEGY.md +71 -0
- package/skills-library/database-solutions/ES_MODULE_SEED_SCRIPT_PATTERN.md +52 -0
- package/skills-library/database-solutions/MIGRATION_GUIDE.md +3 -0
- package/skills-library/database-solutions/PLPGSQL_VARIABLE_CONFLICT_FIX.md +208 -0
- package/skills-library/database-solutions/POSTGRESQL_JSONB_DOUBLE_STRINGIFY_FIX.md +245 -0
- package/skills-library/database-solutions/POSTGRESQL_LICENSE_TABLE_DESIGN.md +393 -0
- package/skills-library/database-solutions/POSTGRESQL_UUID_DOCUMENT_RAG_DUAL_SCOPE.md +732 -0
- package/skills-library/database-solutions/POSTGRES_SQL_TEMPLATE_BINDING_ERROR.md +240 -0
- package/skills-library/database-solutions/PRISMA_DB_PUSH_DATA_LOSS_PREVENTION.md +141 -0
- package/skills-library/database-solutions/PRODUCTION_QUERY_OPTIMIZATION_RESTART_FIX.md +389 -0
- package/skills-library/database-solutions/RLS_SECURITY_GUIDE.md +107 -0
- package/skills-library/database-solutions/SCHEMA_ENHANCEMENTS_GUIDE.md +373 -0
- package/skills-library/database-solutions/SCHEMA_MIGRATION_GUIDE.md +368 -0
- package/skills-library/database-solutions/SCHEMA_VERIFICATION_QUICK_REFERENCE.md +104 -0
- package/skills-library/database-solutions/ai-erd-generator.md +1213 -0
- package/skills-library/database-solutions/content-publishing-states.md +631 -0
- package/skills-library/database-solutions/database-schema-designer.md +522 -0
- package/skills-library/database-solutions/er-diagram-components.md +569 -0
- package/skills-library/database-solutions/er-to-ddl-mapping.md +1405 -0
- package/skills-library/database-solutions/erd-creator-textbook-research.md +433 -0
- package/skills-library/database-solutions/erd-react-flow-architecture.md +1965 -0
- package/skills-library/database-solutions/mariadb-aggregate-function-replacement.md +145 -0
- package/skills-library/database-solutions/normalization-validator.md +778 -0
- package/skills-library/database-solutions/postgres-full-text-search-content.md +494 -0
- package/skills-library/database-solutions/postgresql-to-mysql-runtime-translation.md +286 -0
- package/skills-library/database-solutions/regex-alternation-ordering-sql-types.md +92 -0
- package/skills-library/database-solutions/reserved-word-context-aware-quoting.md +142 -0
- package/skills-library/database-solutions/sql-ddl-generator.md +756 -0
- package/skills-library/database-solutions/supabase-connection-pooler-fix.md +102 -0
- package/skills-library/deployment-security/CPANEL_NODE_DEPLOYMENT.md +166 -0
- package/skills-library/deployment-security/DEPLOYMENT.md +275 -0
- package/skills-library/deployment-security/DEPLOYMENT_CHECKLIST.md +363 -0
- package/skills-library/deployment-security/DEPLOYMENT_PLAN.md +669 -0
- package/skills-library/deployment-security/KNEX_DATABASE_ABSTRACTION.md +444 -0
- package/skills-library/deployment-security/LICENSE_KEY_SYSTEM.md +206 -0
- package/skills-library/deployment-security/NODE18_DEPENDENCY_COMPATIBILITY.md +284 -0
- package/skills-library/deployment-security/PHP_INSTALLER_WIZARD_GUIDE.md +315 -0
- package/skills-library/deployment-security/PM2_ENVIRONMENT_VARIABLE_CACHING.md +256 -0
- package/skills-library/deployment-security/PM2_MEMORY_EXHAUSTION_FIX.md +370 -0
- package/skills-library/deployment-security/PRODUCTION_DEPLOYMENT_GUIDE.md +592 -0
- package/skills-library/deployment-security/PRODUCTION_HARDENING_DOCUMENTATION.md +307 -0
- package/skills-library/deployment-security/PRODUCTION_RECOVERY_CHERRY_PICK_PATTERN.md +202 -0
- package/skills-library/deployment-security/PYINSTALLER_CUDA_WHISPER_BUNDLING.md +236 -0
- package/skills-library/deployment-security/SECURITY.md +41 -0
- package/skills-library/deployment-security/SMTP_SSL_HOSTNAME_MISMATCH_SHARED_HOSTING.md +220 -0
- package/skills-library/deployment-security/SPA_SEO_OPTIMIZATION_CPANEL.md +200 -0
- package/skills-library/deployment-security/SUPABASE_EDGE_FUNCTIONS.md +338 -0
- package/skills-library/deployment-security/VERCEL_GITHUB_DEPLOYMENT_GUIDE.md +858 -0
- package/skills-library/deployment-security/VPS_DEPLOYMENT_READINESS.md +356 -0
- package/skills-library/deployment-security/deployment-changes-not-applying.md +241 -0
- package/skills-library/deployment-security/env-file-management-production-local.md +203 -0
- package/skills-library/deployment-security/express-secure-file-downloads.md +413 -0
- package/skills-library/deployment-security/react-production-deployment-desktop-guide.md +2011 -0
- package/skills-library/deployment-security/self-hosted-supabase-coolify-guide.md +1684 -0
- package/skills-library/deployment-security/unique-features-ai-strategy-plaid-security.md +1613 -0
- package/skills-library/deployment-security/vps-deployment.md +135 -0
- package/skills-library/document-processing/WORD_EXPORT_MARKDOWN_FORMATTING.md +482 -0
- package/skills-library/document-processing/document-ai-landingai-integration.md +677 -0
- package/skills-library/document-processing/express-secure-file-downloads-mern.md +413 -0
- package/skills-library/document-processing/express-secure-file-downloads.md +413 -0
- package/skills-library/document-processing/md-to-word-converter.md +318 -0
- package/skills-library/document-processing/pdf-forms-integration/README.md +101 -0
- package/skills-library/document-processing/pdf-forms-integration/SKILL.md +662 -0
- package/skills-library/ecommerce/ADMIN_PRODUCTS_GUIDE.md +428 -0
- package/skills-library/ecommerce/ECOMMERCE_API_REFERENCE.md +776 -0
- package/skills-library/ecommerce/ECOMMERCE_COMPLETION_SUMMARY.md +673 -0
- package/skills-library/ecommerce/ECOMMERCE_IMPLEMENTATION_GUIDE.md +729 -0
- package/skills-library/ecommerce/ECOMMERCE_QUICK_REFERENCE.md +521 -0
- package/skills-library/ecommerce/ECOMMERCE_TESTING_CHECKLIST.md +565 -0
- package/skills-library/ecommerce/ECOMMERCE_WORKFLOW_GUIDE.md +1059 -0
- package/skills-library/ecommerce/PRODUCT_CREATION_EXPANDED.md +522 -0
- package/skills-library/ecommerce/agentic-commerce-protocol.md +203 -0
- package/skills-library/ecommerce/cart-abandonment-recovery.md +236 -0
- package/skills-library/ecommerce/cart-architecture-patterns.md +300 -0
- package/skills-library/ecommerce/cart-item-count-indicator.md +264 -0
- package/skills-library/ecommerce/checkout-ux-conversion.md +227 -0
- package/skills-library/ecommerce/composable-commerce-selection.md +166 -0
- package/skills-library/ecommerce/ecommerce-analytics-patterns.md +167 -0
- package/skills-library/ecommerce/fraud-detection-patterns.md +179 -0
- package/skills-library/ecommerce/inventory-stock-management.md +270 -0
- package/skills-library/ecommerce/order-saga-state-machine.md +336 -0
- package/skills-library/ecommerce/payment-provider-abstraction.md +245 -0
- package/skills-library/ecommerce/pci-compliance-checklist.md +192 -0
- package/skills-library/ecommerce/refund-chargeback-handling.md +177 -0
- package/skills-library/ecommerce/shipping-carrier-integration.md +218 -0
- package/skills-library/ecommerce/webhook-idempotency-patterns.md +253 -0
- package/skills-library/excalidraw-diagrams/.github/workflows/ci.yml +558 -0
- package/skills-library/excalidraw-diagrams/.github/workflows/prompt-gallery.yml +448 -0
- package/skills-library/excalidraw-diagrams/.github/workflows/release.yml +42 -0
- package/skills-library/excalidraw-diagrams/.github/workflows/test-reusable-ci.yml +25 -0
- package/skills-library/excalidraw-diagrams/CLAUDE.md +57 -0
- package/skills-library/excalidraw-diagrams/LICENSE +21 -0
- package/skills-library/excalidraw-diagrams/README.md +178 -0
- package/skills-library/excalidraw-diagrams/SKILL.md +715 -0
- package/skills-library/form-solutions/BUTTON_TYPE_FORM_SUBMISSION.md +336 -0
- package/skills-library/form-solutions/FILLABLE_PDF_IMPLEMENTATION.md +226 -0
- package/skills-library/form-solutions/SURVEYJS_QUESTIONNAIRE_SYSTEM.md +367 -0
- package/skills-library/form-solutions/tiptap-minimal-setup.md +690 -0
- package/skills-library/frontend/scholarly-classification-bubble-map.md +149 -0
- package/skills-library/infrastructure/ci-cd-pipeline-builder.md +517 -0
- package/skills-library/infrastructure/observability-designer.md +264 -0
- package/skills-library/infrastructure/performance-profiler.md +621 -0
- package/skills-library/installer-wizard-patterns.md +249 -0
- package/skills-library/integrations/CLAUDE_CODE_TOKEN_ANALYTICS.md +160 -0
- package/skills-library/integrations/CONFIGURABLE_AI_PROVIDER_SELECTION.md +728 -0
- package/skills-library/integrations/SOCKET_IO_BROADCAST_ALL_VS_ROOM.md +141 -0
- package/skills-library/integrations/VIRTUAL_MEETINGS_IMPLEMENTATION.md +374 -0
- package/skills-library/integrations/WORDPRESS_LEARNDASH_DATA_RECOVERY.md +53 -0
- package/skills-library/integrations/YOUTUBE_API_SETUP.md +141 -0
- package/skills-library/integrations/YOUTUBE_BOOKMARKING_EXPLANATION.md +252 -0
- package/skills-library/integrations/YOUTUBE_BOOKMARKING_SOLUTION.md +268 -0
- package/skills-library/integrations/YOUTUBE_OAUTH_SETUP_GUIDE.md +200 -0
- package/skills-library/integrations/YOUTUBE_VIDEO_FIX_COMPLETE.md +192 -0
- package/skills-library/integrations/ai-ml/GEMINI_AI_RAG_PIPELINE_COMPLETE_GUIDE.md +195 -0
- package/skills-library/integrations/ai-ml/GEMINI_IMAGE_GENERATION_SETUP.md +64 -0
- package/skills-library/integrations/cloudflare/cloudflare-turnstile-debugging.md +202 -0
- package/skills-library/integrations/cloudflare/cloudflare-turnstile-implementation.md +476 -0
- package/skills-library/integrations/cloudflare-turnstile-debugging.md +202 -0
- package/skills-library/integrations/cloudflare-turnstile-implementation.md +476 -0
- package/skills-library/integrations/ghost-creator-monetization-pattern.md +454 -0
- package/skills-library/integrations/headless-cms-architecture.md +484 -0
- package/skills-library/integrations/headless-cms-stack-selection.md +183 -0
- package/skills-library/integrations/payload-cms-patterns.md +674 -0
- package/skills-library/integrations/realtimestt-openwakeword-cuda-windows.md +229 -0
- package/skills-library/integrations/rss-podcast-integration.md +300 -0
- package/skills-library/integrations/wordpress/WORDPRESS_LEARNDASH_DATA_RECOVERY.md +53 -0
- package/skills-library/integrations/youtube/YOUTUBE_API_SETUP.md +141 -0
- package/skills-library/integrations/youtube/YOUTUBE_BOOKMARKING_EXPLANATION.md +252 -0
- package/skills-library/integrations/youtube/YOUTUBE_BOOKMARKING_SOLUTION.md +268 -0
- package/skills-library/integrations/youtube/YOUTUBE_OAUTH_SETUP_GUIDE.md +200 -0
- package/skills-library/integrations/youtube/YOUTUBE_VIDEO_FIX_COMPLETE.md +192 -0
- package/skills-library/marketing/campaign-analytics.md +97 -0
- package/skills-library/marketing/content-creator.md +105 -0
- package/skills-library/marketing/marketing-strategy-pmm.md +94 -0
- package/skills-library/marketing/social-media-analyzer.md +81 -0
- package/skills-library/methodology/ADVANCED_ORCHESTRATION_PATTERNS.md +401 -0
- package/skills-library/methodology/AGENT_SELF_IMPROVEMENT_LOOP.md +179 -0
- package/skills-library/methodology/BREATH_BASED_PARALLEL_EXECUTION.md +1 -1
- package/skills-library/methodology/CLEANSING_CYCLE.md +358 -0
- package/skills-library/methodology/CONFIDENCE_ANNOTATION_PATTERN.md +143 -0
- package/skills-library/methodology/CRITICAL_PATTERNS_DOCUMENTATION_COMPLETE.md +204 -0
- package/skills-library/methodology/DELIVERABLES_SUMMARY.md +341 -0
- package/skills-library/methodology/DIFFICULTY_AWARE_AGENT_ROUTING.md +252 -0
- package/skills-library/methodology/EVOLUTIONARY_SKILL_SYNTHESIS.md +219 -0
- package/skills-library/methodology/GLOMERULUS_DECISION_GATE.md +223 -0
- package/skills-library/methodology/HIBERNATION_SYSTEM.md +231 -0
- package/skills-library/methodology/INSTRUMENTATION_OVER_RESTRICTION.md +192 -0
- package/skills-library/methodology/MASTER_COMPLETION_SUMMARY.md +444 -0
- package/skills-library/methodology/MASTER_SESSION_COMPLETION.md +743 -0
- package/skills-library/methodology/MERN_QUICK_REFERENCE.md +358 -0
- package/skills-library/methodology/ORGAN_AGENT_MAPPING.md +177 -0
- package/skills-library/methodology/PARALLEL_WAVE_BASED_REFACTORING.md +440 -0
- package/skills-library/methodology/QUICK_REFERENCE.md +358 -0
- package/skills-library/methodology/SDFT_ONPOLICY_SELF_DISTILLATION.md +186 -0
- package/skills-library/methodology/SELF_QUESTIONING_TASK_GENERATION.md +270 -0
- package/skills-library/methodology/SESSION_COMPLETION_SUMMARY.md +304 -0
- package/skills-library/methodology/SESSION_SUMMARY.md +432 -0
- package/skills-library/methodology/WARRIOR_WORKFLOW_DEBUGGING_PROTOCOL.md +252 -0
- package/skills-library/methodology/tech-debt-tracker.md +570 -0
- package/skills-library/parallel-debug/SKILL.md +60 -0
- package/skills-library/patterns-standards/API_PATTERN_FIX_SUMMARY.md +236 -0
- package/skills-library/patterns-standards/BATCH_OPERATIONS_WITH_PROGRESS_MODAL.md +362 -0
- package/skills-library/patterns-standards/CRITICAL_CODING_PATTERNS.md +639 -0
- package/skills-library/patterns-standards/DARK_MODE_MODAL_VISIBILITY.md +258 -0
- package/skills-library/patterns-standards/ERROR_RESILIENCE_IMPLEMENTATION.md +375 -0
- package/skills-library/patterns-standards/ES_MODULE_IMPORT_HOISTING_DOTENV.md +298 -0
- package/skills-library/patterns-standards/NESTED_BACKDROP_FILTER_CSS_ARTIFACT_FIX.md +76 -0
- package/skills-library/patterns-standards/ORDERED_DETECTOR_PIPELINE_GRACEFUL_FALLBACK.md +333 -0
- package/skills-library/patterns-standards/PHASE_IMPORT_ERROR_DEBUGGING.md +271 -0
- package/skills-library/patterns-standards/PYNPUT_GLOBAL_HOTKEY_VK_MATCHING.md +252 -0
- package/skills-library/patterns-standards/REACT_USEEFFECT_CASCADE_RESET_FIX.md +132 -0
- package/skills-library/patterns-standards/SUBMENU_HOVER_DROPDOWN_PATTERN.md +225 -0
- package/skills-library/patterns-standards/TAILWIND_TEXT_VISIBILITY_OVERRIDE.md +322 -0
- package/skills-library/patterns-standards/THEME_AWARE_CSS_VARIABLES_PATTERN.md +209 -0
- package/skills-library/patterns-standards/THEME_USER_OBJECT_PROPERTY_NAMING.md +194 -0
- package/skills-library/patterns-standards/TOOLTIP_BLOCKING_CLICKS_FIX.md +267 -0
- package/skills-library/patterns-standards/claude-code-plugin-structure.md +235 -0
- package/skills-library/patterns-standards/react-i18next-setup.md +429 -0
- package/skills-library/patterns-standards/thesys-c1-generative-ui-integration.md +967 -0
- package/skills-library/plugin-development/CLAUDE_CODE_COMMAND_REGISTRATION_SILENT_FAILURE.md +315 -0
- package/skills-library/plugin-development/plugin-command-namespace-vs-global.md +390 -0
- package/skills-library/plugin-development/plugin-doc-auto-generation.md +172 -0
- package/skills-library/security/GITHUB_REPO_SECURITY_AUDIT.md +115 -0
- package/skills-library/security/admin-deletion-safety.md +396 -0
- package/skills-library/security/application-vuln-patterns.md +477 -0
- package/skills-library/security/env-secrets-manager.md +686 -0
- package/skills-library/security/secure-ai-application-templates.md +347 -0
- package/skills-library/security/sql-injection-prevention-postgresjs.md +151 -0
- package/skills-library/supabase-connection-pooler-fix.md +102 -0
- package/skills-library/system-context/POWERSHELL_BASH_INTEROP.md +82 -0
- package/skills-library/system-context/SERVICE_LIFECYCLE_MANAGEMENT.md +119 -0
- package/skills-library/system-context/SKILL.md +40 -0
- package/skills-library/system-context/WINDOWS_DEV_ENVIRONMENT.md +73 -0
- package/skills-library/testing/E2E_PLAYWRIGHT_PATTERNS.md +99 -0
- package/skills-library/testing/INTEGRATION_TEST_STRATEGY.md +82 -0
- package/skills-library/testing/RED_GREEN_BUGFIX_GATE.md +203 -0
- package/skills-library/testing/TEST_DATA_MANAGEMENT.md +69 -0
- package/skills-library/testing/VITEST_UNIT_TEST_PATTERNS.md +75 -0
- package/skills-library/testing/playwright-api-security-tests.md +202 -0
- package/skills-library/toolbox/SKILL.md +84 -0
- package/skills-library/toolbox/code-graph-and-web-scraping-mcps.md +237 -0
- package/skills-library/ui-ux-pro-max/ACCESSIBILITY_ESSENTIALS.md +115 -0
- package/skills-library/ui-ux-pro-max/DESIGN_SYSTEM_SCAFFOLDING.md +133 -0
- package/skills-library/ui-ux-pro-max/RESPONSIVE_LAYOUT_PATTERNS.md +119 -0
- package/skills-library/ui-ux-pro-max/SKILL.md +386 -0
- package/skills-library/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills-library/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills-library/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills-library/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills-library/ui-ux-pro-max/data/products.csv +97 -0
- package/skills-library/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills-library/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills-library/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills-library/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills-library/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills-library/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills-library/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills-library/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills-library/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills-library/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills-library/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills-library/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills-library/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills-library/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills-library/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills-library/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills-library/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills-library/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills-library/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills-library/wordpress-style-theme-components.md +1526 -0
- package/templates/ASSUMPTIONS.md +1 -1
- package/templates/DECISION_LOG.md +0 -1
- package/templates/phase-prompt.md +1 -1
- package/templates/phoenix-comparison.md +6 -6
- package/templates/skill-api-integration.md +106 -0
- package/templates/skill-architecture-pattern.md +92 -0
- package/templates/skill-debug-pattern.md +98 -0
- package/templates/skill-devops-recipe.md +107 -0
- package/templates/skill-general.md +65 -0
- package/templates/skill-ui-component.md +113 -0
- package/tools/uat-runner.py +179 -0
- package/version.json +7 -3
- package/workflows/handoff-session.md +2 -2
- package/workflows/new-project.md +2 -2
- package/workflows/plan-phase.md +1 -1
- package/.claude-plugin/plugin.json +0 -64
- package/skills-library/_general/methodology/LIVE_BREADCRUMB_PROTOCOL.md +0 -242
- package/skills-library/_general/methodology/llm-judge-memory-crud.md +0 -241
- package/skills-library/methodology/REFLEXION_MEMORY_PATTERN.md +0 -183
- package/skills-library/methodology/RESEARCH_BACKED_WORKFLOW_UPGRADE.md +0 -263
- package/skills-library/methodology/SABBATH_REST_PATTERN.md +0 -267
- package/skills-library/methodology/STONE_AND_SCAFFOLD.md +0 -220
- package/skills-library/specialists/api-architecture/api-designer.md +0 -49
- package/skills-library/specialists/api-architecture/graphql-architect.md +0 -49
- package/skills-library/specialists/api-architecture/mcp-developer.md +0 -51
- package/skills-library/specialists/api-architecture/microservices-architect.md +0 -50
- package/skills-library/specialists/api-architecture/websocket-engineer.md +0 -48
- package/skills-library/specialists/backend/django-expert.md +0 -52
- package/skills-library/specialists/backend/fastapi-expert.md +0 -52
- package/skills-library/specialists/backend/laravel-specialist.md +0 -52
- package/skills-library/specialists/backend/nestjs-expert.md +0 -51
- package/skills-library/specialists/backend/rails-expert.md +0 -53
- package/skills-library/specialists/backend/spring-boot-engineer.md +0 -56
- package/skills-library/specialists/data-ml/fine-tuning-expert.md +0 -48
- package/skills-library/specialists/data-ml/ml-pipeline.md +0 -47
- package/skills-library/specialists/data-ml/pandas-pro.md +0 -47
- package/skills-library/specialists/data-ml/rag-architect.md +0 -51
- package/skills-library/specialists/data-ml/spark-engineer.md +0 -47
- package/skills-library/specialists/frontend/angular-architect.md +0 -52
- package/skills-library/specialists/frontend/flutter-expert.md +0 -51
- package/skills-library/specialists/frontend/nextjs-developer.md +0 -54
- package/skills-library/specialists/frontend/react-native-expert.md +0 -50
- package/skills-library/specialists/frontend/vue-expert.md +0 -51
- package/skills-library/specialists/infrastructure/chaos-engineer.md +0 -74
- package/skills-library/specialists/infrastructure/cloud-architect.md +0 -70
- package/skills-library/specialists/infrastructure/database-optimizer.md +0 -64
- package/skills-library/specialists/infrastructure/devops-engineer.md +0 -70
- package/skills-library/specialists/infrastructure/kubernetes-specialist.md +0 -52
- package/skills-library/specialists/infrastructure/monitoring-expert.md +0 -70
- package/skills-library/specialists/infrastructure/sre-engineer.md +0 -70
- package/skills-library/specialists/infrastructure/terraform-engineer.md +0 -51
- package/skills-library/specialists/languages/cpp-pro.md +0 -74
- package/skills-library/specialists/languages/csharp-developer.md +0 -69
- package/skills-library/specialists/languages/dotnet-core-expert.md +0 -54
- package/skills-library/specialists/languages/golang-pro.md +0 -51
- package/skills-library/specialists/languages/java-architect.md +0 -49
- package/skills-library/specialists/languages/javascript-pro.md +0 -68
- package/skills-library/specialists/languages/kotlin-specialist.md +0 -68
- package/skills-library/specialists/languages/php-pro.md +0 -49
- package/skills-library/specialists/languages/python-pro.md +0 -52
- package/skills-library/specialists/languages/react-expert.md +0 -51
- package/skills-library/specialists/languages/rust-engineer.md +0 -50
- package/skills-library/specialists/languages/sql-pro.md +0 -56
- package/skills-library/specialists/languages/swift-expert.md +0 -69
- package/skills-library/specialists/languages/typescript-pro.md +0 -51
- package/skills-library/specialists/platform/atlassian-mcp.md +0 -52
- package/skills-library/specialists/platform/embedded-systems.md +0 -53
- package/skills-library/specialists/platform/game-developer.md +0 -53
- package/skills-library/specialists/platform/salesforce-developer.md +0 -53
- package/skills-library/specialists/platform/shopify-expert.md +0 -49
- package/skills-library/specialists/platform/wordpress-pro.md +0 -49
- package/skills-library/specialists/quality/code-documenter.md +0 -51
- package/skills-library/specialists/quality/code-reviewer.md +0 -67
- package/skills-library/specialists/quality/debugging-wizard.md +0 -51
- package/skills-library/specialists/quality/fullstack-guardian.md +0 -51
- package/skills-library/specialists/quality/legacy-modernizer.md +0 -50
- package/skills-library/specialists/quality/playwright-expert.md +0 -65
- package/skills-library/specialists/quality/spec-miner.md +0 -56
- package/skills-library/specialists/quality/test-master.md +0 -65
- package/skills-library/specialists/security/secure-code-guardian.md +0 -55
- package/skills-library/specialists/security/security-reviewer.md +0 -53
- package/skills-library/specialists/workflow/architecture-designer.md +0 -53
- package/skills-library/specialists/workflow/cli-developer.md +0 -70
- package/skills-library/specialists/workflow/feature-forge.md +0 -65
- package/skills-library/specialists/workflow/prompt-engineer.md +0 -54
- package/skills-library/specialists/workflow/the-fool.md +0 -62
- /package/skills-library/{performance → _general/performance}/cache-augmented-generation.md +0 -0
- /package/skills-library/{debugging → parallel-debug}/FAILURE_TAXONOMY_CLASSIFICATION.md +0 -0
- /package/skills-library/{debugging → parallel-debug}/THREE_AGENT_HYPOTHESIS_DEBUGGING.md +0 -0
|
@@ -0,0 +1,1146 @@
|
|
|
1
|
+
# Content Repurposing Pipeline
|
|
2
|
+
|
|
3
|
+
## Metadata
|
|
4
|
+
|
|
5
|
+
- **Title:** Automated Content Repurposing Pipeline
|
|
6
|
+
- **Category:** Creative/Multimedia
|
|
7
|
+
- **Version:** v12.6.0
|
|
8
|
+
- **Created:** 2026-03-09
|
|
9
|
+
- **Dependencies:** FFmpeg, Sharp, Whisper/Deepgram/AssemblyAI, Claude/Gemini API
|
|
10
|
+
|
|
11
|
+
## Description
|
|
12
|
+
|
|
13
|
+
End-to-end pipeline that transforms long-form content (sermons, lectures, podcasts, Bible studies) into multi-format output: short video clips with burned-in captions, audiograms, quote cards, blog drafts, social media posts, and YouTube chapters. This is the "crown jewel" skill that combines FFmpeg, transcription, and image processing into a single automated workflow.
|
|
14
|
+
|
|
15
|
+
## When to Use
|
|
16
|
+
|
|
17
|
+
- After recording a sermon, lecture, podcast, or Bible study (30-90 minutes)
|
|
18
|
+
- When a ministry or content creator needs a week's worth of social media from one recording
|
|
19
|
+
- When repurposing existing video/audio archives into modern formats
|
|
20
|
+
- When building a content calendar from long-form source material
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Pipeline Overview
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Long-form content (sermon/lecture/podcast, 30-90 min)
|
|
28
|
+
|
|
|
29
|
+
v
|
|
30
|
+
Step 1: Audio extraction (FFmpeg)
|
|
31
|
+
|
|
|
32
|
+
v
|
|
33
|
+
Step 2: Transcription (Whisper/Deepgram/AssemblyAI)
|
|
34
|
+
|
|
|
35
|
+
v
|
|
36
|
+
Step 3: AI Analysis (Claude/Gemini)
|
|
37
|
+
- Identify key quotes (30-60 sec clips)
|
|
38
|
+
- Generate summary
|
|
39
|
+
- Extract topic segments
|
|
40
|
+
- Create social media captions
|
|
41
|
+
|
|
|
42
|
+
v
|
|
43
|
+
Step 4: Generate outputs
|
|
44
|
+
a) Short video clips with burned-in captions
|
|
45
|
+
b) Audiograms (waveform animation + quote text overlay)
|
|
46
|
+
c) Quote cards (image with text overlay)
|
|
47
|
+
d) Blog post draft from transcript
|
|
48
|
+
e) Social media posts (Twitter, Instagram, Facebook)
|
|
49
|
+
f) YouTube chapters from topic segments
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Node.js Implementation
|
|
55
|
+
|
|
56
|
+
### Dependencies
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"sharp": "^0.33.2",
|
|
62
|
+
"@anthropic-ai/sdk": "^0.39.0",
|
|
63
|
+
"@google/generative-ai": "^0.21.0",
|
|
64
|
+
"deepgram-sdk": "^3.9.0",
|
|
65
|
+
"assemblyai": "^4.8.0"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Complete Pipeline Class
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { execFileSync } from 'child_process';
|
|
74
|
+
import { promises as fs } from 'fs';
|
|
75
|
+
import path from 'path';
|
|
76
|
+
import sharp from 'sharp';
|
|
77
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
78
|
+
|
|
79
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
interface TranscriptSegment {
|
|
82
|
+
start: number; // seconds
|
|
83
|
+
end: number; // seconds
|
|
84
|
+
text: string;
|
|
85
|
+
confidence: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface Transcript {
|
|
89
|
+
segments: TranscriptSegment[];
|
|
90
|
+
fullText: string;
|
|
91
|
+
duration: number; // total seconds
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface KeyMoment {
|
|
95
|
+
start: number;
|
|
96
|
+
end: number;
|
|
97
|
+
quote: string;
|
|
98
|
+
context: string;
|
|
99
|
+
emotionalTone: 'inspiring' | 'teaching' | 'prophetic' | 'worship' | 'testimony';
|
|
100
|
+
socialCaption: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface TopicSegment {
|
|
104
|
+
start: number;
|
|
105
|
+
end: number;
|
|
106
|
+
title: string;
|
|
107
|
+
summary: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface ContentAnalysis {
|
|
111
|
+
title: string;
|
|
112
|
+
summary: string;
|
|
113
|
+
keyMoments: KeyMoment[];
|
|
114
|
+
topicSegments: TopicSegment[];
|
|
115
|
+
blogDraft: string;
|
|
116
|
+
hashtags: string[];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface PipelineConfig {
|
|
120
|
+
outputDir: string;
|
|
121
|
+
brandingLogo?: string; // path to PNG logo
|
|
122
|
+
brandColor?: string; // hex color, e.g. '#1a1a2e'
|
|
123
|
+
accentColor?: string; // hex color, e.g. '#e94560'
|
|
124
|
+
fontPath?: string; // path to .ttf font for captions
|
|
125
|
+
churchName?: string;
|
|
126
|
+
aiProvider: 'claude' | 'gemini';
|
|
127
|
+
transcriptionProvider: 'whisper' | 'deepgram' | 'assemblyai';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── Helper: Safe FFmpeg Execution ───────────────────────────────────────────
|
|
131
|
+
// Uses execFileSync to avoid shell injection. All arguments are passed as arrays.
|
|
132
|
+
|
|
133
|
+
function runFFmpeg(args: string[]): void {
|
|
134
|
+
execFileSync('ffmpeg', args, { stdio: 'pipe' });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function runWhisper(args: string[]): void {
|
|
138
|
+
execFileSync('whisper', args, { stdio: 'pipe', timeout: 600_000 });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ─── Pipeline Class ──────────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
export class ContentRepurposingPipeline {
|
|
144
|
+
private config: PipelineConfig;
|
|
145
|
+
private anthropic: Anthropic | null = null;
|
|
146
|
+
|
|
147
|
+
constructor(config: PipelineConfig) {
|
|
148
|
+
this.config = {
|
|
149
|
+
brandColor: '#1a1a2e',
|
|
150
|
+
accentColor: '#e94560',
|
|
151
|
+
churchName: 'Ministry',
|
|
152
|
+
...config,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
if (config.aiProvider === 'claude') {
|
|
156
|
+
this.anthropic = new Anthropic();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── Step 1: Audio Extraction ────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Extract audio from video file as WAV (16kHz mono for transcription)
|
|
164
|
+
* and as high-quality AAC for audiogram output.
|
|
165
|
+
*/
|
|
166
|
+
async extractAudio(videoPath: string): Promise<{ wavPath: string; aacPath: string }> {
|
|
167
|
+
const baseName = path.basename(videoPath, path.extname(videoPath));
|
|
168
|
+
const wavPath = path.join(this.config.outputDir, `${baseName}_audio.wav`);
|
|
169
|
+
const aacPath = path.join(this.config.outputDir, `${baseName}_audio.aac`);
|
|
170
|
+
|
|
171
|
+
await fs.mkdir(this.config.outputDir, { recursive: true });
|
|
172
|
+
|
|
173
|
+
// WAV for transcription (16kHz mono — optimal for Whisper/Deepgram)
|
|
174
|
+
runFFmpeg([
|
|
175
|
+
'-i', videoPath,
|
|
176
|
+
'-vn', '-acodec', 'pcm_s16le', '-ar', '16000', '-ac', '1',
|
|
177
|
+
wavPath, '-y',
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
// AAC for audiogram output (high quality stereo)
|
|
181
|
+
runFFmpeg([
|
|
182
|
+
'-i', videoPath,
|
|
183
|
+
'-vn', '-acodec', 'aac', '-b:a', '192k', '-ar', '44100',
|
|
184
|
+
aacPath, '-y',
|
|
185
|
+
]);
|
|
186
|
+
|
|
187
|
+
return { wavPath, aacPath };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ─── Step 2: Transcription ───────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Transcribe audio to text with word-level timestamps.
|
|
194
|
+
* Supports Whisper (local), Deepgram, and AssemblyAI.
|
|
195
|
+
*/
|
|
196
|
+
async transcribe(audioPath: string): Promise<Transcript> {
|
|
197
|
+
switch (this.config.transcriptionProvider) {
|
|
198
|
+
case 'whisper':
|
|
199
|
+
return this.transcribeWhisper(audioPath);
|
|
200
|
+
case 'deepgram':
|
|
201
|
+
return this.transcribeDeepgram(audioPath);
|
|
202
|
+
case 'assemblyai':
|
|
203
|
+
return this.transcribeAssemblyAI(audioPath);
|
|
204
|
+
default:
|
|
205
|
+
throw new Error(`Unknown transcription provider: ${this.config.transcriptionProvider}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private async transcribeWhisper(audioPath: string): Promise<Transcript> {
|
|
210
|
+
const outputDir = path.join(this.config.outputDir, 'whisper_out');
|
|
211
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
212
|
+
|
|
213
|
+
// Use whisper CLI (requires: pip install openai-whisper)
|
|
214
|
+
// large-v3 model for best accuracy
|
|
215
|
+
runWhisper([
|
|
216
|
+
audioPath,
|
|
217
|
+
'--model', 'large-v3',
|
|
218
|
+
'--output_format', 'json',
|
|
219
|
+
'--output_dir', outputDir,
|
|
220
|
+
'--language', 'en',
|
|
221
|
+
]);
|
|
222
|
+
|
|
223
|
+
const baseName = path.basename(audioPath, path.extname(audioPath));
|
|
224
|
+
const jsonPath = path.join(outputDir, `${baseName}.json`);
|
|
225
|
+
const result = JSON.parse(await fs.readFile(jsonPath, 'utf-8'));
|
|
226
|
+
|
|
227
|
+
const segments: TranscriptSegment[] = result.segments.map((seg: any) => ({
|
|
228
|
+
start: seg.start,
|
|
229
|
+
end: seg.end,
|
|
230
|
+
text: seg.text.trim(),
|
|
231
|
+
confidence: seg.avg_logprob ? Math.exp(seg.avg_logprob) : 0.9,
|
|
232
|
+
}));
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
segments,
|
|
236
|
+
fullText: segments.map(s => s.text).join(' '),
|
|
237
|
+
duration: segments[segments.length - 1]?.end ?? 0,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private async transcribeDeepgram(audioPath: string): Promise<Transcript> {
|
|
242
|
+
const { createClient } = await import('@deepgram/sdk');
|
|
243
|
+
const deepgram = createClient(process.env.DEEPGRAM_API_KEY!);
|
|
244
|
+
|
|
245
|
+
const audioBuffer = await fs.readFile(audioPath);
|
|
246
|
+
const { result } = await deepgram.listen.prerecorded.transcribeFile(audioBuffer, {
|
|
247
|
+
model: 'nova-2',
|
|
248
|
+
smart_format: true,
|
|
249
|
+
paragraphs: true,
|
|
250
|
+
utterances: true,
|
|
251
|
+
diarize: true, // speaker detection — useful for interviews
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const utterances = result.results?.utterances ?? [];
|
|
255
|
+
const segments: TranscriptSegment[] = utterances.map((u: any) => ({
|
|
256
|
+
start: u.start,
|
|
257
|
+
end: u.end,
|
|
258
|
+
text: u.transcript,
|
|
259
|
+
confidence: u.confidence,
|
|
260
|
+
}));
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
segments,
|
|
264
|
+
fullText: segments.map(s => s.text).join(' '),
|
|
265
|
+
duration: segments[segments.length - 1]?.end ?? 0,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private async transcribeAssemblyAI(audioPath: string): Promise<Transcript> {
|
|
270
|
+
const { AssemblyAI } = await import('assemblyai');
|
|
271
|
+
const client = new AssemblyAI({ apiKey: process.env.ASSEMBLYAI_API_KEY! });
|
|
272
|
+
|
|
273
|
+
const transcript = await client.transcripts.transcribe({
|
|
274
|
+
audio: audioPath,
|
|
275
|
+
speaker_labels: true,
|
|
276
|
+
auto_chapters: true, // AssemblyAI auto-generates chapters
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const segments: TranscriptSegment[] = (transcript.utterances ?? []).map((u: any) => ({
|
|
280
|
+
start: u.start / 1000, // AssemblyAI uses milliseconds
|
|
281
|
+
end: u.end / 1000,
|
|
282
|
+
text: u.text,
|
|
283
|
+
confidence: u.confidence,
|
|
284
|
+
}));
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
segments,
|
|
288
|
+
fullText: transcript.text ?? '',
|
|
289
|
+
duration: segments[segments.length - 1]?.end ?? 0,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ─── Step 3: AI Content Analysis ─────────────────────────────────────────
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Analyze transcript with Claude or Gemini to extract:
|
|
297
|
+
* - Key quotes suitable for 30-60 second clips
|
|
298
|
+
* - Topic segments for YouTube chapters
|
|
299
|
+
* - Summary for blog post
|
|
300
|
+
* - Social media captions
|
|
301
|
+
*/
|
|
302
|
+
async analyzeContent(transcript: Transcript): Promise<ContentAnalysis> {
|
|
303
|
+
const prompt = `You are a content strategist for a Christian ministry. Analyze this transcript and extract structured content for repurposing.
|
|
304
|
+
|
|
305
|
+
TRANSCRIPT (${Math.round(transcript.duration / 60)} minutes):
|
|
306
|
+
${transcript.segments.map(s => `[${this.formatTimestamp(s.start)}] ${s.text}`).join('\n')}
|
|
307
|
+
|
|
308
|
+
Return a JSON object with this exact structure:
|
|
309
|
+
{
|
|
310
|
+
"title": "Compelling title for this content",
|
|
311
|
+
"summary": "2-3 paragraph summary suitable for a blog post introduction",
|
|
312
|
+
"keyMoments": [
|
|
313
|
+
{
|
|
314
|
+
"start": <start_seconds>,
|
|
315
|
+
"end": <end_seconds>,
|
|
316
|
+
"quote": "The exact quote text",
|
|
317
|
+
"context": "Brief context of what's being discussed",
|
|
318
|
+
"emotionalTone": "inspiring|teaching|prophetic|worship|testimony",
|
|
319
|
+
"socialCaption": "Ready-to-post social media caption with emojis and call to action"
|
|
320
|
+
}
|
|
321
|
+
],
|
|
322
|
+
"topicSegments": [
|
|
323
|
+
{
|
|
324
|
+
"start": <start_seconds>,
|
|
325
|
+
"end": <end_seconds>,
|
|
326
|
+
"title": "Segment title",
|
|
327
|
+
"summary": "1-2 sentence summary"
|
|
328
|
+
}
|
|
329
|
+
],
|
|
330
|
+
"blogDraft": "Full blog post draft (800-1200 words) based on the transcript content. Use headers, paragraphs, and scripture references where applicable.",
|
|
331
|
+
"hashtags": ["relevantHashtag1", "relevantHashtag2"]
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
RULES:
|
|
335
|
+
- Select 5-8 key moments, each 30-60 seconds long
|
|
336
|
+
- Key moments should be emotionally powerful, quotable, or contain key teaching points
|
|
337
|
+
- Topic segments should cover the entire recording (for YouTube chapters)
|
|
338
|
+
- First topic segment must start at 0:00
|
|
339
|
+
- Social captions should be platform-ready with hashtags
|
|
340
|
+
- Blog draft should stand alone without needing to watch the video
|
|
341
|
+
- Include scripture references where the speaker quotes or references Bible passages
|
|
342
|
+
|
|
343
|
+
Return ONLY valid JSON, no markdown code fences.`;
|
|
344
|
+
|
|
345
|
+
if (this.config.aiProvider === 'claude') {
|
|
346
|
+
return this.analyzeWithClaude(prompt);
|
|
347
|
+
} else {
|
|
348
|
+
return this.analyzeWithGemini(prompt);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private async analyzeWithClaude(prompt: string): Promise<ContentAnalysis> {
|
|
353
|
+
if (!this.anthropic) throw new Error('Anthropic client not initialized');
|
|
354
|
+
|
|
355
|
+
const response = await this.anthropic.messages.create({
|
|
356
|
+
model: 'claude-sonnet-4-20250514',
|
|
357
|
+
max_tokens: 8192,
|
|
358
|
+
messages: [{ role: 'user', content: prompt }],
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const text = response.content[0].type === 'text' ? response.content[0].text : '';
|
|
362
|
+
return JSON.parse(text);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private async analyzeWithGemini(prompt: string): Promise<ContentAnalysis> {
|
|
366
|
+
const { GoogleGenerativeAI } = await import('@google/generative-ai');
|
|
367
|
+
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);
|
|
368
|
+
const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
|
|
369
|
+
|
|
370
|
+
const result = await model.generateContent(prompt);
|
|
371
|
+
const text = result.response.text();
|
|
372
|
+
// Strip markdown fences if Gemini wraps in ```json
|
|
373
|
+
const cleaned = text.replace(/^```json\n?/, '').replace(/\n?```$/, '');
|
|
374
|
+
return JSON.parse(cleaned);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ─── Step 4a: Generate Short Video Clips ─────────────────────────────────
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Extract short clips from source video with burned-in captions,
|
|
381
|
+
* fade in/out transitions, and optional branding overlay.
|
|
382
|
+
*/
|
|
383
|
+
async generateClips(
|
|
384
|
+
videoPath: string,
|
|
385
|
+
moments: KeyMoment[]
|
|
386
|
+
): Promise<string[]> {
|
|
387
|
+
const clipDir = path.join(this.config.outputDir, 'clips');
|
|
388
|
+
await fs.mkdir(clipDir, { recursive: true });
|
|
389
|
+
|
|
390
|
+
const clipPaths: string[] = [];
|
|
391
|
+
|
|
392
|
+
for (let i = 0; i < moments.length; i++) {
|
|
393
|
+
const moment = moments[i];
|
|
394
|
+
const clipPath = path.join(clipDir, `clip_${i + 1}.mp4`);
|
|
395
|
+
const srtPath = path.join(clipDir, `clip_${i + 1}.srt`);
|
|
396
|
+
|
|
397
|
+
// Generate SRT subtitle file for this clip
|
|
398
|
+
await this.generateSRT(srtPath, moment);
|
|
399
|
+
|
|
400
|
+
const duration = moment.end - moment.start;
|
|
401
|
+
const fadeStart = duration - 0.5;
|
|
402
|
+
|
|
403
|
+
// Build the complex filter string
|
|
404
|
+
const fontStyle = this.config.fontPath
|
|
405
|
+
? `FontName=Custom,FontSize=22`
|
|
406
|
+
: `FontSize=22,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=2,Shadow=1`;
|
|
407
|
+
|
|
408
|
+
const srtPathNormalized = srtPath.replace(/\\/g, '/');
|
|
409
|
+
const videoFilter = [
|
|
410
|
+
`subtitles='${srtPathNormalized}':si=0:force_style='${fontStyle}'`,
|
|
411
|
+
`fade=t=in:st=0:d=0.5`,
|
|
412
|
+
`fade=t=out:st=${fadeStart}:d=0.5`,
|
|
413
|
+
`scale=1080:1920:force_original_aspect_ratio=decrease`,
|
|
414
|
+
`pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black`,
|
|
415
|
+
].join(',');
|
|
416
|
+
|
|
417
|
+
const audioFilter = `afade=t=in:st=0:d=0.3,afade=t=out:st=${fadeStart}:d=0.5`;
|
|
418
|
+
|
|
419
|
+
runFFmpeg([
|
|
420
|
+
'-ss', String(moment.start), '-i', videoPath, '-t', String(duration),
|
|
421
|
+
'-vf', videoFilter,
|
|
422
|
+
'-af', audioFilter,
|
|
423
|
+
'-c:v', 'libx264', '-preset', 'medium', '-crf', '23',
|
|
424
|
+
'-c:a', 'aac', '-b:a', '128k',
|
|
425
|
+
'-movflags', '+faststart',
|
|
426
|
+
clipPath, '-y',
|
|
427
|
+
]);
|
|
428
|
+
|
|
429
|
+
clipPaths.push(clipPath);
|
|
430
|
+
|
|
431
|
+
// Clean up temp SRT
|
|
432
|
+
await fs.unlink(srtPath).catch(() => {});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return clipPaths;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Generate an SRT subtitle file from a key moment.
|
|
440
|
+
* Splits quote into ~10-word chunks for readable captions.
|
|
441
|
+
*/
|
|
442
|
+
private async generateSRT(srtPath: string, moment: KeyMoment): Promise<void> {
|
|
443
|
+
const words = moment.quote.split(' ');
|
|
444
|
+
const chunkSize = 10;
|
|
445
|
+
const chunks: string[] = [];
|
|
446
|
+
|
|
447
|
+
for (let i = 0; i < words.length; i += chunkSize) {
|
|
448
|
+
chunks.push(words.slice(i, i + chunkSize).join(' '));
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const duration = moment.end - moment.start;
|
|
452
|
+
const chunkDuration = duration / chunks.length;
|
|
453
|
+
|
|
454
|
+
let srt = '';
|
|
455
|
+
chunks.forEach((chunk, idx) => {
|
|
456
|
+
const start = chunkDuration * idx;
|
|
457
|
+
const end = chunkDuration * (idx + 1);
|
|
458
|
+
srt += `${idx + 1}\n`;
|
|
459
|
+
srt += `${this.formatSRTTime(start)} --> ${this.formatSRTTime(end)}\n`;
|
|
460
|
+
srt += `${chunk}\n\n`;
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
await fs.writeFile(srtPath, srt, 'utf-8');
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// ─── Step 4b: Generate Audiograms ────────────────────────────────────────
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Create audiogram videos: waveform visualization + quote text overlay
|
|
470
|
+
* on a branded background. Perfect for podcast clips on social media.
|
|
471
|
+
*/
|
|
472
|
+
async generateAudiograms(
|
|
473
|
+
audioPath: string,
|
|
474
|
+
quotes: KeyMoment[]
|
|
475
|
+
): Promise<string[]> {
|
|
476
|
+
const audiogramDir = path.join(this.config.outputDir, 'audiograms');
|
|
477
|
+
await fs.mkdir(audiogramDir, { recursive: true });
|
|
478
|
+
|
|
479
|
+
const audiogramPaths: string[] = [];
|
|
480
|
+
|
|
481
|
+
for (let i = 0; i < quotes.length; i++) {
|
|
482
|
+
const quote = quotes[i];
|
|
483
|
+
const outputPath = path.join(audiogramDir, `audiogram_${i + 1}.mp4`);
|
|
484
|
+
const bgImagePath = path.join(audiogramDir, `bg_${i + 1}.png`);
|
|
485
|
+
|
|
486
|
+
// Create branded background image with quote text
|
|
487
|
+
await this.createAudiogramBackground(bgImagePath, quote.quote, 1080, 1080);
|
|
488
|
+
|
|
489
|
+
const duration = quote.end - quote.start;
|
|
490
|
+
|
|
491
|
+
// FFmpeg: audio segment + waveform visualization + background image
|
|
492
|
+
const filterComplex = [
|
|
493
|
+
`[0:a]showwaves=s=1080x200:mode=cline:rate=30:colors=${this.config.accentColor}[waves]`,
|
|
494
|
+
`[1:v]scale=1080:1080[bg]`,
|
|
495
|
+
`[bg][waves]overlay=0:750[v]`,
|
|
496
|
+
].join(';');
|
|
497
|
+
|
|
498
|
+
runFFmpeg([
|
|
499
|
+
'-ss', String(quote.start), '-t', String(duration), '-i', audioPath,
|
|
500
|
+
'-loop', '1', '-i', bgImagePath,
|
|
501
|
+
'-filter_complex', filterComplex,
|
|
502
|
+
'-map', '[v]', '-map', '0:a',
|
|
503
|
+
'-c:v', 'libx264', '-preset', 'medium', '-crf', '23', '-tune', 'stillimage',
|
|
504
|
+
'-c:a', 'aac', '-b:a', '192k',
|
|
505
|
+
'-shortest', '-movflags', '+faststart',
|
|
506
|
+
'-t', String(duration),
|
|
507
|
+
outputPath, '-y',
|
|
508
|
+
]);
|
|
509
|
+
|
|
510
|
+
audiogramPaths.push(outputPath);
|
|
511
|
+
|
|
512
|
+
// Clean up temp background
|
|
513
|
+
await fs.unlink(bgImagePath).catch(() => {});
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return audiogramPaths;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Create a branded background image for audiogram with quote text overlay.
|
|
521
|
+
*/
|
|
522
|
+
private async createAudiogramBackground(
|
|
523
|
+
outputPath: string,
|
|
524
|
+
quoteText: string,
|
|
525
|
+
width: number,
|
|
526
|
+
height: number
|
|
527
|
+
): Promise<void> {
|
|
528
|
+
// Create gradient background
|
|
529
|
+
const svgBackground = `
|
|
530
|
+
<svg width="${width}" height="${height}">
|
|
531
|
+
<defs>
|
|
532
|
+
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
533
|
+
<stop offset="0%" style="stop-color:${this.config.brandColor};stop-opacity:1" />
|
|
534
|
+
<stop offset="100%" style="stop-color:#16213e;stop-opacity:1" />
|
|
535
|
+
</linearGradient>
|
|
536
|
+
</defs>
|
|
537
|
+
<rect width="${width}" height="${height}" fill="url(#grad)" />
|
|
538
|
+
</svg>`;
|
|
539
|
+
|
|
540
|
+
// Word wrap the quote text for SVG overlay
|
|
541
|
+
const maxCharsPerLine = 35;
|
|
542
|
+
const lines = this.wordWrap(quoteText, maxCharsPerLine);
|
|
543
|
+
const lineHeight = 48;
|
|
544
|
+
const startY = 200;
|
|
545
|
+
|
|
546
|
+
const textSvg = `
|
|
547
|
+
<svg width="${width}" height="${height}">
|
|
548
|
+
<style>
|
|
549
|
+
.quote { fill: white; font-size: 36px; font-family: Georgia, serif; text-anchor: middle; }
|
|
550
|
+
.attribution { fill: ${this.config.accentColor}; font-size: 20px; font-family: Arial, sans-serif; text-anchor: middle; }
|
|
551
|
+
.open-quote { fill: ${this.config.accentColor}; font-size: 120px; font-family: Georgia, serif; opacity: 0.6; }
|
|
552
|
+
</style>
|
|
553
|
+
<text class="open-quote" x="80" y="180">"</text>
|
|
554
|
+
${lines.map((line, idx) =>
|
|
555
|
+
`<text class="quote" x="${width / 2}" y="${startY + idx * lineHeight}">${this.escapeXml(line)}</text>`
|
|
556
|
+
).join('\n')}
|
|
557
|
+
<text class="attribution" x="${width / 2}" y="${startY + lines.length * lineHeight + 40}">
|
|
558
|
+
-- ${this.config.churchName}
|
|
559
|
+
</text>
|
|
560
|
+
</svg>`;
|
|
561
|
+
|
|
562
|
+
await sharp(Buffer.from(svgBackground))
|
|
563
|
+
.composite([{
|
|
564
|
+
input: Buffer.from(textSvg),
|
|
565
|
+
top: 0,
|
|
566
|
+
left: 0,
|
|
567
|
+
}])
|
|
568
|
+
.png()
|
|
569
|
+
.toFile(outputPath);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// ─── Step 4c: Generate Quote Cards ───────────────────────────────────────
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Generate shareable quote card images using Sharp.
|
|
576
|
+
* Creates multiple sizes: 1080x1080 (Instagram), 1200x628 (Twitter/FB).
|
|
577
|
+
*/
|
|
578
|
+
async generateQuoteCards(quotes: KeyMoment[]): Promise<string[]> {
|
|
579
|
+
const cardDir = path.join(this.config.outputDir, 'quote_cards');
|
|
580
|
+
await fs.mkdir(cardDir, { recursive: true });
|
|
581
|
+
|
|
582
|
+
const cardPaths: string[] = [];
|
|
583
|
+
|
|
584
|
+
const sizes = [
|
|
585
|
+
{ name: 'instagram', width: 1080, height: 1080 },
|
|
586
|
+
{ name: 'twitter', width: 1200, height: 628 },
|
|
587
|
+
];
|
|
588
|
+
|
|
589
|
+
for (let i = 0; i < quotes.length; i++) {
|
|
590
|
+
for (const size of sizes) {
|
|
591
|
+
const outputPath = path.join(cardDir, `quote_${i + 1}_${size.name}.png`);
|
|
592
|
+
await this.createQuoteCard(outputPath, quotes[i], size.width, size.height);
|
|
593
|
+
cardPaths.push(outputPath);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return cardPaths;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Create a single quote card with branded background, text overlay,
|
|
602
|
+
* and optional logo watermark.
|
|
603
|
+
*/
|
|
604
|
+
private async createQuoteCard(
|
|
605
|
+
outputPath: string,
|
|
606
|
+
moment: KeyMoment,
|
|
607
|
+
width: number,
|
|
608
|
+
height: number
|
|
609
|
+
): Promise<void> {
|
|
610
|
+
// Create gradient background
|
|
611
|
+
const svgBg = `
|
|
612
|
+
<svg width="${width}" height="${height}">
|
|
613
|
+
<defs>
|
|
614
|
+
<linearGradient id="cardGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
615
|
+
<stop offset="0%" style="stop-color:${this.config.brandColor}" />
|
|
616
|
+
<stop offset="50%" style="stop-color:#0f3460" />
|
|
617
|
+
<stop offset="100%" style="stop-color:${this.config.brandColor}" />
|
|
618
|
+
</linearGradient>
|
|
619
|
+
</defs>
|
|
620
|
+
<rect width="${width}" height="${height}" fill="url(#cardGrad)" />
|
|
621
|
+
<!-- Decorative accent line -->
|
|
622
|
+
<rect x="${width * 0.1}" y="${height * 0.12}" width="60" height="4" fill="${this.config.accentColor}" rx="2" />
|
|
623
|
+
</svg>`;
|
|
624
|
+
|
|
625
|
+
// Calculate font size based on quote length and card dimensions
|
|
626
|
+
const fontSize = moment.quote.length > 150 ? 28 : moment.quote.length > 80 ? 34 : 42;
|
|
627
|
+
const maxChars = Math.floor((width * 0.8) / (fontSize * 0.55));
|
|
628
|
+
const lines = this.wordWrap(moment.quote, maxChars);
|
|
629
|
+
const lineHeight = fontSize * 1.4;
|
|
630
|
+
const textBlockHeight = lines.length * lineHeight;
|
|
631
|
+
const startY = (height - textBlockHeight) / 2;
|
|
632
|
+
|
|
633
|
+
const textSvg = `
|
|
634
|
+
<svg width="${width}" height="${height}">
|
|
635
|
+
<style>
|
|
636
|
+
.q { fill: #ffffff; font-size: ${fontSize}px; font-family: Georgia, 'Times New Roman', serif; text-anchor: middle; }
|
|
637
|
+
.attr { fill: ${this.config.accentColor}; font-size: ${Math.floor(fontSize * 0.5)}px; font-family: Arial, Helvetica, sans-serif; text-anchor: middle; letter-spacing: 2px; }
|
|
638
|
+
.deco { fill: ${this.config.accentColor}; font-size: ${fontSize * 2.5}px; font-family: Georgia, serif; opacity: 0.4; }
|
|
639
|
+
</style>
|
|
640
|
+
<text class="deco" x="${width * 0.08}" y="${startY - 10}">\u201C</text>
|
|
641
|
+
${lines.map((line, idx) =>
|
|
642
|
+
`<text class="q" x="${width / 2}" y="${startY + idx * lineHeight}">${this.escapeXml(line)}</text>`
|
|
643
|
+
).join('\n')}
|
|
644
|
+
<text class="deco" x="${width * 0.85}" y="${startY + textBlockHeight + 20}">\u201D</text>
|
|
645
|
+
<text class="attr" x="${width / 2}" y="${height * 0.88}">
|
|
646
|
+
${this.config.churchName?.toUpperCase() ?? ''}
|
|
647
|
+
</text>
|
|
648
|
+
</svg>`;
|
|
649
|
+
|
|
650
|
+
const composites: sharp.OverlayOptions[] = [
|
|
651
|
+
{ input: Buffer.from(textSvg), top: 0, left: 0 },
|
|
652
|
+
];
|
|
653
|
+
|
|
654
|
+
// Add logo watermark if provided
|
|
655
|
+
if (this.config.brandingLogo) {
|
|
656
|
+
try {
|
|
657
|
+
const logoBuffer = await sharp(this.config.brandingLogo)
|
|
658
|
+
.resize(120, 120, { fit: 'inside' })
|
|
659
|
+
.png()
|
|
660
|
+
.toBuffer();
|
|
661
|
+
|
|
662
|
+
composites.push({
|
|
663
|
+
input: logoBuffer,
|
|
664
|
+
gravity: 'southeast',
|
|
665
|
+
});
|
|
666
|
+
} catch {
|
|
667
|
+
// Logo not found — skip silently
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
await sharp(Buffer.from(svgBg))
|
|
672
|
+
.composite(composites)
|
|
673
|
+
.png({ quality: 95 })
|
|
674
|
+
.toFile(outputPath);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// ─── Step 4d: Generate Social Media Posts ────────────────────────────────
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Generate platform-specific social media posts from analysis.
|
|
681
|
+
*/
|
|
682
|
+
generateSocialPosts(
|
|
683
|
+
analysis: ContentAnalysis
|
|
684
|
+
): { twitter: string[]; instagram: string[]; facebook: string[] } {
|
|
685
|
+
const hashtags = analysis.hashtags.map(h => `#${h}`).join(' ');
|
|
686
|
+
|
|
687
|
+
const twitter = analysis.keyMoments.map((m) => {
|
|
688
|
+
// Twitter: 280 char limit, concise, with link placeholder
|
|
689
|
+
const caption = m.socialCaption.length > 230
|
|
690
|
+
? m.socialCaption.substring(0, 227) + '...'
|
|
691
|
+
: m.socialCaption;
|
|
692
|
+
return `${caption}\n\n${hashtags.substring(0, 50)}\n\n[Link to full message]`;
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
const instagram = analysis.keyMoments.map((m) => {
|
|
696
|
+
// Instagram: longer captions OK, heavy on hashtags
|
|
697
|
+
return [
|
|
698
|
+
m.socialCaption,
|
|
699
|
+
'',
|
|
700
|
+
`"${m.quote.substring(0, 200)}${m.quote.length > 200 ? '...' : ''}"`,
|
|
701
|
+
'',
|
|
702
|
+
`Watch the full message -- link in bio`,
|
|
703
|
+
'',
|
|
704
|
+
hashtags,
|
|
705
|
+
'#sermon #faith #church #sundayservice #worship #bibleverse',
|
|
706
|
+
].join('\n');
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
const facebook = analysis.keyMoments.map((m) => {
|
|
710
|
+
// Facebook: conversational, encourage sharing
|
|
711
|
+
return [
|
|
712
|
+
m.socialCaption,
|
|
713
|
+
'',
|
|
714
|
+
`"${m.quote}"`,
|
|
715
|
+
'',
|
|
716
|
+
`Share this with someone who needs to hear it today.`,
|
|
717
|
+
'',
|
|
718
|
+
`Watch the full message: [Link]`,
|
|
719
|
+
'',
|
|
720
|
+
hashtags,
|
|
721
|
+
].join('\n');
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
return { twitter, instagram, facebook };
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// ─── Step 4e: Generate YouTube Chapters ──────────────────────────────────
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Generate YouTube chapter timestamps from topic segments.
|
|
731
|
+
* Format: "0:00 Introduction\n2:15 First Point\n..."
|
|
732
|
+
*/
|
|
733
|
+
generateYouTubeChapters(segments: TopicSegment[]): string {
|
|
734
|
+
return segments
|
|
735
|
+
.map(seg => `${this.formatTimestamp(seg.start)} ${seg.title}`)
|
|
736
|
+
.join('\n');
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// ─── Step 4f: Generate GIFs from Key Moments ────────────────────────────
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Create animated GIFs from key moments (useful for Twitter/Discord).
|
|
743
|
+
* Limited to 8 seconds max to keep file size reasonable.
|
|
744
|
+
*/
|
|
745
|
+
async generateGifs(
|
|
746
|
+
videoPath: string,
|
|
747
|
+
moments: KeyMoment[]
|
|
748
|
+
): Promise<string[]> {
|
|
749
|
+
const gifDir = path.join(this.config.outputDir, 'gifs');
|
|
750
|
+
await fs.mkdir(gifDir, { recursive: true });
|
|
751
|
+
|
|
752
|
+
const gifPaths: string[] = [];
|
|
753
|
+
|
|
754
|
+
for (let i = 0; i < Math.min(moments.length, 5); i++) {
|
|
755
|
+
const moment = moments[i];
|
|
756
|
+
const gifPath = path.join(gifDir, `moment_${i + 1}.gif`);
|
|
757
|
+
const palettePath = path.join(gifDir, `palette_${i}.png`);
|
|
758
|
+
const duration = Math.min(moment.end - moment.start, 8); // Cap at 8 seconds
|
|
759
|
+
|
|
760
|
+
// Pass 1: Generate optimized palette
|
|
761
|
+
runFFmpeg([
|
|
762
|
+
'-ss', String(moment.start), '-t', String(duration), '-i', videoPath,
|
|
763
|
+
'-vf', 'fps=12,scale=480:-1:flags=lanczos,palettegen=stats_mode=diff',
|
|
764
|
+
'-y', palettePath,
|
|
765
|
+
]);
|
|
766
|
+
|
|
767
|
+
// Pass 2: Create GIF using palette for better quality
|
|
768
|
+
runFFmpeg([
|
|
769
|
+
'-ss', String(moment.start), '-t', String(duration), '-i', videoPath,
|
|
770
|
+
'-i', palettePath,
|
|
771
|
+
'-lavfi', 'fps=12,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=3',
|
|
772
|
+
gifPath, '-y',
|
|
773
|
+
]);
|
|
774
|
+
|
|
775
|
+
// Clean up palette
|
|
776
|
+
await fs.unlink(palettePath).catch(() => {});
|
|
777
|
+
gifPaths.push(gifPath);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
return gifPaths;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// ─── Full Pipeline Orchestrator ──────────────────────────────────────────
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Run the complete pipeline end-to-end.
|
|
787
|
+
* Returns paths to all generated assets.
|
|
788
|
+
*/
|
|
789
|
+
async run(inputPath: string): Promise<{
|
|
790
|
+
clips: string[];
|
|
791
|
+
audiograms: string[];
|
|
792
|
+
quoteCards: string[];
|
|
793
|
+
gifs: string[];
|
|
794
|
+
socialPosts: { twitter: string[]; instagram: string[]; facebook: string[] };
|
|
795
|
+
youtubeChapters: string;
|
|
796
|
+
blogDraft: string;
|
|
797
|
+
analysis: ContentAnalysis;
|
|
798
|
+
}> {
|
|
799
|
+
console.log('Step 1/6: Extracting audio...');
|
|
800
|
+
const { wavPath, aacPath } = await this.extractAudio(inputPath);
|
|
801
|
+
|
|
802
|
+
console.log('Step 2/6: Transcribing...');
|
|
803
|
+
const transcript = await this.transcribe(wavPath);
|
|
804
|
+
|
|
805
|
+
console.log('Step 3/6: Analyzing content with AI...');
|
|
806
|
+
const analysis = await this.analyzeContent(transcript);
|
|
807
|
+
|
|
808
|
+
console.log('Step 4/6: Generating video clips...');
|
|
809
|
+
const clips = await this.generateClips(inputPath, analysis.keyMoments);
|
|
810
|
+
|
|
811
|
+
console.log('Step 5/6: Generating audiograms and quote cards...');
|
|
812
|
+
const [audiograms, quoteCards, gifs] = await Promise.all([
|
|
813
|
+
this.generateAudiograms(aacPath, analysis.keyMoments),
|
|
814
|
+
this.generateQuoteCards(analysis.keyMoments),
|
|
815
|
+
this.generateGifs(inputPath, analysis.keyMoments),
|
|
816
|
+
]);
|
|
817
|
+
|
|
818
|
+
console.log('Step 6/6: Generating text content...');
|
|
819
|
+
const socialPosts = this.generateSocialPosts(analysis);
|
|
820
|
+
const youtubeChapters = this.generateYouTubeChapters(analysis.topicSegments);
|
|
821
|
+
|
|
822
|
+
// Write text outputs to files
|
|
823
|
+
await fs.writeFile(
|
|
824
|
+
path.join(this.config.outputDir, 'youtube_chapters.txt'),
|
|
825
|
+
youtubeChapters, 'utf-8'
|
|
826
|
+
);
|
|
827
|
+
await fs.writeFile(
|
|
828
|
+
path.join(this.config.outputDir, 'blog_draft.md'),
|
|
829
|
+
analysis.blogDraft, 'utf-8'
|
|
830
|
+
);
|
|
831
|
+
await fs.writeFile(
|
|
832
|
+
path.join(this.config.outputDir, 'social_posts.json'),
|
|
833
|
+
JSON.stringify(socialPosts, null, 2), 'utf-8'
|
|
834
|
+
);
|
|
835
|
+
|
|
836
|
+
console.log(`\nDone! All assets saved to: ${this.config.outputDir}`);
|
|
837
|
+
console.log(` - ${clips.length} video clips`);
|
|
838
|
+
console.log(` - ${audiograms.length} audiograms`);
|
|
839
|
+
console.log(` - ${quoteCards.length} quote cards`);
|
|
840
|
+
console.log(` - ${gifs.length} GIFs`);
|
|
841
|
+
console.log(` - Social posts for 3 platforms`);
|
|
842
|
+
console.log(` - YouTube chapters`);
|
|
843
|
+
console.log(` - Blog draft`);
|
|
844
|
+
|
|
845
|
+
return {
|
|
846
|
+
clips,
|
|
847
|
+
audiograms,
|
|
848
|
+
quoteCards,
|
|
849
|
+
gifs,
|
|
850
|
+
socialPosts,
|
|
851
|
+
youtubeChapters,
|
|
852
|
+
blogDraft: analysis.blogDraft,
|
|
853
|
+
analysis,
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// ─── Utility Methods ────────────────────────────────────────────────────
|
|
858
|
+
|
|
859
|
+
private formatTimestamp(seconds: number): string {
|
|
860
|
+
const h = Math.floor(seconds / 3600);
|
|
861
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
862
|
+
const s = Math.floor(seconds % 60);
|
|
863
|
+
if (h > 0) return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
|
|
864
|
+
return `${m}:${s.toString().padStart(2, '0')}`;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
private formatSRTTime(seconds: number): string {
|
|
868
|
+
const h = Math.floor(seconds / 3600);
|
|
869
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
870
|
+
const s = Math.floor(seconds % 60);
|
|
871
|
+
const ms = Math.round((seconds % 1) * 1000);
|
|
872
|
+
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')},${ms.toString().padStart(3, '0')}`;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
private wordWrap(text: string, maxChars: number): string[] {
|
|
876
|
+
const words = text.split(' ');
|
|
877
|
+
const lines: string[] = [];
|
|
878
|
+
let currentLine = '';
|
|
879
|
+
|
|
880
|
+
for (const word of words) {
|
|
881
|
+
if ((currentLine + ' ' + word).trim().length > maxChars) {
|
|
882
|
+
if (currentLine) lines.push(currentLine.trim());
|
|
883
|
+
currentLine = word;
|
|
884
|
+
} else {
|
|
885
|
+
currentLine += ' ' + word;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (currentLine.trim()) lines.push(currentLine.trim());
|
|
889
|
+
return lines;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
private escapeXml(text: string): string {
|
|
893
|
+
return text
|
|
894
|
+
.replace(/&/g, '&')
|
|
895
|
+
.replace(/</g, '<')
|
|
896
|
+
.replace(/>/g, '>')
|
|
897
|
+
.replace(/"/g, '"')
|
|
898
|
+
.replace(/'/g, ''');
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
---
|
|
904
|
+
|
|
905
|
+
## FFmpeg Command Reference
|
|
906
|
+
|
|
907
|
+
### Clip Extraction with Fade In/Out
|
|
908
|
+
|
|
909
|
+
```bash
|
|
910
|
+
# Extract 45-second clip starting at 12:30, with audio/video fades
|
|
911
|
+
ffmpeg -ss 750 -i input.mp4 -t 45 \
|
|
912
|
+
-vf "fade=t=in:st=0:d=0.5,fade=t=out:st=44.5:d=0.5" \
|
|
913
|
+
-af "afade=t=in:st=0:d=0.3,afade=t=out:st=44.5:d=0.5" \
|
|
914
|
+
-c:v libx264 -preset medium -crf 23 \
|
|
915
|
+
-c:a aac -b:a 128k \
|
|
916
|
+
-movflags +faststart \
|
|
917
|
+
clip_output.mp4
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
### Caption Burning (SRT Subtitle Overlay)
|
|
921
|
+
|
|
922
|
+
```bash
|
|
923
|
+
# Burn subtitles into video with styled text
|
|
924
|
+
ffmpeg -i input.mp4 \
|
|
925
|
+
-vf "subtitles='captions.srt':force_style='FontSize=22,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=2,Shadow=1,MarginV=40'" \
|
|
926
|
+
-c:v libx264 -crf 23 \
|
|
927
|
+
-c:a copy \
|
|
928
|
+
output_with_captions.mp4
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
### Audiogram (Audio + Waveform + Static Image)
|
|
932
|
+
|
|
933
|
+
```bash
|
|
934
|
+
# Create audiogram: background image + waveform visualization
|
|
935
|
+
ffmpeg -ss 120 -t 45 -i audio.aac \
|
|
936
|
+
-loop 1 -i background.png \
|
|
937
|
+
-filter_complex \
|
|
938
|
+
"[0:a]showwaves=s=1080x200:mode=cline:rate=30:colors=#e94560[waves]; \
|
|
939
|
+
[1:v]scale=1080:1080[bg]; \
|
|
940
|
+
[bg][waves]overlay=0:750[v]" \
|
|
941
|
+
-map "[v]" -map 0:a \
|
|
942
|
+
-c:v libx264 -crf 23 -tune stillimage \
|
|
943
|
+
-c:a aac -b:a 192k \
|
|
944
|
+
-shortest -movflags +faststart \
|
|
945
|
+
audiogram.mp4
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
### GIF Creation (Two-Pass with Palette)
|
|
949
|
+
|
|
950
|
+
```bash
|
|
951
|
+
# Pass 1: Generate optimized palette
|
|
952
|
+
ffmpeg -ss 30 -t 6 -i input.mp4 \
|
|
953
|
+
-vf "fps=12,scale=480:-1:flags=lanczos,palettegen=stats_mode=diff" \
|
|
954
|
+
palette.png
|
|
955
|
+
|
|
956
|
+
# Pass 2: Create GIF using palette for better quality/smaller size
|
|
957
|
+
ffmpeg -ss 30 -t 6 -i input.mp4 -i palette.png \
|
|
958
|
+
-lavfi "fps=12,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=3" \
|
|
959
|
+
output.gif
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
### Vertical Video (9:16) for Reels/Shorts/TikTok
|
|
963
|
+
|
|
964
|
+
```bash
|
|
965
|
+
# Crop/pad landscape to 9:16 vertical with letterbox
|
|
966
|
+
ffmpeg -i input.mp4 \
|
|
967
|
+
-vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black" \
|
|
968
|
+
-c:v libx264 -crf 23 \
|
|
969
|
+
vertical_output.mp4
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
---
|
|
973
|
+
|
|
974
|
+
## Sharp Command Reference
|
|
975
|
+
|
|
976
|
+
### Quote Card with Gradient Background
|
|
977
|
+
|
|
978
|
+
```typescript
|
|
979
|
+
import sharp from 'sharp';
|
|
980
|
+
|
|
981
|
+
// Create gradient background via SVG
|
|
982
|
+
const svgBg = `
|
|
983
|
+
<svg width="1080" height="1080">
|
|
984
|
+
<defs>
|
|
985
|
+
<linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
986
|
+
<stop offset="0%" style="stop-color:#1a1a2e" />
|
|
987
|
+
<stop offset="100%" style="stop-color:#16213e" />
|
|
988
|
+
</linearGradient>
|
|
989
|
+
</defs>
|
|
990
|
+
<rect width="1080" height="1080" fill="url(#g)" />
|
|
991
|
+
</svg>`;
|
|
992
|
+
|
|
993
|
+
// Text overlay via SVG composite
|
|
994
|
+
const textSvg = `
|
|
995
|
+
<svg width="1080" height="1080">
|
|
996
|
+
<text x="540" y="480" text-anchor="middle" fill="white"
|
|
997
|
+
font-size="38" font-family="Georgia">Your quote here</text>
|
|
998
|
+
</svg>`;
|
|
999
|
+
|
|
1000
|
+
await sharp(Buffer.from(svgBg))
|
|
1001
|
+
.composite([
|
|
1002
|
+
{ input: Buffer.from(textSvg), top: 0, left: 0 },
|
|
1003
|
+
// Logo watermark (bottom-right)
|
|
1004
|
+
{ input: 'logo.png', gravity: 'southeast' },
|
|
1005
|
+
])
|
|
1006
|
+
.png({ quality: 95 })
|
|
1007
|
+
.toFile('quote_card.png');
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
### Multiple Sizes
|
|
1011
|
+
|
|
1012
|
+
```typescript
|
|
1013
|
+
const sizes = [
|
|
1014
|
+
{ name: 'instagram_square', w: 1080, h: 1080 }, // Instagram post
|
|
1015
|
+
{ name: 'instagram_story', w: 1080, h: 1920 }, // Instagram/FB story
|
|
1016
|
+
{ name: 'twitter_card', w: 1200, h: 628 }, // Twitter/X card
|
|
1017
|
+
{ name: 'facebook_post', w: 1200, h: 630 }, // Facebook post
|
|
1018
|
+
{ name: 'youtube_thumb', w: 1280, h: 720 }, // YouTube thumbnail
|
|
1019
|
+
];
|
|
1020
|
+
|
|
1021
|
+
for (const size of sizes) {
|
|
1022
|
+
await createQuoteCard(`output_${size.name}.png`, quote, size.w, size.h);
|
|
1023
|
+
}
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
---
|
|
1027
|
+
|
|
1028
|
+
## Ministry-Specific Patterns
|
|
1029
|
+
|
|
1030
|
+
### Sunday Sermon to Monday Content Calendar
|
|
1031
|
+
|
|
1032
|
+
```
|
|
1033
|
+
SUNDAY: Record sermon (30-60 min)
|
|
1034
|
+
|
|
|
1035
|
+
SUNDAY NIGHT: Run pipeline (automated, ~5 min processing)
|
|
1036
|
+
|
|
|
1037
|
+
MONDAY: Schedule the week's content
|
|
1038
|
+
- Monday: Blog post draft (edit + publish)
|
|
1039
|
+
- Tuesday: Quote card #1 (Instagram) + clip #1 (YouTube Shorts)
|
|
1040
|
+
- Wednesday: Audiogram #1 (Twitter/X) + Facebook text post
|
|
1041
|
+
- Thursday: Quote card #2 (Instagram) + clip #2 (Reels)
|
|
1042
|
+
- Friday: GIF moment (Twitter/Discord) + behind-the-scenes caption
|
|
1043
|
+
- Saturday: Teaser clip for Sunday's message
|
|
1044
|
+
- Sunday: YouTube upload with chapters + full social push
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
### Bible Study to Lesson Outline + Quiz
|
|
1048
|
+
|
|
1049
|
+
```typescript
|
|
1050
|
+
// Extended analysis prompt for Bible study content
|
|
1051
|
+
const bibleStudyPrompt = `
|
|
1052
|
+
Additionally extract:
|
|
1053
|
+
"lessonOutline": {
|
|
1054
|
+
"passage": "Book Chapter:Verse-Verse",
|
|
1055
|
+
"context": "Historical and literary context",
|
|
1056
|
+
"keyTerms": [{ "term": "Greek/Hebrew word", "meaning": "definition", "usage": "how it's used here" }],
|
|
1057
|
+
"mainPoints": ["Point 1", "Point 2", "Point 3"],
|
|
1058
|
+
"applicationQuestions": ["How does this apply to...?"],
|
|
1059
|
+
"crossReferences": ["Related passage 1", "Related passage 2"]
|
|
1060
|
+
},
|
|
1061
|
+
"quizQuestions": [
|
|
1062
|
+
{
|
|
1063
|
+
"question": "Multiple choice or fill-in-the-blank",
|
|
1064
|
+
"options": ["A", "B", "C", "D"],
|
|
1065
|
+
"correctAnswer": "B",
|
|
1066
|
+
"explanation": "Why this is correct with scripture reference"
|
|
1067
|
+
}
|
|
1068
|
+
]
|
|
1069
|
+
`;
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
### Prophetic Word to Sharable Quote Cards
|
|
1073
|
+
|
|
1074
|
+
```typescript
|
|
1075
|
+
// Prophetic content gets special visual treatment
|
|
1076
|
+
const propheticConfig = {
|
|
1077
|
+
// Use bold, dramatic styling for prophetic declarations
|
|
1078
|
+
fontSizeMultiplier: 1.3,
|
|
1079
|
+
accentColor: '#FFD700', // Gold for prophetic authority
|
|
1080
|
+
backgroundStyle: 'dramatic', // Dark with light rays
|
|
1081
|
+
prefixText: 'Thus says the Lord:',
|
|
1082
|
+
// Always include scripture backing
|
|
1083
|
+
requireScriptureReference: true,
|
|
1084
|
+
};
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
---
|
|
1088
|
+
|
|
1089
|
+
## Usage Example
|
|
1090
|
+
|
|
1091
|
+
```typescript
|
|
1092
|
+
const pipeline = new ContentRepurposingPipeline({
|
|
1093
|
+
outputDir: './output/sunday-sermon-2026-03-09',
|
|
1094
|
+
brandingLogo: './assets/church-logo.png',
|
|
1095
|
+
brandColor: '#1a1a2e',
|
|
1096
|
+
accentColor: '#e94560',
|
|
1097
|
+
churchName: 'Living Word Fellowship',
|
|
1098
|
+
aiProvider: 'claude',
|
|
1099
|
+
transcriptionProvider: 'deepgram',
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
const results = await pipeline.run('./recordings/sermon-2026-03-09.mp4');
|
|
1103
|
+
|
|
1104
|
+
console.log('YouTube Chapters:\n', results.youtubeChapters);
|
|
1105
|
+
console.log('Blog Draft saved to: output/sunday-sermon-2026-03-09/blog_draft.md');
|
|
1106
|
+
console.log(`Generated ${results.clips.length} clips, ${results.quoteCards.length} cards`);
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
---
|
|
1110
|
+
|
|
1111
|
+
## Prerequisites
|
|
1112
|
+
|
|
1113
|
+
```bash
|
|
1114
|
+
# FFmpeg (required)
|
|
1115
|
+
# Windows: winget install FFmpeg
|
|
1116
|
+
# macOS: brew install ffmpeg
|
|
1117
|
+
# Linux: sudo apt install ffmpeg
|
|
1118
|
+
|
|
1119
|
+
# Whisper (if using local transcription)
|
|
1120
|
+
pip install openai-whisper
|
|
1121
|
+
|
|
1122
|
+
# Node.js dependencies
|
|
1123
|
+
npm install sharp @anthropic-ai/sdk @deepgram/sdk assemblyai @google/generative-ai
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
## Environment Variables
|
|
1127
|
+
|
|
1128
|
+
```bash
|
|
1129
|
+
# Pick ONE transcription provider:
|
|
1130
|
+
DEEPGRAM_API_KEY=your_key_here
|
|
1131
|
+
ASSEMBLYAI_API_KEY=your_key_here
|
|
1132
|
+
# Whisper uses local model — no API key needed
|
|
1133
|
+
|
|
1134
|
+
# Pick ONE AI provider:
|
|
1135
|
+
ANTHROPIC_API_KEY=your_key_here
|
|
1136
|
+
GEMINI_API_KEY=your_key_here
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
---
|
|
1140
|
+
---
|
|
1141
|
+
|
|
1142
|
+
## Related Skills
|
|
1143
|
+
|
|
1144
|
+
- `creative-multimedia/ffmpeg-command-generator.md` -- Standalone FFmpeg command patterns
|
|
1145
|
+
- `ecommerce/stripe-integration-verification.md` -- For monetizing content (donations, subscriptions)
|
|
1146
|
+
- `methodology/AUTO_REVIEWER_SUBAGENT.md` -- Auto-review generated content quality
|