@gannochenko/staticstripes 0.0.11 → 0.0.14

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 (128) hide show
  1. package/Makefile +37 -4
  2. package/dist/asset-manager.d.ts +1 -0
  3. package/dist/asset-manager.d.ts.map +1 -1
  4. package/dist/asset-manager.js +3 -0
  5. package/dist/asset-manager.js.map +1 -1
  6. package/dist/cli/ai-generation-strategy-factory.d.ts +23 -0
  7. package/dist/cli/ai-generation-strategy-factory.d.ts.map +1 -0
  8. package/dist/cli/ai-generation-strategy-factory.js +44 -0
  9. package/dist/cli/ai-generation-strategy-factory.js.map +1 -0
  10. package/dist/cli/ai-generation-strategy.d.ts +33 -0
  11. package/dist/cli/ai-generation-strategy.d.ts.map +1 -0
  12. package/dist/cli/ai-generation-strategy.js +3 -0
  13. package/dist/cli/ai-generation-strategy.js.map +1 -0
  14. package/dist/cli/ai-music-api-ai/ai-music-api-ai-generation-strategy.d.ts +38 -0
  15. package/dist/cli/ai-music-api-ai/ai-music-api-ai-generation-strategy.d.ts.map +1 -0
  16. package/dist/cli/ai-music-api-ai/ai-music-api-ai-generation-strategy.js +174 -0
  17. package/dist/cli/ai-music-api-ai/ai-music-api-ai-generation-strategy.js.map +1 -0
  18. package/dist/cli/auth-strategy-factory.d.ts +31 -0
  19. package/dist/cli/auth-strategy-factory.d.ts.map +1 -0
  20. package/dist/cli/auth-strategy-factory.js +61 -0
  21. package/dist/cli/auth-strategy-factory.js.map +1 -0
  22. package/dist/cli/auth-strategy.d.ts +31 -0
  23. package/dist/cli/auth-strategy.d.ts.map +1 -0
  24. package/dist/cli/auth-strategy.js +3 -0
  25. package/dist/cli/auth-strategy.js.map +1 -0
  26. package/dist/cli/commands/add-assets.d.ts +3 -0
  27. package/dist/cli/commands/add-assets.d.ts.map +1 -0
  28. package/dist/cli/commands/add-assets.js +113 -0
  29. package/dist/cli/commands/add-assets.js.map +1 -0
  30. package/dist/cli/commands/auth.d.ts +6 -0
  31. package/dist/cli/commands/auth.d.ts.map +1 -0
  32. package/dist/cli/commands/auth.js +103 -0
  33. package/dist/cli/commands/auth.js.map +1 -0
  34. package/dist/cli/commands/bootstrap.d.ts +3 -0
  35. package/dist/cli/commands/bootstrap.d.ts.map +1 -0
  36. package/dist/cli/commands/bootstrap.js +49 -0
  37. package/dist/cli/commands/bootstrap.js.map +1 -0
  38. package/dist/cli/commands/generate.d.ts +3 -0
  39. package/dist/cli/commands/generate.d.ts.map +1 -0
  40. package/dist/cli/commands/generate.js +199 -0
  41. package/dist/cli/commands/generate.js.map +1 -0
  42. package/dist/cli/commands/upload.d.ts +6 -0
  43. package/dist/cli/commands/upload.d.ts.map +1 -0
  44. package/dist/cli/commands/upload.js +67 -0
  45. package/dist/cli/commands/upload.js.map +1 -0
  46. package/dist/cli/instagram/instagram-auth-strategy.d.ts +31 -0
  47. package/dist/cli/instagram/instagram-auth-strategy.d.ts.map +1 -0
  48. package/dist/cli/instagram/instagram-auth-strategy.js +505 -0
  49. package/dist/cli/instagram/instagram-auth-strategy.js.map +1 -0
  50. package/dist/cli/instagram/instagram-upload-strategy.d.ts +45 -0
  51. package/dist/cli/instagram/instagram-upload-strategy.d.ts.map +1 -0
  52. package/dist/cli/instagram/instagram-upload-strategy.js +303 -0
  53. package/dist/cli/instagram/instagram-upload-strategy.js.map +1 -0
  54. package/dist/cli/s3/s3-upload-strategy.d.ts +18 -0
  55. package/dist/cli/s3/s3-upload-strategy.d.ts.map +1 -0
  56. package/dist/cli/s3/s3-upload-strategy.js +153 -0
  57. package/dist/cli/s3/s3-upload-strategy.js.map +1 -0
  58. package/dist/cli/upload-strategy-factory.d.ts +23 -0
  59. package/dist/cli/upload-strategy-factory.d.ts.map +1 -0
  60. package/dist/cli/upload-strategy-factory.js +49 -0
  61. package/dist/cli/upload-strategy-factory.js.map +1 -0
  62. package/dist/cli/upload-strategy.d.ts +25 -0
  63. package/dist/cli/upload-strategy.d.ts.map +1 -0
  64. package/dist/cli/upload-strategy.js +3 -0
  65. package/dist/cli/upload-strategy.js.map +1 -0
  66. package/dist/cli/youtube/youtube-auth-strategy.d.ts +11 -0
  67. package/dist/cli/youtube/youtube-auth-strategy.d.ts.map +1 -0
  68. package/dist/cli/youtube/youtube-auth-strategy.js +320 -0
  69. package/dist/cli/youtube/youtube-auth-strategy.js.map +1 -0
  70. package/dist/cli/youtube/youtube-upload-strategy.d.ts +22 -0
  71. package/dist/cli/youtube/youtube-upload-strategy.d.ts.map +1 -0
  72. package/dist/cli/youtube/youtube-upload-strategy.js +117 -0
  73. package/dist/cli/youtube/youtube-upload-strategy.js.map +1 -0
  74. package/dist/cli.js +11 -281
  75. package/dist/cli.js.map +1 -1
  76. package/dist/html-parser.d.ts +3 -4
  77. package/dist/html-parser.d.ts.map +1 -1
  78. package/dist/html-parser.js +20 -17
  79. package/dist/html-parser.js.map +1 -1
  80. package/dist/html-project-parser.d.ts +64 -1
  81. package/dist/html-project-parser.d.ts.map +1 -1
  82. package/dist/html-project-parser.js +695 -57
  83. package/dist/html-project-parser.js.map +1 -1
  84. package/dist/lib/file.d.ts +2 -0
  85. package/dist/lib/file.d.ts.map +1 -0
  86. package/dist/lib/file.js +13 -0
  87. package/dist/lib/file.js.map +1 -0
  88. package/dist/lib/net.d.ts +19 -0
  89. package/dist/lib/net.d.ts.map +1 -0
  90. package/dist/lib/net.js +101 -0
  91. package/dist/lib/net.js.map +1 -0
  92. package/dist/project.d.ts +18 -2
  93. package/dist/project.d.ts.map +1 -1
  94. package/dist/project.js +65 -1
  95. package/dist/project.js.map +1 -1
  96. package/dist/type.d.ts +43 -4
  97. package/dist/type.d.ts.map +1 -1
  98. package/dist/youtube-uploader.d.ts +40 -0
  99. package/dist/youtube-uploader.d.ts.map +1 -0
  100. package/dist/youtube-uploader.js +227 -0
  101. package/dist/youtube-uploader.js.map +1 -0
  102. package/package.json +6 -2
  103. package/src/asset-manager.ts +4 -0
  104. package/src/cli/ai-generation-strategy-factory.ts +48 -0
  105. package/src/cli/ai-generation-strategy.ts +35 -0
  106. package/src/cli/ai-music-api-ai/ai-music-api-ai-generation-strategy.ts +266 -0
  107. package/src/cli/auth-strategy-factory.ts +67 -0
  108. package/src/cli/auth-strategy.ts +37 -0
  109. package/src/cli/commands/add-assets.ts +159 -0
  110. package/src/cli/commands/auth.ts +120 -0
  111. package/src/cli/commands/bootstrap.ts +57 -0
  112. package/src/cli/commands/generate.ts +242 -0
  113. package/src/cli/commands/upload.ts +83 -0
  114. package/src/cli/instagram/instagram-auth-strategy.ts +569 -0
  115. package/src/cli/instagram/instagram-upload-strategy.ts +398 -0
  116. package/src/cli/s3/s3-upload-strategy.ts +198 -0
  117. package/src/cli/upload-strategy-factory.ts +55 -0
  118. package/src/cli/upload-strategy.ts +31 -0
  119. package/src/cli/youtube/youtube-auth-strategy.ts +323 -0
  120. package/src/cli/youtube/youtube-upload-strategy.ts +174 -0
  121. package/src/cli.ts +13 -391
  122. package/src/html-parser.ts +23 -21
  123. package/src/html-project-parser.ts +821 -62
  124. package/src/lib/file.ts +11 -0
  125. package/src/lib/net.ts +120 -0
  126. package/src/project.ts +81 -1
  127. package/src/type.ts +49 -4
  128. package/src/youtube-uploader.ts +288 -0
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InstagramUploadStrategy = void 0;
7
+ const fs_1 = require("fs");
8
+ const path_1 = require("path");
9
+ const ejs_1 = __importDefault(require("ejs"));
10
+ const net_1 = require("../../lib/net");
11
+ /**
12
+ * Instagram upload strategy implementation
13
+ * Uses Instagram Graph API to post Reels/Videos
14
+ */
15
+ class InstagramUploadStrategy {
16
+ API_VERSION = 'v21.0';
17
+ GRAPH_API_BASE = 'https://graph.instagram.com';
18
+ constructor() { }
19
+ getTag() {
20
+ return 'instagram';
21
+ }
22
+ validate() {
23
+ // Validation happens in execute() since we need upload config
24
+ }
25
+ async execute(project, upload, projectPath) {
26
+ // Validate Instagram configuration exists
27
+ if (!upload.instagram) {
28
+ throw new Error(`❌ Error: Instagram configuration missing for upload "${upload.name}"`);
29
+ }
30
+ const { caption, shareToFeed, thumbOffset, coverUrl, videoUrl } = upload.instagram;
31
+ // Load credentials from .auth/<upload-name>.json
32
+ const authDir = (0, path_1.resolve)(projectPath, '.auth');
33
+ const credentialsPath = (0, path_1.resolve)(authDir, `${upload.name}.json`);
34
+ if (!(0, fs_1.existsSync)(credentialsPath)) {
35
+ throw new Error(`❌ Error: Instagram credentials not found\n\n` +
36
+ `Expected location: ${credentialsPath}\n\n` +
37
+ `💡 Run authentication wizard:\n` +
38
+ ` staticstripes auth --upload-name ${upload.name}\n\n` +
39
+ `📖 Or view detailed setup instructions:\n` +
40
+ ` staticstripes auth-help instagram\n`);
41
+ }
42
+ console.log(`🔐 Loading credentials from: ${credentialsPath}`);
43
+ let credentials;
44
+ try {
45
+ const credentialsJson = (0, fs_1.readFileSync)(credentialsPath, 'utf-8');
46
+ credentials = JSON.parse(credentialsJson);
47
+ if (!credentials.accessToken || !credentials.igUserId) {
48
+ throw new Error('Missing accessToken or igUserId');
49
+ }
50
+ }
51
+ catch (error) {
52
+ throw new Error(`❌ Error: Failed to parse Instagram credentials from ${credentialsPath}\n` +
53
+ `Ensure the file contains valid JSON with accessToken and igUserId.\n` +
54
+ `Error: ${error instanceof Error ? error.message : String(error)}`);
55
+ }
56
+ // Determine video URL
57
+ let publicVideoUrl;
58
+ if (videoUrl) {
59
+ // Use explicitly provided URL
60
+ publicVideoUrl = videoUrl;
61
+ console.log(`\n📹 Using provided video URL: ${publicVideoUrl}`);
62
+ }
63
+ else {
64
+ // Try to infer from S3 upload if available
65
+ const s3Upload = this.findS3Upload(project, upload);
66
+ if (s3Upload && s3Upload.s3) {
67
+ publicVideoUrl = this.constructS3Url(project, s3Upload);
68
+ console.log(`\n📹 Using S3 URL from upload "${s3Upload.name}": ${publicVideoUrl}`);
69
+ }
70
+ else {
71
+ throw new Error(`❌ Error: No video URL specified for Instagram upload "${upload.name}"\n\n` +
72
+ `Either:\n` +
73
+ `1. Add <video-url value="https://..." /> to your Instagram config, or\n` +
74
+ `2. Configure an S3 upload with the same output name to auto-generate the URL`);
75
+ }
76
+ }
77
+ // Determine title (use upload-specific title or fall back to project title)
78
+ const title = upload.title || project.getTitle();
79
+ // Format tags with # and space-separated (Instagram style)
80
+ const formattedTags = upload.tags.map((tag) => `#${tag}`).join(' ');
81
+ // Convert ${variable} syntax to <%= variable %> for EJS compatibility
82
+ const ejsCaption = caption.replace(/\$\{(\w+)\}/g, '<%= $1 %>');
83
+ const processedCaption = ejs_1.default.render(ejsCaption, {
84
+ title,
85
+ tags: formattedTags,
86
+ });
87
+ console.log(`\n📸 Preparing Instagram Reel upload...`);
88
+ console.log(` Title: ${title}`);
89
+ console.log(` Tags: ${formattedTags}`);
90
+ console.log(` Caption: ${processedCaption.substring(0, 50)}${processedCaption.length > 50 ? '...' : ''}`);
91
+ console.log(` Share to Feed: ${shareToFeed ? 'Yes' : 'No'}`);
92
+ if (thumbOffset) {
93
+ console.log(` Thumbnail offset: ${thumbOffset}ms`);
94
+ }
95
+ console.log('');
96
+ // Step 1: Create media container
97
+ console.log('📦 Step 1: Creating media container...');
98
+ const containerId = await this.createMediaContainer(credentials, publicVideoUrl, processedCaption, shareToFeed, thumbOffset, coverUrl);
99
+ console.log(`✅ Container created: ${containerId}`);
100
+ // Step 2: Wait for container to be ready
101
+ console.log('\n⏳ Step 2: Waiting for Instagram to process video...');
102
+ await this.waitForContainerReady(credentials, containerId);
103
+ // Step 3: Publish the Reel
104
+ console.log('\n📤 Step 3: Publishing Reel...');
105
+ const mediaId = await this.publishMedia(credentials, containerId);
106
+ // Step 4: Get permalink
107
+ console.log('\n🔗 Getting permalink...');
108
+ const permalink = await this.getPermalink(credentials, mediaId);
109
+ console.log(`\n✅ Reel published successfully!`);
110
+ console.log(`🔗 Media ID: ${mediaId}`);
111
+ console.log(`📺 View at: ${permalink}\n`);
112
+ }
113
+ /**
114
+ * Creates a media container for the Reel
115
+ */
116
+ async createMediaContainer(credentials, videoUrl, caption, shareToFeed, thumbOffset, coverUrl) {
117
+ const params = new URLSearchParams({
118
+ media_type: 'REELS',
119
+ video_url: videoUrl,
120
+ caption: caption,
121
+ access_token: credentials.accessToken,
122
+ });
123
+ if (shareToFeed) {
124
+ params.append('share_to_feed', 'true');
125
+ }
126
+ if (thumbOffset !== undefined) {
127
+ params.append('thumb_offset', thumbOffset.toString());
128
+ }
129
+ if (coverUrl) {
130
+ params.append('cover_url', coverUrl);
131
+ }
132
+ const url = `${this.GRAPH_API_BASE}/${this.API_VERSION}/${credentials.igUserId}/media`;
133
+ try {
134
+ const data = await (0, net_1.makeRequest)({
135
+ url,
136
+ method: 'POST',
137
+ body: params,
138
+ });
139
+ if (!data.id) {
140
+ throw new Error('No container ID returned from API');
141
+ }
142
+ return data.id;
143
+ }
144
+ catch (error) {
145
+ throw new Error(`❌ Error: Failed to create media container\n` +
146
+ `${error instanceof Error ? error.message : String(error)}\n\n` +
147
+ `Common issues:\n` +
148
+ `- Video URL must be publicly accessible\n` +
149
+ `- Access token may be expired (refresh it)\n` +
150
+ `- Video must be MP4 format and meet Instagram requirements\n` +
151
+ `- Instagram User ID must be correct`);
152
+ }
153
+ }
154
+ /**
155
+ * Waits for the container to be ready for publishing
156
+ * Polls the status endpoint until status_code is FINISHED
157
+ */
158
+ async waitForContainerReady(credentials, containerId) {
159
+ const maxAttempts = 60; // Maximum 60 attempts (5 minutes)
160
+ const delayMs = 5000; // 5 seconds between checks
161
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
162
+ const params = new URLSearchParams({
163
+ fields: 'status_code',
164
+ access_token: credentials.accessToken,
165
+ });
166
+ const url = `${this.GRAPH_API_BASE}/${this.API_VERSION}/${containerId}?${params.toString()}`;
167
+ try {
168
+ const data = await (0, net_1.makeRequest)({
169
+ url,
170
+ method: 'GET',
171
+ });
172
+ if (data.status_code === 'FINISHED') {
173
+ console.log(`✅ Video processed and ready!`);
174
+ return;
175
+ }
176
+ else if (data.status_code === 'ERROR') {
177
+ throw new Error('Instagram failed to process the video');
178
+ }
179
+ else if (data.status_code === 'IN_PROGRESS') {
180
+ console.log(` Processing... (${attempt}/${maxAttempts})`);
181
+ }
182
+ else {
183
+ console.log(` Status: ${data.status_code || 'UNKNOWN'} (${attempt}/${maxAttempts})`);
184
+ }
185
+ }
186
+ catch (error) {
187
+ console.log(` Warning: Status check failed, continuing...`);
188
+ }
189
+ // Wait before next check
190
+ if (attempt < maxAttempts) {
191
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
192
+ }
193
+ }
194
+ // If we get here, we timed out, but let's try to publish anyway
195
+ console.log(`⚠️ Timeout waiting for status. Attempting to publish anyway...`);
196
+ }
197
+ /**
198
+ * Publishes the media container
199
+ */
200
+ async publishMedia(credentials, containerId) {
201
+ const params = new URLSearchParams({
202
+ creation_id: containerId,
203
+ access_token: credentials.accessToken,
204
+ });
205
+ const url = `${this.GRAPH_API_BASE}/${this.API_VERSION}/${credentials.igUserId}/media_publish`;
206
+ try {
207
+ const data = await (0, net_1.makeRequest)({
208
+ url,
209
+ method: 'POST',
210
+ body: params,
211
+ });
212
+ if (!data.id) {
213
+ throw new Error('No media ID returned from API');
214
+ }
215
+ return data.id;
216
+ }
217
+ catch (error) {
218
+ throw new Error(`❌ Error: Failed to publish media\n` +
219
+ `${error instanceof Error ? error.message : String(error)}\n\n` +
220
+ `The container may still be processing. Wait a few seconds and try again.`);
221
+ }
222
+ }
223
+ /**
224
+ * Finds a corresponding S3 upload for the same output
225
+ */
226
+ findS3Upload(project, upload) {
227
+ const uploads = project.getUploads();
228
+ for (const [name, u] of uploads.entries()) {
229
+ if (u.tag === 's3' &&
230
+ u.outputName === upload.outputName &&
231
+ name !== upload.name) {
232
+ return u;
233
+ }
234
+ }
235
+ return undefined;
236
+ }
237
+ /**
238
+ * Constructs the public S3 URL for a video
239
+ */
240
+ constructS3Url(project, s3Upload) {
241
+ if (!s3Upload.s3) {
242
+ throw new Error('S3 configuration missing');
243
+ }
244
+ const { endpoint, region, bucket, path } = s3Upload.s3;
245
+ const output = project.getOutput(s3Upload.outputName);
246
+ if (!output) {
247
+ throw new Error(`Output "${s3Upload.outputName}" not found`);
248
+ }
249
+ // Interpolate path variables
250
+ const slug = this.slugify(project.getTitle());
251
+ const outputName = output.name;
252
+ const interpolatedPath = path
253
+ .replace(/\$\{slug\}/g, slug)
254
+ .replace(/\$\{output\}/g, outputName);
255
+ // Construct URL
256
+ if (endpoint) {
257
+ return `https://${bucket}.${region}.${endpoint}/${interpolatedPath}`;
258
+ }
259
+ else {
260
+ return `https://${bucket}.s3.${region}.amazonaws.com/${interpolatedPath}`;
261
+ }
262
+ }
263
+ /**
264
+ * Converts a string to a URL-friendly slug
265
+ */
266
+ slugify(text) {
267
+ return text
268
+ .toString()
269
+ .toLowerCase()
270
+ .trim()
271
+ .replace(/\s+/g, '-')
272
+ .replace(/[^\w\-]+/g, '')
273
+ .replace(/\-\-+/g, '-')
274
+ .replace(/^-+/, '')
275
+ .replace(/-+$/, '');
276
+ }
277
+ /**
278
+ * Gets the permalink (URL) for the published media
279
+ */
280
+ async getPermalink(credentials, mediaId) {
281
+ const params = new URLSearchParams({
282
+ fields: 'permalink',
283
+ access_token: credentials.accessToken,
284
+ });
285
+ const url = `${this.GRAPH_API_BASE}/${this.API_VERSION}/${mediaId}?${params.toString()}`;
286
+ try {
287
+ const data = await (0, net_1.makeRequest)({
288
+ url,
289
+ method: 'GET',
290
+ });
291
+ if (!data.permalink) {
292
+ return `https://www.instagram.com/ (check your profile)`;
293
+ }
294
+ return data.permalink;
295
+ }
296
+ catch (error) {
297
+ console.log(` Warning: Failed to get permalink: ${error}`);
298
+ return `https://www.instagram.com/ (check your profile)`;
299
+ }
300
+ }
301
+ }
302
+ exports.InstagramUploadStrategy = InstagramUploadStrategy;
303
+ //# sourceMappingURL=instagram-upload-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instagram-upload-strategy.js","sourceRoot":"","sources":["../../../src/cli/instagram/instagram-upload-strategy.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;IAqJhB;;OAEG;IACH,OAAO,CAAC,OAAO;CAWhB"}
@@ -0,0 +1,153 @@
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\n\n` +
37
+ `Expected location: ${credentialsPath}\n\n` +
38
+ `💡 Create a JSON file with your S3 credentials:\n` +
39
+ `{\n` +
40
+ ` "accessKeyId": "YOUR_ACCESS_KEY",\n` +
41
+ ` "secretAccessKey": "YOUR_SECRET_KEY"\n` +
42
+ `}\n\n` +
43
+ `📖 Get credentials from:\n` +
44
+ ` • AWS: IAM → Users → Security Credentials\n` +
45
+ ` • DigitalOcean: API → Spaces Keys\n`);
46
+ }
47
+ console.log(`🔐 Loading credentials from: ${credentialsPath}`);
48
+ let credentials;
49
+ try {
50
+ const credentialsJson = (0, fs_1.readFileSync)(credentialsPath, 'utf-8');
51
+ credentials = JSON.parse(credentialsJson);
52
+ if (!credentials.accessKeyId || !credentials.secretAccessKey) {
53
+ throw new Error('Missing accessKeyId or secretAccessKey');
54
+ }
55
+ }
56
+ catch (error) {
57
+ throw new Error(`❌ Error: Failed to parse S3 credentials from ${credentialsPath}\n` +
58
+ `Ensure the file contains valid JSON with accessKeyId and secretAccessKey.\n` +
59
+ `Error: ${error instanceof Error ? error.message : String(error)}`);
60
+ }
61
+ // Get the output file
62
+ const output = project.getOutput(upload.outputName);
63
+ if (!output) {
64
+ throw new Error(`❌ Error: Output "${upload.outputName}" not found`);
65
+ }
66
+ if (!(0, fs_1.existsSync)(output.path)) {
67
+ throw new Error(`❌ Error: Output file not found: ${output.path}\n` +
68
+ '💡 Please generate the video first');
69
+ }
70
+ // Interpolate path variables
71
+ const slug = this.slugify(project.getTitle());
72
+ const outputName = output.name;
73
+ const interpolatedPath = path
74
+ .replace(/\$\{slug\}/g, slug)
75
+ .replace(/\$\{output\}/g, outputName);
76
+ console.log(`\n📦 Preparing S3 upload...`);
77
+ console.log(` Bucket: ${bucket}`);
78
+ console.log(` Region: ${region}`);
79
+ if (endpoint) {
80
+ console.log(` Endpoint: ${endpoint}`);
81
+ }
82
+ console.log(` Path: ${interpolatedPath}`);
83
+ console.log(` File: ${output.path}\n`);
84
+ // Configure S3 client
85
+ const s3Config = {
86
+ region,
87
+ credentials: {
88
+ accessKeyId: credentials.accessKeyId,
89
+ secretAccessKey: credentials.secretAccessKey,
90
+ },
91
+ };
92
+ // Add custom endpoint for S3-compatible services (DigitalOcean Spaces, etc.)
93
+ if (endpoint) {
94
+ // Construct the endpoint URL with region for S3-compatible services
95
+ // e.g., "ams3.digitaloceanspaces.com" for DigitalOcean Spaces
96
+ s3Config.endpoint = `https://${region}.${endpoint}`;
97
+ // Use virtual-hosted-style addressing (bucket.region.endpoint.com)
98
+ s3Config.forcePathStyle = false;
99
+ }
100
+ const s3Client = new client_s3_1.S3Client(s3Config);
101
+ // Read file
102
+ console.log(`📤 Uploading to S3...`);
103
+ const fileBuffer = (0, fs_1.readFileSync)(output.path);
104
+ // Upload file
105
+ const uploadParams = {
106
+ Bucket: bucket,
107
+ Key: interpolatedPath,
108
+ Body: fileBuffer,
109
+ ContentType: 'video/mp4',
110
+ };
111
+ // Add ACL if specified in configuration
112
+ if (acl) {
113
+ uploadParams.ACL = acl;
114
+ }
115
+ const command = new client_s3_1.PutObjectCommand(uploadParams);
116
+ try {
117
+ await s3Client.send(command);
118
+ // Construct public URL
119
+ let publicUrl;
120
+ if (endpoint) {
121
+ // For DigitalOcean Spaces and similar services
122
+ // Format: https://{bucket}.{region}.{endpoint}/{path}
123
+ publicUrl = `https://${bucket}.${region}.${endpoint}/${interpolatedPath}`;
124
+ }
125
+ else {
126
+ // For AWS S3
127
+ publicUrl = `https://${bucket}.s3.${region}.amazonaws.com/${interpolatedPath}`;
128
+ }
129
+ console.log(`\n✅ Upload successful!`);
130
+ console.log(`🔗 Public URL: ${publicUrl}\n`);
131
+ }
132
+ catch (error) {
133
+ throw new Error(`❌ Error: Failed to upload to S3\n` +
134
+ `${error instanceof Error ? error.message : String(error)}`);
135
+ }
136
+ }
137
+ /**
138
+ * Converts a string to a URL-friendly slug
139
+ */
140
+ slugify(text) {
141
+ return text
142
+ .toString()
143
+ .toLowerCase()
144
+ .trim()
145
+ .replace(/\s+/g, '-') // Replace spaces with -
146
+ .replace(/[^\w\-]+/g, '') // Remove non-word chars
147
+ .replace(/\-\-+/g, '-') // Replace multiple - with single -
148
+ .replace(/^-+/, '') // Trim - from start
149
+ .replace(/-+$/, ''); // Trim - from end
150
+ }
151
+ }
152
+ exports.S3UploadStrategy = S3UploadStrategy;
153
+ //# sourceMappingURL=s3-upload-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-upload-strategy.js","sourceRoot":"","sources":["../../../src/cli/s3/s3-upload-strategy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { UploadStrategy } from './upload-strategy';
2
+ /**
3
+ * Factory for creating upload strategies based on upload tag
4
+ */
5
+ export declare class UploadStrategyFactory {
6
+ private strategies;
7
+ /**
8
+ * Registers an upload strategy
9
+ */
10
+ register(strategy: UploadStrategy): void;
11
+ /**
12
+ * Gets a strategy for the given tag
13
+ * @param tag The upload provider tag (e.g., "youtube", "s3", "instagram")
14
+ * @returns The strategy for this tag
15
+ * @throws Error if no strategy is registered for the tag
16
+ */
17
+ getStrategy(tag: string): UploadStrategy;
18
+ /**
19
+ * Creates a factory with all available strategies registered
20
+ */
21
+ static createDefault(): UploadStrategyFactory;
22
+ }
23
+ //# sourceMappingURL=upload-strategy-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-strategy-factory.d.ts","sourceRoot":"","sources":["../../src/cli/upload-strategy-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKnD;;GAEG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,UAAU,CAA0C;IAE5D;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IAIxC;;;;;OAKG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc;IAcxC;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,qBAAqB;CAa9C"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UploadStrategyFactory = void 0;
4
+ const youtube_upload_strategy_1 = require("./youtube/youtube-upload-strategy");
5
+ const s3_upload_strategy_1 = require("./s3/s3-upload-strategy");
6
+ const instagram_upload_strategy_1 = require("./instagram/instagram-upload-strategy");
7
+ /**
8
+ * Factory for creating upload strategies based on upload tag
9
+ */
10
+ class UploadStrategyFactory {
11
+ strategies = new Map();
12
+ /**
13
+ * Registers an upload strategy
14
+ */
15
+ register(strategy) {
16
+ this.strategies.set(strategy.getTag(), strategy);
17
+ }
18
+ /**
19
+ * Gets a strategy for the given tag
20
+ * @param tag The upload provider tag (e.g., "youtube", "s3", "instagram")
21
+ * @returns The strategy for this tag
22
+ * @throws Error if no strategy is registered for the tag
23
+ */
24
+ getStrategy(tag) {
25
+ const strategy = this.strategies.get(tag);
26
+ if (!strategy) {
27
+ const availableTags = Array.from(this.strategies.keys());
28
+ throw new Error(`No upload strategy registered for tag "${tag}".\n` +
29
+ (availableTags.length > 0
30
+ ? `Available: ${availableTags.join(', ')}`
31
+ : 'No upload strategies registered.'));
32
+ }
33
+ return strategy;
34
+ }
35
+ /**
36
+ * Creates a factory with all available strategies registered
37
+ */
38
+ static createDefault() {
39
+ const factory = new UploadStrategyFactory();
40
+ factory.register(new youtube_upload_strategy_1.YouTubeUploadStrategy());
41
+ // Register S3 strategy
42
+ factory.register(new s3_upload_strategy_1.S3UploadStrategy());
43
+ // Register Instagram strategy
44
+ factory.register(new instagram_upload_strategy_1.InstagramUploadStrategy());
45
+ return factory;
46
+ }
47
+ }
48
+ exports.UploadStrategyFactory = UploadStrategyFactory;
49
+ //# sourceMappingURL=upload-strategy-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-strategy-factory.js","sourceRoot":"","sources":["../../src/cli/upload-strategy-factory.ts"],"names":[],"mappings":";;;AACA,+EAA0E;AAC1E,gEAA2D;AAC3D,qFAAgF;AAEhF;;GAEG;AACH,MAAa,qBAAqB;IACxB,UAAU,GAAgC,IAAI,GAAG,EAAE,CAAC;IAE5D;;OAEG;IACH,QAAQ,CAAC,QAAwB;QAC/B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC;IACnD,CAAC;IAED;;;;;OAKG;IACH,WAAW,CAAC,GAAW;QACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;YACzD,MAAM,IAAI,KAAK,CACb,0CAA0C,GAAG,MAAM;gBACjD,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC;oBACvB,CAAC,CAAC,cAAc,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBAC1C,CAAC,CAAC,kCAAkC,CAAC,CAC1C,CAAC;QACJ,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,aAAa;QAClB,MAAM,OAAO,GAAG,IAAI,qBAAqB,EAAE,CAAC;QAE5C,OAAO,CAAC,QAAQ,CAAC,IAAI,+CAAqB,EAAE,CAAC,CAAC;QAE9C,uBAAuB;QACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,qCAAgB,EAAE,CAAC,CAAC;QAEzC,8BAA8B;QAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,mDAAuB,EAAE,CAAC,CAAC;QAEhD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AA9CD,sDA8CC"}
@@ -0,0 +1,25 @@
1
+ import { Project } from '../project';
2
+ import { YouTubeUpload } from '../type';
3
+ /**
4
+ * Interface for upload strategies
5
+ * Each upload provider (YouTube, S3, etc.) implements this interface
6
+ */
7
+ export interface UploadStrategy {
8
+ /**
9
+ * Returns the tag name this strategy handles (e.g., "youtube", "s3")
10
+ */
11
+ getTag(): string;
12
+ /**
13
+ * Validates that required environment variables and configuration are present
14
+ * @throws Error if validation fails
15
+ */
16
+ validate(): void;
17
+ /**
18
+ * Executes the upload
19
+ * @param project The parsed project
20
+ * @param upload The upload configuration
21
+ * @param projectPath The absolute path to the project directory
22
+ */
23
+ execute(project: Project, upload: YouTubeUpload, projectPath: string): Promise<void>;
24
+ }
25
+ //# sourceMappingURL=upload-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-strategy.d.ts","sourceRoot":"","sources":["../../src/cli/upload-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC;IAEjB;;;OAGG;IACH,QAAQ,IAAI,IAAI,CAAC;IAEjB;;;;;OAKG;IACH,OAAO,CACL,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=upload-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-strategy.js","sourceRoot":"","sources":["../../src/cli/upload-strategy.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ import { AuthStrategy, AuthOptions } from '../auth-strategy';
2
+ /**
3
+ * YouTube authentication strategy
4
+ * Uses OAuth 2.0 flow with local callback server
5
+ */
6
+ export declare class YouTubeAuthStrategy implements AuthStrategy {
7
+ getTag(): string;
8
+ execute(uploadName: string, projectPath: string, _options?: AuthOptions): Promise<void>;
9
+ getSetupInstructions(): string;
10
+ }
11
+ //# sourceMappingURL=youtube-auth-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"youtube-auth-strategy.d.ts","sourceRoot":"","sources":["../../../src/cli/youtube/youtube-auth-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAS7D;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,YAAY;IACtD,MAAM,IAAI,MAAM;IAIV,OAAO,CACX,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,IAAI,CAAC;IAuMhB,oBAAoB,IAAI,MAAM;CAqG/B"}