@vargai/sdk 0.1.1 → 0.2.0

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 (48) hide show
  1. package/.github/workflows/ci.yml +23 -0
  2. package/.husky/README.md +102 -0
  3. package/.husky/commit-msg +9 -0
  4. package/.husky/pre-commit +12 -0
  5. package/.husky/pre-push +9 -0
  6. package/.size-limit.json +8 -0
  7. package/.test-hooks.ts +5 -0
  8. package/CONTRIBUTING.md +150 -0
  9. package/LICENSE.md +53 -0
  10. package/README.md +7 -0
  11. package/action/captions/index.ts +202 -12
  12. package/action/captions/tiktok.ts +538 -0
  13. package/action/cut/index.ts +119 -0
  14. package/action/fade/index.ts +116 -0
  15. package/action/merge/index.ts +177 -0
  16. package/action/remove/index.ts +184 -0
  17. package/action/split/index.ts +133 -0
  18. package/action/transition/index.ts +154 -0
  19. package/action/trim/index.ts +117 -0
  20. package/bun.lock +299 -8
  21. package/cli/commands/upload.ts +215 -0
  22. package/cli/index.ts +3 -1
  23. package/commitlint.config.js +22 -0
  24. package/index.ts +12 -0
  25. package/lib/ass.ts +547 -0
  26. package/lib/fal.ts +75 -1
  27. package/lib/ffmpeg.ts +400 -0
  28. package/lib/higgsfield/example.ts +22 -29
  29. package/lib/higgsfield/index.ts +3 -2
  30. package/lib/higgsfield/soul.ts +0 -5
  31. package/lib/remotion/SKILL.md +240 -21
  32. package/lib/remotion/cli.ts +34 -0
  33. package/package.json +20 -3
  34. package/pipeline/cookbooks/scripts/animate-frames-parallel.ts +83 -0
  35. package/pipeline/cookbooks/scripts/combine-scenes.sh +53 -0
  36. package/pipeline/cookbooks/scripts/generate-frames-parallel.ts +98 -0
  37. package/pipeline/cookbooks/scripts/still-to-video.sh +37 -0
  38. package/pipeline/cookbooks/text-to-tiktok.md +669 -0
  39. package/scripts/.gitkeep +0 -0
  40. package/service/music/index.ts +29 -14
  41. package/tsconfig.json +1 -1
  42. package/utilities/s3.ts +2 -2
  43. package/HIGGSFIELD_REWRITE_SUMMARY.md +0 -300
  44. package/TEST_RESULTS.md +0 -122
  45. package/output.txt +0 -1
  46. package/scripts/produce-menopause-campaign.sh +0 -202
  47. package/test-import.ts +0 -7
  48. package/test-services.ts +0 -97
