@gannochenko/staticstripes 0.0.1
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/.prettierrc +8 -0
- package/Makefile +69 -0
- package/dist/asset-manager.d.ts +16 -0
- package/dist/asset-manager.d.ts.map +1 -0
- package/dist/asset-manager.js +50 -0
- package/dist/asset-manager.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +257 -0
- package/dist/cli.js.map +1 -0
- package/dist/container-renderer.d.ts +21 -0
- package/dist/container-renderer.d.ts.map +1 -0
- package/dist/container-renderer.js +149 -0
- package/dist/container-renderer.js.map +1 -0
- package/dist/expression-parser.d.ts +63 -0
- package/dist/expression-parser.d.ts.map +1 -0
- package/dist/expression-parser.js +145 -0
- package/dist/expression-parser.js.map +1 -0
- package/dist/ffmpeg.d.ts +375 -0
- package/dist/ffmpeg.d.ts.map +1 -0
- package/dist/ffmpeg.js +997 -0
- package/dist/ffmpeg.js.map +1 -0
- package/dist/ffprobe.d.ts +2 -0
- package/dist/ffprobe.d.ts.map +1 -0
- package/dist/ffprobe.js +31 -0
- package/dist/ffprobe.js.map +1 -0
- package/dist/html-parser.d.ts +56 -0
- package/dist/html-parser.d.ts.map +1 -0
- package/dist/html-parser.js +208 -0
- package/dist/html-parser.js.map +1 -0
- package/dist/html-project-parser.d.ts +169 -0
- package/dist/html-project-parser.d.ts.map +1 -0
- package/dist/html-project-parser.js +954 -0
- package/dist/html-project-parser.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/label-generator.d.ts +35 -0
- package/dist/label-generator.d.ts.map +1 -0
- package/dist/label-generator.js +69 -0
- package/dist/label-generator.js.map +1 -0
- package/dist/project.d.ts +29 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +137 -0
- package/dist/project.js.map +1 -0
- package/dist/sample-sequences.d.ts +5 -0
- package/dist/sample-sequences.d.ts.map +1 -0
- package/dist/sample-sequences.js +199 -0
- package/dist/sample-sequences.js.map +1 -0
- package/dist/sample-streams.d.ts +2 -0
- package/dist/sample-streams.d.ts.map +1 -0
- package/dist/sample-streams.js +109 -0
- package/dist/sample-streams.js.map +1 -0
- package/dist/sequence.d.ts +21 -0
- package/dist/sequence.d.ts.map +1 -0
- package/dist/sequence.js +269 -0
- package/dist/sequence.js.map +1 -0
- package/dist/stream.d.ts +135 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +779 -0
- package/dist/stream.js.map +1 -0
- package/dist/type.d.ts +73 -0
- package/dist/type.d.ts.map +1 -0
- package/dist/type.js +3 -0
- package/dist/type.js.map +1 -0
- package/eslint.config.js +44 -0
- package/package.json +50 -0
- package/src/asset-manager.ts +55 -0
- package/src/cli.ts +306 -0
- package/src/container-renderer.ts +190 -0
- package/src/expression-parser.test.ts +459 -0
- package/src/expression-parser.ts +199 -0
- package/src/ffmpeg.ts +1403 -0
- package/src/ffprobe.ts +29 -0
- package/src/html-parser.ts +221 -0
- package/src/html-project-parser.ts +1195 -0
- package/src/index.ts +9 -0
- package/src/label-generator.ts +74 -0
- package/src/project.ts +180 -0
- package/src/sample-sequences.ts +225 -0
- package/src/sample-streams.ts +142 -0
- package/src/sequence.ts +330 -0
- package/src/stream.ts +1012 -0
- package/src/type.ts +81 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { Expression, Parser } from 'expr-eval';
|
|
2
|
+
|
|
3
|
+
export type TimeData = {
|
|
4
|
+
start: number; // when fragment starts in timeline (milliseconds)
|
|
5
|
+
end: number; // when fragment ends (milliseconds)
|
|
6
|
+
duration: number; // fragment duration (milliseconds)
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type FragmentData = {
|
|
10
|
+
time: TimeData;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Context available for expression evaluation
|
|
15
|
+
* Contains fragment data with runtime timing information
|
|
16
|
+
*/
|
|
17
|
+
export type ExpressionContext = {
|
|
18
|
+
fragments: Map<string, FragmentData>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Compiled expression that can be evaluated with runtime context
|
|
23
|
+
*/
|
|
24
|
+
export type CompiledExpression = {
|
|
25
|
+
original: string;
|
|
26
|
+
expression: Expression;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parses a calc() expression into a compiled expression for later evaluation
|
|
31
|
+
*
|
|
32
|
+
* Supported syntax:
|
|
33
|
+
* - Fragment references: #fragment_id.property.path
|
|
34
|
+
* - Math operations: +, -, *, /, parentheses
|
|
35
|
+
* - Constants: numeric values
|
|
36
|
+
*
|
|
37
|
+
* Examples:
|
|
38
|
+
* - calc(-1 * #ending_screen.time.start)
|
|
39
|
+
* - calc(#intro.time.duration + 1000)
|
|
40
|
+
* - calc((#scene1.time.start + #scene2.time.end) / 2)
|
|
41
|
+
*
|
|
42
|
+
* @param expression - The expression string to parse
|
|
43
|
+
* @returns Compiled expression that can be evaluated later
|
|
44
|
+
*/
|
|
45
|
+
export function parseExpression(expression: string): CompiledExpression {
|
|
46
|
+
const parser = new Parser();
|
|
47
|
+
|
|
48
|
+
// Transform expression to replace fragment references with variable names
|
|
49
|
+
// Convert: calc(-1 * #ending_screen.time.start)
|
|
50
|
+
// To: -1 * ending_screen_time_start
|
|
51
|
+
const transformed = transformExpressionToVariables(expression);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
// Parse into Expression object
|
|
55
|
+
const expr = parser.parse(transformed);
|
|
56
|
+
return {
|
|
57
|
+
original: expression,
|
|
58
|
+
expression: expr,
|
|
59
|
+
};
|
|
60
|
+
} catch (error) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Failed to parse expression "${expression}": ${error instanceof Error ? error.message : String(error)}`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Evaluates a compiled expression with runtime context
|
|
69
|
+
*
|
|
70
|
+
* @param compiled - Compiled expression from parseExpression()
|
|
71
|
+
* @param context - Context containing fragment data
|
|
72
|
+
* @returns Evaluated numeric result (in milliseconds)
|
|
73
|
+
*/
|
|
74
|
+
export function evaluateCompiledExpression(
|
|
75
|
+
compiled: CompiledExpression,
|
|
76
|
+
context: ExpressionContext,
|
|
77
|
+
): number {
|
|
78
|
+
// Build evaluation context by resolving all fragment references
|
|
79
|
+
// Convert fragment references to flat variable names
|
|
80
|
+
const evalContext: Record<string, number> = {};
|
|
81
|
+
|
|
82
|
+
// Extract all fragment references from the original expression
|
|
83
|
+
const fragmentRefs = compiled.original.matchAll(
|
|
84
|
+
/#(\w+)\.([\w.]+?)(?=\s|[+\-*/)]|$)/g,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
for (const match of fragmentRefs) {
|
|
88
|
+
const id = match[1];
|
|
89
|
+
const prop = match[2];
|
|
90
|
+
const varName = `${id}_${prop.replace(/\./g, '_')}`;
|
|
91
|
+
|
|
92
|
+
// Resolve fragment value
|
|
93
|
+
const fragment = context.fragments.get(id);
|
|
94
|
+
if (!fragment) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Fragment with id "${id}" not found in expression: ${compiled.original}`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Navigate property path (e.g., "time.start" -> fragment.time.start)
|
|
101
|
+
const parts = prop.split('.');
|
|
102
|
+
let value: any = fragment;
|
|
103
|
+
for (const part of parts) {
|
|
104
|
+
value = value[part];
|
|
105
|
+
if (value === undefined) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Property "${prop}" not found on fragment "${id}" in expression: ${compiled.original}`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
evalContext[varName] = value;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
// Evaluate with resolved context
|
|
117
|
+
return compiled.expression.evaluate(evalContext);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Failed to evaluate expression "${compiled.original}": ${error instanceof Error ? error.message : String(error)}`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Transforms calc() expression syntax to use variable names instead of fragment references
|
|
127
|
+
* and converts time units to milliseconds
|
|
128
|
+
* @param expression - Original expression
|
|
129
|
+
* @returns Transformed expression
|
|
130
|
+
*
|
|
131
|
+
* Example: calc(url(#ending_screen.time.start) + 5s)
|
|
132
|
+
* -> ending_screen_time_start + 5000
|
|
133
|
+
*/
|
|
134
|
+
function transformExpressionToVariables(expression: string): string {
|
|
135
|
+
let transformed = expression
|
|
136
|
+
// Remove calc() wrapper
|
|
137
|
+
.replace(/calc\(/g, '(')
|
|
138
|
+
// Convert fragment references: url(#id.prop.path) -> id_prop_path
|
|
139
|
+
.replace(/url\(#(\w+)\.([\w.]+?)\)/g, (_, id, prop) => {
|
|
140
|
+
return `${id}_${prop.replace(/\./g, '_')}`;
|
|
141
|
+
})
|
|
142
|
+
// Convert time units to milliseconds
|
|
143
|
+
// Match numbers with 's' suffix (seconds): 5s -> 5000
|
|
144
|
+
.replace(/(\d+(?:\.\d+)?)s(?!\w)/g, (_, num) => {
|
|
145
|
+
return String(parseFloat(num) * 1000);
|
|
146
|
+
})
|
|
147
|
+
// Match numbers with 'ms' suffix (milliseconds): 5000ms -> 5000
|
|
148
|
+
.replace(/(\d+(?:\.\d+)?)ms(?!\w)/g, (_, num) => {
|
|
149
|
+
return String(parseFloat(num));
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return transformed;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Checks if a string contains a calc() expression
|
|
157
|
+
* @param value - String to check
|
|
158
|
+
* @returns True if value contains calc()
|
|
159
|
+
*/
|
|
160
|
+
export function isCalcExpression(value: string): boolean {
|
|
161
|
+
return typeof value === 'string' && value.trim().startsWith('calc(');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Parses a value into either a number or a compiled expression for later evaluation
|
|
166
|
+
* Use this during HTML parsing to compile expressions once
|
|
167
|
+
* @param value - Value to parse (number or calc expression string)
|
|
168
|
+
* @returns Number or CompiledExpression
|
|
169
|
+
*/
|
|
170
|
+
export function parseValueLazy(
|
|
171
|
+
value: number | string,
|
|
172
|
+
): number | CompiledExpression {
|
|
173
|
+
if (typeof value === 'number') {
|
|
174
|
+
return value;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (isCalcExpression(value)) {
|
|
178
|
+
return parseExpression(value);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Try to parse as plain number
|
|
182
|
+
const parsed = parseFloat(value);
|
|
183
|
+
if (!isNaN(parsed)) {
|
|
184
|
+
return parsed;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
throw new Error(
|
|
188
|
+
`Invalid value: "${value}". Expected number or calc() expression`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function calculateFinalValue(
|
|
193
|
+
value: number | CompiledExpression,
|
|
194
|
+
context: ExpressionContext,
|
|
195
|
+
) {
|
|
196
|
+
return typeof value === 'number'
|
|
197
|
+
? value
|
|
198
|
+
: evaluateCompiledExpression(value, context);
|
|
199
|
+
}
|