@codellyson/framely-cli 0.1.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 (40) hide show
  1. package/commands/compositions.js +135 -0
  2. package/commands/preview.js +889 -0
  3. package/commands/render.js +295 -0
  4. package/commands/still.js +165 -0
  5. package/index.js +93 -0
  6. package/package.json +60 -0
  7. package/studio/App.css +605 -0
  8. package/studio/App.jsx +185 -0
  9. package/studio/CompositionsView.css +399 -0
  10. package/studio/CompositionsView.jsx +327 -0
  11. package/studio/PropsEditor.css +195 -0
  12. package/studio/PropsEditor.tsx +176 -0
  13. package/studio/RenderDialog.tsx +476 -0
  14. package/studio/ShareDialog.tsx +200 -0
  15. package/studio/index.ts +19 -0
  16. package/studio/player/Player.css +199 -0
  17. package/studio/player/Player.jsx +355 -0
  18. package/studio/styles/design-system.css +592 -0
  19. package/studio/styles/dialogs.css +420 -0
  20. package/studio/templates/AnimatedGradient.jsx +99 -0
  21. package/studio/templates/InstagramStory.jsx +172 -0
  22. package/studio/templates/LowerThird.jsx +139 -0
  23. package/studio/templates/ProductShowcase.jsx +162 -0
  24. package/studio/templates/SlideTransition.jsx +211 -0
  25. package/studio/templates/SocialIntro.jsx +122 -0
  26. package/studio/templates/SubscribeAnimation.jsx +186 -0
  27. package/studio/templates/TemplateCard.tsx +58 -0
  28. package/studio/templates/TemplateFilters.tsx +97 -0
  29. package/studio/templates/TemplatePreviewDialog.tsx +196 -0
  30. package/studio/templates/TemplatesMarketplace.css +686 -0
  31. package/studio/templates/TemplatesMarketplace.tsx +172 -0
  32. package/studio/templates/TextReveal.jsx +134 -0
  33. package/studio/templates/UseTemplateDialog.tsx +154 -0
  34. package/studio/templates/index.ts +45 -0
  35. package/utils/browser.js +188 -0
  36. package/utils/codecs.js +200 -0
  37. package/utils/logger.js +35 -0
  38. package/utils/props.js +42 -0
  39. package/utils/render.js +447 -0
  40. package/utils/validate.js +148 -0
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Codec Configurations
3
+ *
4
+ * FFmpeg encoding settings for different video codecs.
5
+ */
6
+
7
+ /**
8
+ * Supported codec configurations.
9
+ */
10
+ export const codecs = {
11
+ h264: {
12
+ name: 'H.264',
13
+ ffmpegCodec: 'libx264',
14
+ extension: 'mp4',
15
+ pixelFormat: 'yuv420p',
16
+ supportsCrf: true,
17
+ supportsAudio: true,
18
+ description: 'Most compatible format, good quality/size ratio',
19
+ getArgs: (options) => [
20
+ '-c:v', 'libx264',
21
+ '-pix_fmt', 'yuv420p',
22
+ '-preset', options.preset || 'fast',
23
+ '-crf', String(options.crf || 18),
24
+ '-movflags', '+faststart',
25
+ ],
26
+ },
27
+
28
+ h265: {
29
+ name: 'H.265 (HEVC)',
30
+ ffmpegCodec: 'libx265',
31
+ extension: 'mp4',
32
+ pixelFormat: 'yuv420p',
33
+ supportsCrf: true,
34
+ supportsAudio: true,
35
+ description: 'Better compression than H.264, less compatible',
36
+ getArgs: (options) => [
37
+ '-c:v', 'libx265',
38
+ '-pix_fmt', 'yuv420p',
39
+ '-preset', options.preset || 'fast',
40
+ '-crf', String(options.crf || 23),
41
+ '-tag:v', 'hvc1', // For Apple compatibility
42
+ ],
43
+ },
44
+
45
+ vp8: {
46
+ name: 'VP8',
47
+ ffmpegCodec: 'libvpx',
48
+ extension: 'webm',
49
+ pixelFormat: 'yuv420p',
50
+ supportsCrf: true,
51
+ supportsAudio: true,
52
+ description: 'WebM format, good for web',
53
+ getArgs: (options) => [
54
+ '-c:v', 'libvpx',
55
+ '-pix_fmt', 'yuv420p',
56
+ '-crf', String(options.crf || 10),
57
+ '-b:v', options.bitrate || '5M',
58
+ '-deadline', 'good',
59
+ '-cpu-used', '2',
60
+ ],
61
+ },
62
+
63
+ vp9: {
64
+ name: 'VP9',
65
+ ffmpegCodec: 'libvpx-vp9',
66
+ extension: 'webm',
67
+ pixelFormat: 'yuv420p',
68
+ supportsCrf: true,
69
+ supportsAudio: true,
70
+ description: 'WebM format, better compression than VP8',
71
+ getArgs: (options) => [
72
+ '-c:v', 'libvpx-vp9',
73
+ '-pix_fmt', 'yuv420p',
74
+ '-crf', String(options.crf || 31),
75
+ '-b:v', '0', // Required for CRF mode
76
+ '-deadline', 'good',
77
+ '-cpu-used', '2',
78
+ '-row-mt', '1',
79
+ ],
80
+ },
81
+
82
+ prores: {
83
+ name: 'ProRes',
84
+ ffmpegCodec: 'prores_ks',
85
+ extension: 'mov',
86
+ pixelFormat: 'yuva444p10le',
87
+ supportsCrf: false,
88
+ supportsAudio: true,
89
+ supportsAlpha: true,
90
+ description: 'Professional editing format with alpha support',
91
+ profiles: {
92
+ proxy: 0,
93
+ lt: 1,
94
+ standard: 2,
95
+ hq: 3,
96
+ '4444': 4,
97
+ '4444xq': 5,
98
+ },
99
+ getArgs: (options) => {
100
+ const profile = options.profile || 'hq';
101
+ const profileNum = codecs.prores.profiles[profile] != null ? codecs.prores.profiles[profile] : 3;
102
+ return [
103
+ '-c:v', 'prores_ks',
104
+ '-profile:v', String(profileNum),
105
+ '-pix_fmt', options.alpha ? 'yuva444p10le' : 'yuv422p10le',
106
+ '-vendor', 'apl0',
107
+ ];
108
+ },
109
+ },
110
+
111
+ gif: {
112
+ name: 'GIF',
113
+ ffmpegCodec: 'gif',
114
+ extension: 'gif',
115
+ pixelFormat: 'rgb8',
116
+ supportsCrf: false,
117
+ supportsAudio: false,
118
+ description: 'Animated GIF, limited colors',
119
+ getArgs: (options) => {
120
+ // GIF needs special handling with palette generation
121
+ return [
122
+ '-filter_complex',
123
+ `[0:v] fps=${options.fps || 15},scale=${options.width || -1}:${options.height || -1}:flags=lanczos,split [a][b];[a] palettegen=max_colors=256:reserve_transparent=0 [p];[b][p] paletteuse=dither=sierra2_4a`,
124
+ '-loop', String(options.loop != null ? options.loop : 0),
125
+ ];
126
+ },
127
+ // GIF uses a two-pass approach for better quality
128
+ requiresPalette: true,
129
+ },
130
+ };
131
+
132
+ /**
133
+ * Get configuration for a codec.
134
+ *
135
+ * @param {string} codecName - Codec identifier
136
+ * @returns {object|null} Codec configuration or null
137
+ */
138
+ export function getCodecConfig(codecName) {
139
+ return codecs[codecName] || null;
140
+ }
141
+
142
+ /**
143
+ * Get FFmpeg arguments for a codec.
144
+ *
145
+ * @param {string} codecName - Codec identifier
146
+ * @param {object} options - Encoding options
147
+ * @returns {string[]} FFmpeg arguments
148
+ */
149
+ export function getCodecArgs(codecName, options = {}) {
150
+ const config = getCodecConfig(codecName);
151
+ if (!config) {
152
+ throw new Error(`Unknown codec: ${codecName}`);
153
+ }
154
+ return config.getArgs(options);
155
+ }
156
+
157
+ /**
158
+ * Get audio encoding arguments.
159
+ *
160
+ * @param {object} options
161
+ * @param {string} [options.codec='aac'] - Audio codec
162
+ * @param {string} [options.bitrate='320k'] - Audio bitrate
163
+ * @param {number} [options.sampleRate=48000] - Sample rate
164
+ * @returns {string[]} FFmpeg audio arguments
165
+ */
166
+ export function getAudioArgs(options = {}) {
167
+ const codec = options.codec || 'aac';
168
+ const bitrate = options.bitrate || '320k';
169
+ const sampleRate = options.sampleRate || 48000;
170
+
171
+ return [
172
+ '-c:a', codec,
173
+ '-b:a', bitrate,
174
+ '-ar', String(sampleRate),
175
+ ];
176
+ }
177
+
178
+ /**
179
+ * List all available codecs.
180
+ *
181
+ * @returns {Array<{ id: string, name: string, extension: string, description: string }>}
182
+ */
183
+ export function listCodecs() {
184
+ return Object.entries(codecs).map(([id, config]) => ({
185
+ id,
186
+ name: config.name,
187
+ extension: config.extension,
188
+ description: config.description,
189
+ supportsAudio: config.supportsAudio,
190
+ supportsAlpha: config.supportsAlpha || false,
191
+ }));
192
+ }
193
+
194
+ export default {
195
+ codecs,
196
+ getCodecConfig,
197
+ getCodecArgs,
198
+ getAudioArgs,
199
+ listCodecs,
200
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Logger Utility
3
+ *
4
+ * Simple leveled logger for CLI output.
5
+ */
6
+
7
+ import chalk from 'chalk';
8
+
9
+ const LEVELS = { error: 0, warn: 1, info: 2, verbose: 3 };
10
+
11
+ /**
12
+ * Create a logger with the specified level.
13
+ * @param {string} [level='info'] - Log level
14
+ * @returns {object} Logger with error, warn, info, verbose methods
15
+ */
16
+ export function createLogger(level = 'info') {
17
+ const currentLevel = LEVELS[level] ?? LEVELS.info;
18
+
19
+ return {
20
+ error: (...args) => {
21
+ if (currentLevel >= LEVELS.error) console.error(chalk.red(...args));
22
+ },
23
+ warn: (...args) => {
24
+ if (currentLevel >= LEVELS.warn) console.warn(chalk.yellow(...args));
25
+ },
26
+ info: (...args) => {
27
+ if (currentLevel >= LEVELS.info) console.log(...args);
28
+ },
29
+ verbose: (...args) => {
30
+ if (currentLevel >= LEVELS.verbose) console.log(chalk.gray(...args));
31
+ },
32
+ level: currentLevel,
33
+ isVerbose: currentLevel >= LEVELS.verbose,
34
+ };
35
+ }
package/utils/props.js ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Props Loading Utility
3
+ *
4
+ * Shared function for loading input props from JSON string or file.
5
+ */
6
+
7
+ import fs from 'fs';
8
+
9
+ /**
10
+ * Load props from JSON string or file path.
11
+ * @param {string} [propsJson] - JSON string
12
+ * @param {string} [propsFile] - Path to JSON file
13
+ * @returns {object} parsed props
14
+ */
15
+ export function loadProps(propsJson, propsFile) {
16
+ if (propsFile) {
17
+ let content;
18
+ try {
19
+ content = fs.readFileSync(propsFile, 'utf-8');
20
+ } catch (err) {
21
+ if (err.code === 'ENOENT') {
22
+ throw new Error(`Props file not found: ${propsFile}`);
23
+ }
24
+ throw new Error(`Could not read props file ${propsFile}: ${err.message}`);
25
+ }
26
+ try {
27
+ return JSON.parse(content);
28
+ } catch (err) {
29
+ throw new Error(`Invalid JSON in props file ${propsFile}: ${err.message}`);
30
+ }
31
+ }
32
+
33
+ if (propsJson) {
34
+ try {
35
+ return JSON.parse(propsJson);
36
+ } catch (err) {
37
+ throw new Error(`Invalid JSON in --props argument: ${err.message}`);
38
+ }
39
+ }
40
+
41
+ return {};
42
+ }