@@ -0,0 +1,215 @@
1
+ /**
2
+ * varg upload command
3
+ * upload local files to cloudflare r2 storage
4
+ */
5
+
6
+ import { existsSync, statSync } from "node:fs";
7
+ import path from "node:path";
8
+ import { defineCommand } from "citty";
9
+ import { uploadFile } from "../../utilities/s3";
10
+ import { box, c, formatDuration, row, success, error } from "../ui";
11
+
12
+ /**
13
+ * Get file type category from extension
14
+ */
15
+ function getFileType(ext: string): string {
16
+ const normalized = ext.toLowerCase();
17
+
18
+ const imageExts = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg"];
19
+ const videoExts = [".mp4", ".mpeg", ".mpg", ".mov", ".avi", ".webm", ".mkv"];
20
+ const audioExts = [".mp3", ".wav", ".ogg", ".flac", ".aac", ".m4a"];
21
+
22
+ if (imageExts.includes(normalized)) return "image";
23
+ if (videoExts.includes(normalized)) return "video";
24
+ if (audioExts.includes(normalized)) return "audio";
25
+
26
+ return "file";
27
+ }
28
+
29
+ /**
30
+ * Create URL-safe slug from filename
31
+ */
32
+ function slugify(text: string, maxLength = 40): string {
33
+ return text
34
+ .toLowerCase()
35
+ .replace(/[\s_]+/g, "-")
36
+ .replace(/[^\w-]/g, "")
37
+ .replace(/-+/g, "-")
38
+ .replace(/^-|-$/g, "")
39
+ .slice(0, maxLength)
40
+ .replace(/-$/, "") || "file";
41
+ }
42
+
43
+ /**
44
+ * Format file size in human readable format
45
+ */
46
+ function formatSize(bytes: number): string {
47
+ if (bytes < 1024) return `${bytes} B`;
48
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
49
+ if (bytes < 1024 * 1024 * 1024)
50
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
51
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
52
+ }
53
+
54
+ /**
55
+ * Generate S3 object key from file path and optional destination
56
+ */
57
+ function generateObjectKey(filePath: string, destination?: string): string {
58
+ const filename = path.basename(filePath);
59
+
60
+ if (destination) {
61
+ // If ends with "/", append original filename
62
+ if (destination.endsWith("/")) {
63
+ return `${destination}${filename}`;
64
+ }
65
+ // Use destination as-is (full path specified)
66
+ return destination;
67
+ }
68
+
69
+ // Auto-generate path: uploads/{type}s/{slug}_{uuid}.{ext}
70
+ const ext = path.extname(filePath);
71
+ const basename = path.basename(filePath, ext);
72
+ const fileType = getFileType(ext);
73
+ const slug = slugify(basename);
74
+ const uuid = crypto.randomUUID().slice(0, 8);
75
+
76
+ return `uploads/${fileType}s/${slug}_${uuid}${ext}`;
77
+ }
78
+
79
+ export const uploadCmd = defineCommand({
80
+ meta: {
81
+ name: "upload",
82
+ description: "upload local file to cloud storage",
83
+ },
84
+ args: {
85
+ file: {
86
+ type: "positional",
87
+ description: "local file path to upload",
88
+ required: true,
89
+ },
90
+ destination: {
91
+ type: "string",
92
+ alias: "d",
93
+ description:
94
+ "s3 destination path (e.g. users/123/video.mp4 or projects/abc/)",
95
+ },
96
+ json: {
97
+ type: "boolean",
98
+ description: "output result as json",
99
+ },
100
+ quiet: {
101
+ type: "boolean",
102
+ description: "minimal output",
103
+ },
104
+ },
105
+ async run({ args }) {
106
+ const { file: filePath, destination, json, quiet } = args;
107
+
108
+ // Validate file exists
109
+ if (!filePath || !existsSync(filePath)) {
110
+ if (json) {
111
+ console.log(
112
+ JSON.stringify({ success: false, error: "file not found" }),
113
+ );
114
+ } else {
115
+ console.error(`${c.red("error:")} file not found: ${filePath}`);
116
+ }
117
+ process.exit(1);
118
+ }
119
+
120
+ // Get file info
121
+ const stats = statSync(filePath);
122
+ const filename = path.basename(filePath);
123
+ const ext = path.extname(filePath);
124
+ const fileType = getFileType(ext);
125
+ const fileSize = stats.size;
126
+
127
+ // Generate object key
128
+ const objectKey = generateObjectKey(filePath, destination);
129
+
130
+ // Show progress
131
+ if (!quiet && !json) {
132
+ const content = [
133
+ "",
134
+ row("file", filename),
135
+ row("size", formatSize(fileSize)),
136
+ row("type", fileType),
137
+ "",
138
+ ` ${c.cyan("◐")} uploading...`,
139
+ "",
140
+ ];
141
+ console.log(box("upload", content));
142
+ }
143
+
144
+ const startTime = Date.now();
145
+
146
+ try {
147
+ const url = await uploadFile(filePath, objectKey);
148
+ const elapsed = Date.now() - startTime;
149
+
150
+ if (json) {
151
+ console.log(
152
+ JSON.stringify({
153
+ success: true,
154
+ file: filename,
155
+ size: fileSize,
156
+ type: fileType,
157
+ destination: objectKey,
158
+ url,
159
+ time: elapsed,
160
+ }),
161
+ );
162
+ } else if (quiet) {
163
+ console.log(url);
164
+ } else {
165
+ // Clear and show result
166
+ console.log("\x1b[2J\x1b[H");
167
+
168
+ const content = [
169
+ "",
170
+ row("file", filename),
171
+ row("size", formatSize(fileSize)),
172
+ row("type", fileType),
173
+ "",
174
+ success(`uploaded in ${formatDuration(elapsed)}`),
175
+ "",
176
+ row("destination", objectKey),
177
+ "",
178
+ ];
179
+
180
+ console.log(box("upload", content));
181
+ console.log(`\n ${c.cyan("url")} ${url}\n`);
182
+ }
183
+ } catch (err) {
184
+ const elapsed = Date.now() - startTime;
185
+ const errorMsg = err instanceof Error ? err.message : String(err);
186
+
187
+ if (json) {
188
+ console.log(
189
+ JSON.stringify({ success: false, error: errorMsg, time: elapsed }),
190
+ );
191
+ } else if (quiet) {
192
+ console.error(errorMsg);
193
+ } else {
194
+ console.log("\x1b[2J\x1b[H");
195
+
196
+ const content = [
197
+ "",
198
+ row("file", filename),
199
+ row("size", formatSize(fileSize)),
200
+ row("type", fileType),
201
+ "",
202
+ error("upload failed"),
203
+ "",
204
+ row("error", errorMsg),
205
+ "",
206
+ ];
207
+
208
+ console.log(box("upload", content));
209
+ }
210
+
211
+ process.exit(1);
212
+ }
213
+ },
214
+ });
215
+
package/cli/index.ts CHANGED
@@ -10,12 +10,13 @@ import { findCmd } from "./commands/find";
10
10
  import { helpCmd } from "./commands/help";
