@gannochenko/staticstripes 0.0.11 → 0.0.12

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.
Files changed (83) hide show
  1. package/Makefile +17 -4
  2. package/dist/cli/commands/add-assets.d.ts +3 -0
  3. package/dist/cli/commands/add-assets.d.ts.map +1 -0
  4. package/dist/cli/commands/add-assets.js +113 -0
  5. package/dist/cli/commands/add-assets.js.map +1 -0
  6. package/dist/cli/commands/bootstrap.d.ts +3 -0
  7. package/dist/cli/commands/bootstrap.d.ts.map +1 -0
  8. package/dist/cli/commands/bootstrap.js +49 -0
  9. package/dist/cli/commands/bootstrap.js.map +1 -0
  10. package/dist/cli/commands/generate.d.ts +3 -0
  11. package/dist/cli/commands/generate.d.ts.map +1 -0
  12. package/dist/cli/commands/generate.js +132 -0
  13. package/dist/cli/commands/generate.js.map +1 -0
  14. package/dist/cli/commands/upload.d.ts +6 -0
  15. package/dist/cli/commands/upload.d.ts.map +1 -0
  16. package/dist/cli/commands/upload.js +67 -0
  17. package/dist/cli/commands/upload.js.map +1 -0
  18. package/dist/cli/s3/s3-upload-strategy.d.ts +18 -0
  19. package/dist/cli/s3/s3-upload-strategy.d.ts.map +1 -0
  20. package/dist/cli/s3/s3-upload-strategy.js +149 -0
  21. package/dist/cli/s3/s3-upload-strategy.js.map +1 -0
  22. package/dist/cli/upload-strategy-factory.d.ts +23 -0
  23. package/dist/cli/upload-strategy-factory.d.ts.map +1 -0
  24. package/dist/cli/upload-strategy-factory.js +49 -0
  25. package/dist/cli/upload-strategy-factory.js.map +1 -0
  26. package/dist/cli/upload-strategy.d.ts +25 -0
  27. package/dist/cli/upload-strategy.d.ts.map +1 -0
  28. package/dist/cli/upload-strategy.js +3 -0
  29. package/dist/cli/upload-strategy.js.map +1 -0
  30. package/dist/cli/youtube/auth-commands.d.ts +3 -0
  31. package/dist/cli/youtube/auth-commands.d.ts.map +1 -0
  32. package/dist/cli/youtube/auth-commands.js +273 -0
  33. package/dist/cli/youtube/auth-commands.js.map +1 -0
  34. package/dist/cli/youtube/cli.d.ts +7 -0
  35. package/dist/cli/youtube/cli.d.ts.map +1 -0
  36. package/dist/cli/youtube/cli.js +13 -0
  37. package/dist/cli/youtube/cli.js.map +1 -0
  38. package/dist/cli/youtube/upload-handler.d.ts +12 -0
  39. package/dist/cli/youtube/upload-handler.d.ts.map +1 -0
  40. package/dist/cli/youtube/upload-handler.js +66 -0
  41. package/dist/cli/youtube/upload-handler.js.map +1 -0
  42. package/dist/cli/youtube/youtube-upload-strategy.d.ts +15 -0
  43. package/dist/cli/youtube/youtube-upload-strategy.d.ts.map +1 -0
  44. package/dist/cli/youtube/youtube-upload-strategy.js +37 -0
  45. package/dist/cli/youtube/youtube-upload-strategy.js.map +1 -0
  46. package/dist/cli.js +12 -281
  47. package/dist/cli.js.map +1 -1
  48. package/dist/html-parser.d.ts +3 -4
  49. package/dist/html-parser.d.ts.map +1 -1
  50. package/dist/html-parser.js +20 -17
  51. package/dist/html-parser.js.map +1 -1
  52. package/dist/html-project-parser.d.ts +24 -0
  53. package/dist/html-project-parser.d.ts.map +1 -1
  54. package/dist/html-project-parser.js +361 -57
  55. package/dist/html-project-parser.js.map +1 -1
  56. package/dist/project.d.ts +15 -2
  57. package/dist/project.d.ts.map +1 -1
  58. package/dist/project.js +57 -1
  59. package/dist/project.js.map +1 -1
  60. package/dist/type.d.ts +26 -4
  61. package/dist/type.d.ts.map +1 -1
  62. package/dist/youtube-uploader.d.ts +40 -0
  63. package/dist/youtube-uploader.d.ts.map +1 -0
  64. package/dist/youtube-uploader.js +227 -0
  65. package/dist/youtube-uploader.js.map +1 -0
  66. package/package.json +6 -2
  67. package/src/cli/commands/add-assets.ts +159 -0
  68. package/src/cli/commands/bootstrap.ts +57 -0
  69. package/src/cli/commands/generate.ts +189 -0
  70. package/src/cli/commands/upload.ts +83 -0
  71. package/src/cli/s3/s3-upload-strategy.ts +194 -0
  72. package/src/cli/upload-strategy-factory.ts +58 -0
  73. package/src/cli/upload-strategy.ts +31 -0
  74. package/src/cli/youtube/auth-commands.ts +312 -0
  75. package/src/cli/youtube/cli.ts +11 -0
  76. package/src/cli/youtube/upload-handler.ts +101 -0
  77. package/src/cli/youtube/youtube-upload-strategy.ts +43 -0
  78. package/src/cli.ts +14 -390
  79. package/src/html-parser.ts +23 -21
  80. package/src/html-project-parser.ts +400 -62
  81. package/src/project.ts +71 -1
  82. package/src/type.ts +30 -4
  83. package/src/youtube-uploader.ts +288 -0
