etro 0.10.1 → 0.12.0
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/.github/workflows/nodejs.yml +17 -4
- package/.github/workflows/shipjs-trigger.yml +4 -1
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +10 -0
- package/.husky/prepare-commit-msg +11 -0
- package/CHANGELOG.md +24 -0
- package/CONTRIBUTING.md +18 -43
- package/README.md +1 -2
- package/commitlint.config.ts +39 -0
- package/dist/etro-cjs.js +83 -47
- package/dist/etro-iife.js +83 -47
- package/dist/layer/text.d.ts +16 -1
- package/dist/movie/movie.d.ts +20 -3
- package/karma.conf.js +10 -3
- package/package.json +14 -6
- package/scripts/effect/save-effect-samples.js +1 -1
- package/ship.config.js +12 -1
- package/src/layer/audio-source.ts +17 -28
- package/src/layer/text.ts +48 -2
- package/src/movie/movie.ts +38 -24
|
@@ -21,12 +21,25 @@ jobs:
|
|
|
21
21
|
- name: Update npm
|
|
22
22
|
run: |
|
|
23
23
|
npm i -g npm@^7.x
|
|
24
|
-
- name: npm
|
|
24
|
+
- name: Install npm dependencies
|
|
25
25
|
run: |
|
|
26
26
|
npm ci
|
|
27
27
|
node node_modules/puppeteer/install.js
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
env:
|
|
29
|
+
CI: true
|
|
30
|
+
- name: lint code
|
|
31
|
+
run: npm run lint
|
|
32
|
+
env:
|
|
33
|
+
CI: true
|
|
34
|
+
- name: compile project
|
|
35
|
+
run: npm run build
|
|
36
|
+
env:
|
|
37
|
+
CI: true
|
|
38
|
+
- name: run unit tests
|
|
39
|
+
run: xvfb-run --auto-servernum npm run test:unit
|
|
40
|
+
env:
|
|
41
|
+
CI: true
|
|
42
|
+
- name: run smoke tests
|
|
43
|
+
run: xvfb-run --auto-servernum npm run test:smoke
|
|
31
44
|
env:
|
|
32
45
|
CI: true
|
|
@@ -22,7 +22,10 @@ jobs:
|
|
|
22
22
|
else
|
|
23
23
|
npm install
|
|
24
24
|
fi
|
|
25
|
-
- run:
|
|
25
|
+
- run: |
|
|
26
|
+
git config --global user.email "16855387+clabe45@users.noreply.github.com"
|
|
27
|
+
git config --global user.name "Caleb Sacks"
|
|
28
|
+
npx shipjs trigger
|
|
26
29
|
env:
|
|
27
30
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
28
31
|
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [0.12.0] - 2024-01-15
|
|
9
|
+
### Added
|
|
10
|
+
- `stroke` option for `Text` layer ([#239](https://github.com/etro-js/etro/pull/239)).
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
- Bump @babel/traverse from 7.18.13 to 7.23.2 ([#244](https://github.com/etro-js/etro/pull/244)).
|
|
14
|
+
- Bump browserify-sign from 4.2.1 to 4.2.2 ([#245](https://github.com/etro-js/etro/pull/245)).
|
|
15
|
+
- Bump follow-redirects from 1.14.9 to 1.15.4 ([#247](https://github.com/etro-js/etro/pull/247)).
|
|
16
|
+
- Bump gitmoji-cli from 8.4.0 to 9.0.0.
|
|
17
|
+
|
|
18
|
+
## [0.11.0] - 2023-08-05
|
|
19
|
+
### Added
|
|
20
|
+
- `duration` option for `Movie#play` ([#208](https://github.com/etro-js/etro/pull/208)).
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- Audio and video layers going silent after the first time recording the movie ([#106](https://github.com/etro-js/etro/issues/106)).
|
|
24
|
+
- `Failed to set the 'currentTime' property on 'HTMLMediaElement'` error when seeking audio and video layers ([#227](https://github.com/etro-js/etro/pull/227)).
|
|
25
|
+
- Seeking while playing not updating the movie's current time ([#233](https://github.com/etro-js/etro/issues/233)).
|
|
26
|
+
|
|
27
|
+
### Security
|
|
28
|
+
- Bump word-wrap from 1.2.3 to 1.2.5 ([#222](https://github.com/etro-js/etro/pull/222)).
|
|
29
|
+
|
|
8
30
|
## [0.10.1] - 2023-07-16
|
|
9
31
|
### Security
|
|
10
32
|
- Bump engine.io and socket.io.
|
|
@@ -280,6 +302,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
280
302
|
- Gaussian blur
|
|
281
303
|
- Transform
|
|
282
304
|
|
|
305
|
+
[0.12.0]: https://github.com/etro-js/etro/compare/v0.11.0...v0.12.0
|
|
306
|
+
[0.11.0]: https://github.com/etro-js/etro/compare/v0.10.1...v0.11.0
|
|
283
307
|
[0.10.1]: https://github.com/etro-js/etro/compare/v0.10.0...v0.10.1
|
|
284
308
|
[0.10.0]: https://github.com/etro-js/etro/compare/v0.9.1...v0.10.0
|
|
285
309
|
[0.9.1]: https://github.com/etro-js/etro/compare/v0.9.0...v0.9.1
|
package/CONTRIBUTING.md
CHANGED
|
@@ -10,83 +10,58 @@ Thank you for considering contributing to Etro! There are many ways you can cont
|
|
|
10
10
|
|
|
11
11
|
## Setting up your local environment
|
|
12
12
|
|
|
13
|
-
#### Step 0: Dependencies
|
|
14
|
-
|
|
15
13
|
- You will need Git, Node, NPM (at least 7.x) and Firefox (for headless functional testing) installed.
|
|
16
|
-
|
|
17
|
-
#### Step 1: Fork
|
|
18
|
-
|
|
19
|
-
- Create your own fork of Etro. Then run
|
|
20
|
-
|
|
14
|
+
- To get started, create your own fork of Etro. Then run
|
|
21
15
|
```
|
|
22
16
|
git clone https://github.com/YOUR_USERNAME/etro.git
|
|
23
17
|
cd etro
|
|
24
18
|
npm install
|
|
25
|
-
npm test
|
|
19
|
+
npm run test:unit
|
|
20
|
+
npm run test:smoke
|
|
21
|
+
npm run test:integration
|
|
26
22
|
```
|
|
27
23
|
|
|
28
24
|
## Making your changes
|
|
29
25
|
|
|
30
|
-
#### Step 2: Code
|
|
31
|
-
|
|
32
26
|
- Make some changes and update tests
|
|
33
27
|
- If you are writing code, the linter uses [StandardJS](https://standardjs.com/rules.html) for style conventions
|
|
34
28
|
- If you're adding or updating an effect:
|
|
35
29
|
- Add your effect to **scripts/gen-effect-samples.html**
|
|
36
30
|
- Run `npm run effects`
|
|
37
31
|
- Briefly review the images in **spec/integration/assets/effect/**
|
|
38
|
-
-
|
|
32
|
+
- As you work, you can run
|
|
39
33
|
```
|
|
40
|
-
npm run
|
|
34
|
+
npm run fix
|
|
41
35
|
npm run build
|
|
42
|
-
npm test
|
|
36
|
+
npm run test:unit
|
|
37
|
+
npm run test:smoke
|
|
38
|
+
npm run test:integration
|
|
43
39
|
```
|
|
44
40
|
|
|
45
|
-
to lint
|
|
41
|
+
to lint and compile the code and run the tests on them. Husky will run these commands automatically when you commit.
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- Please follow these commit message guidelines:
|
|
50
|
-
- Optionally, prefix each commit message with [an appropriate emoji](https://gitmoji.dev), such as `:bug:` for fixes.
|
|
51
|
-
- Write in the imperative tense
|
|
52
|
-
- Wrap lines after 72 characters (for Vim add `filetype indent plugin on` to ~/.vimrc, it's enabled by default in Atom).
|
|
53
|
-
- Format:
|
|
54
|
-
```
|
|
55
|
-
:emoji: One-liner
|
|
56
|
-
|
|
57
|
-
Optional description
|
|
58
|
-
```
|
|
43
|
+
- Please commit to a new branch, not master
|
|
59
44
|
|
|
60
45
|
## Submitting your changes
|
|
61
46
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
- First, rebase (please avoid merging) to integrate your work with any new changes in the main repository
|
|
65
|
-
|
|
47
|
+
- Before pushing to your fork, rebase (please avoid merging) to integrate your work with any new changes in the main repository
|
|
66
48
|
```
|
|
67
49
|
git fetch upstream
|
|
68
50
|
git rebase upstream/master
|
|
69
51
|
```
|
|
70
|
-
|
|
71
|
-
- Push to the fork
|
|
72
|
-
|
|
73
|
-
#### Step 5: Pull request
|
|
74
|
-
|
|
75
|
-
- Open a pull request from the branch in your fork to the main repository
|
|
76
|
-
- If you changed any core functionality, make sure you explain your motives for those changes
|
|
77
|
-
|
|
78
|
-
#### Step 6: Feedback
|
|
79
|
-
|
|
52
|
+
- Open a pull request from the branch in your fork to the main repository. If you changed any core functionality, make sure you explain your motives for those changes
|
|
80
53
|
- A large part of the submission process is receiving feedback on how you can improve you pull request. If you need to change your pull request, feel free to push more commits.
|
|
81
54
|
|
|
82
|
-
##
|
|
55
|
+
## Tests
|
|
83
56
|
|
|
84
|
-
|
|
57
|
+
Automated tests are run with [KarmaJS](https://karma-runner.github.io/) in a headless Firefox session. Unit tests validate the logic of the code in etro, with the DOM and any other external dependencies mocked. Because audio cannot be captured in the GitHub Actions runner, the end-to-end tests are divided into two suites. All end-to-end tests that need to validate audio output should be placed in **spec/integration/**. All end-to-end tests that do **not** require an audio device should be placed in **spec/smoke/**. The integration tests can only be run locally, but the other two suites can be run anywhere.
|
|
85
58
|
|
|
86
|
-
|
|
59
|
+
## Code overview
|
|
87
60
|
|
|
88
61
|
### Events
|
|
89
62
|
|
|
63
|
+
> Events were deprecated in v0.10.0 in favor of async methods with callbacks.
|
|
64
|
+
|
|
90
65
|
Events emitted by Etro objects use a [pub/sub system](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern). To emit an event, use `event.publish(target, type, event)`. For instance,
|
|
91
66
|
|
|
92
67
|
```js
|
package/README.md
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/etro)
|
|
4
4
|
[](https://actions-badge.atrox.dev/etro-js/etro/goto)
|
|
5
|
+
[](https://discord.gg/myrBsQ8Cht)
|
|
5
6
|
|
|
6
7
|
Etro is a typescript framework for programmatically editing videos. It lets you
|
|
7
8
|
composite layers and add filters (effects). Etro comes shipped with text, video,
|
|
8
9
|
audio and image layers, along with a bunch of GLSL effects. You can also define
|
|
9
10
|
your own layers and effects with javascript and GLSL.
|
|
10
11
|
|
|
11
|
-
[Join our Discord](https://discord.gg/myrBsQ8Cht)
|
|
12
|
-
|
|
13
12
|
## Features
|
|
14
13
|
|
|
15
14
|
- Composite video and audio layers
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type {UserConfig} from '@commitlint/types';
|
|
2
|
+
import { RuleConfigSeverity } from "@commitlint/types";
|
|
3
|
+
|
|
4
|
+
const Configuration: UserConfig = {
|
|
5
|
+
/*
|
|
6
|
+
* Resolve and load @commitlint/format from node_modules.
|
|
7
|
+
* Referenced package must be installed
|
|
8
|
+
*/
|
|
9
|
+
formatter: '@commitlint/format',
|
|
10
|
+
/*
|
|
11
|
+
* Any rules defined here will override the default ones
|
|
12
|
+
*/
|
|
13
|
+
rules: {
|
|
14
|
+
'header-max-length': [RuleConfigSeverity.Error, 'always', 72],
|
|
15
|
+
'body-max-line-length': [RuleConfigSeverity.Error, 'always', 72],
|
|
16
|
+
},
|
|
17
|
+
/*
|
|
18
|
+
* Functions that return true if commitlint should ignore the given message.
|
|
19
|
+
*/
|
|
20
|
+
ignores: [(commit) => commit === ''],
|
|
21
|
+
/*
|
|
22
|
+
* Whether commitlint uses the default ignore rules.
|
|
23
|
+
*/
|
|
24
|
+
defaultIgnores: true,
|
|
25
|
+
/*
|
|
26
|
+
* Custom URL to show upon failure
|
|
27
|
+
*/
|
|
28
|
+
helpUrl:
|
|
29
|
+
'https://github.com/conventional-changelog/commitlint/#what-is-commitlint',
|
|
30
|
+
/*
|
|
31
|
+
* Custom prompt configs
|
|
32
|
+
*/
|
|
33
|
+
prompt: {
|
|
34
|
+
messages: {},
|
|
35
|
+
questions: {},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
module.exports = Configuration;
|
package/dist/etro-cjs.js
CHANGED
|
@@ -667,9 +667,11 @@ function AudioSourceMixin(superclass) {
|
|
|
667
667
|
_this._sourceStartTime = options.sourceStartTime || 0;
|
|
668
668
|
applyOptions(options, _this);
|
|
669
669
|
var load = function () {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
670
|
+
if (options.duration < 0) {
|
|
671
|
+
throw new Error('Invalid options.duration. It must be a non-negative value.');
|
|
672
|
+
}
|
|
673
|
+
if (_this.sourceStartTime > _this.source.duration) {
|
|
674
|
+
throw new Error('options.sourceStartTime cannot exceed options.source.duration');
|
|
673
675
|
}
|
|
674
676
|
_this._unstretchedDuration = options.duration || (_this.source.duration - _this.sourceStartTime);
|
|
675
677
|
_this.duration = _this._unstretchedDuration / (_this.playbackRate);
|
|
@@ -715,32 +717,15 @@ function AudioSourceMixin(superclass) {
|
|
|
715
717
|
_super.prototype.attach.call(this, movie);
|
|
716
718
|
// TODO: on unattach?
|
|
717
719
|
subscribe(movie, 'audiodestinationupdate', function (event) {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
_this.audioNode.disconnect(movie.actx.destination);
|
|
722
|
-
_this.audioNode.connect(event.destination);
|
|
723
|
-
}
|
|
720
|
+
_this.audioNode.disconnect(_this._lastAudioDestination);
|
|
721
|
+
_this.audioNode.connect(event.destination);
|
|
722
|
+
_this._lastAudioDestination = event.destination;
|
|
724
723
|
});
|
|
725
724
|
// connect to audiocontext
|
|
726
725
|
this._audioNode = this.audioNode || movie.actx.createMediaElementSource(this.source);
|
|
727
|
-
// Spy on connect and disconnect to remember if it connected to
|
|
728
|
-
// actx.destination (for Movie#record).
|
|
729
|
-
var oldConnect = this._audioNode.connect.bind(this.audioNode);
|
|
730
|
-
this._audioNode.connect = function (destination, outputIndex, inputIndex) {
|
|
731
|
-
_this._connectedToDestination = destination === movie.actx.destination;
|
|
732
|
-
return oldConnect(destination, outputIndex, inputIndex);
|
|
733
|
-
};
|
|
734
|
-
var oldDisconnect = this._audioNode.disconnect.bind(this.audioNode);
|
|
735
|
-
this._audioNode.disconnect = function (destination, output, input) {
|
|
736
|
-
if (_this._connectedToDestination &&
|
|
737
|
-
destination === movie.actx.destination) {
|
|
738
|
-
_this._connectedToDestination = false;
|
|
739
|
-
}
|
|
740
|
-
return oldDisconnect(destination, output, input);
|
|
741
|
-
};
|
|
742
726
|
// Connect to actx.destination by default (can be rewired by user)
|
|
743
727
|
this.audioNode.connect(movie.actx.destination);
|
|
728
|
+
this._lastAudioDestination = movie.actx.destination;
|
|
744
729
|
};
|
|
745
730
|
MixedAudioSource.prototype.detach = function () {
|
|
746
731
|
// Cache dest before super.detach() unsets this.movie
|
|
@@ -754,7 +739,12 @@ function AudioSourceMixin(superclass) {
|
|
|
754
739
|
};
|
|
755
740
|
MixedAudioSource.prototype.seek = function (time) {
|
|
756
741
|
_super.prototype.seek.call(this, time);
|
|
757
|
-
|
|
742
|
+
if (isNaN(this.currentTime)) {
|
|
743
|
+
this.source.currentTime = this.sourceStartTime;
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
this.source.currentTime = this.currentTime + this.sourceStartTime;
|
|
747
|
+
}
|
|
758
748
|
};
|
|
759
749
|
MixedAudioSource.prototype.render = function () {
|
|
760
750
|
_super.prototype.render.call(this);
|
|
@@ -1444,6 +1434,12 @@ var Image = /** @class */ (function (_super) {
|
|
|
1444
1434
|
return Image;
|
|
1445
1435
|
}(VisualSourceMixin(Visual)));
|
|
1446
1436
|
|
|
1437
|
+
var TextStrokePosition;
|
|
1438
|
+
(function (TextStrokePosition) {
|
|
1439
|
+
TextStrokePosition[TextStrokePosition["Inside"] = 0] = "Inside";
|
|
1440
|
+
TextStrokePosition[TextStrokePosition["Center"] = 1] = "Center";
|
|
1441
|
+
TextStrokePosition[TextStrokePosition["Outside"] = 2] = "Outside";
|
|
1442
|
+
})(TextStrokePosition || (TextStrokePosition = {}));
|
|
1447
1443
|
var Text = /** @class */ (function (_super) {
|
|
1448
1444
|
__extends(Text, _super);
|
|
1449
1445
|
/**
|
|
@@ -1468,6 +1464,7 @@ var Text = /** @class */ (function (_super) {
|
|
|
1468
1464
|
// this._prevMaxWidth = undefined;
|
|
1469
1465
|
}
|
|
1470
1466
|
Text.prototype.doRender = function () {
|
|
1467
|
+
var _a, _b;
|
|
1471
1468
|
_super.prototype.doRender.call(this);
|
|
1472
1469
|
var text = val(this, 'text', this.currentTime);
|
|
1473
1470
|
var font = val(this, 'font', this.currentTime);
|
|
@@ -1481,6 +1478,28 @@ var Text = /** @class */ (function (_super) {
|
|
|
1481
1478
|
this.cctx.textBaseline = val(this, 'textBaseline', this.currentTime);
|
|
1482
1479
|
this.cctx.direction = val(this, 'textDirection', this.currentTime);
|
|
1483
1480
|
this.cctx.fillText(text, val(this, 'textX', this.currentTime), val(this, 'textY', this.currentTime), maxWidth);
|
|
1481
|
+
var textStroke = val(this, 'textStroke', this.currentTime);
|
|
1482
|
+
if (textStroke) {
|
|
1483
|
+
this.cctx.strokeStyle = textStroke.color;
|
|
1484
|
+
this.cctx.lineWidth = (_a = textStroke.thickness) !== null && _a !== void 0 ? _a : 1;
|
|
1485
|
+
var position = (_b = textStroke.position) !== null && _b !== void 0 ? _b : 'outer';
|
|
1486
|
+
// Save the globalCompositeOperation, we have to revert it after stroking the text.
|
|
1487
|
+
var globalCompositionOperation = this.cctx.globalCompositeOperation;
|
|
1488
|
+
switch (position) {
|
|
1489
|
+
case TextStrokePosition.Inside:
|
|
1490
|
+
this.cctx.globalCompositeOperation = 'source-atop';
|
|
1491
|
+
this.cctx.lineWidth *= 2;
|
|
1492
|
+
break;
|
|
1493
|
+
case TextStrokePosition.Center:
|
|
1494
|
+
break;
|
|
1495
|
+
case TextStrokePosition.Outside:
|
|
1496
|
+
this.cctx.globalCompositeOperation = 'destination-over';
|
|
1497
|
+
this.cctx.lineWidth *= 2;
|
|
1498
|
+
break;
|
|
1499
|
+
}
|
|
1500
|
+
this.cctx.strokeText(text, val(this, 'textX', this.currentTime), val(this, 'textY', this.currentTime), maxWidth);
|
|
1501
|
+
this.cctx.globalCompositeOperation = globalCompositionOperation;
|
|
1502
|
+
}
|
|
1484
1503
|
this._prevText = text;
|
|
1485
1504
|
this._prevFont = font;
|
|
1486
1505
|
this._prevMaxWidth = maxWidth;
|
|
@@ -1509,7 +1528,7 @@ var Text = /** @class */ (function (_super) {
|
|
|
1509
1528
|
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
1510
1529
|
*/
|
|
1511
1530
|
Text.prototype.getDefaultOptions = function () {
|
|
1512
|
-
return __assign(__assign({}, Visual.prototype.getDefaultOptions()), { background: null, text: undefined, font: '10px sans-serif', color: parseColor('#fff'), textX: 0, textY: 0, maxWidth: null, textAlign: 'start', textBaseline: 'top', textDirection: 'ltr' });
|
|
1531
|
+
return __assign(__assign({}, Visual.prototype.getDefaultOptions()), { background: null, text: undefined, font: '10px sans-serif', color: parseColor('#fff'), textX: 0, textY: 0, maxWidth: null, textAlign: 'start', textBaseline: 'top', textDirection: 'ltr', textStroke: null });
|
|
1513
1532
|
};
|
|
1514
1533
|
return Text;
|
|
1515
1534
|
}(Visual));
|
|
@@ -1547,6 +1566,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
1547
1566
|
Base: Base,
|
|
1548
1567
|
Image: Image,
|
|
1549
1568
|
Text: Text,
|
|
1569
|
+
get TextStrokePosition () { return TextStrokePosition; },
|
|
1550
1570
|
Video: Video,
|
|
1551
1571
|
VisualSourceMixin: VisualSourceMixin,
|
|
1552
1572
|
Visual: Visual
|
|
@@ -2843,10 +2863,6 @@ var Movie = /** @class */ (function () {
|
|
|
2843
2863
|
// `render`). It's only valid while rendering.
|
|
2844
2864
|
this._renderingFrame = false;
|
|
2845
2865
|
this.currentTime = 0;
|
|
2846
|
-
// The last time `play` was called, -1 works well in comparisons
|
|
2847
|
-
this._lastPlayed = -1;
|
|
2848
|
-
// What `currentTime` was when `play` was called
|
|
2849
|
-
this._lastPlayedOffset = -1;
|
|
2850
2866
|
}
|
|
2851
2867
|
Movie.prototype._whenReady = function () {
|
|
2852
2868
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -2868,6 +2884,7 @@ var Movie = /** @class */ (function () {
|
|
|
2868
2884
|
*
|
|
2869
2885
|
* @param [options]
|
|
2870
2886
|
* @param [options.onStart] Called when the movie starts playing
|
|
2887
|
+
* @param [options.duration] The duration of the movie to play in seconds
|
|
2871
2888
|
*
|
|
2872
2889
|
* @return Fulfilled when the movie is done playing, never fails
|
|
2873
2890
|
*/
|
|
@@ -2885,8 +2902,8 @@ var Movie = /** @class */ (function () {
|
|
|
2885
2902
|
throw new Error('Already playing');
|
|
2886
2903
|
}
|
|
2887
2904
|
this._paused = this._ended = false;
|
|
2888
|
-
this.
|
|
2889
|
-
this.
|
|
2905
|
+
this._lastRealTime = performance.now();
|
|
2906
|
+
this._endTime = options.duration ? this.currentTime + options.duration : this.duration;
|
|
2890
2907
|
(_a = options.onStart) === null || _a === void 0 ? void 0 : _a.call(options);
|
|
2891
2908
|
// For backwards compatibility
|
|
2892
2909
|
publish(this, 'movie.play', {});
|
|
@@ -2894,15 +2911,19 @@ var Movie = /** @class */ (function () {
|
|
|
2894
2911
|
return [4 /*yield*/, new Promise(function (resolve) {
|
|
2895
2912
|
if (!_this.renderingFrame) {
|
|
2896
2913
|
// Not rendering (and not playing), so play.
|
|
2897
|
-
_this._render(
|
|
2914
|
+
_this._render(undefined, resolve);
|
|
2898
2915
|
}
|
|
2899
2916
|
// Stop rendering frame if currently doing so, because playing has higher
|
|
2900
2917
|
// priority. This will affect the next _render call.
|
|
2901
2918
|
_this._renderingFrame = false;
|
|
2902
|
-
})
|
|
2919
|
+
})
|
|
2920
|
+
// After we're done playing, clear the last timestamp
|
|
2921
|
+
];
|
|
2903
2922
|
case 2:
|
|
2904
2923
|
// Repeatedly render frames until the movie ends
|
|
2905
2924
|
_b.sent();
|
|
2925
|
+
// After we're done playing, clear the last timestamp
|
|
2926
|
+
this._lastRealTime = undefined;
|
|
2906
2927
|
return [2 /*return*/];
|
|
2907
2928
|
}
|
|
2908
2929
|
});
|
|
@@ -2976,16 +2997,17 @@ var Movie = /** @class */ (function () {
|
|
|
2976
2997
|
// Create the stream
|
|
2977
2998
|
this._currentStream = new MediaStream(tracks);
|
|
2978
2999
|
// Play the movie
|
|
2979
|
-
this._endTime = options.duration ? this.currentTime + options.duration : this.duration;
|
|
2980
3000
|
return [4 /*yield*/, this.play({
|
|
2981
3001
|
onStart: function () {
|
|
2982
3002
|
// Call the user's onStart callback
|
|
2983
3003
|
options.onStart(_this._currentStream);
|
|
2984
|
-
}
|
|
3004
|
+
},
|
|
3005
|
+
duration: options.duration
|
|
2985
3006
|
})
|
|
2986
3007
|
// Clear the stream after the movie is done playing
|
|
2987
3008
|
];
|
|
2988
3009
|
case 2:
|
|
3010
|
+
// Play the movie
|
|
2989
3011
|
_a.sent();
|
|
2990
3012
|
// Clear the stream after the movie is done playing
|
|
2991
3013
|
this._currentStream.getTracks().forEach(function (track) {
|
|
@@ -3120,11 +3142,13 @@ var Movie = /** @class */ (function () {
|
|
|
3120
3142
|
return this;
|
|
3121
3143
|
};
|
|
3122
3144
|
/**
|
|
3145
|
+
* Processes one frame of the movie and draws it to the canvas
|
|
3146
|
+
*
|
|
3123
3147
|
* @param [timestamp=performance.now()]
|
|
3124
3148
|
* @param [done=undefined] - Called when done playing or when the current
|
|
3125
3149
|
* frame is loaded
|
|
3126
3150
|
*/
|
|
3127
|
-
Movie.prototype._render = function (
|
|
3151
|
+
Movie.prototype._render = function (timestamp, done) {
|
|
3128
3152
|
var _this = this;
|
|
3129
3153
|
if (timestamp === void 0) { timestamp = performance.now(); }
|
|
3130
3154
|
if (done === void 0) { done = undefined; }
|
|
@@ -3161,8 +3185,6 @@ var Movie = /** @class */ (function () {
|
|
|
3161
3185
|
// value and publish a 'imeupdate' event.
|
|
3162
3186
|
this._currentTime = 0;
|
|
3163
3187
|
publish(this, 'movie.timeupdate', { movie: this });
|
|
3164
|
-
this._lastPlayed = performance.now();
|
|
3165
|
-
this._lastPlayedOffset = 0; // this.currentTime
|
|
3166
3188
|
this._renderingFrame = false;
|
|
3167
3189
|
// Stop playback or recording if done (except if it's playing and repeat
|
|
3168
3190
|
// is true)
|
|
@@ -3214,18 +3236,19 @@ var Movie = /** @class */ (function () {
|
|
|
3214
3236
|
}
|
|
3215
3237
|
// TODO: Is making a new arrow function every frame bad for performance?
|
|
3216
3238
|
window.requestAnimationFrame(function () {
|
|
3217
|
-
_this._render(
|
|
3239
|
+
_this._render(undefined, done);
|
|
3218
3240
|
});
|
|
3219
3241
|
};
|
|
3220
3242
|
Movie.prototype._updateCurrentTime = function (timestampMs, end) {
|
|
3221
3243
|
// If we're only frame-rendering (current frame only), it doesn't matter if
|
|
3222
3244
|
// it's paused or not.
|
|
3223
3245
|
if (!this._renderingFrame) {
|
|
3224
|
-
var
|
|
3225
|
-
var
|
|
3226
|
-
|
|
3246
|
+
var timestamp = timestampMs / 1000;
|
|
3247
|
+
var delta = timestamp - this._lastRealTime;
|
|
3248
|
+
this._lastRealTime = timestamp;
|
|
3249
|
+
if (delta > 0) {
|
|
3227
3250
|
// Update the current time (don't use setter)
|
|
3228
|
-
this._currentTime
|
|
3251
|
+
this._currentTime += delta;
|
|
3229
3252
|
// For backwards compatibility, publish a 'movie.timeupdate' event.
|
|
3230
3253
|
publish(this, 'movie.timeupdate', { movie: this });
|
|
3231
3254
|
}
|
|
@@ -3234,6 +3257,11 @@ var Movie = /** @class */ (function () {
|
|
|
3234
3257
|
}
|
|
3235
3258
|
}
|
|
3236
3259
|
};
|
|
3260
|
+
/**
|
|
3261
|
+
* Draws the movie's background to the canvas
|
|
3262
|
+
*
|
|
3263
|
+
* @param timestamp The current high-resolution timestamp in milliseconds
|
|
3264
|
+
*/
|
|
3237
3265
|
Movie.prototype._renderBackground = function (timestamp) {
|
|
3238
3266
|
this.cctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
3239
3267
|
// Evaluate background color (since it's a dynamic property)
|
|
@@ -3244,7 +3272,7 @@ var Movie = /** @class */ (function () {
|
|
|
3244
3272
|
}
|
|
3245
3273
|
};
|
|
3246
3274
|
/**
|
|
3247
|
-
*
|
|
3275
|
+
* Ticks all layers and renders them to the canvas
|
|
3248
3276
|
*/
|
|
3249
3277
|
Movie.prototype._renderLayers = function () {
|
|
3250
3278
|
for (var i = 0; i < this.layers.length; i++) {
|
|
@@ -3289,6 +3317,12 @@ var Movie = /** @class */ (function () {
|
|
|
3289
3317
|
}
|
|
3290
3318
|
}
|
|
3291
3319
|
};
|
|
3320
|
+
/**
|
|
3321
|
+
* Applies all of the movie's effects to the canvas
|
|
3322
|
+
*
|
|
3323
|
+
* Note: This method only applies the movie's effects, not the layers'
|
|
3324
|
+
* effects.
|
|
3325
|
+
*/
|
|
3292
3326
|
Movie.prototype._applyEffects = function () {
|
|
3293
3327
|
for (var i = 0; i < this.effects.length; i++) {
|
|
3294
3328
|
var effect = this.effects[i];
|
|
@@ -3315,7 +3349,7 @@ var Movie = /** @class */ (function () {
|
|
|
3315
3349
|
}
|
|
3316
3350
|
return new Promise(function (resolve) {
|
|
3317
3351
|
_this._renderingFrame = true;
|
|
3318
|
-
_this._render(
|
|
3352
|
+
_this._render(undefined, resolve);
|
|
3319
3353
|
});
|
|
3320
3354
|
};
|
|
3321
3355
|
/**
|
|
@@ -3364,7 +3398,7 @@ var Movie = /** @class */ (function () {
|
|
|
3364
3398
|
*
|
|
3365
3399
|
* Calculated from the end time of the last layer
|
|
3366
3400
|
*/
|
|
3367
|
-
// TODO:
|
|
3401
|
+
// TODO: cache
|
|
3368
3402
|
get: function () {
|
|
3369
3403
|
return this.layers.reduce(function (end, layer) { return Math.max(layer.startTime + layer.duration, end); }, 0);
|
|
3370
3404
|
},
|
|
@@ -3373,6 +3407,7 @@ var Movie = /** @class */ (function () {
|
|
|
3373
3407
|
});
|
|
3374
3408
|
/**
|
|
3375
3409
|
* Convenience method for `layers.push()`
|
|
3410
|
+
*
|
|
3376
3411
|
* @param layer
|
|
3377
3412
|
* @return The movie
|
|
3378
3413
|
*/
|
|
@@ -3382,6 +3417,7 @@ var Movie = /** @class */ (function () {
|
|
|
3382
3417
|
};
|
|
3383
3418
|
/**
|
|
3384
3419
|
* Convenience method for `effects.push()`
|
|
3420
|
+
*
|
|
3385
3421
|
* @param effect
|
|
3386
3422
|
* @return the movie
|
|
3387
3423
|
*/
|