11
11
  import { listCmd } from "./commands/list";
12
12
  import { runCmd } from "./commands/run";
13
+ import { uploadCmd } from "./commands/upload";
13
14
  import { whichCmd } from "./commands/which";
14
15
 
15
16
  const main = defineCommand({
16
17
  meta: {
17
18
  name: "varg",
18
- version: "0.1.0",
19
+ version: "0.2.0",
19
20
  description: "ai video infrastructure from your terminal",
20
21
  },
21
22
  subCommands: {
@@ -26,6 +27,7 @@ const main = defineCommand({
26
27
  search: findCmd,
27
28
  which: whichCmd,
28
29
  inspect: whichCmd,
30
+ upload: uploadCmd,
29
31
  help: helpCmd,
30
32
  },
31
33
  });
@@ -0,0 +1,22 @@
1
+ export default {
2
+ extends: ["@commitlint/config-conventional"],
3
+ rules: {
4
+ "type-enum": [
5
+ 2,
6
+ "always",
7
+ [
8
+ "feat", // New feature
9
+ "fix", // Bug fix
10
+ "docs", // Documentation changes
11
+ "style", // Code style changes (formatting, etc)
12
+ "refactor", // Code refactoring
13
+ "perf", // Performance improvements
14
+ "test", // Test changes
15
+ "build", // Build system changes
16
+ "ci", // CI/CD changes
17
+ "chore", // Other changes
18
+ "revert", // Revert previous commit
19
+ ],
20
+ ],
21
+ },
22
+ };
package/index.ts CHANGED
@@ -14,6 +14,7 @@ export {
14
14
  addCaptions,
15
15
  type SubtitleStyle,
16
16
  } from "./action/captions";
17
+ export { type CutOptions, type CutResult, cut } from "./action/cut";
17
18
  export {
18
19
  type CreateMontageOptions,
19
20
  createMontage,
@@ -26,11 +27,15 @@ export {
26
27
  quickResize,
27
28
  quickTrim,
28
29
  } from "./action/edit";
30
+ export { type FadeOptions, type FadeResult, fade } from "./action/fade";
29
31
  export {
30
32
  generateWithFal,
31
33
  generateWithSoul,
32
34
  type ImageGenerationResult,
33
35
  } from "./action/image";
36
+ export { type MergeOptions, type MergeResult, merge } from "./action/merge";
37
+ export { type RemoveOptions, type RemoveResult, remove } from "./action/remove";
38
+ export { type SplitOptions, type SplitResult, split } from "./action/split";
34
39
  export {
35
40
  type LipsyncOptions,
36
41
  lipsync,
@@ -43,6 +48,13 @@ export {
43
48
  type TranscribeResult,
44
49
  transcribe,
45
50
  } from "./action/transcribe";
51
+ export {
52
+ type TransitionOptions,
53
+ type TransitionResult,
54
+ transition,
55
+ } from "./action/transition";
56
+ // new action exports - video editing
57
+ export { type TrimOptions, type TrimResult, trim } from "./action/trim";
46
58
  export {
47
59
  generateVideoFromImage,
48
60
  generateVideoFromText,