@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/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// This file is kept for backward compatibility
|
|
2
|
+
// For CLI usage, use the 'staticstripes' command instead
|
|
3
|
+
// Example: npx staticstripes generate -p ./examples/demo
|
|
4
|
+
|
|
5
|
+
export { HTMLParser } from './html-parser.js';
|
|
6
|
+
export { HTMLProjectParser } from './html-project-parser.js';
|
|
7
|
+
export { Project } from './project.js';
|
|
8
|
+
export { makeFFmpegCommand, runFFMpeg } from './ffmpeg.js';
|
|
9
|
+
export { getAssetDuration } from './ffprobe.js';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LabelGenerator - Generates unique labels for FFmpeg filter graph streams
|
|
3
|
+
* Maintains a ledger of used labels to ensure uniqueness
|
|
4
|
+
*/
|
|
5
|
+
export class LabelGenerator {
|
|
6
|
+
private usedLabels: Set<string> = new Set();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generates a unique random label
|
|
10
|
+
* Format: [a-z][0-999]
|
|
11
|
+
* If collision occurs, regenerates until unique
|
|
12
|
+
*/
|
|
13
|
+
generate(): string {
|
|
14
|
+
let label: string;
|
|
15
|
+
let attempts = 0;
|
|
16
|
+
const maxAttempts = 10000; // Safety limit
|
|
17
|
+
|
|
18
|
+
do {
|
|
19
|
+
label = this.generateRandom();
|
|
20
|
+
attempts++;
|
|
21
|
+
|
|
22
|
+
if (attempts >= maxAttempts) {
|
|
23
|
+
// Fallback: use timestamp-based label to guarantee uniqueness
|
|
24
|
+
label = `t${Date.now()}${Math.random().toString(36).substring(2, 5)}`;
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
} while (this.usedLabels.has(label));
|
|
28
|
+
|
|
29
|
+
this.usedLabels.add(label);
|
|
30
|
+
return label;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Generates a random label (may not be unique)
|
|
35
|
+
*/
|
|
36
|
+
private generateRandom(): string {
|
|
37
|
+
const letter = String.fromCharCode(97 + Math.floor(Math.random() * 26)); // a-z
|
|
38
|
+
const num = Math.floor(Math.random() * 1000);
|
|
39
|
+
return `${letter}${num}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Marks a label as used (for external labels like '0:v', 'outv')
|
|
44
|
+
*/
|
|
45
|
+
markUsed(label: string): void {
|
|
46
|
+
this.usedLabels.add(label);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Checks if a label is already used
|
|
51
|
+
*/
|
|
52
|
+
isUsed(label: string): boolean {
|
|
53
|
+
return this.usedLabels.has(label);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Clears all used labels (for testing or reset)
|
|
58
|
+
*/
|
|
59
|
+
clear(): void {
|
|
60
|
+
this.usedLabels.clear();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Returns the count of used labels
|
|
65
|
+
*/
|
|
66
|
+
getUsedCount(): number {
|
|
67
|
+
return this.usedLabels.size;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const gen = new LabelGenerator();
|
|
72
|
+
|
|
73
|
+
// fucking singleton
|
|
74
|
+
export const getLabel = () => gen.generate();
|
package/src/project.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Asset, Output, SequenceDefinition } from './type';
|
|
2
|
+
import { Label } from './ffmpeg';
|
|
3
|
+
import { AssetManager } from './asset-manager';
|
|
4
|
+
import { Sequence } from './sequence';
|
|
5
|
+
import { FilterBuffer } from './stream';
|
|
6
|
+
import { ExpressionContext, FragmentData } from './expression-parser';
|
|
7
|
+
import { renderContainers } from './container-renderer';
|
|
8
|
+
import { dirname } from 'path';
|
|
9
|
+
|
|
10
|
+
export class Project {
|
|
11
|
+
private assetManager: AssetManager;
|
|
12
|
+
private expressionContext: ExpressionContext;
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
private sequencesDefinitions: SequenceDefinition[],
|
|
16
|
+
assets: Asset[],
|
|
17
|
+
private outputs: Map<string, Output>,
|
|
18
|
+
private cssText: string,
|
|
19
|
+
private projectPath: string,
|
|
20
|
+
) {
|
|
21
|
+
this.assetManager = new AssetManager(assets);
|
|
22
|
+
this.expressionContext = {
|
|
23
|
+
fragments: new Map<string, FragmentData>(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async build(outputName: string): Promise<FilterBuffer> {
|
|
28
|
+
const output = this.getOutput(outputName);
|
|
29
|
+
if (!output) {
|
|
30
|
+
throw new Error(`Output "${outputName}" not found`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let buf = new FilterBuffer();
|
|
34
|
+
let mainSequence: Sequence | null = null;
|
|
35
|
+
|
|
36
|
+
this.sequencesDefinitions.forEach((sequenceDefinition) => {
|
|
37
|
+
const seq = new Sequence(
|
|
38
|
+
buf,
|
|
39
|
+
sequenceDefinition,
|
|
40
|
+
output,
|
|
41
|
+
this.getAssetManager(),
|
|
42
|
+
this.expressionContext,
|
|
43
|
+
);
|
|
44
|
+
if (seq.isEmpty()) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
seq.build();
|
|
49
|
+
|
|
50
|
+
if (!mainSequence) {
|
|
51
|
+
mainSequence = seq;
|
|
52
|
+
} else {
|
|
53
|
+
mainSequence.overlayWith(seq);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (mainSequence) {
|
|
58
|
+
const sequence: Sequence = mainSequence;
|
|
59
|
+
sequence.getVideoStream().endTo({
|
|
60
|
+
tag: 'outv',
|
|
61
|
+
isAudio: false,
|
|
62
|
+
});
|
|
63
|
+
sequence.getAudioStream().endTo({
|
|
64
|
+
tag: 'outa',
|
|
65
|
+
isAudio: true,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return buf;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public printStats() {
|
|
73
|
+
console.log('\n=== Project stats ===\n');
|
|
74
|
+
console.log('== Assets ==\n');
|
|
75
|
+
this.assetManager.getAssetIndexMap().forEach((_index, assetName) => {
|
|
76
|
+
const asset = this.assetManager.getAssetByName(assetName)!;
|
|
77
|
+
|
|
78
|
+
console.log(
|
|
79
|
+
`Asset "${asset.name}" (${asset.type}) dimensions: w=${asset.width}, h=${asset.height}, rotation: ${asset.rotation}°, duration: ${asset.duration}, hasVideo: ${asset.hasVideo}, hasAudio: ${asset.hasAudio}`,
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public getAssetManager(): AssetManager {
|
|
85
|
+
return this.assetManager;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public getOutput(outputName: string): Output | undefined {
|
|
89
|
+
return this.outputs.get(outputName);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public getOutputs(): Map<string, Output> {
|
|
93
|
+
return this.outputs;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public getCssText(): string {
|
|
97
|
+
return this.cssText;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public getSequenceDefinitions(): SequenceDefinition[] {
|
|
101
|
+
return this.sequencesDefinitions;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Delegation methods for convenience
|
|
105
|
+
public getAssetIndexMap(): Map<string, number> {
|
|
106
|
+
return this.assetManager.getAssetIndexMap();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public getAssetByName(name: string): Asset | undefined {
|
|
110
|
+
return this.assetManager.getAssetByName(name);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public getVideoInputLabelByAssetName(name: string): Label {
|
|
114
|
+
return this.assetManager.getVideoInputLabelByAssetName(name);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public getAudioInputLabelByAssetName(name: string): Label {
|
|
118
|
+
return this.assetManager.getAudioInputLabelByAssetName(name);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Renders all containers and creates virtual assets for them
|
|
123
|
+
*/
|
|
124
|
+
public async renderContainers(outputName: string): Promise<void> {
|
|
125
|
+
const output = this.getOutput(outputName);
|
|
126
|
+
if (!output) {
|
|
127
|
+
throw new Error(`Output "${outputName}" not found`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Collect all fragments with containers
|
|
131
|
+
const fragmentsWithContainers = this.sequencesDefinitions.flatMap((seq) =>
|
|
132
|
+
seq.fragments.filter((frag) => frag.container),
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
if (fragmentsWithContainers.length === 0) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log('\n=== Rendering Containers ===\n');
|
|
140
|
+
|
|
141
|
+
const containers = fragmentsWithContainers.map((frag) => frag.container!);
|
|
142
|
+
const projectDir = dirname(this.projectPath);
|
|
143
|
+
|
|
144
|
+
const results = await renderContainers(
|
|
145
|
+
containers,
|
|
146
|
+
this.cssText,
|
|
147
|
+
output.resolution.width,
|
|
148
|
+
output.resolution.height,
|
|
149
|
+
projectDir,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Create virtual assets and update fragment assetNames
|
|
153
|
+
for (const result of results) {
|
|
154
|
+
const virtualAssetName = result.container.id;
|
|
155
|
+
|
|
156
|
+
// Create virtual asset
|
|
157
|
+
const virtualAsset: Asset = {
|
|
158
|
+
name: virtualAssetName,
|
|
159
|
+
path: result.screenshotPath,
|
|
160
|
+
type: 'image',
|
|
161
|
+
duration: 0,
|
|
162
|
+
width: output.resolution.width,
|
|
163
|
+
height: output.resolution.height,
|
|
164
|
+
rotation: 0,
|
|
165
|
+
hasVideo: true,
|
|
166
|
+
hasAudio: false,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
this.assetManager.addVirtualAsset(virtualAsset);
|
|
170
|
+
|
|
171
|
+
// Update fragment assetName
|
|
172
|
+
const fragment = fragmentsWithContainers.find(
|
|
173
|
+
(frag) => frag.container?.id === result.container.id,
|
|
174
|
+
);
|
|
175
|
+
if (fragment) {
|
|
176
|
+
fragment.assetName = virtualAssetName;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { ExpressionContext, parseExpression } from './expression-parser';
|
|
2
|
+
import { Project } from './project';
|
|
3
|
+
import { Sequence } from './sequence';
|
|
4
|
+
import { FilterBuffer } from './stream';
|
|
5
|
+
|
|
6
|
+
export const getSampleSequences = (
|
|
7
|
+
project: Project,
|
|
8
|
+
buf: FilterBuffer,
|
|
9
|
+
expressionContext: ExpressionContext,
|
|
10
|
+
outputName: string,
|
|
11
|
+
) => {
|
|
12
|
+
const output = project.getOutput(outputName);
|
|
13
|
+
if (!output) {
|
|
14
|
+
throw new Error(`Output "${outputName}" not found`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const seq1 = new Sequence(
|
|
18
|
+
buf,
|
|
19
|
+
{
|
|
20
|
+
fragments: [
|
|
21
|
+
{
|
|
22
|
+
id: 'f_01',
|
|
23
|
+
enabled: true,
|
|
24
|
+
assetName: 'intro_image',
|
|
25
|
+
duration: 4000, // asset duration is 0
|
|
26
|
+
trimLeft: 0,
|
|
27
|
+
overlayLeft: 0,
|
|
28
|
+
overlayZIndex: 1,
|
|
29
|
+
transitionIn: '',
|
|
30
|
+
transitionInDuration: 0,
|
|
31
|
+
transitionOut: 'fade',
|
|
32
|
+
transitionOutDuration: 500,
|
|
33
|
+
objectFit: 'cover',
|
|
34
|
+
objectFitContain: 'ambient',
|
|
35
|
+
objectFitContainAmbientBlurStrength: 25,
|
|
36
|
+
objectFitContainAmbientBrightness: -0.1,
|
|
37
|
+
objectFitContainAmbientSaturation: 0.7,
|
|
38
|
+
objectFitContainPillarboxColor: '#000000',
|
|
39
|
+
chromakey: false,
|
|
40
|
+
chromakeyBlend: 0.1,
|
|
41
|
+
chromakeySimilarity: 0.1,
|
|
42
|
+
chromakeyColor: '#000000',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'f_02',
|
|
46
|
+
enabled: true,
|
|
47
|
+
assetName: 'clip_01',
|
|
48
|
+
duration: 11330 - 3000, // asset duration is 11330
|
|
49
|
+
trimLeft: 3000,
|
|
50
|
+
overlayLeft: 0,
|
|
51
|
+
overlayZIndex: 1,
|
|
52
|
+
transitionIn: 'fade',
|
|
53
|
+
transitionInDuration: 1000,
|
|
54
|
+
transitionOut: '',
|
|
55
|
+
transitionOutDuration: 0,
|
|
56
|
+
objectFit: 'contain',
|
|
57
|
+
objectFitContain: 'ambient',
|
|
58
|
+
objectFitContainAmbientBlurStrength: 25,
|
|
59
|
+
objectFitContainAmbientBrightness: -0.1,
|
|
60
|
+
objectFitContainAmbientSaturation: 0.7,
|
|
61
|
+
objectFitContainPillarboxColor: '#000000',
|
|
62
|
+
chromakey: false,
|
|
63
|
+
chromakeyBlend: 0.1,
|
|
64
|
+
chromakeySimilarity: 0.1,
|
|
65
|
+
chromakeyColor: '#000000',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'f_03',
|
|
69
|
+
enabled: true,
|
|
70
|
+
assetName: 'glitch',
|
|
71
|
+
duration: 500, // asset duration is 10000
|
|
72
|
+
trimLeft: 0,
|
|
73
|
+
overlayLeft: -250,
|
|
74
|
+
overlayZIndex: 1,
|
|
75
|
+
transitionIn: '',
|
|
76
|
+
transitionInDuration: 0,
|
|
77
|
+
transitionOut: '',
|
|
78
|
+
transitionOutDuration: 0,
|
|
79
|
+
objectFit: 'cover',
|
|
80
|
+
objectFitContain: 'pillarbox',
|
|
81
|
+
objectFitContainAmbientBlurStrength: 25,
|
|
82
|
+
objectFitContainAmbientBrightness: -0.1,
|
|
83
|
+
objectFitContainAmbientSaturation: 0.7,
|
|
84
|
+
objectFitContainPillarboxColor: '#000000',
|
|
85
|
+
chromakey: true,
|
|
86
|
+
chromakeyBlend: 0.1,
|
|
87
|
+
chromakeySimilarity: 0.1,
|
|
88
|
+
chromakeyColor: '#000000',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: 'f_04',
|
|
92
|
+
enabled: true,
|
|
93
|
+
assetName: 'clip_02',
|
|
94
|
+
duration: 3000, // asset duration is 90245
|
|
95
|
+
trimLeft: 0,
|
|
96
|
+
overlayLeft: -250,
|
|
97
|
+
overlayZIndex: -1,
|
|
98
|
+
transitionIn: '',
|
|
99
|
+
transitionInDuration: 0,
|
|
100
|
+
transitionOut: 'fade',
|
|
101
|
+
transitionOutDuration: 1000,
|
|
102
|
+
objectFit: 'contain',
|
|
103
|
+
objectFitContain: 'ambient',
|
|
104
|
+
objectFitContainAmbientBlurStrength: 25,
|
|
105
|
+
objectFitContainAmbientBrightness: -0.1,
|
|
106
|
+
objectFitContainAmbientSaturation: 0.7,
|
|
107
|
+
objectFitContainPillarboxColor: '#000000',
|
|
108
|
+
chromakey: false,
|
|
109
|
+
chromakeyBlend: 0.1,
|
|
110
|
+
chromakeySimilarity: 0.1,
|
|
111
|
+
chromakeyColor: '#000000',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: 'ending_screen',
|
|
115
|
+
enabled: true,
|
|
116
|
+
assetName: 'intro_image',
|
|
117
|
+
duration: 4000, // asset duration is 0
|
|
118
|
+
trimLeft: 0,
|
|
119
|
+
overlayLeft: 0,
|
|
120
|
+
overlayZIndex: 1,
|
|
121
|
+
transitionIn: '',
|
|
122
|
+
transitionInDuration: 0,
|
|
123
|
+
transitionOut: 'fade',
|
|
124
|
+
transitionOutDuration: 500,
|
|
125
|
+
objectFit: 'cover',
|
|
126
|
+
objectFitContain: 'ambient',
|
|
127
|
+
objectFitContainAmbientBlurStrength: 25,
|
|
128
|
+
objectFitContainAmbientBrightness: -0.1,
|
|
129
|
+
objectFitContainAmbientSaturation: 0.7,
|
|
130
|
+
objectFitContainPillarboxColor: '#000000',
|
|
131
|
+
chromakey: false,
|
|
132
|
+
chromakeyBlend: 0.1,
|
|
133
|
+
chromakeySimilarity: 0.1,
|
|
134
|
+
chromakeyColor: '#000000',
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
output,
|
|
139
|
+
project.getAssetManager(),
|
|
140
|
+
expressionContext,
|
|
141
|
+
);
|
|
142
|
+
seq1.build();
|
|
143
|
+
|
|
144
|
+
const seq2 = new Sequence(
|
|
145
|
+
buf,
|
|
146
|
+
{
|
|
147
|
+
fragments: [
|
|
148
|
+
{
|
|
149
|
+
id: 'f_06',
|
|
150
|
+
enabled: true,
|
|
151
|
+
assetName: 'guitar_music',
|
|
152
|
+
duration: 4000, // asset duration is 224653
|
|
153
|
+
trimLeft: 0,
|
|
154
|
+
overlayLeft: 0,
|
|
155
|
+
overlayZIndex: 1,
|
|
156
|
+
transitionIn: '',
|
|
157
|
+
transitionInDuration: 0,
|
|
158
|
+
transitionOut: 'fade',
|
|
159
|
+
transitionOutDuration: 500,
|
|
160
|
+
objectFit: 'cover',
|
|
161
|
+
objectFitContain: 'ambient',
|
|
162
|
+
objectFitContainAmbientBlurStrength: 25,
|
|
163
|
+
objectFitContainAmbientBrightness: -0.1,
|
|
164
|
+
objectFitContainAmbientSaturation: 0.7,
|
|
165
|
+
objectFitContainPillarboxColor: '#000000',
|
|
166
|
+
chromakey: false,
|
|
167
|
+
chromakeyBlend: 0.1,
|
|
168
|
+
chromakeySimilarity: 0.1,
|
|
169
|
+
chromakeyColor: '#000000',
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
output,
|
|
174
|
+
project.getAssetManager(),
|
|
175
|
+
expressionContext,
|
|
176
|
+
);
|
|
177
|
+
seq2.build();
|
|
178
|
+
|
|
179
|
+
const seq3 = new Sequence(
|
|
180
|
+
buf,
|
|
181
|
+
{
|
|
182
|
+
fragments: [
|
|
183
|
+
{
|
|
184
|
+
id: 'end_music',
|
|
185
|
+
enabled: true,
|
|
186
|
+
assetName: 'guitar_music',
|
|
187
|
+
duration: 4000, // asset duration is 224653
|
|
188
|
+
trimLeft: 0,
|
|
189
|
+
overlayLeft: parseExpression('calc(#ending_screen.time.start)'),
|
|
190
|
+
overlayZIndex: 1,
|
|
191
|
+
transitionIn: '',
|
|
192
|
+
transitionInDuration: 0,
|
|
193
|
+
transitionOut: 'fade',
|
|
194
|
+
transitionOutDuration: 500,
|
|
195
|
+
objectFit: 'cover',
|
|
196
|
+
objectFitContain: 'ambient',
|
|
197
|
+
objectFitContainAmbientBlurStrength: 25,
|
|
198
|
+
objectFitContainAmbientBrightness: -0.1,
|
|
199
|
+
objectFitContainAmbientSaturation: 0.7,
|
|
200
|
+
objectFitContainPillarboxColor: '#000000',
|
|
201
|
+
chromakey: false,
|
|
202
|
+
chromakeyBlend: 0.1,
|
|
203
|
+
chromakeySimilarity: 0.1,
|
|
204
|
+
chromakeyColor: '#000000',
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
output,
|
|
209
|
+
project.getAssetManager(),
|
|
210
|
+
expressionContext,
|
|
211
|
+
);
|
|
212
|
+
seq3.build();
|
|
213
|
+
|
|
214
|
+
seq1.overlayWith(seq2);
|
|
215
|
+
seq1.overlayWith(seq3);
|
|
216
|
+
|
|
217
|
+
seq1.getVideoStream().endTo({
|
|
218
|
+
tag: 'outv',
|
|
219
|
+
isAudio: false,
|
|
220
|
+
});
|
|
221
|
+
seq1.getAudioStream().endTo({
|
|
222
|
+
tag: 'outa',
|
|
223
|
+
isAudio: true,
|
|
224
|
+
});
|
|
225
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Project } from './project';
|
|
2
|
+
import {
|
|
3
|
+
ChromakeyBlend,
|
|
4
|
+
ChromakeySimilarity,
|
|
5
|
+
FilterBuffer,
|
|
6
|
+
makeStream,
|
|
7
|
+
} from './stream';
|
|
8
|
+
|
|
9
|
+
// @ts-expect-error unused
|
|
10
|
+
const addSampleStreams = (project: Project, buf: FilterBuffer) => {
|
|
11
|
+
const glitchStream = makeStream(
|
|
12
|
+
project.getAssetManager().getVideoInputLabelByAssetName('glitch'),
|
|
13
|
+
buf,
|
|
14
|
+
)
|
|
15
|
+
.trim(0, 2)
|
|
16
|
+
.fitOutputContain({ width: 1920, height: 1080 })
|
|
17
|
+
.fps(30)
|
|
18
|
+
.chromakey({
|
|
19
|
+
blend: ChromakeyBlend.Smooth,
|
|
20
|
+
similarity: ChromakeySimilarity.Strict,
|
|
21
|
+
color: '#000000',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const clip02VideoStream = makeStream(
|
|
25
|
+
project.getAssetManager().getVideoInputLabelByAssetName('clip_02'),
|
|
26
|
+
buf,
|
|
27
|
+
)
|
|
28
|
+
.trim(0, 5)
|
|
29
|
+
.fitOutputContain(
|
|
30
|
+
{
|
|
31
|
+
width: 1920,
|
|
32
|
+
height: 1080,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
ambient: {
|
|
36
|
+
blurStrength: 25,
|
|
37
|
+
brightness: -0.1,
|
|
38
|
+
saturation: 0.7,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
.fps(30);
|
|
43
|
+
|
|
44
|
+
const introImageStream = makeStream(
|
|
45
|
+
project.getAssetManager().getVideoInputLabelByAssetName('intro_image'),
|
|
46
|
+
buf,
|
|
47
|
+
)
|
|
48
|
+
.fps(30)
|
|
49
|
+
.fitOutputCover({ width: 1920, height: 1080 })
|
|
50
|
+
.tPad({
|
|
51
|
+
start: 3,
|
|
52
|
+
startMode: 'clone',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
makeStream(
|
|
56
|
+
project.getAssetManager().getVideoInputLabelByAssetName('clip_01'),
|
|
57
|
+
buf,
|
|
58
|
+
)
|
|
59
|
+
.trim(0, 5) // length = 5
|
|
60
|
+
.fitOutputContain(
|
|
61
|
+
{
|
|
62
|
+
width: 1920,
|
|
63
|
+
height: 1080,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
ambient: {
|
|
67
|
+
blurStrength: 25,
|
|
68
|
+
brightness: -0.1,
|
|
69
|
+
saturation: 0.7,
|
|
70
|
+
},
|
|
71
|
+
// pillarbox: {
|
|
72
|
+
// color: '#ff0000',
|
|
73
|
+
// },
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
.fps(30)
|
|
77
|
+
.fade({
|
|
78
|
+
fades: [
|
|
79
|
+
{
|
|
80
|
+
type: 'in',
|
|
81
|
+
startTime: 0,
|
|
82
|
+
duration: 1,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
})
|
|
86
|
+
.overlayStream(glitchStream, {
|
|
87
|
+
// length = 5
|
|
88
|
+
offset: {
|
|
89
|
+
streamDuration: 5, // value from trim()
|
|
90
|
+
otherStreamDuration: 2, // value from trim() of glitch
|
|
91
|
+
otherStreamOffsetLeft: 4, // start of the glitch
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
.overlayStream(clip02VideoStream, {
|
|
95
|
+
// length = 11 ?
|
|
96
|
+
flipLayers: true,
|
|
97
|
+
offset: {
|
|
98
|
+
streamDuration: 5, // value from trim()
|
|
99
|
+
otherStreamDuration: 6, // 5 seconds of clip01, 1 second of glitch
|
|
100
|
+
otherStreamOffsetLeft: 5, // start of the of clip01stream
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
.fade({
|
|
104
|
+
fades: [
|
|
105
|
+
{
|
|
106
|
+
type: 'out',
|
|
107
|
+
startTime: 9,
|
|
108
|
+
duration: 1,
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
})
|
|
112
|
+
.concatStream(introImageStream)
|
|
113
|
+
.endTo({
|
|
114
|
+
tag: 'outv',
|
|
115
|
+
isAudio: false,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const clip02AudioStream = makeStream(
|
|
119
|
+
project.getAssetManager().getAudioInputLabelByAssetName('clip_02'),
|
|
120
|
+
buf,
|
|
121
|
+
).trim(0, 5);
|
|
122
|
+
|
|
123
|
+
makeStream(
|
|
124
|
+
project.getAssetManager().getAudioInputLabelByAssetName('clip_01'),
|
|
125
|
+
buf,
|
|
126
|
+
)
|
|
127
|
+
.trim(0, 5)
|
|
128
|
+
.fade({
|
|
129
|
+
fades: [
|
|
130
|
+
{
|
|
131
|
+
type: 'in',
|
|
132
|
+
startTime: 0,
|
|
133
|
+
duration: 1,
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
})
|
|
137
|
+
.concatStream(clip02AudioStream)
|
|
138
|
+
.endTo({
|
|
139
|
+
tag: 'outa',
|
|
140
|
+
isAudio: true,
|
|
141
|
+
});
|
|
142
|
+
};
|