@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.
- package/Makefile +17 -4
- package/dist/cli/commands/add-assets.d.ts +3 -0
- package/dist/cli/commands/add-assets.d.ts.map +1 -0
- package/dist/cli/commands/add-assets.js +113 -0
- package/dist/cli/commands/add-assets.js.map +1 -0
- package/dist/cli/commands/bootstrap.d.ts +3 -0
- package/dist/cli/commands/bootstrap.d.ts.map +1 -0
- package/dist/cli/commands/bootstrap.js +49 -0
- package/dist/cli/commands/bootstrap.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +3 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +132 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/upload.d.ts +6 -0
- package/dist/cli/commands/upload.d.ts.map +1 -0
- package/dist/cli/commands/upload.js +67 -0
- package/dist/cli/commands/upload.js.map +1 -0
- package/dist/cli/s3/s3-upload-strategy.d.ts +18 -0
- package/dist/cli/s3/s3-upload-strategy.d.ts.map +1 -0
- package/dist/cli/s3/s3-upload-strategy.js +149 -0
- package/dist/cli/s3/s3-upload-strategy.js.map +1 -0
- package/dist/cli/upload-strategy-factory.d.ts +23 -0
- package/dist/cli/upload-strategy-factory.d.ts.map +1 -0
- package/dist/cli/upload-strategy-factory.js +49 -0
- package/dist/cli/upload-strategy-factory.js.map +1 -0
- package/dist/cli/upload-strategy.d.ts +25 -0
- package/dist/cli/upload-strategy.d.ts.map +1 -0
- package/dist/cli/upload-strategy.js +3 -0
- package/dist/cli/upload-strategy.js.map +1 -0
- package/dist/cli/youtube/auth-commands.d.ts +3 -0
- package/dist/cli/youtube/auth-commands.d.ts.map +1 -0
- package/dist/cli/youtube/auth-commands.js +273 -0
- package/dist/cli/youtube/auth-commands.js.map +1 -0
- package/dist/cli/youtube/cli.d.ts +7 -0
- package/dist/cli/youtube/cli.d.ts.map +1 -0
- package/dist/cli/youtube/cli.js +13 -0
- package/dist/cli/youtube/cli.js.map +1 -0
- package/dist/cli/youtube/upload-handler.d.ts +12 -0
- package/dist/cli/youtube/upload-handler.d.ts.map +1 -0
- package/dist/cli/youtube/upload-handler.js +66 -0
- package/dist/cli/youtube/upload-handler.js.map +1 -0
- package/dist/cli/youtube/youtube-upload-strategy.d.ts +15 -0
- package/dist/cli/youtube/youtube-upload-strategy.d.ts.map +1 -0
- package/dist/cli/youtube/youtube-upload-strategy.js +37 -0
- package/dist/cli/youtube/youtube-upload-strategy.js.map +1 -0
- package/dist/cli.js +12 -281
- package/dist/cli.js.map +1 -1
- package/dist/html-parser.d.ts +3 -4
- package/dist/html-parser.d.ts.map +1 -1
- package/dist/html-parser.js +20 -17
- package/dist/html-parser.js.map +1 -1
- package/dist/html-project-parser.d.ts +24 -0
- package/dist/html-project-parser.d.ts.map +1 -1
- package/dist/html-project-parser.js +361 -57
- package/dist/html-project-parser.js.map +1 -1
- package/dist/project.d.ts +15 -2
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +57 -1
- package/dist/project.js.map +1 -1
- package/dist/type.d.ts +26 -4
- package/dist/type.d.ts.map +1 -1
- package/dist/youtube-uploader.d.ts +40 -0
- package/dist/youtube-uploader.d.ts.map +1 -0
- package/dist/youtube-uploader.js +227 -0
- package/dist/youtube-uploader.js.map +1 -0
- package/package.json +6 -2
- package/src/cli/commands/add-assets.ts +159 -0
- package/src/cli/commands/bootstrap.ts +57 -0
- package/src/cli/commands/generate.ts +189 -0
- package/src/cli/commands/upload.ts +83 -0
- package/src/cli/s3/s3-upload-strategy.ts +194 -0
- package/src/cli/upload-strategy-factory.ts +58 -0
- package/src/cli/upload-strategy.ts +31 -0
- package/src/cli/youtube/auth-commands.ts +312 -0
- package/src/cli/youtube/cli.ts +11 -0
- package/src/cli/youtube/upload-handler.ts +101 -0
- package/src/cli/youtube/youtube-upload-strategy.ts +43 -0
- package/src/cli.ts +14 -390
- package/src/html-parser.ts +23 -21
- package/src/html-project-parser.ts +400 -62
- package/src/project.ts +71 -1
- package/src/type.ts +30 -4
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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
|