@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
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
|
+
// }
|