etro 0.8.5 → 0.9.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.
@@ -10,7 +10,7 @@ jobs:
10
10
 
11
11
  strategy:
12
12
  matrix:
13
- node-version: [10.x, 12.x]
13
+ node-version: [15.x, 16.x, 17.x]
14
14
 
15
15
  steps:
16
16
  - uses: actions/checkout@v1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ 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.9.0] - 2022-07-17
9
+ ### Changed
10
+ - Methods in the `Base` effect now accept `Base` layers instead of `Visual` layers.
11
+
12
+ ### Deprecated
13
+ - `autoRefresh` option ([#130](https://github.com/etro-js/etro/issues/130)).
14
+ - `publicExcludes` ([#130](https://github.com/etro-js/etro/issues/130)).
15
+ - All `change` events ([#130](https://github.com/etro-js/etro/issues/130)).
16
+
17
+ ### Fixed
18
+ - Layers no longer trigger infinite loops when their active states change ([#127](https://github.com/etro-js/etro/issues/127)).
19
+ - Add missing `VisualSource` options to `Image` layer ([#128](https://github.com/etro-js/etro/pull/128)).
20
+ - Layers are now stopped when recording ends.
21
+ - `stop()` is no longer called on inactive layers.
22
+ - Movies no longer publish `'movie.ended'` when done recording.
23
+ - `Audio` and `Video` layers not detaching properly.
24
+ - When done playing or recording, movies only reset their time if they're in repeat mode.
25
+ - The `timeupdate` event is no longer fired when `currentTime` remains the same (due to `performance.now()` rounding).
26
+
8
27
  ## [0.8.5] - 2022-03-06
9
28
  ### Deprecated
10
29
  - `vd.effect.Base` - All visual effects now inherit from `vd.effect.Visual` instead.
@@ -208,6 +227,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
208
227
  - Gaussian blur
209
228
  - Transform
210
229
 
230
+ [0.9.0]: https://github.com/etro-js/etro/compare/v0.8.5...v0.9.0
211
231
  [0.8.5]: https://github.com/etro-js/etro/compare/v0.8.4...v0.8.5
212
232
  [0.8.4]: https://github.com/etro-js/etro/compare/v0.8.3...v0.8.4
213
233
  [0.8.3]: https://github.com/etro-js/etro/compare/v0.8.2...v0.8.3
package/CONTRIBUTING.md CHANGED
@@ -4,13 +4,13 @@
4
4
 
5
5
  Thank you for considering contributing to Etro! There are many ways you can contribute to Etro, like creating issues for features or bugs, improving the docs or wiki, or writing the actual code for the library. This page covers how to make changes to the repository files (either code or docs).
6
6
 
7
- > Etro has a [Taiga Project](https://tree.taiga.io/project/etro-js-etro/epics) for managing issues and a [GitHub Discussion page](https://github.com/etro-js/etro/discussions) for questions, ideas and casual discussion
7
+ [Join our Discord](https://discord.gg/myrBsQ8Cht)
8
8
 
9
9
  ## Setting up your local environment
10
10
 
11
11
  #### Step 0: Dependencies
12
12
 
13
- - You will need Git, Node, NPM (at least 7.x) and Chrome (for headless unit testing) installed
13
+ - You will need Git, Node, NPM (at least 7.x) and Firefox (for headless unit testing) installed
14
14
 
15
15
  #### Step 1: Fork
16
16
 
@@ -27,9 +27,9 @@ Thank you for considering contributing to Etro! There are many ways you can cont
27
27
 
28
28
  #### Step 2: Code
29
29
 
30
- - Make some changes.
31
- - If you are writing code, the linter uses [StandardJS](https://standardjs.com/rules.html) for style conventions.
32
- - When you're ready to submit a piece of code, first run
30
+ - Make some changes
31
+ - If you are writing code, the linter uses [StandardJS](https://standardjs.com/rules.html) for style conventions
32
+ - When you're ready to submit, first run
33
33
 
34
34
  ```
35
35
  npm run lint
@@ -57,19 +57,18 @@ Thank you for considering contributing to Etro! There are many ways you can cont
57
57
 
58
58
  #### Step 3: Push
59
59
 
60
- - First, rebase (don't merge) to integrate your work with the main repository
60
+ - First, rebase (please avoid merging) to integrate your work with any new changes in the main repository
61
61
 
62
62
  ```
63
63
  git fetch upstream
64
64
  git rebase upstream/master
65
65
  ```
66
66
 
67
- - Push to your fork
67
+ - Push to the fork
68
68
 
69
69
  #### Step 4: Pull request
70
70
 
71
- - Open a pull request from your the branch in your fork to the main repository
72
- - In the PR title, include **fixes ###** for bugs and **resolves ###** for feature requests
71
+ - Open a pull request from the branch in your fork to the main repository
73
72
  - If you changed any core functionality, make sure you explain your motives for those changes
74
73
 
75
74
  #### Step 5: Feedback
@@ -80,7 +79,7 @@ Thank you for considering contributing to Etro! There are many ways you can cont
80
79
 
81
80
  ### Etro Overview
82
81
 
83
- If you are new to the core elements of etro, you should probably read [the overview guide](https://etrojs.dev/docs/overview).
82
+ Check out [the overview guide](https://etrojs.dev/docs/overview) for usage information
84
83
 
85
84
  ### API Structure
86
85
 
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![](https://img.shields.io/npm/v/etro)](https://www.npmjs.com/package/etro)
4
4
  [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fetro-js%2Fetro%2Fbadge&style=flat)](https://actions-badge.atrox.dev/etro-js/etro/goto)
5
5
 
6
- > Etro was previously known as Vidar, but it had to be renamed to avoid
6
+ > Etro was previously known as Etro, but it had to be renamed to avoid
7
7
  > confusion with an existing software product.
8
8
 
9
9
  Etro is a typescript framework for programmatically editing videos. Similar to
@@ -12,6 +12,8 @@ Etro comes shipped with text, video, audio and image layers, along with a bunch
12
12
  of GLSL effects. You can also define your own layers and effects with javascript
13
13
  and GLSL.
14
14
 
15
+ [Join our Discord](https://discord.gg/myrBsQ8Cht)
16
+
15
17
  ## Features
16
18
 
17
19
  - Composite video and audio layers
@@ -1,5 +1,5 @@
1
1
  import { Movie } from '../movie';
2
- import { Visual } from '../layer/index';
2
+ import { Base as BaseLayer } from '../layer/index';
3
3
  import BaseObject from '../object';
4
4
  /**
5
5
  * @deprecated All visual effects now inherit from `Visual` instead
@@ -21,8 +21,8 @@ export declare class Base implements BaseObject {
21
21
  * Attaches this effect to `target` if not already attached.
22
22
  * @ignore
23
23
  */
24
- tryAttach(target: Movie | Visual): void;
25
- attach(movie: Movie | Visual): void;
24
+ tryAttach(target: Movie | BaseLayer): void;
25
+ attach(movie: Movie | BaseLayer): void;
26
26
  /**
27
27
  * Dettaches this effect from its target if the number of times `tryDetach`
28
28
  * has been called (including this call) equals the number of times
@@ -40,12 +40,12 @@ export declare class Base implements BaseObject {
40
40
  * (will soon be replaced with an instance getter)
41
41
  * @abstract
42
42
  */
43
- apply(target: Movie | Visual, reltime: number): void;
43
+ apply(target: Movie | BaseLayer, reltime: number): void;
44
44
  /**
45
45
  * The current time of the target
46
46
  */
47
47
  get currentTime(): number;
48
- get parent(): Movie | Visual;
48
+ get parent(): Movie | BaseLayer;
49
49
  get movie(): Movie;
50
50
  getDefaultOptions(): Record<string, unknown>;
51
51
  }
@@ -1,6 +1,17 @@
1
+ import { Movie } from '../movie';
2
+ import { Visual as VisualLayer } from '../layer/index';
1
3
  import { Base } from './base';
2
4
  /**
3
5
  * Modifies the visual contents of a layer.
4
6
  */
5
7
  export declare class Visual extends Base {
8
+ /**
9
+ * Apply this effect to a target at the given time
10
+ *
11
+ * @param target
12
+ * @param reltime - the movie's current time relative to the layer
13
+ * (will soon be replaced with an instance getter)
14
+ * @abstract
15
+ */
16
+ apply(target: Movie | VisualLayer, reltime: number): void;
6
17
  }
package/dist/etro-cjs.js CHANGED
@@ -485,6 +485,8 @@ function mapPixels(mapper, canvas, ctx, x, y, width, height, flush) {
485
485
  * <p>Must be called before any watchable properties are set, and only once in
486
486
  * the prototype chain.
487
487
  *
488
+ * @deprecated Will be removed in the future (see issue #130)
489
+ *
488
490
  * @param target - object to watch
489
491
  */
490
492
  function watchPublic(target) {
@@ -618,7 +620,10 @@ function AudioSourceMixin(superclass) {
618
620
  this.audioNode.connect(movie.actx.destination);
619
621
  };
620
622
  MixedAudioSource.prototype.detach = function () {
621
- this.audioNode.disconnect(this.movie.actx.destination);
623
+ // Cache dest before super.detach() unsets this.movie
624
+ var dest = this.movie.actx.destination;
625
+ _super.prototype.detach.call(this);
626
+ this.audioNode.disconnect(dest);
622
627
  };
623
628
  MixedAudioSource.prototype.start = function () {
624
629
  this.source.currentTime = this.currentTime + this.sourceStartTime;
@@ -836,7 +841,7 @@ var Base = /** @class */ (function () {
836
841
  // id for events (independent of instance, but easy to access when on prototype
837
842
  // chain)
838
843
  Base.prototype.type = 'layer';
839
- Base.prototype.publicExcludes = [];
844
+ Base.prototype.publicExcludes = ['active'];
840
845
  Base.prototype.propertyFilters = {};
841
846
 
842
847
  // TODO: rename to something more consistent with the naming convention of Visual and VisualSourceMixin
@@ -1282,6 +1287,18 @@ var Visual$1 = /** @class */ (function (_super) {
1282
1287
  function Visual() {
1283
1288
  return _super !== null && _super.apply(this, arguments) || this;
1284
1289
  }
1290
+ // subclasses must implement apply
1291
+ /**
1292
+ * Apply this effect to a target at the given time
1293
+ *
1294
+ * @param target
1295
+ * @param reltime - the movie's current time relative to the layer
1296
+ * (will soon be replaced with an instance getter)
1297
+ * @abstract
1298
+ */
1299
+ Visual.prototype.apply = function (target, reltime) {
1300
+ _super.prototype.apply.call(this, target, reltime);
1301
+ };
1285
1302
  return Visual;
1286
1303
  }(Base$1));
1287
1304
 
@@ -2312,16 +2329,16 @@ var Transform = /** @class */ (function (_super) {
2312
2329
  */
2313
2330
 
2314
2331
  var index$1 = /*#__PURE__*/Object.freeze({
2315
- GaussianBlur: GaussianBlur,
2316
- GaussianBlurHorizontal: GaussianBlurHorizontal,
2317
- GaussianBlurVertical: GaussianBlurVertical,
2318
2332
  Base: Base$1,
2333
+ Brightness: Brightness,
2319
2334
  Channels: Channels,
2320
2335
  ChromaKey: ChromaKey,
2321
2336
  Contrast: Contrast,
2322
2337
  EllipticalMaskOptions: EllipticalMaskOptions,
2323
2338
  EllipticalMask: EllipticalMask,
2324
- Brightness: Brightness,
2339
+ GaussianBlur: GaussianBlur,
2340
+ GaussianBlurHorizontal: GaussianBlurHorizontal,
2341
+ GaussianBlurVertical: GaussianBlurVertical,
2325
2342
  Grayscale: Grayscale,
2326
2343
  Pixelate: Pixelate,
2327
2344
  Shader: Shader,
@@ -2615,18 +2632,27 @@ var Movie = /** @class */ (function () {
2615
2632
  return;
2616
2633
  }
2617
2634
  this._updateCurrentTime(timestamp);
2618
- var recordingEnd = this.recording ? this._recordEndTime : this.duration;
2619
- var recordingEnded = this.currentTime > recordingEnd;
2620
- if (recordingEnded)
2621
- publish(this, 'movie.recordended', { movie: this });
2622
- // Bad for performance? (remember, it's calling Array.reduce)
2623
- var end = this.duration;
2624
- var ended = this.currentTime > end;
2625
- if (ended) {
2635
+ // TODO: Is calling duration every frame bad for performance? (remember,
2636
+ // it's calling Array.reduce)
2637
+ var end = this.recording ? this._recordEndTime : this.duration;
2638
+ if (this.currentTime > end) {
2639
+ if (this.recording)
2640
+ publish(this, 'movie.recordended', { movie: this });
2641
+ if (this.currentTime > this.duration)
2642
+ publish(this, 'movie.ended', { movie: this, repeat: this.repeat });
2643
+ // TODO: only reset currentTime if repeating
2644
+ if (this.repeat) {
2645
+ // Don't use setter, which publishes 'movie.seek'. Instead, update the
2646
+ // value and publish a 'movie.timeupdate' event.
2647
+ this._currentTime = 0;
2648
+ publish(this, 'movie.timeupdate', { movie: this });
2649
+ }
2626
2650
  this._lastPlayed = performance.now();
2627
2651
  this._lastPlayedOffset = 0; // this.currentTime
2628
2652
  this._renderingFrame = false;
2629
- if (!this.repeat || this.recording) {
2653
+ // Stop playback or recording if done (except if it's playing and repeat
2654
+ // is true)
2655
+ if (!(!this.recording && this.repeat)) {
2630
2656
  this._paused = true;
2631
2657
  this._ended = true;
2632
2658
  // Deactivate all layers
@@ -2635,22 +2661,15 @@ var Movie = /** @class */ (function () {
2635
2661
  var layer = this.layers[i];
2636
2662
  // A layer that has been deleted before layers.length has been updated
2637
2663
  // (see the layers proxy in the constructor).
2638
- if (!layer)
2664
+ if (!layer || !layer.active)
2639
2665
  continue;
2640
2666
  layer.stop();
2641
2667
  layer.active = false;
2642
2668
  }
2669
+ if (done)
2670
+ done();
2671
+ return;
2643
2672
  }
2644
- publish(this, 'movie.ended', { movie: this, repeat: this.repeat });
2645
- // TODO: only reset currentTime if repeating
2646
- this._currentTime = 0; // don't use setter
2647
- publish(this, 'movie.timeupdate', { movie: this });
2648
- }
2649
- // Stop playback or recording if done
2650
- if (recordingEnded || (ended && !this.repeat)) {
2651
- if (done)
2652
- done();
2653
- return;
2654
2673
  }
2655
2674
  // Do render
2656
2675
  this._renderBackground(timestamp);
@@ -2678,8 +2697,11 @@ var Movie = /** @class */ (function () {
2678
2697
  if (!this._renderingFrame) {
2679
2698
  // if ((timestamp - this._lastUpdate) >= this._updateInterval) {
2680
2699
  var sinceLastPlayed = (timestamp - this._lastPlayed) / 1000;
2681
- this._currentTime = this._lastPlayedOffset + sinceLastPlayed; // don't use setter
2682
- publish(this, 'movie.timeupdate', { movie: this });
2700
+ var currentTime = this._lastPlayedOffset + sinceLastPlayed; // don't use setter
2701
+ if (this.currentTime !== currentTime) {
2702
+ this._currentTime = currentTime;
2703
+ publish(this, 'movie.timeupdate', { movie: this });
2704
+ }
2683
2705
  // this._lastUpdate = timestamp;
2684
2706
  // }
2685
2707
  }
package/dist/etro-iife.js CHANGED
@@ -486,6 +486,8 @@ var etro = (function () {
486
486
  * <p>Must be called before any watchable properties are set, and only once in
487
487
  * the prototype chain.
488
488
  *
489
+ * @deprecated Will be removed in the future (see issue #130)
490
+ *
489
491
  * @param target - object to watch
490
492
  */
491
493
  function watchPublic(target) {
@@ -619,7 +621,10 @@ var etro = (function () {
619
621
  this.audioNode.connect(movie.actx.destination);
620
622
  };
621
623
  MixedAudioSource.prototype.detach = function () {
622
- this.audioNode.disconnect(this.movie.actx.destination);
624
+ // Cache dest before super.detach() unsets this.movie
625
+ var dest = this.movie.actx.destination;
626
+ _super.prototype.detach.call(this);
627
+ this.audioNode.disconnect(dest);
623
628
  };
624
629
  MixedAudioSource.prototype.start = function () {
625
630
  this.source.currentTime = this.currentTime + this.sourceStartTime;
@@ -837,7 +842,7 @@ var etro = (function () {
837
842
  // id for events (independent of instance, but easy to access when on prototype
838
843
  // chain)
839
844
  Base.prototype.type = 'layer';
840
- Base.prototype.publicExcludes = [];
845
+ Base.prototype.publicExcludes = ['active'];
841
846
  Base.prototype.propertyFilters = {};
842
847
 
843
848
  // TODO: rename to something more consistent with the naming convention of Visual and VisualSourceMixin
@@ -1283,6 +1288,18 @@ var etro = (function () {
1283
1288
  function Visual() {
1284
1289
  return _super !== null && _super.apply(this, arguments) || this;
1285
1290
  }
1291
+ // subclasses must implement apply
1292
+ /**
1293
+ * Apply this effect to a target at the given time
1294
+ *
1295
+ * @param target
1296
+ * @param reltime - the movie's current time relative to the layer
1297
+ * (will soon be replaced with an instance getter)
1298
+ * @abstract
1299
+ */
1300
+ Visual.prototype.apply = function (target, reltime) {
1301
+ _super.prototype.apply.call(this, target, reltime);
1302
+ };
1286
1303
  return Visual;
1287
1304
  }(Base$1));
1288
1305
 
@@ -2313,16 +2330,16 @@ var etro = (function () {
2313
2330
  */
2314
2331
 
2315
2332
  var index$1 = /*#__PURE__*/Object.freeze({
2316
- GaussianBlur: GaussianBlur,
2317
- GaussianBlurHorizontal: GaussianBlurHorizontal,
2318
- GaussianBlurVertical: GaussianBlurVertical,
2319
2333
  Base: Base$1,
2334
+ Brightness: Brightness,
2320
2335
  Channels: Channels,
2321
2336
  ChromaKey: ChromaKey,
2322
2337
  Contrast: Contrast,
2323
2338
  EllipticalMaskOptions: EllipticalMaskOptions,
2324
2339
  EllipticalMask: EllipticalMask,
2325
- Brightness: Brightness,
2340
+ GaussianBlur: GaussianBlur,
2341
+ GaussianBlurHorizontal: GaussianBlurHorizontal,
2342
+ GaussianBlurVertical: GaussianBlurVertical,
2326
2343
  Grayscale: Grayscale,
2327
2344
  Pixelate: Pixelate,
2328
2345
  Shader: Shader,
@@ -2616,18 +2633,27 @@ var etro = (function () {
2616
2633
  return;
2617
2634
  }
2618
2635
  this._updateCurrentTime(timestamp);
2619
- var recordingEnd = this.recording ? this._recordEndTime : this.duration;
2620
- var recordingEnded = this.currentTime > recordingEnd;
2621
- if (recordingEnded)
2622
- publish(this, 'movie.recordended', { movie: this });
2623
- // Bad for performance? (remember, it's calling Array.reduce)
2624
- var end = this.duration;
2625
- var ended = this.currentTime > end;
2626
- if (ended) {
2636
+ // TODO: Is calling duration every frame bad for performance? (remember,
2637
+ // it's calling Array.reduce)
2638
+ var end = this.recording ? this._recordEndTime : this.duration;
2639
+ if (this.currentTime > end) {
2640
+ if (this.recording)
2641
+ publish(this, 'movie.recordended', { movie: this });
2642
+ if (this.currentTime > this.duration)
2643
+ publish(this, 'movie.ended', { movie: this, repeat: this.repeat });
2644
+ // TODO: only reset currentTime if repeating
2645
+ if (this.repeat) {
2646
+ // Don't use setter, which publishes 'movie.seek'. Instead, update the
2647
+ // value and publish a 'movie.timeupdate' event.
2648
+ this._currentTime = 0;
2649
+ publish(this, 'movie.timeupdate', { movie: this });
2650
+ }
2627
2651
  this._lastPlayed = performance.now();
2628
2652
  this._lastPlayedOffset = 0; // this.currentTime
2629
2653
  this._renderingFrame = false;
2630
- if (!this.repeat || this.recording) {
2654
+ // Stop playback or recording if done (except if it's playing and repeat
2655
+ // is true)
2656
+ if (!(!this.recording && this.repeat)) {
2631
2657
  this._paused = true;
2632
2658
  this._ended = true;
2633
2659
  // Deactivate all layers
@@ -2636,22 +2662,15 @@ var etro = (function () {
2636
2662
  var layer = this.layers[i];
2637
2663
  // A layer that has been deleted before layers.length has been updated
2638
2664
  // (see the layers proxy in the constructor).
2639
- if (!layer)
2665
+ if (!layer || !layer.active)
2640
2666
  continue;
2641
2667
  layer.stop();
2642
2668
  layer.active = false;
2643
2669
  }
2670
+ if (done)
2671
+ done();
2672
+ return;
2644
2673
  }
2645
- publish(this, 'movie.ended', { movie: this, repeat: this.repeat });
2646
- // TODO: only reset currentTime if repeating
2647
- this._currentTime = 0; // don't use setter
2648
- publish(this, 'movie.timeupdate', { movie: this });
2649
- }
2650
- // Stop playback or recording if done
2651
- if (recordingEnded || (ended && !this.repeat)) {
2652
- if (done)
2653
- done();
2654
- return;
2655
2674
  }
2656
2675
  // Do render
2657
2676
  this._renderBackground(timestamp);
@@ -2679,8 +2698,11 @@ var etro = (function () {
2679
2698
  if (!this._renderingFrame) {
2680
2699
  // if ((timestamp - this._lastUpdate) >= this._updateInterval) {
2681
2700
  var sinceLastPlayed = (timestamp - this._lastPlayed) / 1000;
2682
- this._currentTime = this._lastPlayedOffset + sinceLastPlayed; // don't use setter
2683
- publish(this, 'movie.timeupdate', { movie: this });
2701
+ var currentTime = this._lastPlayedOffset + sinceLastPlayed; // don't use setter
2702
+ if (this.currentTime !== currentTime) {
2703
+ this._currentTime = currentTime;
2704
+ publish(this, 'movie.timeupdate', { movie: this });
2705
+ }
2684
2706
  // this._lastUpdate = timestamp;
2685
2707
  // }
2686
2708
  }
@@ -1,5 +1,5 @@
1
- import { VisualOptions } from './visual';
2
- declare type ImageOptions = VisualOptions;
1
+ import { VisualSourceOptions } from './visual-source';
2
+ declare type ImageOptions = VisualSourceOptions;
3
3
  declare const Image_base: new (...args: unknown[]) => import("./visual-source").VisualSource;
4
4
  declare class Image extends Image_base {
5
5
  }
package/dist/movie.d.ts CHANGED
@@ -32,10 +32,19 @@ export declare class MovieOptions {
32
32
  */
33
33
  export declare class Movie {
34
34
  type: string;
35
+ /**
36
+ * @deprecated Auto-refresh will be removed in the future (see issue #130).
37
+ */
35
38
  publicExcludes: string[];
36
39
  propertyFilters: Record<string, <T>(value: T) => T>;
37
40
  repeat: boolean;
38
- /** Call `refresh` when the user changes a property on the movie or any of its layers or effects */
41
+ /**
42
+ * Call `refresh` when the user changes a property on the movie or any of its
43
+ * layers or effects
44
+ *
45
+ * @deprecated Auto-refresh will be removed in the future. If you want to
46
+ * refresh the canvas, call `refresh`. See issue #130.
47
+ */
39
48
  autoRefresh: boolean;
40
49
  /** The background color of the movie as a cSS string */
41
50
  background: Dynamic<string>;
package/dist/util.d.ts CHANGED
@@ -120,6 +120,8 @@ export declare function mapPixels(mapper: (pixels: Uint8ClampedArray, i: number)
120
120
  * <p>Must be called before any watchable properties are set, and only once in
121
121
  * the prototype chain.
122
122
  *
123
+ * @deprecated Will be removed in the future (see issue #130)
124
+ *
123
125
  * @param target - object to watch
124
126
  */
125
127
  export declare function watchPublic(target: EtroObject): EtroObject;
package/karma.conf.js CHANGED
@@ -67,12 +67,22 @@ module.exports = function (config) {
67
67
  captureConsole: true
68
68
  },
69
69
 
70
+ browserConsoleLogOptions: {
71
+ level: 'log'
72
+ },
73
+
70
74
  // Continuous Integration mode
71
75
  // if true, Karma captures browsers, runs the tests and exits
72
76
  singleRun: true,
73
77
 
74
78
  // Concurrency level
75
79
  // how many browser should be started simultaneous
76
- concurrency: Infinity
80
+ concurrency: Infinity,
81
+
82
+ client: {
83
+ jasmine: {
84
+ timeoutInterval: 10000
85
+ }
86
+ }
77
87
  })
78
88
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "etro",
3
- "version": "0.8.5",
3
+ "version": "0.9.0",
4
4
  "description": "An extendable video-editing framework for the browser and Node",
5
5
  "browser": "dist/etro-cjs.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,7 +23,7 @@
23
23
  "eslint-plugin-promise": "^4.2.1",
24
24
  "eslint-plugin-standard": "^4.0.1",
25
25
  "ev": "0.0.7",
26
- "http-server": "^0.12.3",
26
+ "http-server": "^14.1.1",
27
27
  "jasmine": "^3.4.0",
28
28
  "karma": "^6.1.1",
29
29
  "karma-es6-shim": "^1.0.0",
@@ -40,7 +40,7 @@
40
40
  "rollup-plugin-node-resolve": "^5.2.0",
41
41
  "rollup-plugin-typescript2": "^0.29.0",
42
42
  "rollup-plugin-uglify-es": "^0.0.1",
43
- "shipjs": "0.23.3",
43
+ "shipjs": "^0.24.4",
44
44
  "typedoc": "^0.22.11",
45
45
  "typescript": "^4.1.3"
46
46
  },
@@ -0,0 +1,70 @@
1
+ (A) 2021-04-21 offline recording with ffmpeg.wasm
2
+ (A) 2021-04-21 document events
3
+ (A) 2021-05-17 test recording to mp4
4
+ (A) 2021-05-17 test that keyframes extend last frame for infinity!!
5
+ (B) 2020-08-03 make sure each event's purpose is to notify the public world of notable things that happen in the object.
6
+ (B) 2020-08-27 transitions?
7
+ (B) 2020-12-30 emit modify events when effects are added to layer?
8
+ (B) 2020-12-30 ensure order of `set` and `deleteProperty` are correct in array proxy with `pop`
9
+ (B) 2021-04-12 disable autoRefresh in tests and test integration with this switch some other way
10
+ (C) 2019-10-13 idea: layer inheritance
11
+ (C) 2019-10-13 ignore leading underscore in shader uniform property names
12
+ (C) 2019-10-13 fix opacity with shader effects
13
+ (C) 2020-08-08 Replace val(element, 'property', reltime) with element.property.value
14
+ (C) 2020-08-12 call URL.revokeObjectURL when recording
15
+ (C) 2020-12-30 simplify movie.change.* into movie.change
16
+ (C) 2021-03-28 make all color properties use `Color`
17
+ (C) 2021-04-02 abort recording
18
+ (C) 2021-04-21 webcam example doesn't work on FF unless autoRefresh is set to false
19
+ (C) 2021-07-13 make all layers canvases the same size as the movie
20
+ (C) 2021-07-18 movie stops recording a few frames late when duration is supplied
21
+ (D) 2019-10-15 change chroma key constructor signature
22
+ (D) 2019-11-11 treat zero as invalid for pixelate radius
23
+ (D) 2019-11-11 decide on effect.Transform using hardware acceleration
24
+ (D) 2019-11-18 Look at linearInterp objectKeys default value
25
+ (D) 2019-12-25 Rename `Stack` to something else
26
+ (D) 2020-08-02 Not every option should be set as a property on the object
27
+ (D) 2021-01-24 Remove path support in val (because users would expect it to evaluate every path part)
28
+ (D) 2021-01-26 Fix inheritance checking for media mixin
29
+ (D) 2021-01-26 Media docstring type
30
+ (D) 2021-01-26 Call super in all media mixin methods
31
+ (D) 2021-01-27 Use options.js
32
+ (D) 2021-04-02 error on invalid `record` options
33
+ (D) 2021-04-12 use val() when recalculating AudioSource#duration based on playbackRate
34
+ (D) 2021-07-09 multiple layers of proxies in watched objects
35
+
36
+ x (B) 2021-04-12 make error range for pixel tests
37
+ x (C) 2021-02-26 make sure that movie background gets val'd
38
+ x (B) 2021-04-03 expose MovieOptions
39
+ x (D) 2019-10-09 probably convert to typescript
40
+ x (C) 2019-10-22 make image and video both extend a mixin
41
+ x (C) 2021-04-03 2019-01-07 record for interval of time
42
+ x (B) 2021-04-03 2021-04-03 add missing properties to MixedVisualSource
43
+ x (D) 2021-04-03 2021-04-03 move opacity out of `border` in Visual
44
+ x (B) 2021-01-28 2020-12-18 split layers up into multiple files
45
+ x (B) 2021-01-28 2020-12-18 split effects up into multiple files
46
+ x (B) 2020-08-27 individual interpolation for keyframe sets
47
+ x (B) 2019-12-24 support node
48
+ x (B) 2020-12-31 2020-12-30 Detach effect when replaced in layer
49
+ x (D) 2020-12-18 2020-08-02 Rename `cctx` -> `vctx`
50
+ x (B) 2019-01-02 fix movie record test failing with GitHub actions
51
+ x (B) 2020-08-03 2020-08-03 don't set Image#clipWidth in init
52
+ x (B) 2020-08-03 2020-08-03 remove properties like Video#mediaWidth, effectively removing layer borders
53
+ x (B) 2020-08-03 2019-10-04 decide on the purpose of events - it's to notify the outside world of notable things that happen in the object
54
+ x (B) 2019-12-25 decide on public / private properties
55
+ x (C) 2019-12-25 2019-12-24 make getDefaultOptions private
56
+ x (D) 2019-11-18 Change Color constructor default a from 255 -> 1.0
57
+ x (A) 2019-12-06 2019-11-11 Start roadmap
58
+ x (A) 2019-11-19 2019-10-22 Util tests
59
+ x (A) 2019-10-22 Effect tests
60
+ x (D) 2019-11-18 2019-11-18 update watchPublic description, removing part that says "after all public properties are initialized in constructor"
61
+ x (B) 2019-10-13 mv examples/media examples/assets
62
+ x (A) 2019-10-22 2019-10-22 Layer tests
63
+ x (B) 2019-10-12 support media live streams
64
+ x (C) 2019-10-03 research if dist/etris-esm.js is necessary
65
+ x (A) 2019-10-04 upload to npm
66
+ x (C) 2019-10-07 lint examples
67
+ x (B) 2019-10-04 fix jsdoc comments after linting
68
+ x (A) 2019-10-07 2019-10-07 fix recording silent movie on chrome
69
+ x (A) 2019-10-05 2019-10-03 research if I can include open media in my repo
70
+ x 2019-10-04 2019-10-03 use one of eslint's official configs
@@ -1,7 +1,7 @@
1
1
  import { watchPublic } from '../util'
2
2
  import { publish, subscribe } from '../event'
3
3
  import { Movie } from '../movie'
4
- import { Visual } from '../layer/index'
4
+ import { Base as BaseLayer } from '../layer/index'
5
5
  import BaseObject from '../object'
6
6
 
7
7
  /**
@@ -14,7 +14,7 @@ export class Base implements BaseObject {
14
14
 
15
15
  enabled: boolean
16
16
 
17
- private _target: Movie | Visual
17
+ private _target: Movie | BaseLayer
18
18
  /**
19
19
  * The number of times this effect has been attached to a target minus the
20
20
  * number of times it's been detached. (Used for the target's array proxy with
@@ -45,14 +45,14 @@ export class Base implements BaseObject {
45
45
  * Attaches this effect to `target` if not already attached.
46
46
  * @ignore
47
47
  */
48
- tryAttach (target: Movie | Visual): void {
48
+ tryAttach (target: Movie | BaseLayer): void {
49
49
  if (this._occurrenceCount === 0)
50
50
  this.attach(target)
51
51
 
52
52
  this._occurrenceCount++
53
53
  }
54
54
 
55
- attach (movie: Movie | Visual): void {
55
+ attach (movie: Movie | BaseLayer): void {
56
56
  this._target = movie
57
57
  }
58
58
 
@@ -87,7 +87,7 @@ export class Base implements BaseObject {
87
87
  * (will soon be replaced with an instance getter)
88
88
  * @abstract
89
89
  */
90
- apply (target: Movie | Visual, reltime: number): void {} // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
90
+ apply (target: Movie | BaseLayer, reltime: number): void {} // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
91
91
 
92
92
  /**
93
93
  * The current time of the target
@@ -96,7 +96,7 @@ export class Base implements BaseObject {
96
96
  return this._target ? this._target.currentTime : undefined
97
97
  }
98
98
 
99
- get parent (): Movie | Visual {
99
+ get parent (): Movie | BaseLayer {
100
100
  return this._target
101
101
  }
102
102
 
@@ -1,6 +1,21 @@
1
+ import { Movie } from '../movie'
2
+ import { Visual as VisualLayer } from '../layer/index'
1
3
  import { Base } from './base'
2
4
 
3
5
  /**
4
6
  * Modifies the visual contents of a layer.
5
7
  */
6
- export class Visual extends Base {}
8
+ export class Visual extends Base {
9
+ // subclasses must implement apply
10
+ /**
11
+ * Apply this effect to a target at the given time
12
+ *
13
+ * @param target
14
+ * @param reltime - the movie's current time relative to the layer
15
+ * (will soon be replaced with an instance getter)
16
+ * @abstract
17
+ */
18
+ apply (target: Movie | VisualLayer, reltime: number): void { // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
19
+ super.apply(target, reltime)
20
+ }
21
+ }
@@ -134,7 +134,10 @@ function AudioSourceMixin<OptionsSuperclass extends BaseOptions> (superclass: Co
134
134
  }
135
135
 
136
136
  detach () {
137
- this.audioNode.disconnect(this.movie.actx.destination)
137
+ // Cache dest before super.detach() unsets this.movie
138
+ const dest = this.movie.actx.destination
139
+ super.detach()
140
+ this.audioNode.disconnect(dest)
138
141
  }
139
142
 
140
143
  start () {
package/src/layer/base.ts CHANGED
@@ -169,7 +169,7 @@ class Base implements EtroObject {
169
169
  // id for events (independent of instance, but easy to access when on prototype
170
170
  // chain)
171
171
  Base.prototype.type = 'layer'
172
- Base.prototype.publicExcludes = []
172
+ Base.prototype.publicExcludes = ['active']
173
173
  Base.prototype.propertyFilters = {}
174
174
 
175
175
  export { Base, BaseOptions }
@@ -1,7 +1,7 @@
1
- import { Visual, VisualOptions } from './visual'
2
- import { VisualSourceMixin } from './visual-source'
1
+ import { Visual } from './visual'
2
+ import { VisualSourceMixin, VisualSourceOptions } from './visual-source'
3
3
 
4
- type ImageOptions = VisualOptions
4
+ type ImageOptions = VisualSourceOptions
5
5
 
6
6
  class Image extends VisualSourceMixin(Visual) {}
7
7
 
package/src/movie.ts CHANGED
@@ -44,11 +44,20 @@ export class MovieOptions {
44
44
  // TODO: rename renderingFrame -> refreshing
45
45
  export class Movie {
46
46
  type: string
47
+ /**
48
+ * @deprecated Auto-refresh will be removed in the future (see issue #130).
49
+ */
47
50
  publicExcludes: string[]
48
51
  propertyFilters: Record<string, <T>(value: T) => T>
49
52
 
50
53
  repeat: boolean
51
- /** Call `refresh` when the user changes a property on the movie or any of its layers or effects */
54
+ /**
55
+ * Call `refresh` when the user changes a property on the movie or any of its
56
+ * layers or effects
57
+ *
58
+ * @deprecated Auto-refresh will be removed in the future. If you want to
59
+ * refresh the canvas, call `refresh`. See issue #130.
60
+ */
52
61
  autoRefresh: boolean
53
62
  /** The background color of the movie as a cSS string */
54
63
  background: Dynamic<string>
@@ -379,19 +388,32 @@ export class Movie {
379
388
  }
380
389
 
381
390
  this._updateCurrentTime(timestamp)
382
- const recordingEnd = this.recording ? this._recordEndTime : this.duration
383
- const recordingEnded = this.currentTime > recordingEnd
384
- if (recordingEnded)
385
- publish(this, 'movie.recordended', { movie: this })
386
-
387
- // Bad for performance? (remember, it's calling Array.reduce)
388
- const end = this.duration
389
- const ended = this.currentTime > end
390
- if (ended) {
391
+
392
+ // TODO: Is calling duration every frame bad for performance? (remember,
393
+ // it's calling Array.reduce)
394
+ const end = this.recording ? this._recordEndTime : this.duration
395
+ if (this.currentTime > end) {
396
+ if (this.recording)
397
+ publish(this, 'movie.recordended', { movie: this })
398
+
399
+ if (this.currentTime > this.duration)
400
+ publish(this, 'movie.ended', { movie: this, repeat: this.repeat })
401
+
402
+ // TODO: only reset currentTime if repeating
403
+ if (this.repeat) {
404
+ // Don't use setter, which publishes 'movie.seek'. Instead, update the
405
+ // value and publish a 'movie.timeupdate' event.
406
+ this._currentTime = 0
407
+ publish(this, 'movie.timeupdate', { movie: this })
408
+ }
409
+
391
410
  this._lastPlayed = performance.now()
392
411
  this._lastPlayedOffset = 0 // this.currentTime
393
412
  this._renderingFrame = false
394
- if (!this.repeat || this.recording) {
413
+
414
+ // Stop playback or recording if done (except if it's playing and repeat
415
+ // is true)
416
+ if (!(!this.recording && this.repeat)) {
395
417
  this._paused = true
396
418
  this._ended = true
397
419
  // Deactivate all layers
@@ -400,27 +422,18 @@ export class Movie {
400
422
  const layer = this.layers[i]
401
423
  // A layer that has been deleted before layers.length has been updated
402
424
  // (see the layers proxy in the constructor).
403
- if (!layer)
425
+ if (!layer || !layer.active)
404
426
  continue
405
427
 
406
428
  layer.stop()
407
429
  layer.active = false
408
430
  }
409
- }
410
-
411
- publish(this, 'movie.ended', { movie: this, repeat: this.repeat })
412
431
 
413
- // TODO: only reset currentTime if repeating
414
- this._currentTime = 0 // don't use setter
415
- publish(this, 'movie.timeupdate', { movie: this })
416
- }
432
+ if (done)
433
+ done()
417
434
 
418
- // Stop playback or recording if done
419
- if (recordingEnded || (ended && !this.repeat)) {
420
- if (done)
421
- done()
422
-
423
- return
435
+ return
436
+ }
424
437
  }
425
438
 
426
439
  // Do render
@@ -454,8 +467,11 @@ export class Movie {
454
467
  if (!this._renderingFrame) {
455
468
  // if ((timestamp - this._lastUpdate) >= this._updateInterval) {
456
469
  const sinceLastPlayed = (timestamp - this._lastPlayed) / 1000
457
- this._currentTime = this._lastPlayedOffset + sinceLastPlayed // don't use setter
458
- publish(this, 'movie.timeupdate', { movie: this })
470
+ const currentTime = this._lastPlayedOffset + sinceLastPlayed // don't use setter
471
+ if (this.currentTime !== currentTime) {
472
+ this._currentTime = currentTime
473
+ publish(this, 'movie.timeupdate', { movie: this })
474
+ }
459
475
  // this._lastUpdate = timestamp;
460
476
  // }
461
477
  }
package/src/util.ts CHANGED
@@ -415,6 +415,8 @@ export function mapPixels (
415
415
  * <p>Must be called before any watchable properties are set, and only once in
416
416
  * the prototype chain.
417
417
  *
418
+ * @deprecated Will be removed in the future (see issue #130)
419
+ *
418
420
  * @param target - object to watch
419
421
  */
420
422
  export function watchPublic (target: EtroObject): EtroObject {