Skip to content

Commit 65628b1

Browse files
committed
added verdant caption preset
1 parent 4740c3b commit 65628b1

File tree

5 files changed

+127
-1
lines changed

5 files changed

+127
-1
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@diffusionstudio/core",
33
"private": false,
4-
"version": "1.0.0-beta.13",
4+
"version": "1.0.0-beta.14",
55
"type": "module",
66
"description": "Build bleeding edge video processing applications",
77
"files": [

src/tracks/caption/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ export * from './preset.deserializer';
1414
export * from './preset.interface';
1515
export * from './preset.solar';
1616
export * from './preset.whisper';
17+
export * from './preset.verdant';

src/tracks/caption/preset.0.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { SpotlightCaptionPreset } from './preset.spotlight';
1515
import { CascadeCaptionPreset } from './preset.cascade';
1616
import { GuineaCaptionPreset } from './preset.guinea';
1717
import { CaptionPresetDeserializer } from './preset.deserializer';
18+
import { VerdantCaptionPreset } from './preset.verdant';
1819

1920
import type { frame, hex } from '../../types';
2021

@@ -123,6 +124,25 @@ describe('(0) The Caption Presets', () => {
123124
}
124125
});
125126

127+
it('should generate captions with a highlighted green word', async () => {
128+
const strategy = new VerdantCaptionPreset({
129+
color: '#333333',
130+
generatorOptions: { count: [3] },
131+
});
132+
133+
await strategy.applyTo(track);
134+
expect(strategy.type).toBe('VERDANT');
135+
136+
expect(track.clips.length).toBe(18);
137+
expect(track.clips.at(0)?.start.seconds).toBe(10);
138+
139+
for (const clip of track.clips as ComplexTextClip[]) {
140+
expect(clip.text.length).toBeGreaterThan(0);
141+
expect(clip.segments.length).toBe(1);
142+
expect(clip.segments[0].start).not.toBeGreaterThan(clip.segments[0].stop ?? 0);
143+
}
144+
});
145+
126146
it('should generate captions with hidden words', async () => {
127147
const strategy = new CascadeCaptionPreset({
128148
generatorOptions: { count: [6] },

src/tracks/caption/preset.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export type CaptionPresetType =
1616
| 'GUINEA'
1717
| 'SOLAR'
1818
| 'WHISPER'
19+
| 'VERDANT'
1920
| string;
2021

2122
export type DefaultCaptionPresetConfig = {
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/**
2+
* Copyright (c) 2024 The Diffusion Studio Authors
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla
5+
* Public License, v. 2.0 that can be found in the LICENSE file.
6+
*/
7+
8+
import { Timestamp } from '../../models';
9+
import { Serializer, serializable } from '../../services';
10+
import { ComplexTextClip, Font } from '../../clips';
11+
12+
import type { SingleColorCaptionPresetConfig, CaptionPresetType, ScreenSize } from './preset.types';
13+
import type { CaptionPresetStrategy } from './preset.interface';
14+
import type { GeneratorOptions } from '../../models';
15+
import type { CaptionTrack } from './caption';
16+
import type { hex } from '../../types';
17+
18+
export class VerdantCaptionPreset extends Serializer implements CaptionPresetStrategy {
19+
private _initialized = false;
20+
21+
@serializable()
22+
public generatorOptions: GeneratorOptions;
23+
24+
@serializable()
25+
readonly type: CaptionPresetType = 'VERDANT';
26+
27+
@serializable()
28+
public color: hex;
29+
30+
@serializable(ComplexTextClip)
31+
public clip: ComplexTextClip | undefined;
32+
33+
public constructor(config: Partial<SingleColorCaptionPresetConfig> = {}) {
34+
super();
35+
36+
this.generatorOptions = config.generatorOptions ?? { duration: [1] };
37+
this.clip = config.clip;
38+
this.color = config.color ?? '#69E34C';
39+
}
40+
41+
public async init(composition?: ScreenSize) {
42+
if (this._initialized || !composition) return;
43+
44+
if (!this.clip) {
45+
this.clip = await new ComplexTextClip({
46+
textAlign: 'center',
47+
textBaseline: 'middle',
48+
fontSize: 15,
49+
fillStyle: '#FFFFFF',
50+
shadow: {
51+
color: '#000000',
52+
blur: 4,
53+
alpha: 0.7,
54+
angle: Math.PI / 4,
55+
distance: 2,
56+
},
57+
stroke: {
58+
width: 3,
59+
color: '#000000',
60+
},
61+
maxWidth: composition.width * 0.5,
62+
leading: 1.1,
63+
font: Font.fromFamily({ family: 'Montserrat', weight: '800' }),
64+
textCase: 'upper',
65+
styles: [{
66+
fillStyle: this.color,
67+
fontSize: 19,
68+
}],
69+
position: 'center',
70+
});
71+
}
72+
73+
await this.clip.font.load();
74+
this._initialized = true;
75+
}
76+
77+
public async applyTo(track: CaptionTrack): Promise<void> {
78+
await this.init(track.composition);
79+
80+
if (!this.clip || !track.clip?.transcript) {
81+
throw new Error('Preset needs to be initialized first');
82+
}
83+
84+
const offset = track.clip?.offset ?? new Timestamp();
85+
86+
// add captions
87+
for (const sequence of track.clip.transcript.iter(this.generatorOptions)) {
88+
for (let i = 0; i < sequence.words.length; i++) {
89+
const tokens = sequence.words.map((s) => s.text);
90+
const clip = this.clip.copy().set({
91+
text: tokens.join(' '),
92+
stop: sequence.words[i].stop.add(offset),
93+
start: sequence.words[i].start.add(offset),
94+
segments: [{
95+
index: 0,
96+
start: tokens.slice(0, i).join(' ').length,
97+
stop: tokens.slice(0, i + 1).join(' ').length,
98+
}],
99+
});
100+
await track.appendClip(clip);
101+
}
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)