@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.
Files changed (86) hide show
  1. package/.prettierrc +8 -0
  2. package/Makefile +69 -0
  3. package/dist/asset-manager.d.ts +16 -0
  4. package/dist/asset-manager.d.ts.map +1 -0
  5. package/dist/asset-manager.js +50 -0
  6. package/dist/asset-manager.js.map +1 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +257 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/container-renderer.d.ts +21 -0
  12. package/dist/container-renderer.d.ts.map +1 -0
  13. package/dist/container-renderer.js +149 -0
  14. package/dist/container-renderer.js.map +1 -0
  15. package/dist/expression-parser.d.ts +63 -0
  16. package/dist/expression-parser.d.ts.map +1 -0
  17. package/dist/expression-parser.js +145 -0
  18. package/dist/expression-parser.js.map +1 -0
  19. package/dist/ffmpeg.d.ts +375 -0
  20. package/dist/ffmpeg.d.ts.map +1 -0
  21. package/dist/ffmpeg.js +997 -0
  22. package/dist/ffmpeg.js.map +1 -0
  23. package/dist/ffprobe.d.ts +2 -0
  24. package/dist/ffprobe.d.ts.map +1 -0
  25. package/dist/ffprobe.js +31 -0
  26. package/dist/ffprobe.js.map +1 -0
  27. package/dist/html-parser.d.ts +56 -0
  28. package/dist/html-parser.d.ts.map +1 -0
  29. package/dist/html-parser.js +208 -0
  30. package/dist/html-parser.js.map +1 -0
  31. package/dist/html-project-parser.d.ts +169 -0
  32. package/dist/html-project-parser.d.ts.map +1 -0
  33. package/dist/html-project-parser.js +954 -0
  34. package/dist/html-project-parser.js.map +1 -0
  35. package/dist/index.d.ts +6 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +18 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/label-generator.d.ts +35 -0
  40. package/dist/label-generator.d.ts.map +1 -0
  41. package/dist/label-generator.js +69 -0
  42. package/dist/label-generator.js.map +1 -0
  43. package/dist/project.d.ts +29 -0
  44. package/dist/project.d.ts.map +1 -0
  45. package/dist/project.js +137 -0
  46. package/dist/project.js.map +1 -0
  47. package/dist/sample-sequences.d.ts +5 -0
  48. package/dist/sample-sequences.d.ts.map +1 -0
  49. package/dist/sample-sequences.js +199 -0
  50. package/dist/sample-sequences.js.map +1 -0
  51. package/dist/sample-streams.d.ts +2 -0
  52. package/dist/sample-streams.d.ts.map +1 -0
  53. package/dist/sample-streams.js +109 -0
  54. package/dist/sample-streams.js.map +1 -0
  55. package/dist/sequence.d.ts +21 -0
  56. package/dist/sequence.d.ts.map +1 -0
  57. package/dist/sequence.js +269 -0
  58. package/dist/sequence.js.map +1 -0
  59. package/dist/stream.d.ts +135 -0
  60. package/dist/stream.d.ts.map +1 -0
  61. package/dist/stream.js +779 -0
  62. package/dist/stream.js.map +1 -0
  63. package/dist/type.d.ts +73 -0
  64. package/dist/type.d.ts.map +1 -0
  65. package/dist/type.js +3 -0
  66. package/dist/type.js.map +1 -0
  67. package/eslint.config.js +44 -0
  68. package/package.json +50 -0
  69. package/src/asset-manager.ts +55 -0
  70. package/src/cli.ts +306 -0
  71. package/src/container-renderer.ts +190 -0
  72. package/src/expression-parser.test.ts +459 -0
  73. package/src/expression-parser.ts +199 -0
  74. package/src/ffmpeg.ts +1403 -0
  75. package/src/ffprobe.ts +29 -0
  76. package/src/html-parser.ts +221 -0
  77. package/src/html-project-parser.ts +1195 -0
  78. package/src/index.ts +9 -0
  79. package/src/label-generator.ts +74 -0
  80. package/src/project.ts +180 -0
  81. package/src/sample-sequences.ts +225 -0
  82. package/src/sample-streams.ts +142 -0
  83. package/src/sequence.ts +330 -0
  84. package/src/stream.ts +1012 -0
  85. package/src/type.ts +81 -0
  86. package/tsconfig.json +24 -0