package/Makefile CHANGED
@@ -40,10 +40,6 @@ test:
40
40
  test-ui:
41
41
  npm run test:ui
42
42
 
43
- # Generate demo video (youtube output) with preview preset
44
- demo:
45
- node dist/cli.js generate -p ../../examples/demo -o youtube --option meh --debug
46
-
47
43
  # Generate demo video with production quality
48
44
  demo-prod:
49
45
  node dist/cli.js generate -p ../../examples/demo -o youtube --option production
@@ -67,3 +63,20 @@ clean-cache:
67
63
 
68
64
  # Full build
69
65
  all: install build
66
+
67
+ # Generate demo video (youtube output) with preview preset
68
+ demo:
69
+ make build
70
+ node dist/cli.js generate -p ../../examples/demo -o youtube --option meh --debug
71
+
72
+ demoauth:
73
+ make build
74
+ node dist/cli.js auth -p ../../examples/demo --upload-name yt_primary
75
+
76
+ demoupload:
77
+ make build
78
+ node dist/cli.js upload -p ../../examples/demo --upload-name yt_primary
79
+
80
+ demouploads3:
81
+ make build
82
+ node dist/cli.js upload -p ../../examples/demo --upload-name s3_primary
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerAddAssetsCommand(program: Command, handleError: (error: any, operation: string) => void): void;
3
+ //# sourceMappingURL=add-assets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-assets.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/add-assets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,GACnD,IAAI,CAiJN"}
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerAddAssetsCommand = registerAddAssetsCommand;
4
+ const path_1 = require("path");
5
+ const fs_1 = require("fs");
6
+ function registerAddAssetsCommand(program, handleError) {
7
+ program
8
+ .command('add-assets')
9
+ .description('Scan for media files and add them as assets to project.html')
10
+ .option('-p, --project <path>', 'Path to project directory', '.')
11
+ .action((options) => {
12
+ try {
13
+ // Resolve project path
14
+ const projectPath = (0, path_1.resolve)(process.cwd(), options.project);
15
+ const projectFilePath = (0, path_1.resolve)(projectPath, 'project.html');
16
+ // Validate project.html exists
17
+ if (!(0, fs_1.existsSync)(projectFilePath)) {
18
+ console.error(`Error: project.html not found in ${projectPath}`);
19
+ process.exit(1);
20
+ }
21
+ console.log(`📁 Project: ${projectPath}`);
22
+ console.log(`📄 Scanning for media files...\n`);
23
+ // Find all media files recursively
24
+ const mediaFiles = [];
25
+ const scanDirectory = (dir) => {
26
+ const entries = (0, fs_1.readdirSync)(dir);
27
+ for (const entry of entries) {
28
+ const fullPath = (0, path_1.join)(dir, entry);
29
+ const stat = (0, fs_1.statSync)(fullPath);
30
+ if (stat.isDirectory()) {
31
+ scanDirectory(fullPath);
32
+ }
33
+ else {
34
+ const ext = entry.toLowerCase().split('.').pop();
35
+ let type = null;
36
+ if (ext === 'mp4') {
37
+ type = 'video';
38
+ }
39
+ else if (ext === 'mp3') {
40
+ type = 'audio';
41
+ }
42
+ else if (ext === 'jpg' || ext === 'png') {
43
+ type = 'image';
44
+ }
45
+ if (type) {
46
+ const relativePath = (0, path_1.relative)(projectPath, fullPath);
47
+ mediaFiles.push({ path: fullPath, relativePath, type });
48
+ }
49
+ }
50
+ }
51
+ };
52
+ scanDirectory(projectPath);
53
+ // Sort by relative path (name)
54
+ mediaFiles.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
55
+ // Group by type and assign names
56
+ const videos = mediaFiles.filter((f) => f.type === 'video');
57
+ const audios = mediaFiles.filter((f) => f.type === 'audio');
58
+ const images = mediaFiles.filter((f) => f.type === 'image');
59
+ console.log(`Found ${videos.length} video(s), ${audios.length} audio(s), ${images.length} image(s)\n`);
60
+ if (mediaFiles.length === 0) {
61
+ console.log('No media files found.');
62
+ process.exit(0);
63
+ }
64
+ // Generate asset tags
65
+ const assetTags = [];
66
+ videos.forEach((file, index) => {
67
+ const name = `clip_${index + 1}`;
68
+ assetTags.push(` <asset data-name="${name}" data-path="./${file.relativePath}" />`);
69
+ console.log(`${name}: ${file.relativePath}`);
70
+ });
71
+ audios.forEach((file, index) => {
72
+ const name = `track_${index + 1}`;
73
+ assetTags.push(` <asset data-name="${name}" data-path="./${file.relativePath}" />`);
74
+ console.log(`${name}: ${file.relativePath}`);
75
+ });
76
+ images.forEach((file, index) => {
77
+ const name = `image_${index + 1}`;
78
+ assetTags.push(` <asset data-name="${name}" data-path="./${file.relativePath}" />`);
79
+ console.log(`${name}: ${file.relativePath}`);
80
+ });
81
+ // Read project.html
82
+ let content = (0, fs_1.readFileSync)(projectFilePath, 'utf-8');
83
+ // Check if <assets> section exists
84
+ const assetsMatch = content.match(/<assets>([\s\S]*?)<\/assets>/);
85
+ if (assetsMatch) {
86
+ // Replace existing assets section
87
+ const newAssetsSection = `<assets>\n${assetTags.join('\n')}\n</assets>`;
88
+ content = content.replace(/<assets>[\s\S]*?<\/assets>/, newAssetsSection);
89
+ }
90
+ else {
91
+ // Add assets section before </project> or at the end
92
+ const newAssetsSection = `\n<assets>\n${assetTags.join('\n')}\n</assets>\n`;
93
+ if (content.includes('</outputs>')) {
94
+ content = content.replace('</outputs>', `</outputs>${newAssetsSection}`);
95
+ }
96
+ else if (content.includes('</style>')) {
97
+ content = content.replace('</style>', `</style>${newAssetsSection}`);
98
+ }
99
+ else {
100
+ content += newAssetsSection;
101
+ }
102
+ }
103
+ // Write back to project.html
104
+ (0, fs_1.writeFileSync)(projectFilePath, content, 'utf-8');
105
+ console.log(`\n✅ Assets added to ${projectFilePath}`);
106
+ }
107
+ catch (error) {
108
+ handleError(error, 'Asset scanning');
109
+ process.exit(1);
110
+ }
111
+ });
112
+ }
113
+ //# sourceMappingURL=add-assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-assets.js","sourceRoot":"","sources":["../../../src/cli/commands/add-assets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerBootstrapCommand(program: Command, handleError: (error: any, operation: string) => void): void;
3
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,GACnD,IAAI,CAiDN"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerBootstrapCommand = registerBootstrapCommand;
4
+ const path_1 = require("path");
5
+ const fs_1 = require("fs");
6
+ function registerBootstrapCommand(program, handleError) {
7
+ program
8
+ .command('bootstrap')
9
+ .description('Create a new project from template')
10
+ .requiredOption('-n, --name <name>', 'Name of the new project')
11
+ .action((options) => {
12
+ try {
13
+ const projectName = options.name;
14
+ const targetPath = (0, path_1.resolve)(process.cwd(), projectName);
15
+ // Check if target directory already exists
16
+ if ((0, fs_1.existsSync)(targetPath)) {
17
+ console.error(`Error: Directory "${projectName}" already exists`);
18
+ process.exit(1);
19
+ }
20
+ // Get the template path (relative to the CLI script location)
21
+ // When built, cli.js is in apps/renderer/dist/, and template is at ../../../examples/template
22
+ // Use realpathSync to resolve symlinks when globally linked via npm link
23
+ const scriptPath = (0, fs_1.realpathSync)(process.argv[1]);
24
+ const scriptDir = (0, path_1.dirname)(scriptPath);
25
+ const templatePath = (0, path_1.resolve)(scriptDir, '../../../examples/template');
26
+ // Validate template exists
27
+ if (!(0, fs_1.existsSync)(templatePath)) {
28
+ console.error(`Error: Template directory not found at ${templatePath}`);
29
+ process.exit(1);
30
+ }
31
+ console.log(`📦 Creating new project "${projectName}"...`);
32
+ console.log(`📂 Template: ${templatePath}`);
33
+ console.log(`🎯 Target: ${targetPath}\n`);
34
+ // Create target directory and copy template contents
35
+ (0, fs_1.mkdirSync)(targetPath, { recursive: true });
36
+ (0, fs_1.cpSync)(templatePath, targetPath, { recursive: true });
37
+ console.log(`✅ Project "${projectName}" created successfully!\n`);
38
+ console.log('Next steps:');
39
+ console.log(` cd ${projectName}`);
40
+ console.log(' # Edit project.html to customize your video');
41
+ console.log(` staticstripes generate -p . -o youtube -d\n`);
42
+ }
43
+ catch (error) {
44
+ handleError(error, 'Project bootstrap');
45
+ process.exit(1);
46
+ }
47
+ });
48
+ }
49
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../../src/cli/commands/bootstrap.ts"],"names":[],"mappings":";;AAIA,4DAoDC;AAvDD,+BAAwC;AACxC,2BAAiE;AAEjE,SAAgB,wBAAwB,CACtC,OAAgB,EAChB,WAAoD;IAEpD,OAAO;SACJ,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,oCAAoC,CAAC;SACjD,cAAc,CAAC,mBAAmB,EAAE,yBAAyB,CAAC;SAC9D,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;QAClB,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;YACjC,MAAM,UAAU,GAAG,IAAA,cAAO,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;YAEvD,2CAA2C;YAC3C,IAAI,IAAA,eAAU,EAAC,UAAU,CAAC,EAAE,CAAC;gBAC3B,OAAO,CAAC,KAAK,CAAC,qBAAqB,WAAW,kBAAkB,CAAC,CAAC;gBAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,8DAA8D;YAC9D,8FAA8F;YAC9F,yEAAyE;YACzE,MAAM,UAAU,GAAG,IAAA,iBAAY,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACjD,MAAM,SAAS,GAAG,IAAA,cAAO,EAAC,UAAU,CAAC,CAAC;YACtC,MAAM,YAAY,GAAG,IAAA,cAAO,EAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC;YAEtE,2BAA2B;YAC3B,IAAI,CAAC,IAAA,eAAU,EAAC,YAAY,CAAC,EAAE,CAAC;gBAC9B,OAAO,CAAC,KAAK,CACX,0CAA0C,YAAY,EAAE,CACzD,CAAC;gBACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,4BAA4B,WAAW,MAAM,CAAC,CAAC;YAC3D,OAAO,CAAC,GAAG,CAAC,gBAAgB,YAAY,EAAE,CAAC,CAAC;YAC5C,OAAO,CAAC,GAAG,CAAC,cAAc,UAAU,IAAI,CAAC,CAAC;YAE1C,qDAAqD;YACrD,IAAA,cAAS,EAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,IAAA,WAAM,EAAC,YAAY,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAEtD,OAAO,CAAC,GAAG,CAAC,cAAc,WAAW,2BAA2B,CAAC,CAAC;YAClE,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,QAAQ,WAAW,EAAE,CAAC,CAAC;YACnC,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;YAC7D,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;YACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerGenerateCommand(program: Command, isDebugMode: () => boolean, handleError: (error: any, operation: string) => void): void;
3
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,MAAM,OAAO,EAC1B,WAAW,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,GACnD,IAAI,CA2KN"}
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerGenerateCommand = registerGenerateCommand;
4
+ const path_1 = require("path");
5
+ const fs_1 = require("fs");
6
+ const html_parser_js_1 = require("../../html-parser.js");
7
+ const html_project_parser_js_1 = require("../../html-project-parser.js");
8
+ const ffmpeg_js_1 = require("../../ffmpeg.js");
9
+ const ffprobe_js_1 = require("../../ffprobe.js");
10
+ const container_renderer_js_1 = require("../../container-renderer.js");
11
+ function registerGenerateCommand(program, isDebugMode, handleError) {
12
+ program
13
+ .command('generate')
14
+ .description('Generate video output from a project')
15
+ .option('-p, --project <path>', 'Path to project directory', '.')
16
+ .option('-o, --output <name>', 'Output name to render (renders all if not specified)')
17
+ .option('--option <name>', 'FFmpeg option preset to use (from project.html <ffmpeg> section)')
18
+ .action(async (options) => {
19
+ try {
20
+ // Check if FFmpeg is installed
21
+ console.log('🔍 Checking for FFmpeg...');
22
+ await (0, ffmpeg_js_1.checkFFmpegInstalled)();
23
+ console.log('✅ FFmpeg found\n');
24
+ // Resolve project path
25
+ const projectPath = (0, path_1.resolve)(process.cwd(), options.project);
26
+ const projectFilePath = (0, path_1.resolve)(projectPath, 'project.html');
27
+ // Validate project.html exists
28
+ if (!(0, fs_1.existsSync)(projectFilePath)) {
29
+ console.error(`Error: project.html not found in ${projectPath}`);
30
+ process.exit(1);
31
+ }
32
+ console.log(`📁 Project: ${projectPath}`);
33
+ console.log(`📄 Loading: ${projectFilePath}\n`);
34
+ // Parse the project HTML file once to get output names
35
+ const initialParser = new html_project_parser_js_1.HTMLProjectParser(await new html_parser_js_1.HTMLParser().parseFile(projectFilePath), projectFilePath);
36
+ const initialProject = await initialParser.parse();
37
+ // Determine which outputs to render
38
+ const allOutputs = Array.from(initialProject.getOutputs().keys());
39
+ const outputsToRender = options.output ? [options.output] : allOutputs;
40
+ if (outputsToRender.length === 0) {
41
+ console.error('Error: No outputs defined in project.html');
42
+ process.exit(1);
43
+ }
44
+ // Validate requested output exists
45
+ if (options.output && !allOutputs.includes(options.output)) {
46
+ console.error(`Error: Output "${options.output}" not found in project.html`);
47
+ console.error(`Available outputs: ${allOutputs.join(', ')}`);
48
+ process.exit(1);
49
+ }
50
+ // Log which outputs will be rendered
51
+ console.log(`🎬 Rendering outputs: ${outputsToRender.join(', ')}\n`);
52
+ // Create a shared cache key store for all outputs
53
+ const activeCacheKeys = new Set();
54
+ // Render each output
55
+ for (const outputName of outputsToRender) {
56
+ // Re-parse the project for each output to ensure clean state
57
+ const parser = new html_project_parser_js_1.HTMLProjectParser(await new html_parser_js_1.HTMLParser().parseFile(projectFilePath), projectFilePath);
58
+ const project = await parser.parse();
59
+ console.log(`\n${'='.repeat(60)}`);
60
+ console.log(`📹 Rendering: ${outputName}`);
61
+ console.log(`${'='.repeat(60)}\n`);
62
+ // Get output info and ensure output directory exists
63
+ const output = project.getOutput(outputName);
64
+ if (!output) {
65
+ throw new Error(`Output "${outputName}" not found`);
66
+ }
67
+ const outputDir = (0, path_1.dirname)(output.path);
68
+ if (!(0, fs_1.existsSync)(outputDir)) {
69
+ console.log(`📂 Creating output directory: ${outputDir}`);
70
+ (0, fs_1.mkdirSync)(outputDir, { recursive: true });
71
+ }
72
+ // Render containers for this output (accumulate cache keys)
73
+ await project.renderContainers(outputName, activeCacheKeys);
74
+ // Print project statistics
75
+ project.printStats();
76
+ // Build filter graph
77
+ const filterBuf = await project.build(outputName);
78
+ const filter = filterBuf.render();
79
+ // Determine FFmpeg arguments to use
80
+ let ffmpegArgs;
81
+ const defaultArgs = '-pix_fmt yuv420p -preset medium -c:a aac -b:a 192k';
82
+ if (options.option) {
83
+ // User specified an option name, look it up in project
84
+ const ffmpegOption = project.getFfmpegOption(options.option);
85
+ if (!ffmpegOption) {
86
+ const availableOptions = Array.from(project.getFfmpegOptions().keys());
87
+ console.error(`Error: FFmpeg option "${options.option}" not found in project.html`);
88
+ if (availableOptions.length > 0) {
89
+ console.error(`Available options: ${availableOptions.join(', ')}`);
90
+ }
91
+ else {
92
+ console.error('No FFmpeg options defined in project.html <ffmpeg> section');
93
+ }
94
+ process.exit(1);
95
+ }
96
+ ffmpegArgs = ffmpegOption.args;
97
+ console.log(`⚡ Using FFmpeg option: ${options.option}`);
98
+ }
99
+ else {
100
+ // No option specified, use default
101
+ ffmpegArgs = defaultArgs;
102
+ console.log(`⚡ Using default FFmpeg arguments`);
103
+ }
104
+ // Generate FFmpeg command
105
+ const ffmpegCommand = (0, ffmpeg_js_1.makeFFmpegCommand)(project, filter, outputName, ffmpegArgs);
106
+ if (isDebugMode()) {
107
+ console.log('\n=== FFmpeg Command ===\n');
108
+ console.log(ffmpegCommand);
109
+ console.log('\n======================\n');
110
+ }
111
+ console.log('\n=== Starting Render ===\n');
112
+ // Run FFmpeg
113
+ await (0, ffmpeg_js_1.runFFMpeg)(ffmpegCommand);
114
+ const resultPath = output.path;
115
+ console.log(`\n✅ Output file: ${resultPath}`);
116
+ const resultDuration = await (0, ffprobe_js_1.getAssetDuration)(resultPath);
117
+ console.log(`⏱️ Duration: ${resultDuration}ms`);
118
+ }
119
+ // Clean up stale cache entries after all outputs are rendered
120
+ if (activeCacheKeys.size > 0) {
121
+ console.log('\n=== Cleaning up stale cache ===\n');
122
+ await (0, container_renderer_js_1.cleanupStaleCache)(projectPath, activeCacheKeys);
123
+ }
124
+ console.log('\n🎉 All outputs rendered successfully!\n');
125
+ }
126
+ catch (error) {
127
+ handleError(error, 'Video generation');
128
+ process.exit(1);
129
+ }
130
+ });
131
+ }
132
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * Registers the generic upload command that works with any upload provider
4
+ */
5
+ export declare function registerUploadCommand(program: Command, handleError: (error: any, operation: string) => void): void;
6
+ //# sourceMappingURL=upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,GACnD,IAAI,CAqEN"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerUploadCommand = registerUploadCommand;
4
+ const path_1 = require("path");
5
+ const fs_1 = require("fs");
6
+ const html_parser_js_1 = require("../../html-parser.js");
7
+ const html_project_parser_js_1 = require("../../html-project-parser.js");
8
+ const upload_strategy_factory_js_1 = require("../upload-strategy-factory.js");
9
+ /**
10
+ * Registers the generic upload command that works with any upload provider
11
+ */
12
+ function registerUploadCommand(program, handleError) {
13
+ program
14
+ .command('upload')
15
+ .description('Upload video to configured platform (YouTube, S3, etc.)')
16
+ .option('-p, --project <path>', 'Path to project directory', '.')
17
+ .requiredOption('--upload-name <name>', 'Name of the upload configuration')
18
+ .action(async (options) => {
19
+ try {
20
+ // Resolve project path
21
+ const projectPath = (0, path_1.resolve)(process.cwd(), options.project);
22
+ const projectFilePath = (0, path_1.resolve)(projectPath, 'project.html');
23
+ // Validate project.html exists
24
+ if (!(0, fs_1.existsSync)(projectFilePath)) {
25
+ console.error(`❌ Error: project.html not found in ${projectPath}`);
26
+ process.exit(1);
27
+ }
28
+ console.log(`📁 Project: ${projectPath}`);
29
+ console.log(`📄 Loading: ${projectFilePath}\n`);
30
+ // Parse the project HTML file
31
+ const parser = new html_project_parser_js_1.HTMLProjectParser(await new html_parser_js_1.HTMLParser().parseFile(projectFilePath), projectFilePath);
32
+ const project = await parser.parse();
33
+ // Get the upload configuration
34
+ const upload = project.getUpload(options.uploadName);
35
+ if (!upload) {
36
+ const availableUploads = Array.from(project.getUploads().keys());
37
+ console.error(`❌ Upload "${options.uploadName}" not found in project.html\n`);
38
+ if (availableUploads.length > 0) {
39
+ console.error(`Available uploads: ${availableUploads.join(', ')}`);
40
+ }
41
+ else {
42
+ console.error('No uploads defined in project.html');
43
+ }
44
+ process.exit(1);
45
+ }
46
+ // Validate output file exists
47
+ const output = project.getOutput(upload.outputName);
48
+ if (output && !(0, fs_1.existsSync)(output.path)) {
49
+ console.error(`❌ Error: Output file not found: ${output.path}`);
50
+ console.error('💡 Please generate the video first with: staticstripes generate\n');
51
+ process.exit(1);
52
+ }
53
+ // Get the appropriate strategy for this upload tag
54
+ const factory = upload_strategy_factory_js_1.UploadStrategyFactory.createDefault();
55
+ const strategy = factory.getStrategy(upload.tag);
56
+ // Validate strategy requirements
57
+ strategy.validate();
58
+ // Execute the upload
59
+ await strategy.execute(project, upload, projectPath);
60
+ }
61
+ catch (error) {
62
+ handleError(error, 'Upload');
63
+ process.exit(1);
64
+ }
65
+ });
66
+ }
67
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../../../src/cli/commands/upload.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { UploadStrategy } from '../upload-strategy';
2
+ import { Project } from '../../project';
3
+ import { Upload } from '../../type';
4
+ /**
5
+ * S3 upload strategy implementation
6
+ * Supports generic S3-compatible storage (AWS S3, DigitalOcean Spaces, etc.)
7
+ */
8
+ export declare class S3UploadStrategy implements UploadStrategy {
9
+ constructor();
10
+ getTag(): string;
11
+ validate(): void;
12
+ execute(project: Project, upload: Upload, projectPath: string): Promise<void>;
13
+ /**
14
+ * Converts a string to a URL-friendly slug
15
+ */
16
+ private slugify;
17
+ }
18
+ //# sourceMappingURL=s3-upload-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-upload-strategy.d.ts","sourceRoot":"","sources":["../../../src/cli/s3/s3-upload-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAapC;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,cAAc;;IAGrD,MAAM,IAAI,MAAM;IAIhB,QAAQ,IAAI,IAAI;IAIV,OAAO,CACX,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAiJhB;;OAEG;IACH,OAAO,CAAC,OAAO;CAWhB"}
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.S3UploadStrategy = void 0;
4
+ const client_s3_1 = require("@aws-sdk/client-s3");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ /**
8
+ * S3 upload strategy implementation
9
+ * Supports generic S3-compatible storage (AWS S3, DigitalOcean Spaces, etc.)
10
+ */
11
+ class S3UploadStrategy {
12
+ constructor() { }
13
+ getTag() {
14
+ return 's3';
15
+ }
16
+ validate() {
17
+ // Validation happens in execute() since we need upload config
18
+ }
19
+ async execute(project, upload, projectPath) {
20
+ // Validate S3 configuration exists
21
+ if (!upload.s3) {
22
+ throw new Error(`❌ Error: S3 configuration missing for upload "${upload.name}"`);
23
+ }
24
+ const { endpoint, region, bucket, path, acl } = upload.s3;
25
+ // Validate ACL value if specified
26
+ const allowedAcls = ['private', 'public-read', 'authenticated-read'];
27
+ if (acl && !allowedAcls.includes(acl)) {
28
+ throw new Error(`❌ Error: Invalid ACL value "${acl}" for upload "${upload.name}"\n\n` +
29
+ `Allowed values: ${allowedAcls.join(', ')}\n` +
30
+ `Note: "public-read-write" is not supported for security reasons.`);
31
+ }
32
+ // Load credentials from .auth/<upload-name>.json
33
+ const authDir = (0, path_1.resolve)(projectPath, '.auth');
34
+ const credentialsPath = (0, path_1.resolve)(authDir, `${upload.name}.json`);
35
+ if (!(0, fs_1.existsSync)(credentialsPath)) {
36
+ throw new Error(`❌ Error: S3 credentials not found at ${credentialsPath}\n\n` +
37
+ `💡 Create a JSON file with the following format:\n` +
38
+ `{\n` +
39
+ ` "accessKeyId": "YOUR_ACCESS_KEY",\n` +
40
+ ` "secretAccessKey": "YOUR_SECRET_KEY"\n` +
41
+ `}\n`);
42
+ }
43
+ console.log(`🔐 Loading credentials from: ${credentialsPath}`);
44
+ let credentials;
45
+ try {
46
+ const credentialsJson = (0, fs_1.readFileSync)(credentialsPath, 'utf-8');
47
+ credentials = JSON.parse(credentialsJson);
48
+ if (!credentials.accessKeyId || !credentials.secretAccessKey) {
49
+ throw new Error('Missing accessKeyId or secretAccessKey');
50
+ }
51
+ }
52
+ catch (error) {
53
+ throw new Error(`❌ Error: Failed to parse S3 credentials from ${credentialsPath}\n` +
54
+ `Ensure the file contains valid JSON with accessKeyId and secretAccessKey.\n` +
55
+ `Error: ${error instanceof Error ? error.message : String(error)}`);
56
+ }
57
+ // Get the output file
58
+ const output = project.getOutput(upload.outputName);
59
+ if (!output) {
60
+ throw new Error(`❌ Error: Output "${upload.outputName}" not found`);
61
+ }
62
+ if (!(0, fs_1.existsSync)(output.path)) {
63
+ throw new Error(`❌ Error: Output file not found: ${output.path}\n` +
64
+ '💡 Please generate the video first');
65
+ }
66
+ // Interpolate path variables
67
+ const slug = this.slugify(project.getTitle());
68
+ const outputName = output.name;
69
+ const interpolatedPath = path
70
+ .replace(/\$\{slug\}/g, slug)
71
+ .replace(/\$\{output\}/g, outputName);
72
+ console.log(`\n📦 Preparing S3 upload...`);
73
+ console.log(` Bucket: ${bucket}`);
74
+ console.log(` Region: ${region}`);
75
+ if (endpoint) {
76
+ console.log(` Endpoint: ${endpoint}`);
77
+ }
78
+ console.log(` Path: ${interpolatedPath}`);
79
+ console.log(` File: ${output.path}\n`);
80
+ // Configure S3 client
81
+ const s3Config = {
82
+ region,
83
+ credentials: {
84
+ accessKeyId: credentials.accessKeyId,
85
+ secretAccessKey: credentials.secretAccessKey,
86
+ },
87
+ };
88
+ // Add custom endpoint for S3-compatible services (DigitalOcean Spaces, etc.)
89
+ if (endpoint) {
90
+ // Construct the endpoint URL with region for S3-compatible services
91
+ // e.g., "ams3.digitaloceanspaces.com" for DigitalOcean Spaces
92
+ s3Config.endpoint = `https://${region}.${endpoint}`;
93
+ // Use virtual-hosted-style addressing (bucket.region.endpoint.com)
94
+ s3Config.forcePathStyle = false;
95
+ }
96
+ const s3Client = new client_s3_1.S3Client(s3Config);
97
+ // Read file
98
+ console.log(`📤 Uploading to S3...`);
99
+ const fileBuffer = (0, fs_1.readFileSync)(output.path);
100
+ // Upload file
101
+ const uploadParams = {
102
+ Bucket: bucket,
103
+ Key: interpolatedPath,
104
+ Body: fileBuffer,
105
+ ContentType: 'video/mp4',
106
+ };
107
+ // Add ACL if specified in configuration
108
+ if (acl) {
109
+ uploadParams.ACL = acl;
110
+ }
111
+ const command = new client_s3_1.PutObjectCommand(uploadParams);
112
+ try {
113
+ await s3Client.send(command);
114
+ // Construct public URL
115
+ let publicUrl;
116
+ if (endpoint) {
117
+ // For DigitalOcean Spaces and similar services
118
+ // Format: https://{bucket}.{region}.{endpoint}/{path}
119
+ publicUrl = `https://${bucket}.${region}.${endpoint}/${interpolatedPath}`;
120
+ }
121
+ else {
122
+ // For AWS S3
123
+ publicUrl = `https://${bucket}.s3.${region}.amazonaws.com/${interpolatedPath}`;
124
+ }
125
+ console.log(`\n✅ Upload successful!`);
126
+ console.log(`🔗 Public URL: ${publicUrl}\n`);
127
+ }
128
+ catch (error) {
129
+ throw new Error(`❌ Error: Failed to upload to S3\n` +
130
+ `${error instanceof Error ? error.message : String(error)}`);
131
+ }
132
+ }
133
+ /**
134
+ * Converts a string to a URL-friendly slug
135
+ */
136
+ slugify(text) {
137
+ return text
138
+ .toString()
139
+ .toLowerCase()
140
+ .trim()
141
+ .replace(/\s+/g, '-') // Replace spaces with -
142
+ .replace(/[^\w\-]+/g, '') // Remove non-word chars
143
+ .replace(/\-\-+/g, '-') // Replace multiple - with single -
144
+ .replace(/^-+/, '') // Trim - from start
145
+ .replace(/-+$/, ''); // Trim - from end
146
+ }
147
+ }
148
+ exports.S3UploadStrategy = S3UploadStrategy;
149
+ //# sourceMappingURL=s3-upload-strategy.js.map