package/src/ffprobe.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { execFile } from 'child_process';
2
+ import { promisify } from 'util';
3
+
4
+ const execFileAsync = promisify(execFile);
5
+
6
+ export const getAssetDuration = async (path: string): Promise<number> => {
7
+ try {
8
+ const { stdout } = await execFileAsync('ffprobe', [
9
+ '-v',
10
+ 'error',
11
+ '-show_entries',
12
+ 'format=duration',
13
+ '-of',
14
+ 'default=noprint_wrappers=1:nokey=1',
15
+ path,
16
+ ]);
17
+
18
+ const durationSeconds = parseFloat(stdout.trim());
19
+ if (isNaN(durationSeconds)) {
20
+ console.warn(`Could not parse duration for asset: ${path}`);
21
+ return 0;
22
+ }
23
+
24
+ return Math.round(durationSeconds * 1000);
25
+ } catch (error) {
26
+ console.error(`Failed to get duration for asset: ${path}`, error);
27
+ return 0;
28
+ }
29
+ };
@@ -0,0 +1,221 @@
1
+ import { parse, type DefaultTreeAdapterMap } from 'parse5';
2
+ import { readFile } from 'fs/promises';
3
+ import * as csstree from 'css-tree';
4
+ import { CSSProperties, ParsedHtml } from './type';
5
+
6
+ export type ASTNode = DefaultTreeAdapterMap['node'];
7
+ export type Document = DefaultTreeAdapterMap['document'];
8
+ export type Element = DefaultTreeAdapterMap['element'];
9
+ // export type Attribute = DefaultTreeAdapterMap['attribute'];
10
+
11
+ export interface EnhancedElement extends Element {
12
+ computedStyles?: CSSProperties;
13
+ }
14
+
15
+ interface StyleRule {
16
+ selector: string;
17
+ properties: CSSProperties;
18
+ }
19
+
20
+ export class HTMLParser {
21
+ /**
22
+ * Parses an HTML file into an AST with computed CSS using parse5 and css-tree
23
+ * @param filePath - Absolute or relative path to the HTML file
24
+ * @returns Promise resolving to the parsed project with AST and computed styles
25
+ */
26
+ public async parseFile(filePath: string): Promise<ParsedHtml> {
27
+ const content = await readFile(filePath, 'utf-8');
28
+ return this.parse(content);
29
+ }
30
+
31
+ /**
32
+ * Parses HTML string into an AST with computed CSS
33
+ * @param html - HTML string to parse
34
+ * @returns The parsed project with AST and computed styles
35
+ */
36
+ public parse(html: string): ParsedHtml {
37
+ const ast = parse(html);
38
+ const cssText = this.extractCSS(ast);
39
+ const cssRules = csstree.parse(cssText);
40
+ const elements = new Map<Element, CSSProperties>();
41
+
42
+ // Build the CSS rule map
43
+ const styleRules = this.buildStyleRules(cssRules);
44
+
45
+ // Apply styles to all elements
46
+ this.traverseAndApplyStyles(ast, styleRules, elements);
47
+
48
+ return { ast, css: elements, cssText };
49
+ }
50
+
51
+ /**
52
+ * Extracts CSS text from <style> elements in the document
53
+ */
54
+ private extractCSS(ast: Document): string {
55
+ const styleElements = findElementsByTagName(ast, 'style');
56
+ return styleElements.map((el) => getTextContent(el)).join('\n');
57
+ }
58
+
59
+ /**
60
+ * Builds a map of CSS rules from the parsed CSS AST
61
+ */
62
+ private buildStyleRules(cssAst: csstree.CssNode): StyleRule[] {
63
+ const rules: StyleRule[] = [];
64
+
65
+ csstree.walk(cssAst, {
66
+ visit: 'Rule',
67
+ enter: (node) => {
68
+ const rule = node as csstree.Rule;
69
+ const selector = csstree.generate(rule.prelude);
70
+ const properties: CSSProperties = {};
71
+
72
+ csstree.walk(rule.block, {
73
+ visit: 'Declaration',
74
+ enter: (declNode) => {
75
+ const decl = declNode as csstree.Declaration;
76
+ const property = decl.property;
77
+ const value = csstree.generate(decl.value);
78
+ properties[property] = value;
79
+ },
80
+ });
81
+
82
+ rules.push({ selector, properties });
83
+ },
84
+ });
85
+
86
+ return rules;
87
+ }
88
+
89
+ /**
90
+ * Gets the class attribute value from an element
91
+ */
92
+ private getClassNames(element: Element): string[] {
93
+ const classAttr = element.attrs.find((attr) => attr.name === 'class');
94
+ return classAttr ? classAttr.value.split(/\s+/).filter(Boolean) : [];
95
+ }
96
+
97
+ /**
98
+ * Checks if an element matches a CSS selector (simplified implementation)
99
+ */
100
+ private matchesSelector(element: Element, selector: string): boolean {
101
+ const trimmedSelector = selector.trim();
102
+
103
+ // Class selector
104
+ if (trimmedSelector.startsWith('.')) {
105
+ const className = trimmedSelector.slice(1);
106
+ return this.getClassNames(element).includes(className);
107
+ }
108
+
109
+ // Tag selector
110
+ if (/^[a-z]+$/i.test(trimmedSelector)) {
111
+ return element.tagName === trimmedSelector;
112
+ }
113
+
114
+ // ID selector (basic support)
115
+ if (trimmedSelector.startsWith('#')) {
116
+ const id = trimmedSelector.slice(1);
117
+ const idAttr = element.attrs.find((attr) => attr.name === 'id');
118
+ return idAttr?.value === id;
119
+ }
120
+
121
+ return false;
122
+ }
123
+
124
+ /**
125
+ * Applies CSS rules to all elements in the AST
126
+ */
127
+ private traverseAndApplyStyles(
128
+ node: ASTNode,
129
+ styleRules: StyleRule[],
130
+ elementsMap: Map<Element, CSSProperties>,
131
+ ): void {
132
+ const traverse = (currentNode: ASTNode) => {
133
+ if ('tagName' in currentNode) {
134
+ const element = currentNode as Element;
135
+ const computedStyles: CSSProperties = {};
136
+
137
+ // Apply matching rules
138
+ for (const rule of styleRules) {
139
+ if (this.matchesSelector(element, rule.selector)) {
140
+ Object.assign(computedStyles, rule.properties);
141
+ }
142
+ }
143
+
144
+ elementsMap.set(element, computedStyles);
145
+ }
146
+
147
+ if ('childNodes' in currentNode && currentNode.childNodes) {
148
+ for (const child of currentNode.childNodes) {
149
+ traverse(child);
150
+ }
151
+ }
152
+ };
153
+
154
+ traverse(node);
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Helper to find elements by tag name in the AST
160
+ * @param node - Starting node to search from
161
+ * @param tagName - Tag name to search for
162
+ * @returns Array of matching element nodes
163
+ */
164
+ export function findElementsByTagName(
165
+ node: ASTNode,
166
+ tagName: string,
167
+ ): Element[] {
168
+ const results: Element[] = [];
169
+
170
+ function traverse(currentNode: ASTNode) {
171
+ if ('tagName' in currentNode && currentNode.tagName === tagName) {
172
+ results.push(currentNode as Element);
173
+ }
174
+
175
+ if ('childNodes' in currentNode && currentNode.childNodes) {
176
+ for (const child of currentNode.childNodes) {
177
+ traverse(child);
178
+ }
179
+ }
180
+ }
181
+
182
+ traverse(node);
183
+ return results;
184
+ }
185
+
186
+ /**
187
+ * Helper to get text content from a node
188
+ * @param node - Node to extract text from
189
+ * @returns Concatenated text content
190
+ */
191
+ export function getTextContent(node: ASTNode): string {
192
+ let text = '';
193
+
194
+ function traverse(currentNode: ASTNode) {
195
+ if ('value' in currentNode && typeof currentNode.value === 'string') {
196
+ text += currentNode.value;
197
+ }
198
+
199
+ if ('childNodes' in currentNode && currentNode.childNodes) {
200
+ for (const child of currentNode.childNodes) {
201
+ traverse(child);
202
+ }
203
+ }
204
+ }
205
+
206
+ traverse(node);
207
+ return text;
208
+ }
209
+
210
+ // /**
211
+ // * Helper to get computed styles for an element
212
+ // * @param element - Element to get styles for
213
+ // * @param elementsMap - Map of elements to their computed styles
214
+ // * @returns Computed styles for the element
215
+ // */
216
+ // export function getComputedStyles(
217
+ // element: Element,
218
+ // elementsMap: Map<Element, CSSProperties>,
219
+ // ): CSSProperties {
220
+ // return elementsMap.get(element) || {};
221
+ // }